publishport-opencli 1.0.3 → 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.
@@ -1,2 +1,2 @@
1
- import{getRegistry as i,fullName as R}from"./registry.js";import{pathToFileURL as m}from"node:url";import*as n from"node:crypto";import*as h from"node:fs";import*as t from"node:os";import{executePipeline as b}from"./pipeline/index.js";import{adapterLoadError as d,ArgumentError as F,CommandExecutionError as U,attachTraceReceipt as s,getErrorMessage as f}from"./errors.js";import{shouldUseBrowserSession as o}from"./capabilityRouting.js";import{getBrowserFactory as a,browserSession as r,runWithTimeout as v,DEFAULT_BROWSER_COMMAND_TIMEOUT as e}from"./runtime.js";import{withBrowserSessionLockIf as qq}from"./browser-session-lock.js";import{resolveProfile as Qq}from"./browser/profile.js";import{ensureManagedChrome as Xq}from"./browser/managed-chrome.js";import{generateFingerprintJs as Yq}from"./browser/fingerprint.js";import{CDPBridge as Zq}from"./browser/index.js";import{emitHook as C}from"./hooks.js";import{log as w}from"./logger.js";import{isElectronApp as $q}from"./electron-apps.js";import{probeCDP as Jq,resolveElectronEndpoint as Gq}from"./launcher.js";import{ObservationSession as Hq,exportObservationSession as Vq}from"./observation/index.js";import{resolveAdapterSourcePath as Lq}from"./adapter-source.js";const j=new Map,N=new Map,zq=`${t.homedir()}/.ppcli/clis/`;function Wq(q){if(q===void 0||q===null||q===""||q==="off")return"off";if(q==="on"||q==="retain-on-failure")return q;throw new F(`--trace must be one of: off, on, retain-on-failure. Received: "${String(q)}"`)}export function coerceAndValidateArgs(q,Q){const Y={...Q};for(const X of q){const Z=Y[X.name];if(X.required&&(Z===void 0||Z===null||Z===""))throw new F(`Argument "${X.name}" is required.`,X.help??`Provide a value for --${X.name}`);if(Z!==void 0&&Z!==null){if(X.type==="int"||X.type==="number"){const V=Number(Z);if(Number.isNaN(V))throw new F(`Argument "${X.name}" must be a valid number. Received: "${Z}"`);Y[X.name]=V}else if(X.type==="boolean"||X.type==="bool")if(typeof Z==="string"){const V=Z.toLowerCase();if(V==="true"||V==="1")Y[X.name]=!0;else if(V==="false"||V==="0")Y[X.name]=!1;else throw new F(`Argument "${X.name}" must be a boolean (true/false). Received: "${Z}"`)}else Y[X.name]=Boolean(Z);const J=Y[X.name];if(X.choices&&X.choices.length>0){if(!X.choices.map(String).includes(String(J)))throw new F(`Argument "${X.name}" must be one of: ${X.choices.join(", ")}. Received: "${J}"`)}}else if(X.default!==void 0)Y[X.name]=X.default}return Y}async function x(q,Q,Y,X){const Z=q;if(Z._lazy&&Z._modulePath){const J=Z._modulePath;if(J.startsWith(zq)&&j.has(J))try{const z=h.statSync(J),$=N.get(J);if($!==void 0&&z.mtimeMs!==$)j.delete(J)}catch{}if(!j.has(J)){const z=m(J).href,y=import(N.has(J)?`${z}?t=${Date.now()}`:z).then(()=>{try{N.set(J,h.statSync(J).mtimeMs)}catch{}},(K)=>{j.delete(J);throw d(`Failed to load adapter module ${J}: ${f(K)}`,"Check that the adapter file exists and has no syntax errors.")});j.set(J,y)}await j.get(J);const W=i().get(R(q));if(W?.func)return u(W,Q,Y,X);if(W?.pipeline)return b(Q,W.pipeline,{args:Y,debug:X})}if(q.func)return u(q,Q,Y,X);if(q.pipeline)return b(Q,q.pipeline,{args:Y,debug:X});throw new U(`Command ${R(q)} has no func or pipeline`,"This is likely a bug in the adapter definition. Please report this issue.")}function u(q,Q,Y,X){if(q.browser===!1)return q.func(Y,X);if(!Q)throw new U(`Command ${R(q)} requires a browser session but none was provided`);return q.func(Q,Y,X)}function Fq(q){if(q.navigateBefore===!1)return null;if(typeof q.navigateBefore==="string")return q.navigateBefore;return null}function Kq(q,Q){if(!q||!Q)return!1;try{const Y=new URL(q).hostname;return Y===Q||Y.endsWith(`.${Q}`)}catch{return!1}}function Bq(q,Q){if(!Q)return!1;try{const Y=new URL(q),X=Y.hostname===Q||Y.hostname.endsWith(`.${Q}`),Z=Y.pathname===""||Y.pathname==="/";return X&&Z&&Y.search===""&&Y.hash===""}catch{return!1}}async function Iq(q,Q,Y,X){if(Y!=="persistent"||!q.domain)return!0;if(!Bq(X,q.domain))return!0;const Z=await Q.getCurrentUrl?.().catch(()=>null);return!Kq(Z,q.domain)}export async function executeCommand(q,Q,Y=!1,X={}){let Z;try{Z=X.prepared?Q:prepareCommandArgs(q,Q)}catch($){if($ instanceof F)throw $;throw new F(f($))}const J=Nq(q,Z),V=Wq(X.trace),W={command:R(q),args:Z,startedAt:Date.now()};await C("onBeforeExecute",W);let z;try{if(o(q)){const $=$q(q.site),y=Qq(X.profile),K=!$&&y.kind==="managed"?y:null;let O,S,D;if(K){O=await Xq(K.name,K.entry);const L=K.entry.fingerprint;if(L?.enabled)S=Yq(L);const G=K.entry.proxy;if(G?.username)D={username:G.username,password:G.password??""}}else if($){const L=process.env.OPENCLI_CDP_ENDPOINT;if(L){const G=Number(new URL(L).port);if(!await Jq(G))throw new U(`CDP not reachable at ${L}`,"Check that the app is running with --remote-debugging-port and the endpoint is correct.");O=L}else O=await Gq(q.site)}const l=K?Zq:a(q.site),M=!K&&y.kind==="extension"?y.contextId:void 0,c=q,_=jq(q,X.siteSession),k=Oq(q,_),P=Uq(_,X.keepTab),E=Cq(q.defaultWindowMode??"background",X.windowMode);z=await qq(E!=="foreground",()=>r(l,async(L)=>{const G=V==="off"?null:new Hq({scope:{contextId:M??K?.name,session:k,target:L.getActivePage?.(),site:q.site,command:R(q),adapterSourcePath:Lq(c)}});if(G){G.record({stream:"action",name:"command",phase:"start",data:{args:Z}});await L.startNetworkCapture?.().catch(()=>!1)}const I=Fq(q);if(I&&await Iq(q,L,_,I)){G?.record({stream:"action",name:"pre_navigate",phase:"start",data:{url:I}});try{await L.goto(I);G?.record({stream:"action",name:"pre_navigate",phase:"end",data:{url:I}})}catch(H){G?.record({stream:"action",name:"pre_navigate",phase:"error",data:{url:I,error:H instanceof Error?H.message:String(H)}});const B=new U(`Pre-navigation to ${I} failed: ${H instanceof Error?H.message:H}`,"Check that the site is reachable and the browser extension is running.");if(G&&(V==="on"||V==="retain-on-failure")){G.record({stream:"error",message:B.message,stack:B.stack,code:B.code,hint:B.hint});await A(G,L).catch(()=>{});T(G,"failure",B,X.onTraceExport)}throw B}}try{const H=J!==null?J+p:e,B=await v(x(q,L,Z,Y),{timeout:H,label:R(q)});G?.record({stream:"action",name:"command",phase:"end"});if(G&&V==="on"){await A(G,L).catch(()=>{});T(G,"success",void 0,X.onTraceExport)}if(!P)await L.closeWindow?.().catch(()=>{});return B}catch(H){if(G){G.record({stream:"action",name:"command",phase:"error",data:{error:H instanceof Error?H.message:String(H)}});G.record({stream:"error",message:H instanceof Error?H.message:String(H),stack:H instanceof Error?H.stack:void 0});if(V==="on"||V==="retain-on-failure"){await A(G,L).catch(()=>{});T(G,"failure",H,X.onTraceExport)}}if(!P)await L.closeWindow?.().catch(()=>{});throw H}},{session:k,cdpEndpoint:O,contextId:M,windowMode:E,surface:"adapter",siteSession:_,initScript:S,proxyAuth:D}),K?.name)}else if(J!==null){const $=J+p;z=await v(x(q,null,Z,Y),{timeout:$,label:R(q),hint:`Pass a higher --timeout value (currently ${J}s)`})}else z=await x(q,null,Z,Y)}catch($){W.error=$;W.finishedAt=Date.now();await C("onAfterExecute",W);throw $}W.finishedAt=Date.now();await C("onAfterExecute",W,z);return z}async function A(q,Q){const Y=Q.getActivePage?.()??q.scope.target,[X,Z,J,V,W]=await Promise.all([Q.getCurrentUrl?.().catch(()=>null)??Promise.resolve(null),Q.snapshot().catch(()=>{return}),Q.readNetworkCapture?.().catch(()=>[])??Promise.resolve([]),Q.consoleMessages("all").catch(()=>[]),Q.screenshot({format:"png"}).catch(()=>{return})]);if(Z!==void 0||X!==void 0)q.record({stream:"state",url:X,target:Y,snapshot:Z,label:"final"});for(const z of Array.isArray(J)?J:[]){const $=z;q.record({stream:"network",url:String($.url??""),method:typeof $.method==="string"?$.method:void 0,status:typeof $.responseStatus==="number"?$.responseStatus:void 0,contentType:typeof $.responseContentType==="string"?$.responseContentType:void 0,size:typeof $.responseBodyFullSize==="number"?$.responseBodyFullSize:void 0,requestHeaders:$.requestHeaders,responseHeaders:$.responseHeaders,requestBody:$.requestBodyPreview,responseBody:$.responsePreview,ts:typeof $.timestamp==="number"?$.timestamp:void 0})}for(const z of Array.isArray(V)?V:[])if(z&&typeof z==="object"){const $=z;q.record({stream:"console",level:String($.type??$.level??"log"),text:String($.text??$.message??""),ts:typeof $.timestamp==="number"?$.timestamp:void 0})}else q.record({stream:"console",level:"log",text:String(z)});if(typeof W==="string"&&W)q.record({stream:"screenshot",format:"png",data:W,label:"final"})}function T(q,Q,Y,X){try{const Z=Vq(q,{error:Y,status:Q});if(Q==="failure"&&Y!==void 0)s(Y,Z.receipt);else process.stderr.write(`ppcli trace artifact: ${Z.dir}
1
+ import{getRegistry as i,fullName as R}from"./registry.js";import{pathToFileURL as m}from"node:url";import*as n from"node:crypto";import*as h from"node:fs";import*as t from"node:os";import{executePipeline as b}from"./pipeline/index.js";import{adapterLoadError as d,ArgumentError as F,CommandExecutionError as U,attachTraceReceipt as s,getErrorMessage as f}from"./errors.js";import{shouldUseBrowserSession as o}from"./capabilityRouting.js";import{getBrowserFactory as a,browserSession as r,runWithTimeout as v,DEFAULT_BROWSER_COMMAND_TIMEOUT as e}from"./runtime.js";import{withBrowserSessionLockIf as qq}from"./browser-session-lock.js";import{resolveProfile as Qq}from"./browser/profile.js";import{ensureManagedChrome as Xq}from"./browser/managed-chrome.js";import{generateFingerprintJs as Yq}from"./browser/fingerprint.js";import{CDPBridge as Zq}from"./browser/index.js";import{emitHook as N}from"./hooks.js";import{log as w}from"./logger.js";import{isElectronApp as $q}from"./electron-apps.js";import{probeCDP as Jq,resolveElectronEndpoint as Gq}from"./launcher.js";import{ObservationSession as Hq,exportObservationSession as Vq}from"./observation/index.js";import{resolveAdapterSourcePath as Lq}from"./adapter-source.js";const j=new Map,x=new Map,zq=`${t.homedir()}/.ppcli/clis/`;function Wq(q){if(q===void 0||q===null||q===""||q==="off")return"off";if(q==="on"||q==="retain-on-failure")return q;throw new F(`--trace must be one of: off, on, retain-on-failure. Received: "${String(q)}"`)}export function coerceAndValidateArgs(q,Q){const Y={...Q};for(const X of q){const Z=Y[X.name];if(X.required&&(Z===void 0||Z===null||Z===""))throw new F(`Argument "${X.name}" is required.`,X.help??`Provide a value for --${X.name}`);if(Z!==void 0&&Z!==null){if(X.type==="int"||X.type==="number"){const V=Number(Z);if(Number.isNaN(V))throw new F(`Argument "${X.name}" must be a valid number. Received: "${Z}"`);Y[X.name]=V}else if(X.type==="boolean"||X.type==="bool")if(typeof Z==="string"){const V=Z.toLowerCase();if(V==="true"||V==="1")Y[X.name]=!0;else if(V==="false"||V==="0")Y[X.name]=!1;else throw new F(`Argument "${X.name}" must be a boolean (true/false). Received: "${Z}"`)}else Y[X.name]=Boolean(Z);const J=Y[X.name];if(X.choices&&X.choices.length>0){if(!X.choices.map(String).includes(String(J)))throw new F(`Argument "${X.name}" must be one of: ${X.choices.join(", ")}. Received: "${J}"`)}}else if(X.default!==void 0)Y[X.name]=X.default}return Y}async function A(q,Q,Y,X){const Z=q;if(Z._lazy&&Z._modulePath){const J=Z._modulePath;if(J.startsWith(zq)&&j.has(J))try{const z=h.statSync(J),$=x.get(J);if($!==void 0&&z.mtimeMs!==$)j.delete(J)}catch{}if(!j.has(J)){const z=m(J).href,y=import(x.has(J)?`${z}?t=${Date.now()}`:z).then(()=>{try{x.set(J,h.statSync(J).mtimeMs)}catch{}},(K)=>{j.delete(J);throw d(`Failed to load adapter module ${J}: ${f(K)}`,"Check that the adapter file exists and has no syntax errors.")});j.set(J,y)}await j.get(J);const W=i().get(R(q));if(W?.func)return u(W,Q,Y,X);if(W?.pipeline)return b(Q,W.pipeline,{args:Y,debug:X})}if(q.func)return u(q,Q,Y,X);if(q.pipeline)return b(Q,q.pipeline,{args:Y,debug:X});throw new U(`Command ${R(q)} has no func or pipeline`,"This is likely a bug in the adapter definition. Please report this issue.")}function u(q,Q,Y,X){if(q.browser===!1)return q.func(Y,X);if(!Q)throw new U(`Command ${R(q)} requires a browser session but none was provided`);return q.func(Q,Y,X)}function Fq(q){if(q.navigateBefore===!1)return null;if(typeof q.navigateBefore==="string")return q.navigateBefore;return null}function Kq(q,Q){if(!q||!Q)return!1;try{const Y=new URL(q).hostname;return Y===Q||Y.endsWith(`.${Q}`)}catch{return!1}}function Bq(q,Q){if(!Q)return!1;try{const Y=new URL(q),X=Y.hostname===Q||Y.hostname.endsWith(`.${Q}`),Z=Y.pathname===""||Y.pathname==="/";return X&&Z&&Y.search===""&&Y.hash===""}catch{return!1}}async function Iq(q,Q,Y,X){if(Y!=="persistent"||!q.domain)return!0;if(!Bq(X,q.domain))return!0;const Z=await Q.getCurrentUrl?.().catch(()=>null);return!Kq(Z,q.domain)}export async function executeCommand(q,Q,Y=!1,X={}){let Z;try{Z=X.prepared?Q:prepareCommandArgs(q,Q)}catch($){if($ instanceof F)throw $;throw new F(f($))}const J=Nq(q,Z),V=Wq(X.trace),W={command:R(q),args:Z,startedAt:Date.now()};await N("onBeforeExecute",W);let z;try{if(o(q)){const $=$q(q.site),y=Qq(X.profile),K=!$&&y.kind==="managed"?y:null;let O;const C=Cq(q.defaultWindowMode??"background",X.windowMode);let D,M;if(K){O=await Xq(K.name,K.entry,{windowMode:C});const L=K.entry.fingerprint;if(L?.enabled)D=Yq(L);const G=K.entry.proxy;if(G?.username)M={username:G.username,password:G.password??""}}else if($){const L=process.env.OPENCLI_CDP_ENDPOINT;if(L){const G=Number(new URL(L).port);if(!await Jq(G))throw new U(`CDP not reachable at ${L}`,"Check that the app is running with --remote-debugging-port and the endpoint is correct.");O=L}else O=await Gq(q.site)}const l=K?Zq:a(q.site),k=!K&&y.kind==="extension"?y.contextId:void 0,c=q,_=jq(q,X.siteSession),P=Oq(q,_),E=Uq(_,X.keepTab);z=await qq(C!=="foreground",()=>r(l,async(L)=>{const G=V==="off"?null:new Hq({scope:{contextId:k??K?.name,session:P,target:L.getActivePage?.(),site:q.site,command:R(q),adapterSourcePath:Lq(c)}});if(G){G.record({stream:"action",name:"command",phase:"start",data:{args:Z}});await L.startNetworkCapture?.().catch(()=>!1)}const I=Fq(q);if(I&&await Iq(q,L,_,I)){G?.record({stream:"action",name:"pre_navigate",phase:"start",data:{url:I}});try{await L.goto(I);G?.record({stream:"action",name:"pre_navigate",phase:"end",data:{url:I}})}catch(H){G?.record({stream:"action",name:"pre_navigate",phase:"error",data:{url:I,error:H instanceof Error?H.message:String(H)}});const B=new U(`Pre-navigation to ${I} failed: ${H instanceof Error?H.message:H}`,"Check that the site is reachable and the browser extension is running.");if(G&&(V==="on"||V==="retain-on-failure")){G.record({stream:"error",message:B.message,stack:B.stack,code:B.code,hint:B.hint});await T(G,L).catch(()=>{});S(G,"failure",B,X.onTraceExport)}throw B}}try{const H=J!==null?J+p:e,B=await v(A(q,L,Z,Y),{timeout:H,label:R(q)});G?.record({stream:"action",name:"command",phase:"end"});if(G&&V==="on"){await T(G,L).catch(()=>{});S(G,"success",void 0,X.onTraceExport)}if(!E)await L.closeWindow?.().catch(()=>{});return B}catch(H){if(G){G.record({stream:"action",name:"command",phase:"error",data:{error:H instanceof Error?H.message:String(H)}});G.record({stream:"error",message:H instanceof Error?H.message:String(H),stack:H instanceof Error?H.stack:void 0});if(V==="on"||V==="retain-on-failure"){await T(G,L).catch(()=>{});S(G,"failure",H,X.onTraceExport)}}if(!E)await L.closeWindow?.().catch(()=>{});throw H}},{session:P,cdpEndpoint:O,contextId:k,windowMode:C,surface:"adapter",siteSession:_,initScript:D,proxyAuth:M}),K?.name)}else if(J!==null){const $=J+p;z=await v(A(q,null,Z,Y),{timeout:$,label:R(q),hint:`Pass a higher --timeout value (currently ${J}s)`})}else z=await A(q,null,Z,Y)}catch($){W.error=$;W.finishedAt=Date.now();await N("onAfterExecute",W);throw $}W.finishedAt=Date.now();await N("onAfterExecute",W,z);return z}async function T(q,Q){const Y=Q.getActivePage?.()??q.scope.target,[X,Z,J,V,W]=await Promise.all([Q.getCurrentUrl?.().catch(()=>null)??Promise.resolve(null),Q.snapshot().catch(()=>{return}),Q.readNetworkCapture?.().catch(()=>[])??Promise.resolve([]),Q.consoleMessages("all").catch(()=>[]),Q.screenshot({format:"png"}).catch(()=>{return})]);if(Z!==void 0||X!==void 0)q.record({stream:"state",url:X,target:Y,snapshot:Z,label:"final"});for(const z of Array.isArray(J)?J:[]){const $=z;q.record({stream:"network",url:String($.url??""),method:typeof $.method==="string"?$.method:void 0,status:typeof $.responseStatus==="number"?$.responseStatus:void 0,contentType:typeof $.responseContentType==="string"?$.responseContentType:void 0,size:typeof $.responseBodyFullSize==="number"?$.responseBodyFullSize:void 0,requestHeaders:$.requestHeaders,responseHeaders:$.responseHeaders,requestBody:$.requestBodyPreview,responseBody:$.responsePreview,ts:typeof $.timestamp==="number"?$.timestamp:void 0})}for(const z of Array.isArray(V)?V:[])if(z&&typeof z==="object"){const $=z;q.record({stream:"console",level:String($.type??$.level??"log"),text:String($.text??$.message??""),ts:typeof $.timestamp==="number"?$.timestamp:void 0})}else q.record({stream:"console",level:"log",text:String(z)});if(typeof W==="string"&&W)q.record({stream:"screenshot",format:"png",data:W,label:"final"})}function S(q,Q,Y,X){try{const Z=Vq(q,{error:Y,status:Q});if(Q==="failure"&&Y!==void 0)s(Y,Z.receipt);else process.stderr.write(`ppcli trace artifact: ${Z.dir}
2
2
  `);try{X?.(Z)}catch(J){w.warn(`[trace] Trace export callback failed: ${J instanceof Error?J.message:String(J)}`)}return Z}catch(Z){w.warn(`[trace] Failed to export trace artifact: ${Z instanceof Error?Z.message:String(Z)}`);return}}export function prepareCommandArgs(q,Q){const Y=coerceAndValidateArgs(q.args,Q);q.validateArgs?.(Y);return Y}const p=30;function Rq(q){if(q===void 0||q===null||q==="")return null;if(q==="ephemeral"||q==="persistent")return q;throw new F(`--site-session must be one of: ephemeral, persistent. Received: "${String(q)}"`)}function yq(){const q=process.env.PUBLISHPORT_SITE_SESSION;if(q==="persistent"||q==="ephemeral")return q;return null}function jq(q,Q){return Rq(Q)??yq()??q.siteSession??"persistent"}function Oq(q,Q){if(Q==="persistent")return`site:${q.site}`;return`site:${q.site}:${n.randomUUID()}`}function _q(q,Q){if(Q===void 0||Q==="")return null;if(Q==="true")return!0;if(Q==="false")return!1;throw new F(`${q} must be one of: true, false. Received: "${String(Q)}"`)}function Uq(q,Q){if(q==="persistent")return!0;return _q("--keep-tab",Q)??!1}function g(q,Q){if(Q===void 0||Q==="")return null;if(Q==="foreground"||Q==="background")return Q;throw new F(`${q} must be one of: foreground, background. Received: "${String(Q)}"`)}function Cq(q="background",Q){return g("--window",Q)??g("OPENCLI_WINDOW",process.env.OPENCLI_WINDOW)??q}function Nq(q,Q){if(!q.args.some((Z)=>Z.name==="timeout"))return null;const Y=Q.timeout;if(Y===void 0||Y===null||Y==="")throw new F(`Argument "timeout" must be a positive integer. Received: "${String(Y)}"`);const X=Number(Y);if(!Number.isInteger(X)||X<=0)throw new F(`Argument "timeout" must be a positive integer. Received: "${String(Y)}"`);return X}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "publishport-opencli",
3
- "version": "1.0.3",
3
+ "version": "1.0.6",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": false