publishport-opencli 1.0.18 → 1.0.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.
@@ -1,3 +1,3 @@
1
- import{mkdir as p,readFile as n,rename as jG,writeFile as c}from"node:fs/promises";import{homedir as i}from"node:os";import{dirname as m,join as a}from"node:path";import{pathToFileURL as zG}from"node:url";import{InvalidArgumentError as b,Option as qG}from"commander";import{AuthRequiredError as d,CliError as s,getErrorMessage as R}from"../errors.js";import{executeCommand as M}from"../execution.js";import{ensureBrowserBridgeReady as UG}from"../browser/daemon-lifecycle.js";import{resolveProfile as VG}from"../browser/profile.js";import{isElectronApp as E}from"../electron-apps.js";import{fullName as OG,getRegistry as o}from"../registry.js";import{render as r}from"../output.js";const v=1,S=86400000;function I(G,J,X){if(G===void 0||G===null||G==="")return X;const $=Number(G);if(!Number.isInteger($)||$<=0)throw new b(`${J} must be a positive integer. Received: "${String(G)}"`);return $}function t(G,J,X){if(G===void 0||G===null||G==="")return X;const $=Number(G);if(!Number.isInteger($)||$<0)throw new b(`${J} must be a non-negative integer. Received: "${String(G)}"`);return $}function e(G){if(!G||!G.trim())return null;const J=G.split(",").map((X)=>X.trim()).filter(Boolean);return J.length>0?new Set(J):null}function NG(){return a(i(),".ppcli","auth-refresh.json")}const f=1,BG=600000;function HG(){return a(i(),".ppcli","auth-state.json")}async function MG(G){try{const J=JSON.parse(await n(G,"utf8"));if(J&&J.version===f&&J.profiles&&typeof J.profiles==="object")return{version:f,profiles:J.profiles}}catch{}return{version:f,profiles:{}}}async function IG(G,J){await p(m(G),{recursive:!0});const X=`${G}.${process.pid}.tmp`;await c(X,`${JSON.stringify(J,null,2)}
2
- `,"utf8");await jG(X,G)}function CG(G,J){return{site:G,status:J.status,logged_in:J.logged_in,identity:J.identity,checked:"cached",error:J.error,checked_at:new Date(J.at).toISOString(),verified:J.verifiedAt!==void 0}}function xG(G,J,X,$){return{status:G.status,logged_in:G.logged_in,identity:G.identity,checked:G.checked,error:G.error,at:J,...X!==void 0?{verifiedAt:X}:{},...$!==void 0?{verifyAttemptAt:$}:{}}}function _G(){return{version:v,sites:{}}}async function FG(G){try{const J=JSON.parse(await n(G,"utf8"));if(J&&J.version===v&&J.sites&&typeof J.sites==="object")return{version:v,sites:J.sites}}catch(J){if(J.code!=="ENOENT")throw J}return _G()}async function PG(G,J){await p(m(G),{recursive:!0});await c(G,`${JSON.stringify(J,null,2)}
3
- `,"utf8")}function GG(G){if(!G?.last_touched_at)return null;const J=Date.parse(G.last_touched_at);return Number.isFinite(J)?J:null}function TG(G,J){const X=GG(G);return X!==null&&J.getTime()-X<S}function V(G){const J=GG(G);return J===null?"":new Date(J+S).toISOString()}function JG(){const G=new Set;return[...o().values()].filter((J)=>{if(G.has(J))return!1;G.add(J);return J.name==="whoami"&&J.access==="read"}).sort((J,X)=>J.site.localeCompare(X.site))}async function g(G){const J=G;if(!J._lazy||!J._modulePath)return G;await import(zG(J._modulePath).href);return o().get(OG(G))??G}function C(G,J){const X=G.args.some(($)=>$.name==="timeout");return{...G,args:X?G.args:[...G.args,{name:"timeout",type:"int",default:J,help:"Per-site auth command timeout in seconds"}]}}function kG(G,J){if(G.browser!==!0||typeof G.authStatus?.quickCheck!=="function")return null;return C({...G,func:G.authStatus.quickCheck,navigateBefore:!1,siteSession:"ephemeral",defaultWindowMode:"background"},J)}function XG(G){if(typeof G==="boolean")return G;if(G&&typeof G==="object"&&!Array.isArray(G)){const J=G.logged_in;if(typeof J==="boolean")return J}return null}function bG(G){const J=Array.isArray(G)?G[0]:G;if(J&&typeof J==="object"&&!Array.isArray(J)){const X=J.logged_in;if(typeof X==="boolean")return X}return null}function RG(G){const J=Array.isArray(G)?G[0]:G;if(J&&typeof J==="object"&&!Array.isArray(J)){const X=J.error;if(typeof X==="string"&&X.trim())return X.trim()}return""}function ZG(G){if(G===void 0||G===null)return"";if(typeof G==="string"||typeof G==="number"||typeof G==="boolean")return String(G);return""}function $G(G){const J=Array.isArray(G)?G[0]:G;if(!J||typeof J!=="object"||Array.isArray(J))return"";const X=J,$=/(?:email|phone|real.?name|first.?name|last.?name|cookie|token|session|secret|password|csrf|jwt|bearer|wt2)/i;for(const Z of["username","handle","user_id","id","name","nickname","user_type","url"]){if($.test(Z))continue;const Y=ZG(X[Z]);if(Y)return Y}for(const[Z,Y]of Object.entries(X)){if(Z==="site"||Z==="logged_in"||$.test(Z))continue;const Q=ZG(Y);if(Q)return Q}return""}function A(G){return G instanceof d||typeof G==="object"&&G!==null&&G.code==="AUTH_REQUIRED"}function DG(G,J,X){if(A(X))return{site:G,status:"not_logged_in",logged_in:!1,identity:"",checked:J,error:""};const $=X instanceof s?X.code:"",Z=R(X);return{site:G,status:"error",logged_in:"",identity:"",checked:J,error:$?`${$}: ${Z}`:Z}}function EG(G,J){if(G.browser!==!0)return null;let X=G.authStatus?.refresh;if(typeof X!=="function"){const $=G.authStatus?.quickCheck;if(typeof $!=="function"||!G.domain)return null;const Z=G.domain.startsWith("http://")||G.domain.startsWith("https://")?G.domain:`https://${G.domain}`;X=async(Y,Q,K)=>{await Y.goto(Z);await Y.wait(1);if(XG(await $(Y,Q,K))!==!0)throw new d(G.domain??G.site,`Auth refresh quickCheck failed for ${G.site}`);return{status:"touched"}}}return C({...G,func:X,navigateBefore:!1,siteSession:"persistent",defaultWindowMode:"background"},J)}function vG(G){if(G&&typeof G==="object"&&!Array.isArray(G)){const J=G;if(J.status==="refreshed"||J.refreshed===!0)return"refreshed"}return"touched"}function SG(G,J,X){if(A(X))return{site:G,status:"not_logged_in",last_touched_at:J?.last_touched_at??"",next_refresh_at:V(J),error:""};const $=X instanceof s?X.code:"",Z=R(X);return{site:G,status:"error",last_touched_at:J?.last_touched_at??"",next_refresh_at:V(J),error:$?`${$}: ${Z}`:Z}}function YG(){return process.env.PUBLISHPORT_SITE_SESSION==="ephemeral"?{siteSession:"ephemeral",keepTab:"false"}:{siteSession:"persistent",keepTab:"true"}}async function fG(G,J){try{const X=await g(G);if(X.browser!==!0){const Q=C(X,J.timeoutSeconds),K=await M(Q,{timeout:J.timeoutSeconds},!1,{...J.profile?{profile:J.profile}:{}});return{site:G.site,status:"logged_in",logged_in:!0,identity:$G(K),checked:"quick",error:""}}if(X.authStatus?.quickCheckRequiresNavigation===!0)return{site:G.site,status:"unknown",logged_in:"",identity:"",checked:"navigation_required",error:""};const $=kG(X,J.timeoutSeconds);if(!$)return{site:G.site,status:"unknown",logged_in:"",identity:"",checked:"skipped",error:"quickCheck not implemented; use --full to run whoami"};const Z=await M($,{timeout:J.timeoutSeconds},!1,{...YG(),windowMode:"background",...J.profile?{profile:J.profile}:{}}),Y=XG(Z);if(Y===!0)return{site:G.site,status:"logged_in",logged_in:!0,identity:"",checked:"quick",error:""};if(Y===!1)return{site:G.site,status:"not_logged_in",logged_in:!1,identity:"",checked:"quick",error:""};return{site:G.site,status:"unknown",logged_in:"",identity:"",checked:"quick",error:"quickCheck returned no boolean logged_in signal"}}catch(X){return DG(G.site,"quick",X)}}async function QG(G,J){try{const X=await g(G),$=C(X,J.timeoutSeconds),Z=await M($,{timeout:J.timeoutSeconds},!1,{...YG(),windowMode:"background",...J.profile?{profile:J.profile}:{}});if(bG(Z)===!1)return{site:G.site,status:"not_logged_in",logged_in:!1,identity:"",checked:"full",error:RG(Z)};return{site:G.site,status:"logged_in",logged_in:!0,identity:$G(Z),checked:"full",error:""}}catch(X){return DG(G.site,"full",X)}}function gG(G,J){return J?`${G}@${J}`:G}async function AG(G,J){const X=gG(G.site,J.profile),$=J.state.sites[X];if(!J.force&&TG($,J.now))return{site:G.site,status:"skipped",last_touched_at:$?.last_touched_at??"",next_refresh_at:V($),error:""};const Z=J.now.toISOString();try{const Y=await g(G),Q=EG(Y,J.timeoutSeconds);if(!Q){J.state.sites[X]={...$,last_attempt_at:Z,last_status:"unsupported"};return{site:G.site,status:"unsupported",last_touched_at:$?.last_touched_at??"",next_refresh_at:V($),error:"refresh probe is not available for this site"}}const K=await M(Q,{timeout:J.timeoutSeconds},!1,{siteSession:"persistent",keepTab:"true",windowMode:"background",...J.profile?{profile:J.profile}:{}}),q=vG(K);J.state.sites[X]={...$,last_attempt_at:Z,last_touched_at:Z,last_status:q};return{site:G.site,status:q,last_touched_at:Z,next_refresh_at:new Date(J.now.getTime()+S).toISOString(),error:""}}catch(Y){const Q=A(Y)?"not_logged_in":"error";J.state.sites[X]={...$,last_attempt_at:Z,last_status:Q};return SG(G.site,$,Y)}}async function WG(G,J){const X=VG(J);if(X.kind!=="extension")return null;if(!G.some((Z)=>Z.browser===!0&&!E(Z.site)))return null;try{await UG({timeoutSeconds:15,contextId:X.contextId,verbose:!1});return null}catch(Z){return R(Z)}}function yG(G,J,X){return{site:G,status:"error",logged_in:"",identity:"",checked:J,error:`BROWSER_CONNECT: ${X}`}}async function y(G,J,X){const $=Array(G.length);let Z=0;const Y=Array.from({length:Math.min(J,G.length)},async()=>{while(Z<G.length){const Q=Z++;$[Q]=await X(G[Q])}});await Promise.all(Y);return $}export async function collectAuthStatus(G){const J=e(G.sites),X=G.full?"full":"quick",$=I(G.concurrency,"--concurrency",X==="full"?3:8),Z=I(G.timeout,"--timeout",X==="full"?20:8),Y=String(G.only??"all");if(!["all","logged-in","not-logged-in","unknown","error"].includes(Y))throw new b("--only must be one of: all, logged-in, not-logged-in, unknown, error");const Q=t(G.maxAge,"--max-age",0)*1000,K=t(G.verify,"--verify",0),q=G.statePath??HG(),z=(G.now??new Date).getTime(),j=G.profile??"",x=JG().filter((D)=>!J||J.has(D.site)),_=await MG(q),O=_.profiles[j]??{},F=new Map,N=[];for(const D of x){const L=O[D.site];if(Q>0&&L&&z-L.at<Q)F.set(D.site,CG(D.site,L));else N.push(D)}const P=N.length>0?await WG(N,G.profile):null,KG=await y(N,$,(D)=>P&&D.browser===!0&&!E(D.site)?Promise.resolve(yG(D.site,X,P)):X==="full"?QG(D,{timeoutSeconds:Z,profile:G.profile}):fG(D,{timeoutSeconds:Z,profile:G.profile})),LG=new Map(x.map((D)=>[D.site,D])),U=new Map(F),T=new Map,k=new Map;for(const D of KG){U.set(D.site,D);if(X==="full"){k.set(D.site,z);if(D.status!=="error")T.set(D.site,z)}}const h=(D)=>{const L=T.get(D.site);if(L!==void 0)return L;const W=O[D.site];return W&&W.status===D.status?W.verifiedAt:void 0},B=(D)=>k.get(D.site)??O[D.site]?.verifyAttemptAt;if(K>0&&!P){const D=[...U.values()].filter((W)=>W.status==="logged_in"&&W.checked!=="full").filter((W)=>{const H=B(W);return H===void 0||z-H>=BG}).sort((W,H)=>(B(W)??0)-(B(H)??0)).slice(0,K).map((W)=>LG.get(W.site)).filter((W)=>W!==void 0),L=await y(D,Math.min($,3),(W)=>QG(W,{timeoutSeconds:Math.max(Z,20),profile:G.profile}));for(const W of L){k.set(W.site,z);if(W.status==="error")continue;U.set(W.site,W);T.set(W.site,z)}}const w={...O};for(const D of U.keys()){if(F.has(D))continue;const L=U.get(D);if(!L)continue;w[D]=xG(L,z,h(L),B(L))}_.profiles[j]=w;await IG(q,_).catch(()=>{});const l=x.map((D)=>U.get(D.site)).filter((D)=>D!==void 0).map((D)=>({...D,checked_at:D.checked_at??new Date(z).toISOString(),verified:D.verified??h(D)!==void 0})),u=Y.replace(/-/g,"_");return u==="all"?l:l.filter((D)=>D.status===u)}export async function collectAuthRefresh(G){const J=e(G.sites),X=I(G.concurrency,"--concurrency",3),$=I(G.timeout,"--timeout",20),Z=G.statePath??NG(),Y=G.now??new Date,Q=await FG(Z),K=JG().filter((j)=>!J||J.has(j.site)),q=await WG(K,G.profile),z=await y(K,X,(j)=>q&&j.browser===!0&&!E(j.site)?Promise.resolve({site:j.site,status:"error",last_touched_at:Q.sites[j.site]?.last_touched_at??"",next_refresh_at:V(Q.sites[j.site]),error:`BROWSER_CONNECT: ${q}`}):AG(j,{timeoutSeconds:$,profile:G.profile,now:Y,state:Q,force:G.all===!0}));await PG(Z,Q);return z}export function registerAuthCommands(G){const J=G.command("auth").description("Inspect website login status"),X=J.command("status").description("Show login status for sites with auth adapters").option("--site <sites>","Comma-separated site names to check, e.g. github,chatgpt").option("--full","Run full per-site whoami probes instead of quick no-navigation checks",!1).option("--concurrency <n>","Maximum sites to check at once").option("--timeout <seconds>","Per-site timeout in seconds").option("--max-age <seconds>","Reuse a local cached verdict younger than this instead of probing").option("--verify <n>",'Re-confirm at most N cookie-only "logged in" verdicts with a real whoami').addOption(new qG("--only <status>","Filter rows by status").choices(["all","logged-in","not-logged-in","unknown","error"]).default("all")).option("-f, --format <fmt>","Output format: table, plain, json, yaml, md, csv","table").action(async(Z)=>{const Y=typeof X.optsWithGlobals==="function"?X.optsWithGlobals():{},Q=await collectAuthStatus({sites:Z.site,full:Z.full===!0,concurrency:Z.concurrency,timeout:Z.timeout,only:Z.only,maxAge:Z.maxAge,verify:Z.verify,profile:typeof Y.profile==="string"&&Y.profile.trim()?Y.profile.trim():void 0}),K=typeof Z.format==="string"?Z.format:"table";r(Q,{fmt:K,fmtExplicit:X.getOptionValueSource("format")==="cli",columns:["site","status","identity","checked","error"],title:"ppcli/auth status",source:Z.full?"full whoami probe":"quick auth check"})}),$=J.command("refresh").description("Touch logged-in site sessions to keep browser auth fresh").option("--site <sites>","Comma-separated site names to refresh, e.g. github,claude").option("--all","Ignore the 24h refresh throttle and force every selected site",!1).option("--concurrency <n>","Maximum sites to refresh at once").option("--timeout <seconds>","Per-site timeout in seconds").option("-f, --format <fmt>","Output format: table, plain, json, yaml, md, csv","table").action(async(Z)=>{const Y=typeof $.optsWithGlobals==="function"?$.optsWithGlobals():{},Q=await collectAuthRefresh({sites:Z.site,all:Z.all===!0,concurrency:Z.concurrency,timeout:Z.timeout,profile:typeof Y.profile==="string"&&Y.profile.trim()?Y.profile.trim():void 0}),K=typeof Z.format==="string"?Z.format:"table";r(Q,{fmt:K,fmtExplicit:$.getOptionValueSource("format")==="cli",columns:["site","status","last_touched_at","next_refresh_at","error"],title:"ppcli/auth refresh",source:Z.all?"forced persistent touch":"persistent touch with 24h throttle"})});return J}
1
+ import{mkdir as w,readFile as u,rename as WG,writeFile as l}from"node:fs/promises";import{homedir as p}from"node:os";import{dirname as n,join as c}from"node:path";import{pathToFileURL as KG}from"node:url";import{InvalidArgumentError as O,Option as LG}from"commander";import{AuthRequiredError as i,CliError as m,getErrorMessage as x}from"../errors.js";import{executeCommand as N}from"../execution.js";import{ensureBrowserBridgeReady as jG}from"../browser/daemon-lifecycle.js";import{resolveProfile as zG}from"../browser/profile.js";import{isElectronApp as P}from"../electron-apps.js";import{fullName as qG,getRegistry as a}from"../registry.js";import{render as d}from"../output.js";const T=1,k=86400000;function M(G,J,Y){if(G===void 0||G===null||G==="")return Y;const $=Number(G);if(!Number.isInteger($)||$<=0)throw new O(`${J} must be a positive integer. Received: "${String(G)}"`);return $}function s(G,J,Y){if(G===void 0||G===null||G==="")return Y;const $=Number(G);if(!Number.isInteger($)||$<0)throw new O(`${J} must be a non-negative integer. Received: "${String(G)}"`);return $}function o(G){if(!G||!G.trim())return null;const J=G.split(",").map((Y)=>Y.trim()).filter(Boolean);return J.length>0?new Set(J):null}function UG(){return c(p(),".ppcli","auth-refresh.json")}const b=1,dG=600000;function VG(){return c(p(),".ppcli","auth-state.json")}async function BG(G){try{const J=JSON.parse(await u(G,"utf8"));if(J&&J.version===b&&J.profiles&&typeof J.profiles==="object")return{version:b,profiles:J.profiles}}catch{}return{version:b,profiles:{}}}async function OG(G,J){await w(n(G),{recursive:!0});const Y=`${G}.${process.pid}.tmp`;await l(Y,`${JSON.stringify(J,null,2)}
2
+ `,"utf8");await WG(Y,G)}function NG(G,J){return{site:G,status:J.status,logged_in:J.logged_in,identity:J.identity,checked:"cached",error:J.error,checked_at:new Date(J.at).toISOString(),verified:J.verifiedAt!==void 0}}function MG(G,J,Y,$){return{status:G.status,logged_in:G.logged_in,identity:G.identity,checked:G.checked,error:G.error,at:J,...Y!==void 0?{verifiedAt:Y}:{},...$!==void 0?{verifyAttemptAt:$}:{}}}function HG(){return{version:T,sites:{}}}async function IG(G){try{const J=JSON.parse(await u(G,"utf8"));if(J&&J.version===T&&J.sites&&typeof J.sites==="object")return{version:T,sites:J.sites}}catch(J){if(J.code!=="ENOENT")throw J}return HG()}async function CG(G,J){await w(n(G),{recursive:!0});await l(G,`${JSON.stringify(J,null,2)}
3
+ `,"utf8")}function r(G){if(!G?.last_touched_at)return null;const J=Date.parse(G.last_touched_at);return Number.isFinite(J)?J:null}function _G(G,J){const Y=r(G);return Y!==null&&J.getTime()-Y<k}function q(G){const J=r(G);return J===null?"":new Date(J+k).toISOString()}function t(){const G=new Set;return[...a().values()].filter((J)=>{if(G.has(J))return!1;G.add(J);return J.name==="whoami"&&J.access==="read"}).sort((J,Y)=>J.site.localeCompare(Y.site))}async function R(G){const J=G;if(!J._lazy||!J._modulePath)return G;await import(KG(J._modulePath).href);return a().get(qG(G))??G}function H(G,J){const Y=G.args.some(($)=>$.name==="timeout");return{...G,args:Y?G.args:[...G.args,{name:"timeout",type:"int",default:J,help:"Per-site auth command timeout in seconds"}]}}function FG(G,J){if(G.browser!==!0||typeof G.authStatus?.quickCheck!=="function")return null;return H({...G,func:G.authStatus.quickCheck,navigateBefore:!1,siteSession:"ephemeral",defaultWindowMode:"background"},J)}function e(G){if(typeof G==="boolean")return G;if(G&&typeof G==="object"&&!Array.isArray(G)){const J=G.logged_in;if(typeof J==="boolean")return J}return null}function xG(G){const J=Array.isArray(G)?G[0]:G;if(J&&typeof J==="object"&&!Array.isArray(J)){const Y=J.logged_in;if(typeof Y==="boolean")return Y}return null}function PG(G){const J=Array.isArray(G)?G[0]:G;if(J&&typeof J==="object"&&!Array.isArray(J)){const Y=J.error;if(typeof Y==="string"&&Y.trim())return Y.trim()}return""}function GG(G){if(G===void 0||G===null)return"";if(typeof G==="string"||typeof G==="number"||typeof G==="boolean")return String(G);return""}function JG(G){const J=Array.isArray(G)?G[0]:G;if(!J||typeof J!=="object"||Array.isArray(J))return"";const Y=J,$=/(?:email|phone|real.?name|first.?name|last.?name|cookie|token|session|secret|password|csrf|jwt|bearer|wt2)/i;for(const Z of["username","handle","user_id","id","name","nickname","user_type","url"]){if($.test(Z))continue;const X=GG(Y[Z]);if(X)return X}for(const[Z,X]of Object.entries(Y)){if(Z==="site"||Z==="logged_in"||$.test(Z))continue;const Q=GG(X);if(Q)return Q}return""}function E(G){return G instanceof i||typeof G==="object"&&G!==null&&G.code==="AUTH_REQUIRED"}function YG(G,J,Y){if(E(Y))return{site:G,status:"not_logged_in",logged_in:!1,identity:"",checked:J,error:""};const $=Y instanceof m?Y.code:"",Z=x(Y);return{site:G,status:"error",logged_in:"",identity:"",checked:J,error:$?`${$}: ${Z}`:Z}}function TG(G,J){if(G.browser!==!0)return null;let Y=G.authStatus?.refresh;if(typeof Y!=="function"){const $=G.authStatus?.quickCheck;if(typeof $!=="function"||!G.domain)return null;const Z=G.domain.startsWith("http://")||G.domain.startsWith("https://")?G.domain:`https://${G.domain}`;Y=async(X,Q,W)=>{await X.goto(Z);await X.wait(1);if(e(await $(X,Q,W))!==!0)throw new i(G.domain??G.site,`Auth refresh quickCheck failed for ${G.site}`);return{status:"touched"}}}return H({...G,func:Y,navigateBefore:!1,siteSession:"persistent",defaultWindowMode:"background"},J)}function kG(G){if(G&&typeof G==="object"&&!Array.isArray(G)){const J=G;if(J.status==="refreshed"||J.refreshed===!0)return"refreshed"}return"touched"}function bG(G,J,Y){if(E(Y))return{site:G,status:"not_logged_in",last_touched_at:J?.last_touched_at??"",next_refresh_at:q(J),error:""};const $=Y instanceof m?Y.code:"",Z=x(Y);return{site:G,status:"error",last_touched_at:J?.last_touched_at??"",next_refresh_at:q(J),error:$?`${$}: ${Z}`:Z}}function ZG(){return process.env.PUBLISHPORT_SITE_SESSION==="ephemeral"?{siteSession:"ephemeral",keepTab:"false"}:{siteSession:"persistent",keepTab:"true"}}async function RG(G,J){try{const Y=await R(G);if(Y.browser!==!0){const Q=H(Y,J.timeoutSeconds),W=await N(Q,{timeout:J.timeoutSeconds},!1,{...J.profile?{profile:J.profile}:{}});return{site:G.site,status:"logged_in",logged_in:!0,identity:JG(W),checked:"quick",error:""}}if(Y.authStatus?.quickCheckRequiresNavigation===!0)return{site:G.site,status:"unknown",logged_in:"",identity:"",checked:"navigation_required",error:""};const $=FG(Y,J.timeoutSeconds);if(!$)return{site:G.site,status:"unknown",logged_in:"",identity:"",checked:"skipped",error:"quickCheck not implemented; use --full to run whoami"};const Z=await N($,{timeout:J.timeoutSeconds},!1,{...ZG(),windowMode:"background",...J.profile?{profile:J.profile}:{}}),X=e(Z);if(X===!0)return{site:G.site,status:"logged_in",logged_in:!0,identity:"",checked:"quick",error:""};if(X===!1)return{site:G.site,status:"not_logged_in",logged_in:!1,identity:"",checked:"quick",error:""};return{site:G.site,status:"unknown",logged_in:"",identity:"",checked:"quick",error:"quickCheck returned no boolean logged_in signal"}}catch(Y){return YG(G.site,"quick",Y)}}async function EG(G,J){try{const Y=await R(G),$=H(Y,J.timeoutSeconds),Z=await N($,{timeout:J.timeoutSeconds},!1,{...ZG(),windowMode:"background",...J.profile?{profile:J.profile}:{}});if(xG(Z)===!1)return{site:G.site,status:"not_logged_in",logged_in:!1,identity:"",checked:"full",error:PG(Z)};return{site:G.site,status:"logged_in",logged_in:!0,identity:JG(Z),checked:"full",error:""}}catch(Y){return YG(G.site,"full",Y)}}function SG(G,J){return J?`${G}@${J}`:G}async function vG(G,J){const Y=SG(G.site,J.profile),$=J.state.sites[Y];if(!J.force&&_G($,J.now))return{site:G.site,status:"skipped",last_touched_at:$?.last_touched_at??"",next_refresh_at:q($),error:""};const Z=J.now.toISOString();try{const X=await R(G),Q=TG(X,J.timeoutSeconds);if(!Q){J.state.sites[Y]={...$,last_attempt_at:Z,last_status:"unsupported"};return{site:G.site,status:"unsupported",last_touched_at:$?.last_touched_at??"",next_refresh_at:q($),error:"refresh probe is not available for this site"}}const W=await N(Q,{timeout:J.timeoutSeconds},!1,{siteSession:"persistent",keepTab:"true",windowMode:"background",...J.profile?{profile:J.profile}:{}}),j=kG(W);J.state.sites[Y]={...$,last_attempt_at:Z,last_touched_at:Z,last_status:j};return{site:G.site,status:j,last_touched_at:Z,next_refresh_at:new Date(J.now.getTime()+k).toISOString(),error:""}}catch(X){const Q=E(X)?"not_logged_in":"error";J.state.sites[Y]={...$,last_attempt_at:Z,last_status:Q};return bG(G.site,$,X)}}async function $G(G,J){const Y=zG(J);if(Y.kind!=="extension")return null;if(!G.some((Z)=>Z.browser===!0&&!P(Z.site)))return null;try{await jG({timeoutSeconds:15,contextId:Y.contextId,verbose:!1});return null}catch(Z){return x(Z)}}function fG(G,J,Y){return{site:G,status:"error",logged_in:"",identity:"",checked:J,error:`BROWSER_CONNECT: ${Y}`}}async function DG(G,J,Y){const $=Array(G.length);let Z=0;const X=Array.from({length:Math.min(J,G.length)},async()=>{while(Z<G.length){const Q=Z++;$[Q]=await Y(G[Q])}});await Promise.all(X);return $}export async function collectAuthStatus(G){const J=o(G.sites),Y=G.full?"full":"quick",$=M(G.concurrency,"--concurrency",Y==="full"?3:8),Z=M(G.timeout,"--timeout",Y==="full"?20:8),X=String(G.only??"all");if(!["all","logged-in","not-logged-in","unknown","error"].includes(X))throw new O("--only must be one of: all, logged-in, not-logged-in, unknown, error");if(Y==="full"&&!J)throw new O("--full 会对每个站点真实打开页面,不允许全量使用。"+"要实证某个站点请点名:auth status --site <site> --full;"+"想看全部站点就用默认的 quick(纯 cookie,不开任何页面)。");const Q=s(G.maxAge,"--max-age",0)*1000,W=s(G.verify,"--verify",0),j=G.statePath??VG(),z=(G.now??new Date).getTime(),K=G.profile??"",I=t().filter((D)=>!J||J.has(D.site)),C=await BG(j),U=C.profiles[K]??{},_=new Map,V=[];for(const D of I){const L=U[D.site];if(Q>0&&L&&z-L.at<Q)_.set(D.site,NG(D.site,L));else V.push(D)}const S=V.length>0?await $G(V,G.profile):null,XG=await DG(V,$,(D)=>S&&D.browser===!0&&!P(D.site)?Promise.resolve(fG(D.site,Y,S)):Y==="full"?EG(D,{timeoutSeconds:Z,profile:G.profile}):RG(D,{timeoutSeconds:Z,profile:G.profile})),gG=new Map(I.map((D)=>[D.site,D])),B=new Map(_),v=new Map,f=new Map;for(const D of XG){B.set(D.site,D);if(Y==="full"){f.set(D.site,z);if(D.status!=="error")v.set(D.site,z)}}const g=(D)=>{const L=v.get(D.site);if(L!==void 0)return L;const F=U[D.site];return F&&F.status===D.status?F.verifiedAt:void 0},QG=(D)=>f.get(D.site)??U[D.site]?.verifyAttemptAt,A={...U};for(const D of B.keys()){if(_.has(D))continue;const L=B.get(D);if(!L)continue;A[D]=MG(L,z,g(L),QG(L))}C.profiles[K]=A;await OG(j,C).catch(()=>{});const y=I.map((D)=>B.get(D.site)).filter((D)=>D!==void 0).map((D)=>({...D,checked_at:D.checked_at??new Date(z).toISOString(),verified:D.verified??g(D)!==void 0})),h=X.replace(/-/g,"_");return h==="all"?y:y.filter((D)=>D.status===h)}export async function collectAuthRefresh(G){const J=o(G.sites),Y=M(G.concurrency,"--concurrency",3),$=M(G.timeout,"--timeout",20),Z=G.statePath??UG(),X=G.now??new Date,Q=await IG(Z),W=t().filter((K)=>!J||J.has(K.site)),j=await $G(W,G.profile),z=await DG(W,Y,(K)=>j&&K.browser===!0&&!P(K.site)?Promise.resolve({site:K.site,status:"error",last_touched_at:Q.sites[K.site]?.last_touched_at??"",next_refresh_at:q(Q.sites[K.site]),error:`BROWSER_CONNECT: ${j}`}):vG(K,{timeoutSeconds:$,profile:G.profile,now:X,state:Q,force:G.all===!0}));await CG(Z,Q);return z}export function registerAuthCommands(G){const J=G.command("auth").description("Inspect website login status"),Y=J.command("status").description("Show login status for sites with auth adapters").option("--site <sites>","Comma-separated site names to check, e.g. github,chatgpt").option("--full","Run full per-site whoami probes instead of quick no-navigation checks",!1).option("--concurrency <n>","Maximum sites to check at once").option("--timeout <seconds>","Per-site timeout in seconds").option("--max-age <seconds>","Reuse a local cached verdict younger than this instead of probing").option("--verify <n>","Deprecated and ignored: batch auth status never navigates. Use --site <s> --full to confirm one site with a real whoami").addOption(new LG("--only <status>","Filter rows by status").choices(["all","logged-in","not-logged-in","unknown","error"]).default("all")).option("-f, --format <fmt>","Output format: table, plain, json, yaml, md, csv","table").action(async(Z)=>{const X=typeof Y.optsWithGlobals==="function"?Y.optsWithGlobals():{},Q=await collectAuthStatus({sites:Z.site,full:Z.full===!0,concurrency:Z.concurrency,timeout:Z.timeout,only:Z.only,maxAge:Z.maxAge,verify:Z.verify,profile:typeof X.profile==="string"&&X.profile.trim()?X.profile.trim():void 0}),W=typeof Z.format==="string"?Z.format:"table";d(Q,{fmt:W,fmtExplicit:Y.getOptionValueSource("format")==="cli",columns:["site","status","identity","checked","error"],title:"ppcli/auth status",source:Z.full?"full whoami probe":"quick auth check"})}),$=J.command("refresh").description("Touch logged-in site sessions to keep browser auth fresh").option("--site <sites>","Comma-separated site names to refresh, e.g. github,claude").option("--all","Ignore the 24h refresh throttle and force every selected site",!1).option("--concurrency <n>","Maximum sites to refresh at once").option("--timeout <seconds>","Per-site timeout in seconds").option("-f, --format <fmt>","Output format: table, plain, json, yaml, md, csv","table").action(async(Z)=>{const X=typeof $.optsWithGlobals==="function"?$.optsWithGlobals():{},Q=await collectAuthRefresh({sites:Z.site,all:Z.all===!0,concurrency:Z.concurrency,timeout:Z.timeout,profile:typeof X.profile==="string"&&X.profile.trim()?X.profile.trim():void 0}),W=typeof Z.format==="string"?Z.format:"table";d(Q,{fmt:W,fmtExplicit:$.getOptionValueSource("format")==="cli",columns:["site","status","last_touched_at","next_refresh_at","error"],title:"ppcli/auth refresh",source:Z.all?"forced persistent touch":"persistent touch with 24h throttle"})});return J}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "publishport-opencli",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": false