@elixpo/lixblogs-cli 1.5.3 → 1.5.5

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/dist/lixblogs.mjs CHANGED
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env node
2
- import{parseArgs as Mr}from"node:util";import{spawn as Fr}from"node:child_process";var V={environment:"production",profile:"default",accountsBaseUrl:"https://accounts.elixpo.com",apiBaseUrl:"https://blogs.elixpo.com"},Re={development:{clientId:"lixblogs-cli-dev",audience:"localhost"},staging:{clientId:"lixblogs-cli-staging",audience:"staging.blogs.elixpo.com"},production:{clientId:"lixblogs-cli-prod",audience:"blogs.elixpo.com"},test:{clientId:"lixblogs-cli-dev",audience:"localhost"}};function I({flags:t={},env:e=process.env}={}){let r=t.env??e.LIXBLOGS_ENV??V.environment,o=t.profile??e.LIXBLOGS_PROFILE??V.profile,i=Re[r]||Re.production,n=t.authProvider??e.LIXBLOGS_AUTH_PROVIDER??(r==="production"?"elixpo":"mock"),a=t.accountsUrl??e.LIXBLOGS_ACCOUNTS_URL??V.accountsBaseUrl,s=t.apiUrl??e.LIXBLOGS_API_URL??V.apiBaseUrl,l=t.clientId??e.LIXBLOGS_CLIENT_ID??i.clientId,u=t.audience??e.LIXBLOGS_AUDIENCE??i.audience;return{environment:r,profile:o,profileExplicit:t.profile!==void 0||e.LIXBLOGS_PROFILE!==void 0,authProvider:n,accountsBaseUrl:a,apiBaseUrl:s,clientId:l,audience:u}}var P=class{get providerId(){throw new Error("AuthProvider.providerId must be implemented by subclass")}async requestDeviceCode(e){throw new Error("AuthProvider.requestDeviceCode must be implemented by subclass")}async pollDeviceCode(e){throw new Error("AuthProvider.pollDeviceCode must be implemented by subclass")}async refresh(e){throw new Error("AuthProvider.refresh must be implemented by subclass")}async revoke(e){throw new Error("AuthProvider.revoke must be implemented by subclass")}};var Pe={APPROVE_IMMEDIATELY:"mock-approve-",PENDING_THEN_APPROVE:"mock-pending-then-approve-",DENY:"mock-deny-",EXPIRE:"mock-expire-",SLOW_DOWN_THEN_APPROVE:"mock-slow-down-then-approve-"},ir=5,Te=0;function nr(t){return Te+=1,`${t}${Te}`}var J=class extends P{constructor(){super(),this._devicesCodes=new Map,this._revoked=new Set,this._refreshWillFail=new Set}get providerId(){return"mock"}async requestDeviceCode({scopes:e,scenario:r="APPROVE_IMMEDIATELY"}){let o=Pe[r]??Pe.APPROVE_IMMEDIATELY,i=nr(o),n=i.slice(-6).toUpperCase();return this._devicesCodes.set(i,{scenario:r,pollCount:0,createdAt:Date.now(),scopes:[...e]}),{deviceCode:i,userCode:n,verificationUri:"https://mock.lixblogs.local/device",verificationUriComplete:`https://mock.lixblogs.local/device?user_code=${encodeURIComponent(n)}`,expiresInSeconds:r==="EXPIRE"?1:600,pollIntervalSeconds:1}}async pollDeviceCode({deviceCode:e}){let r=this._devicesCodes.get(e);return r?r.scenario==="EXPIRE"?{status:"expired"}:r.scenario==="DENY"?{status:"denied"}:r.scenario==="PENDING_THEN_APPROVE"&&(r.pollCount+=1,r.pollCount<2)?{status:"pending"}:r.scenario==="SLOW_DOWN_THEN_APPROVE"&&(r.pollCount+=1,r.pollCount<2)?{status:"slow_down",pollIntervalIncreaseSeconds:ir}:{status:"approved",token:{accessToken:`mock-access-${e}`,refreshToken:`mock-refresh-${e}`,expiresInSeconds:3600,scopes:r.scopes}}:{status:"denied"}}async refresh({refreshToken:e,scopes:r=[]}){if(this._revoked.has(e))throw new Error("refresh token has been revoked");if(this._refreshWillFail.has(e))throw new Error("mock refresh failure (test-injected)");return{accessToken:`mock-access-refreshed-${e}`,refreshToken:e,expiresInSeconds:3600,scopes:r}}async revoke({token:e}){this._revoked.add(e)}_simulateRefreshFailureFor(e){this._refreshWillFail.add(e)}};var Oe="urn:ietf:params:oauth:grant-type:device_code",sr=1,ar=15e3,lr={access_denied:"Login was denied.",authorization_pending:"Login is awaiting approval.",expired_token:"The device authorization expired. Start login again.",invalid_client:"The LixBlogs CLI client is not registered for this environment.",invalid_grant:"This session is no longer valid. Log in again.",invalid_request:"Accounts rejected the authentication request.",invalid_scope:"The requested LixBlogs permissions are not available for this client.",server_error:"Accounts could not complete authentication. Try again later.",slow_down:"Accounts requested slower polling.",temporarily_unavailable:"Accounts is temporarily unavailable. Try again later."},x=class extends Error{constructor(e,{status:r=0,requiresLogin:o=!1}={}){super(lr[e]||"Authentication failed."),this.name="AuthProviderError",this.code=e||"authentication_failed",this.status=r,this.requiresLogin=o}},k=class extends Error{constructor(e){super(e),this.name="CompatibilityError",this.code="incompatible_accounts_contract"}};function de(t){return String(t||"0.0.0").split(".").slice(0,3).map(e=>Number.parseInt(e,10)||0)}function cr(t,e){let r=de(t),o=de(e);for(let i=0;i<3;i+=1)if(r[i]!==o[i])return r[i]>o[i];return!0}function ur(t){let e=new URL(t);if(e.pathname=e.pathname.replace(/\/$/,""),e.search="",e.hash="",e.protocol!=="https:"&&e.hostname!=="localhost"&&e.hostname!=="127.0.0.1")throw new k("Accounts must use HTTPS outside local development.");return e.toString().replace(/\/$/,"")}async function F(t){try{return await t.json()}catch{throw new x("server_error",{status:t.status})}}function H(t,e){let r=typeof t?.error=="string"?t.error:"server_error";return new x(r,{status:e.status,requiresLogin:r==="invalid_grant"||r==="access_denied"||r==="expired_token"})}function je(t,e){if(!e.ok)throw H(t,e);if(typeof t?.access_token!="string"||typeof t?.refresh_token!="string"||!Number.isFinite(Number(t?.expires_in)))throw new x("server_error",{status:e.status});return{accessToken:t.access_token,refreshToken:t.refresh_token,expiresInSeconds:Number(t.expires_in),scopes:typeof t.scope=="string"?t.scope.split(/\s+/).filter(Boolean):[]}}var W=class extends P{constructor({accountsBaseUrl:e="https://accounts.elixpo.com",clientId:r="lixblogs-cli-prod",audience:o="blogs.elixpo.com",cliVersion:i="1.2.0",fetchImpl:n=globalThis.fetch,timeoutMs:a=ar}={}){if(super(),typeof n!="function")throw new TypeError("A fetch implementation is required.");this.accountsBaseUrl=ur(e),this.clientId=r,this.audience=o,this.cliVersion=i,this.fetchImpl=n,this.timeoutMs=a,this._metadata=null,this._discoveryPromise=null}get providerId(){return"elixpo"}async _fetch(e,r={}){let o=new AbortController,i=setTimeout(()=>o.abort(),this.timeoutMs);try{return await this.fetchImpl(e,{...r,signal:r.signal||o.signal,headers:{accept:"application/json",...r.headers}})}catch{throw new x("temporarily_unavailable")}finally{clearTimeout(i)}}async discover({scopes:e=[]}={}){if(!this._metadata){this._discoveryPromise||(this._discoveryPromise=this._loadDiscovery());try{this._metadata=await this._discoveryPromise}finally{this._discoveryPromise=null}}if(e.filter(o=>!this._metadata.scopes_supported.includes(o)).length)throw new x("invalid_scope");return this._metadata}async _loadDiscovery(){let e=await this._fetch(`${this.accountsBaseUrl}/.well-known/oauth-authorization-server`),r=await F(e);if(!e.ok)throw new x("temporarily_unavailable",{status:e.status});if(de(r.elixpo_contract_version)[0]!==sr)throw new k("Accounts uses an unsupported device-flow contract version.");if(!cr(this.cliVersion,r.elixpo_min_compatible_cli_version))throw new k(`This CLI is too old for Accounts. Upgrade to version ${r.elixpo_min_compatible_cli_version} or newer.`);if(!Array.isArray(r.grant_types_supported)||!r.grant_types_supported.includes(Oe))throw new k("Accounts does not advertise OAuth device authorization.");let i=["device_authorization_endpoint","token_endpoint","revocation_endpoint"];for(let n of i){if(typeof r[n]!="string")throw new k(`Accounts discovery is missing ${n}.`);let a=new URL(r[n]),s=new URL(this.accountsBaseUrl);if(a.origin!==s.origin)throw new k(`Accounts discovery returned an untrusted ${n}.`)}return{...r,scopes_supported:Array.isArray(r.scopes_supported)?r.scopes_supported:[]}}async requestDeviceCode({scopes:e}){let r=await this.discover({scopes:e}),o=await this._fetch(r.device_authorization_endpoint,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_id:this.clientId,scope:e.join(" "),audience:this.audience})}),i=await F(o);if(!o.ok)throw H(i,o);if(typeof i.device_code!="string"||typeof i.user_code!="string"||typeof i.verification_uri!="string")throw new x("server_error",{status:o.status});return{deviceCode:i.device_code,userCode:i.user_code,verificationUri:i.verification_uri,verificationUriComplete:i.verification_uri_complete||i.verification_uri,expiresInSeconds:Number(i.expires_in)||600,pollIntervalSeconds:Number(i.interval)||5}}async pollDeviceCode({deviceCode:e}){let r=await this.discover(),o=new URLSearchParams({grant_type:Oe,device_code:e,client_id:this.clientId}),i=await this._fetch(r.token_endpoint,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:o}),n=await F(i);if(i.ok)return{status:"approved",token:je(n,i)};if(n?.error==="authorization_pending")return{status:"pending"};if(n?.error==="slow_down"){let a=r.elixpo_device_flow_polling||{};return{status:"slow_down",pollIntervalIncreaseSeconds:Math.max(5,Number(a.slow_down_interval_seconds||10)-Number(a.interval_seconds||5))}}if(n?.error==="access_denied")return{status:"denied"};if(n?.error==="expired_token")return{status:"expired"};throw H(n,i)}async refresh({refreshToken:e,scopes:r}){let o=await this.discover({scopes:r||[]}),i=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:this.clientId});r?.length&&i.set("scope",r.join(" "));let n=await this._fetch(o.token_endpoint,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:i}),a=await F(n);return je(a,n)}async revoke({token:e}){let r=await this.discover(),o=await this._fetch(r.revocation_endpoint,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({token:e,client_id:this.clientId})});if(!o.ok){let i=await F(o);throw H(i,o)}}};var Ce="elixpo",pe=class extends Error{constructor(e){super(e),this.name="ProductionAuthGateError"}};function Le({providerId:t,environment:e}){if(e==="production"&&t!==Ce)throw new pe(`Provider "${t}" is not approved for production. Only "${Ce}" may be used in production.`)}function T(t){if(t.authProvider==="mock"&&t.environment==="production")throw new Error("The mock auth provider cannot run in production.");if(t.authProvider!=="mock"&&t.authProvider!=="elixpo")throw new Error(`Unknown auth provider "${t.authProvider}".`);let e=t.authProvider==="mock"?new J:new W({accountsBaseUrl:t.accountsBaseUrl,clientId:t.clientId,audience:t.audience,cliVersion:t.cliVersion||"1.2.0",fetchImpl:t.fetchImpl});return Le({providerId:e.providerId,environment:t.environment}),e}var S=class extends Error{constructor(e){super(e),this.name="CredentialStoreUnavailableError"}},A=class{async get(e){throw new Error("CredentialStore.get must be implemented by subclass")}async set(e,r){throw new Error("CredentialStore.set must be implemented by subclass")}async delete(e){throw new Error("CredentialStore.delete must be implemented by subclass")}async listProfiles(){throw new Error("CredentialStore.listProfiles must be implemented by subclass")}},X=class extends A{constructor(){super(),this._store=new Map}async get(e){return this._store.get(e)??null}async set(e,r){this._store.set(e,r)}async delete(e){this._store.delete(e)}async listProfiles(){return[...this._store.keys()]}},K=class extends A{constructor(e){super(),this._realStore=e}async get(e){try{return await this._realStore.get(e)}catch(r){throw new S(`OS keychain is unavailable: ${r.message}. Re-run with an explicit fallback flag if you want to opt in to a less secure storage method.`)}}async set(e,r){try{await this._realStore.set(e,r)}catch(o){throw new S(`OS keychain is unavailable: ${o.message}. Re-run with an explicit fallback flag if you want to opt in to a less secure storage method.`)}}async delete(e){try{await this._realStore.delete(e)}catch(r){throw new S(`OS keychain is unavailable: ${r.message}`)}}async listProfiles(){try{return await this._realStore.listProfiles()}catch(e){throw new S(`OS keychain is unavailable: ${e.message}`)}}};import{Entry as dr}from"@napi-rs/keyring";var pr="lixblogs-cli",fr="__lixblogs_availability_probe__";function Y(t){return new dr(pr,t)}async function De(){let t=Y(fr);try{return t.setPassword("probe"),t.deletePassword(),{available:!0}}catch(e){return{available:!1,error:String(e.message??e).split(`
3
- `)[0].trim()}}}var fe=class extends A{async get(e){let r=Y(e),o;try{o=r.getPassword()}catch(i){if(Ue(i))return null;throw i}return o==null?null:JSON.parse(o)}async set(e,r){Y(e).setPassword(JSON.stringify(r))}async delete(e){let r=Y(e);try{r.deletePassword()}catch(o){if(Ue(o))return;throw o}}async listProfiles(){throw new Error("KeychainCredentialStore.listProfiles is not supported directly \u2014 use ProfileRegistry to track known profile IDs, then look up each one via get().")}};function Ue(t){let e=String(t?.message??"");return/no such|not found|nosuchkeyring|nosuchitem/i.test(e)}var Z=class extends A{constructor(e){super(),this._keychain=new fe,this._registry=e}async get(e){return this._keychain.get(e)}async set(e,r){await this._keychain.set(e,r),await this._registry.add(e)}async delete(e){await this._keychain.delete(e),await this._registry.remove(e)}async listProfiles(){return this._registry.list()}};import{promises as Q}from"node:fs";import Ne from"node:path";import mr from"node:os";function gr(){return Ne.join(mr.homedir(),".config","lixblogs","profiles.json")}var w=class{constructor(e=gr()){this._path=e}async list(){return(await this._read()).profiles}async getActive(){let e=await this._read();return e.activeProfile&&e.profiles.includes(e.activeProfile)?e.activeProfile:e.profiles[0]||null}async setActive(e){E(e);let r=await this._read();if(!r.profiles.includes(e))throw new Error(`Profile "${e}" does not exist. Log in with it first.`);await this._write(r.profiles,e)}async _read(){try{let e=await Q.readFile(this._path,"utf8"),r=JSON.parse(e);return{profiles:Array.isArray(r.profiles)?r.profiles.filter(i=>typeof i=="string"):[],activeProfile:typeof r.activeProfile=="string"?r.activeProfile:null}}catch(e){if(e.code==="ENOENT")return{profiles:[],activeProfile:null};throw e}}async add(e){E(e);let r=await this._read(),o=new Set(r.profiles);o.add(e),await this._write([...o],r.activeProfile||e)}async remove(e){let r=await this._read(),o=r.profiles.filter(n=>n!==e),i=r.activeProfile===e?o[0]||null:r.activeProfile;await this._write(o,i)}async _write(e,r=null){await Q.mkdir(Ne.dirname(this._path),{recursive:!0});let o=`${this._path}.${process.pid}.tmp`;await Q.writeFile(o,JSON.stringify({activeProfile:r,profiles:e},null,2),{encoding:"utf8",mode:384}),await Q.rename(o,this._path)}};function E(t){if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(t||""))throw new Error("Profile names must be 1-64 characters using letters, numbers, dot, dash, or underscore.");return t}async function Be({allowInsecureFallback:t=!1,profileRegistry:e}={}){let r=await De();if(!r.available){if(!t)throw new S(`OS keychain is unavailable: ${r.error}. Re-run with an explicit fallback flag if you want to opt in to a less secure storage method.`);return process.stderr.write(`warning: OS keychain unavailable (${r.error}); using in-memory fallback because --allow-insecure-fallback was passed. Credentials will NOT persist between CLI runs.
4
- `),new X}let o=e||new w,i=new Z(o);return new K(i)}var ge="[REDACTED]",hr=/token|refresh|secret|password|authorization/i,yr=/^(mock-(access|refresh)-|Bearer\s+)\S+/i;function Me(t){return typeof t=="string"&&yr.test(t)?ge:t}function me(t){if(Array.isArray(t))return t.map(e=>me(e));if(t&&typeof t=="object"){let e={};for(let[r,o]of Object.entries(t))hr.test(r)?e[r]=ge:typeof o=="object"&&o!==null?e[r]=me(o):e[r]=Me(o);return e}return Me(t)}function ee(t,e){return JSON.stringify(me(t),null,e)}function O(t){return typeof t!="string"?t:t.replace(/(mock-(access|refresh)-\S+|Bearer\s+\S+)/gi,ge)}async function Fe({provider:t,credentialStore:e,profileId:r,scopes:o,openBrowser:i,resolveProfileId:n,sleep:a=l=>new Promise(u=>setTimeout(u,l)),onStatus:s=()=>{}}){let l;try{l=await t.requestDeviceCode({scopes:o})}catch(c){return{ok:!1,reason:O(c.message)}}s({type:"verification_pending",verificationUri:l.verificationUri,verificationUriComplete:l.verificationUriComplete,userCode:l.userCode,expiresInSeconds:l.expiresInSeconds}),i&&await i(l.verificationUriComplete||l.verificationUri);let u=l.pollIntervalSeconds*1e3,d=Date.now()+l.expiresInSeconds*1e3;for(;Date.now()<d;){await a(u);let c;try{c=await t.pollDeviceCode({deviceCode:l.deviceCode})}catch(p){return{ok:!1,reason:O(p.message)}}if(c.status==="approved"){let p=r;if(n)try{p=await n({accessToken:c.token.accessToken,requestedProfileId:r})}catch(v){return{ok:!1,reason:O(v.message)}}return await e.set(p,{accessToken:c.token.accessToken,refreshToken:c.token.refreshToken,expiresAt:Date.now()+c.token.expiresInSeconds*1e3,scopes:c.token.scopes}),s({type:"approved"}),{ok:!0,profileId:p}}if(c.status==="denied")return s({type:"denied"}),{ok:!1,reason:"Login was denied."};if(c.status==="expired")return s({type:"expired"}),{ok:!1,reason:"Device code expired before login was approved."};if(c.status==="slow_down"){u+=c.pollIntervalIncreaseSeconds*1e3,s({type:"slow_down",newIntervalMs:u});continue}s({type:"pending"})}return{ok:!1,reason:"Device code expired before login was approved."}}async function ze({credentialStore:t,profileId:e}){let r=e?[e]:await t.listProfiles(),o=[];for(let i of r){let n=await t.get(i);if(!n){o.push({profileId:i,loggedIn:!1});continue}o.push({profileId:i,loggedIn:!0,expired:Date.now()>=n.expiresAt,scopes:n.scopes})}return o}async function Ge({credentialStore:t,profileId:e}){return await t.delete(e),{ok:!0}}async function Ve({provider:t,credentialStore:e,profileId:r,confirmed:o}){if(o!==!0)return{ok:!1,reason:"Revoke was not confirmed. This is a destructive action and requires explicit confirmation (interactive prompt, or --yes in a non-interactive session)."};let i=await e.get(r);return i?(await t.revoke({token:i.refreshToken}),await e.delete(r),{ok:!0}):{ok:!1,reason:`No stored credentials for profile "${r}".`}}async function Je({credentialStore:t,profileRegistry:e}){let r=await e.getActive(),o=await t.listProfiles(),i=[];for(let n of o){let a=await t.get(n);i.push({profileId:n,active:n===r,loggedIn:!!a,expired:a?Date.now()>=a.expiresAt:void 0,scopes:a?.scopes||[]})}return{activeProfile:r,profiles:i}}async function He({credentialStore:t,profileRegistry:e,profileId:r}){return await t.get(r)?(await e.setActive(r),{ok:!0,profileId:r}):{ok:!1,reason:`Profile "${r}" is not logged in.`}}async function We({accessToken:t,apiBaseUrl:e,fetchImpl:r=globalThis.fetch}){let o=new URL("/api/v1/me",e),i=await r(o,{headers:{accept:"application/json",authorization:`Bearer ${t}`}}),n;try{n=await i.json()}catch{throw new Error("LixBlogs could not resolve the signed-in username.")}if(!i.ok||typeof n?.data?.username!="string")throw new Error(n?.error?.message||"LixBlogs could not resolve the signed-in username.");return E(n.data.username)}var wr=6e4,Xe=new WeakMap;function br(t){let e=Xe.get(t);return e||(e=new Map,Xe.set(t,e)),e}var te=class extends Error{constructor(e){super(`Profile "${e}" needs to log in again.`),this.name="LoginRequiredError",this.code="login_required"}},he=class extends Error{constructor(e,r){super("The configured LixBlogs origin is not serving the API v1 JSON contract."),this.name="ApiContractUnavailableError",this.code="api_contract_unavailable",this.status=e,this.details={contentType:r||"unknown"},this.hint="Deploy the LixBlogs API v1 stack, or select an origin that exposes /api/v1."}},j=class{constructor({provider:e,credentialStore:r,profileId:o,apiBaseUrl:i="https://blogs.elixpo.com",fetchImpl:n=globalThis.fetch,refreshSkewMs:a=wr}){this.provider=e,this.credentialStore=r,this.profileId=o,this.apiBaseUrl=new URL(i),this.fetchImpl=n,this.refreshSkewMs=a}async _refresh(e,{force:r=!1}={}){let o=br(this.credentialStore),i=o.get(this.profileId);if(i)return i;let n=(async()=>{let a=await this.credentialStore.get(this.profileId)||e;if(!r&&a.expiresAt-Date.now()>this.refreshSkewMs)return a;try{let s=await this.provider.refresh({refreshToken:a.refreshToken,scopes:a.scopes}),l={accessToken:s.accessToken,refreshToken:s.refreshToken,expiresAt:Date.now()+s.expiresInSeconds*1e3,scopes:s.scopes};return await this.credentialStore.set(this.profileId,l),l}catch(s){throw s instanceof x&&s.requiresLogin?(await this.credentialStore.delete(this.profileId),new te(this.profileId)):s}})();o.set(this.profileId,n);try{return await n}finally{o.get(this.profileId)===n&&o.delete(this.profileId)}}async credentials({forceRefresh:e=!1}={}){let r=await this.credentialStore.get(this.profileId);if(!r)throw new te(this.profileId);return e||r.expiresAt-Date.now()<=this.refreshSkewMs?this._refresh(r,{force:e}):r}async request(e,r={}){let o=await this.requestRaw(e,r),i=o.headers.get("content-type")||"";if(!i.toLowerCase().includes("application/json"))throw new he(o.status,i);return o}async requestRaw(e,r={}){let o=new URL(e,this.apiBaseUrl);if(o.origin!==this.apiBaseUrl.origin||!o.pathname.startsWith("/api/v1/"))throw new Error("Authenticated CLI requests are restricted to the configured LixBlogs /api/v1 resource server.");let i=await this.credentials(),n=()=>this.fetchImpl(o.toString(),{...r,headers:{...r.headers,authorization:`Bearer ${i.accessToken}`}}),a=await n();return a.status===401&&(i=await this.credentials({forceRefresh:!0}),a=await n()),a}async requireScopes(e){let r=await this.credentials(),o=e.filter(i=>!r.scopes.includes(i));if(o.length){let i=new Error(`Login again with the required scope${o.length>1?"s":""}: ${o.join(", ")}`);throw i.name="InsufficientScopeError",i.code="insufficient_scope",i.missingScopes=o,i}}};import{randomUUID as Ke}from"node:crypto";var h=class extends Error{constructor(e,r,{status:o,requestId:i,details:n}={}){super(r),this.name="BlogApiError",this.code=e||"api_error",this.status=o||0,this.requestId=i||null,this.details=n||null}};async function vr(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new h(e?.error?.code||`http_${t.status}`,e?.error?.message||`LixBlogs returned HTTP ${t.status}.`,{status:t.status,requestId:e?.error?.requestId||t.headers.get("x-request-id"),details:e?.error?.details});return{payload:e,etag:t.headers.get("etag")}}var z=class{constructor(e,{sleep:r=o=>new Promise(i=>setTimeout(i,o))}={}){this.http=e,this.sleep=r}async request(e,r={}){let o={...r,headers:{accept:"application/json",...r.body?{"content-type":"application/json"}:{},...r.headers}},n=(o.method||"GET")==="GET"||!!o.headers["idempotency-key"];for(let a=0;a<2;a+=1)try{let s=await this.http.request(e,o);if(n&&a===0&&(s.status===429||s.status>=500)){let l=Math.min(2,Number.parseInt(s.headers.get("retry-after")||"1",10)||1);await this.sleep(l*1e3);continue}return vr(s)}catch(s){if(!n||a>0||s instanceof h||s?.code)throw s;await this.sleep(250)}throw new h("request_failed","The LixBlogs request failed after retrying.")}async requireScopes(e){typeof this.http.requireScopes=="function"&&await this.http.requireScopes(e)}async whoami(){return await this.requireScopes(["lixblogs:profile:read"]),(await this.request("/api/v1/me")).payload.data}async list({status:e="all",limit:r=20,cursor:o}={}){await this.requireScopes(["lixblogs:blog:read"]);let i=new URLSearchParams({status:e,limit:String(r)});return o&&i.set("cursor",o),(await this.request(`/api/v1/blogs?${i}`)).payload}async get(e){await this.requireScopes(["lixblogs:blog:read"]);let r=await this.request(`/api/v1/blogs/${encodeURIComponent(e)}`);return{...r.payload.data,etag:r.etag||r.payload.data.etag}}async create(e,{idempotencyKey:r=Ke()}={}){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request("/api/v1/blogs",{method:"POST",headers:{"idempotency-key":r},body:JSON.stringify(e)})).payload.data}async update(e,r,{etag:o}){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}`,{method:"PATCH",headers:{"if-match":o},body:JSON.stringify(r)})).payload.data}async publish(e,{etag:r,status:o="published",idempotencyKey:i=Ke()}){return await this.requireScopes(["lixblogs:blog:publish"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/publish`,{method:"POST",headers:{"if-match":r,"idempotency-key":i},body:JSON.stringify({status:o})})).payload.data}async unpublish(e,{etag:r}){return await this.requireScopes(["lixblogs:blog:publish"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/unpublish`,{method:"POST",headers:{"if-match":r}})).payload.data}async delete(e,{etag:r,permanent:o=!1}){return await this.requireScopes(["lixblogs:blog:delete",...o?["lixblogs:blog:delete:permanent"]:[]]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}${o?"?permanent=true":""}`,{method:"DELETE",headers:{"if-match":r,...o?{"x-confirm-permanent-delete":e}:{}}})).payload.data}async restore(e,{etag:r}){return await this.requireScopes(["lixblogs:blog:delete"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/restore`,{method:"POST",headers:{"if-match":r}})).payload.data}async versions(e){return await this.requireScopes(["lixblogs:blog:read"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/versions`)).payload.data}async restoreVersion(e,r,{etag:o}){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/versions`,{method:"POST",headers:{"if-match":o},body:JSON.stringify({versionId:r})})).payload.data}async comments(e){return await this.requireScopes(["lixblogs:blog:read"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/comments`)).payload.data}async comment(e,r,{parentId:o}={}){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/comments`,{method:"POST",body:JSON.stringify({content:r,parentId:o})})).payload.data}async deleteComment(e,r){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/comments/${encodeURIComponent(r)}`,{method:"DELETE"})).payload.data}};async function xr(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new h(e?.error?.code||`http_${t.status}`,e?.error?.message||`LixBlogs returned HTTP ${t.status}.`,{status:t.status,requestId:e?.error?.requestId||t.headers.get("x-request-id"),details:e?.error?.details});return{payload:e,etag:t.headers.get("etag")}}var re=class{constructor(e,{sleep:r=o=>new Promise(i=>setTimeout(i,o))}={}){this.http=e,this.sleep=r}async request(e,r={}){let o={...r,headers:{accept:"application/json",...r.body?{"content-type":"application/json"}:{},...r.headers}},n=(o.method||"GET")==="GET";for(let a=0;a<2;a+=1)try{let s=await this.http.request(e,o);if(n&&a===0&&(s.status===429||s.status>=500)){let l=Math.min(2,Number.parseInt(s.headers.get("retry-after")||"1",10)||1);await this.sleep(l*1e3);continue}return xr(s)}catch(s){if(!n||a>0||s instanceof h||s?.code)throw s;await this.sleep(250)}throw new h("request_failed","The LixBlogs request failed after retrying.")}async requireScopes(e){typeof this.http.requireScopes=="function"&&await this.http.requireScopes(e)}async list(){return await this.requireScopes(["lixblogs:organizations:read"]),(await this.request("/api/v1/orgs")).payload}async get(e){if(!e)throw new Error("An organization ID or handle is required.");return await this.requireScopes(["lixblogs:organizations:read"]),(await this.request(`/api/v1/orgs/${encodeURIComponent(e)}`)).payload.data}async collections(e){if(!e)throw new Error("An organization ID or handle is required.");return await this.requireScopes(["lixblogs:organizations:read"]),(await this.request(`/api/v1/orgs/${encodeURIComponent(e)}/collections`)).payload.data}async members(e){if(!e)throw new Error("An organization ID or handle is required.");return await this.requireScopes(["lixblogs:organizations:read"]),(await this.request(`/api/v1/orgs/${encodeURIComponent(e)}/members`)).payload.data}async targets(){await this.requireScopes(["lixblogs:organizations:read"]);let o=((await this.list())?.data||[]).filter(n=>n.canWrite),i=await Promise.all(o.map(async n=>{let a=[];try{a=await this.collections(n.id)}catch{a=[]}return{target:`org:${n.id}`,orgId:n.id,slug:n.slug,name:n.name,role:n.role,collections:a.map(s=>({id:s.id,slug:s.slug,name:s.name}))}}));return{personal:{target:"personal",name:"Personal Blog"},organizations:i}}};import{randomUUID as oe}from"node:crypto";async function Ir(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new h(e?.error?.code||`http_${t.status}`,e?.error?.message||`LixBlogs returned HTTP ${t.status}.`,{status:t.status,requestId:e?.error?.requestId||t.headers.get("x-request-id"),details:e?.error?.details});return e.data}var ie=class{constructor(e){this.http=e}async request(e,r={}){let o=await this.http.request(e,{...r,headers:{accept:"application/json",...r.body?{"content-type":"application/json"}:{},...r.headers}});return Ir(o)}async list(e){return await this.http.requireScopes(["lixblogs:collaboration:read"]),this.request(`/api/v1/blogs/${encodeURIComponent(e)}/collaborators`)}async invitations(){return await this.http.requireScopes(["lixblogs:collaboration:read"]),this.request("/api/v1/collaboration/invitations")}async invite(e,{user:r,role:o,idempotencyKey:i=oe()}){return await this.http.requireScopes(["lixblogs:collaboration:write"]),this.request(`/api/v1/blogs/${encodeURIComponent(e)}/collaborators`,{method:"POST",headers:{"idempotency-key":i},body:JSON.stringify({user:r,role:o})})}async role(e,{user:r,role:o,idempotencyKey:i=oe()}){return await this.http.requireScopes(["lixblogs:collaboration:write"]),this.request(`/api/v1/blogs/${encodeURIComponent(e)}/collaborators`,{method:"PATCH",headers:{"idempotency-key":i},body:JSON.stringify({user:r,role:o})})}async remove(e,{user:r,idempotencyKey:o=oe()}={}){return await this.http.requireScopes(["lixblogs:collaboration:write"]),this.request(`/api/v1/blogs/${encodeURIComponent(e)}/collaborators`,{method:"DELETE",headers:{"idempotency-key":o},body:JSON.stringify({...r?{user:r}:{}})})}async resolveInvitation(e,{action:r,showOnProfile:o=!0,idempotencyKey:i=oe()}){return await this.http.requireScopes(["lixblogs:collaboration:write"]),this.request("/api/v1/collaboration/invitations",{method:"POST",headers:{"idempotency-key":i},body:JSON.stringify({blogId:e,action:r,showOnProfile:o})})}};async function Er(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new h(e?.error?.code||`http_${t.status}`,e?.error?.message||`LixBlogs returned HTTP ${t.status}.`,{status:t.status,requestId:e?.error?.requestId||t.headers.get("x-request-id"),details:e?.error?.details});return e}var ne=class{constructor(e){this.http=e}async query(e={}){let r=e.scope||"personal";await this.http.requireScopes(["lixblogs:analytics:read",...r.startsWith("org:")?["lixblogs:organizations:read"]:[]]);let o=new URLSearchParams({scope:r,range:e.range||(e.from||e.to?"custom":"30d"),dimension:e.dimension||"overview",limit:String(e.limit||20)});return e.from&&o.set("from",e.from),e.to&&o.set("to",e.to),e.cursor&&o.set("cursor",e.cursor),Er(await this.http.request(`/api/v1/analytics?${o}`,{headers:{accept:"application/json"}}))}};var se=class{constructor(e){this.http=e}async requireScopes(e){typeof this.http.requireScopes=="function"&&await this.http.requireScopes(e)}async _request(e,r={}){let o=await this.http.request(e,r),i;try{i=await o.json()}catch{i=null}if(!o.ok||i?.error){let n=new Error(i?.error?.message||`Request failed with HTTP ${o.status}`);throw n.code=i?.error?.code||`http_${o.status}`,n.status=o.status,n.requestId=i?.error?.requestId||o.headers.get("x-request-id")||null,n.details=i?.error?.details||null,n}return i.data}async cloudinaryStatus(){return await this.requireScopes(["lixblogs:integrations:cloudinary:read"]),this._request("/api/v1/integrations/cloudinary")}async cloudinaryDisconnect(){return await this.requireScopes(["lixblogs:integrations:cloudinary:disconnect"]),this._request("/api/v1/integrations/cloudinary",{method:"DELETE"})}async pollinationsStatus({refresh:e=!1}={}){return await this.requireScopes(["lixblogs:media:read"]),this._request(`/api/v1/integrations/pollinations${e?"?refresh=1":""}`)}async pollinationsDisconnect(){return await this.requireScopes(["lixblogs:media:write"]),this._request("/api/v1/integrations/pollinations",{method:"DELETE"})}};import{randomUUID as Ye}from"node:crypto";var _r=Object.freeze({"image/avif":"avif","image/bmp":"bmp","image/jpeg":"jpg","image/png":"png","image/svg+xml":"svg","image/webp":"webp"});async function ye(t){let e=await t.json().catch(()=>({})),r=new Error(e.error?.message||e.error||`Media request failed with HTTP ${t.status}`);return r.code=e.error?.code||e.code||`http_${t.status}`,r.status=t.status,r}var ae=class{constructor(e){this.http=e}async generate({prompt:e,model:r="flux",seed:o,width:i,height:n,destination:a="inline",generationId:s=Ye(),reference:l}){await this.http.requireScopes(["lixblogs:media:write"]);let u,d;if(l){u=new FormData;for(let[p,v]of Object.entries({prompt:e,model:r,seed:o,width:i,height:n,destination:a,generationId:s}))v!==void 0&&u.append(p,String(v));u.append("referenceImage",new Blob([l.bytes],{type:l.mimeType}),l.name||"reference-image"),d={accept:"image/*, application/json"}}else u=JSON.stringify({prompt:e,model:r,seed:o,width:i,height:n,destination:a,generationId:s}),d={"content-type":"application/json",accept:"image/*, application/json"};let c=await this.http.requestRaw("/api/v1/media/generate",{method:"POST",headers:d,body:u});if(!c.ok)throw await ye(c);return{bytes:new Uint8Array(await c.arrayBuffer()),mimeType:c.headers.get("content-type")||"image/jpeg",generationId:s}}async upload({bytes:e,mimeType:r,blogId:o,mediaType:i="inline",uploadId:n=Ye()}){await this.http.requireScopes(["lixblogs:media:write"]);let a=new FormData,s=_r[r];if(!s)throw new Error(`Unsupported image MIME type: ${r}`);a.append("file",new Blob([e],{type:r}),`lixblogs-${n}.${s}`),a.append("type",i),a.append("uploadId",n),o&&a.append("blogId",o);let l=await this.http.requestRaw("/api/v1/media/upload",{method:"POST",body:a,headers:{accept:"application/json"}});if(!l.ok)throw await ye(l);return l.json()}async delete(e){if(!e)throw new Error("A media ID is required.");await this.http.requireScopes(["lixblogs:media:write"]);let r=await this.http.request(`/api/v1/media/${encodeURIComponent(e)}`,{method:"DELETE"});if(!r.ok)throw await ye(r);let o=await r.json();return o.data||o}};var m=Object.freeze({OK:0,ERROR:1,USAGE:2,CONFLICT:3,AUTH:4,CONFIRMATION:5}),kr=Object.freeze({login:["auth","login"],logout:["auth","logout"],whoami:["auth","whoami"],profiles:["auth","profiles"],use:["auth","use"]});function Ze(t){let[e,...r]=t,o=kr[e];return o?[...o,...r]:t}function Qe(t,e="cli_error"){if(t&&typeof t=="object"&&t.error&&!Array.isArray(t.error))return t;let r=t&&typeof t=="object"?t:{message:String(t||"Command failed.")};return{ok:!1,error:{code:r.code||e,message:r.message||"Command failed.",hint:r.hint||null,requestId:r.requestId||null,...r.details?{details:r.details}:{}}}}function g(t,e){if(t.yes)return;let r=new Error(`${e} requires --yes in non-interactive operation.`);throw r.code="confirmation_required",r.hint="Review the operation, then run it again with --yes.",r.exitCode=m.CONFIRMATION,r}var q=Object.freeze({reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",violet:"\x1B[38;5;141m",green:"\x1B[38;5;42m"});function we(t=process.stdout,e=process.env){return!!t.isTTY&&e.NO_COLOR===void 0&&e.TERM!=="dumb"}function R(t,e,r){return r?`${e}${t}${q.reset}`:t}function et({url:t,code:e,expiresInSeconds:r,profile:o,interactive:i,color:n=!1}){let a=`${R("\u25C6",q.violet,n)} ${R("LixBlogs",q.bold,n)}`,s=i?"Press Enter to open here, or use the URL on another device.":"Open the URL in any browser and approve this device.";return["",` ${a}`,` ${R("Device login",q.dim,n)}`," \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",` URL ${t}`,` Code ${R(e,q.bold,n)}`,` Expires ${Math.ceil(r/60)} min`,o?` Profile ${o} ${R("(local credential slot)",q.dim,n)}`:` Profile ${R("your Accounts username after approval",q.dim,n)}`,"",` ${s}`," No localhost callback or exposed port is required.",""].join(`
5
- `)}function tt(t,e=!1){return` ${R("\u2713",q.green,e)} ${t}`}function rt({input:t=process.stdin,open:e,url:r}){if(!t.isTTY||typeof e!="function")return()=>{};let o=()=>{Promise.resolve(e(r)).catch(()=>{})};return t.setEncoding?.("utf8"),t.once("data",o),t.resume?.(),()=>{t.off?.("data",o),t.pause?.()}}function C(t){return[{type:"text",text:t}]}function ot(t){let e=String(t||"").replace(/\r\n/g,`
2
+ import{parseArgs as Hr}from"node:util";import{spawn as Wr}from"node:child_process";var X={environment:"production",profile:"default",accountsBaseUrl:"https://accounts.elixpo.com",apiBaseUrl:"https://blogs.elixpo.com"},je={development:{clientId:"lixblogs-cli-dev",audience:"localhost"},staging:{clientId:"lixblogs-cli-staging",audience:"staging.blogs.elixpo.com"},production:{clientId:"lixblogs-cli-prod",audience:"blogs.elixpo.com"},test:{clientId:"lixblogs-cli-dev",audience:"localhost"}};function S({flags:t={},env:e=process.env}={}){let r=t.env??e.LIXBLOGS_ENV??X.environment,o=t.profile??e.LIXBLOGS_PROFILE??X.profile,n=je[r]||je.production,i=t.authProvider??e.LIXBLOGS_AUTH_PROVIDER??(r==="production"?"elixpo":"mock"),s=t.accountsUrl??e.LIXBLOGS_ACCOUNTS_URL??X.accountsBaseUrl,a=t.apiUrl??e.LIXBLOGS_API_URL??X.apiBaseUrl,l=t.clientId??e.LIXBLOGS_CLIENT_ID??n.clientId,u=t.audience??e.LIXBLOGS_AUDIENCE??n.audience;return{environment:r,profile:o,profileExplicit:t.profile!==void 0||e.LIXBLOGS_PROFILE!==void 0,authProvider:i,accountsBaseUrl:s,apiBaseUrl:a,clientId:l,audience:u}}var L=class{get providerId(){throw new Error("AuthProvider.providerId must be implemented by subclass")}async requestDeviceCode(e){throw new Error("AuthProvider.requestDeviceCode must be implemented by subclass")}async pollDeviceCode(e){throw new Error("AuthProvider.pollDeviceCode must be implemented by subclass")}async refresh(e){throw new Error("AuthProvider.refresh must be implemented by subclass")}async revoke(e){throw new Error("AuthProvider.revoke must be implemented by subclass")}};var Ce={APPROVE_IMMEDIATELY:"mock-approve-",PENDING_THEN_APPROVE:"mock-pending-then-approve-",DENY:"mock-deny-",EXPIRE:"mock-expire-",SLOW_DOWN_THEN_APPROVE:"mock-slow-down-then-approve-"},cr=5,Le=0;function ur(t){return Le+=1,`${t}${Le}`}var Y=class extends L{constructor(){super(),this._devicesCodes=new Map,this._revoked=new Set,this._refreshWillFail=new Set}get providerId(){return"mock"}async requestDeviceCode({scopes:e,scenario:r="APPROVE_IMMEDIATELY"}){let o=Ce[r]??Ce.APPROVE_IMMEDIATELY,n=ur(o),i=n.slice(-6).toUpperCase();return this._devicesCodes.set(n,{scenario:r,pollCount:0,createdAt:Date.now(),scopes:[...e]}),{deviceCode:n,userCode:i,verificationUri:"https://mock.lixblogs.local/device",verificationUriComplete:`https://mock.lixblogs.local/device?user_code=${encodeURIComponent(i)}`,expiresInSeconds:r==="EXPIRE"?1:600,pollIntervalSeconds:1}}async pollDeviceCode({deviceCode:e}){let r=this._devicesCodes.get(e);return r?r.scenario==="EXPIRE"?{status:"expired"}:r.scenario==="DENY"?{status:"denied"}:r.scenario==="PENDING_THEN_APPROVE"&&(r.pollCount+=1,r.pollCount<2)?{status:"pending"}:r.scenario==="SLOW_DOWN_THEN_APPROVE"&&(r.pollCount+=1,r.pollCount<2)?{status:"slow_down",pollIntervalIncreaseSeconds:cr}:{status:"approved",token:{accessToken:`mock-access-${e}`,refreshToken:`mock-refresh-${e}`,expiresInSeconds:3600,scopes:r.scopes}}:{status:"denied"}}async refresh({refreshToken:e,scopes:r=[]}){if(this._revoked.has(e))throw new Error("refresh token has been revoked");if(this._refreshWillFail.has(e))throw new Error("mock refresh failure (test-injected)");return{accessToken:`mock-access-refreshed-${e}`,refreshToken:e,expiresInSeconds:3600,scopes:r}}async revoke({token:e}){this._revoked.add(e)}_simulateRefreshFailureFor(e){this._refreshWillFail.add(e)}};var Ue="urn:ietf:params:oauth:grant-type:device_code",dr=1,pr=15e3,fr={access_denied:"Login was denied.",authorization_pending:"Login is awaiting approval.",expired_token:"The device authorization expired. Start login again.",invalid_client:"The LixBlogs CLI client is not registered for this environment.",invalid_grant:"This session is no longer valid. Log in again.",invalid_request:"Accounts rejected the authentication request.",invalid_scope:"The requested LixBlogs permissions are not available for this client.",server_error:"Accounts could not complete authentication. Try again later.",slow_down:"Accounts requested slower polling.",temporarily_unavailable:"Accounts is temporarily unavailable. Try again later."},_=class extends Error{constructor(e,{status:r=0,requiresLogin:o=!1}={}){super(fr[e]||"Authentication failed."),this.name="AuthProviderError",this.code=e||"authentication_failed",this.status=r,this.requiresLogin=o}},R=class extends Error{constructor(e){super(e),this.name="CompatibilityError",this.code="incompatible_accounts_contract"}};function he(t){return String(t||"0.0.0").split(".").slice(0,3).map(e=>Number.parseInt(e,10)||0)}function mr(t,e){let r=he(t),o=he(e);for(let n=0;n<3;n+=1)if(r[n]!==o[n])return r[n]>o[n];return!0}function gr(t){let e=new URL(t);if(e.pathname=e.pathname.replace(/\/$/,""),e.search="",e.hash="",e.protocol!=="https:"&&e.hostname!=="localhost"&&e.hostname!=="127.0.0.1")throw new R("Accounts must use HTTPS outside local development.");return e.toString().replace(/\/$/,"")}async function H(t){try{return await t.json()}catch{throw new _("server_error",{status:t.status})}}function Z(t,e){let r=typeof t?.error=="string"?t.error:"server_error";return new _(r,{status:e.status,requiresLogin:r==="invalid_grant"||r==="access_denied"||r==="expired_token"})}function De(t,e){if(!e.ok)throw Z(t,e);if(typeof t?.access_token!="string"||typeof t?.refresh_token!="string"||!Number.isFinite(Number(t?.expires_in)))throw new _("server_error",{status:e.status});return{accessToken:t.access_token,refreshToken:t.refresh_token,expiresInSeconds:Number(t.expires_in),scopes:typeof t.scope=="string"?t.scope.split(/\s+/).filter(Boolean):[]}}var Q=class extends L{constructor({accountsBaseUrl:e="https://accounts.elixpo.com",clientId:r="lixblogs-cli-prod",audience:o="blogs.elixpo.com",cliVersion:n="1.2.0",fetchImpl:i=globalThis.fetch,timeoutMs:s=pr}={}){if(super(),typeof i!="function")throw new TypeError("A fetch implementation is required.");this.accountsBaseUrl=gr(e),this.clientId=r,this.audience=o,this.cliVersion=n,this.fetchImpl=i,this.timeoutMs=s,this._metadata=null,this._discoveryPromise=null}get providerId(){return"elixpo"}async _fetch(e,r={}){let o=new AbortController,n=setTimeout(()=>o.abort(),this.timeoutMs);try{return await this.fetchImpl(e,{...r,signal:r.signal||o.signal,headers:{accept:"application/json",...r.headers}})}catch{throw new _("temporarily_unavailable")}finally{clearTimeout(n)}}async discover({scopes:e=[]}={}){if(!this._metadata){this._discoveryPromise||(this._discoveryPromise=this._loadDiscovery());try{this._metadata=await this._discoveryPromise}finally{this._discoveryPromise=null}}if(e.filter(o=>!this._metadata.scopes_supported.includes(o)).length)throw new _("invalid_scope");return this._metadata}async _loadDiscovery(){let e=await this._fetch(`${this.accountsBaseUrl}/.well-known/oauth-authorization-server`),r=await H(e);if(!e.ok)throw new _("temporarily_unavailable",{status:e.status});if(he(r.elixpo_contract_version)[0]!==dr)throw new R("Accounts uses an unsupported device-flow contract version.");if(!mr(this.cliVersion,r.elixpo_min_compatible_cli_version))throw new R(`This CLI is too old for Accounts. Upgrade to version ${r.elixpo_min_compatible_cli_version} or newer.`);if(!Array.isArray(r.grant_types_supported)||!r.grant_types_supported.includes(Ue))throw new R("Accounts does not advertise OAuth device authorization.");let n=["device_authorization_endpoint","token_endpoint","revocation_endpoint"];for(let i of n){if(typeof r[i]!="string")throw new R(`Accounts discovery is missing ${i}.`);let s=new URL(r[i]),a=new URL(this.accountsBaseUrl);if(s.origin!==a.origin)throw new R(`Accounts discovery returned an untrusted ${i}.`)}return{...r,scopes_supported:Array.isArray(r.scopes_supported)?r.scopes_supported:[]}}async requestDeviceCode({scopes:e}){let r=await this.discover({scopes:e}),o=await this._fetch(r.device_authorization_endpoint,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({client_id:this.clientId,scope:e.join(" "),audience:this.audience})}),n=await H(o);if(!o.ok)throw Z(n,o);if(typeof n.device_code!="string"||typeof n.user_code!="string"||typeof n.verification_uri!="string")throw new _("server_error",{status:o.status});return{deviceCode:n.device_code,userCode:n.user_code,verificationUri:n.verification_uri,verificationUriComplete:n.verification_uri_complete||n.verification_uri,expiresInSeconds:Number(n.expires_in)||600,pollIntervalSeconds:Number(n.interval)||5}}async pollDeviceCode({deviceCode:e}){let r=await this.discover(),o=new URLSearchParams({grant_type:Ue,device_code:e,client_id:this.clientId}),n=await this._fetch(r.token_endpoint,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:o}),i=await H(n);if(n.ok)return{status:"approved",token:De(i,n)};if(i?.error==="authorization_pending")return{status:"pending"};if(i?.error==="slow_down"){let s=r.elixpo_device_flow_polling||{};return{status:"slow_down",pollIntervalIncreaseSeconds:Math.max(5,Number(s.slow_down_interval_seconds||10)-Number(s.interval_seconds||5))}}if(i?.error==="access_denied")return{status:"denied"};if(i?.error==="expired_token")return{status:"expired"};throw Z(i,n)}async refresh({refreshToken:e,scopes:r}){let o=await this.discover({scopes:r||[]}),n=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:this.clientId});r?.length&&n.set("scope",r.join(" "));let i=await this._fetch(o.token_endpoint,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:n}),s=await H(i);return De(s,i)}async revoke({token:e}){let r=await this.discover(),o=await this._fetch(r.revocation_endpoint,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({token:e,client_id:this.clientId})});if(!o.ok){let n=await H(o);throw Z(n,o)}}};var Ne="elixpo",ye=class extends Error{constructor(e){super(e),this.name="ProductionAuthGateError"}};function Be({providerId:t,environment:e}){if(e==="production"&&t!==Ne)throw new ye(`Provider "${t}" is not approved for production. Only "${Ne}" may be used in production.`)}function U(t){if(t.authProvider==="mock"&&t.environment==="production")throw new Error("The mock auth provider cannot run in production.");if(t.authProvider!=="mock"&&t.authProvider!=="elixpo")throw new Error(`Unknown auth provider "${t.authProvider}".`);let e=t.authProvider==="mock"?new Y:new Q({accountsBaseUrl:t.accountsBaseUrl,clientId:t.clientId,audience:t.audience,cliVersion:t.cliVersion||"1.2.0",fetchImpl:t.fetchImpl});return Be({providerId:e.providerId,environment:t.environment}),e}var P=class extends Error{constructor(e){super(e),this.name="CredentialStoreUnavailableError"}},j=class{async get(e){throw new Error("CredentialStore.get must be implemented by subclass")}async set(e,r){throw new Error("CredentialStore.set must be implemented by subclass")}async delete(e){throw new Error("CredentialStore.delete must be implemented by subclass")}async listProfiles(){throw new Error("CredentialStore.listProfiles must be implemented by subclass")}},ee=class extends j{constructor(){super(),this._store=new Map}async get(e){return this._store.get(e)??null}async set(e,r){this._store.set(e,r)}async delete(e){this._store.delete(e)}async listProfiles(){return[...this._store.keys()]}},te=class extends j{constructor(e){super(),this._realStore=e}async get(e){try{return await this._realStore.get(e)}catch(r){throw new P(`OS keychain is unavailable: ${r.message}. Re-run with an explicit fallback flag if you want to opt in to a less secure storage method.`)}}async set(e,r){try{await this._realStore.set(e,r)}catch(o){throw new P(`OS keychain is unavailable: ${o.message}. Re-run with an explicit fallback flag if you want to opt in to a less secure storage method.`)}}async delete(e){try{await this._realStore.delete(e)}catch(r){throw new P(`OS keychain is unavailable: ${r.message}`)}}async listProfiles(){try{return await this._realStore.listProfiles()}catch(e){throw new P(`OS keychain is unavailable: ${e.message}`)}}};import{Entry as hr}from"@napi-rs/keyring";var yr="lixblogs-cli",wr="__lixblogs_availability_probe__";function re(t){return new hr(yr,t)}async function Fe(){let t=re(wr);try{return t.setPassword("probe"),t.deletePassword(),{available:!0}}catch(e){return{available:!1,error:String(e.message??e).split(`
3
+ `)[0].trim()}}}var we=class extends j{async get(e){let r=re(e),o;try{o=r.getPassword()}catch(n){if(Me(n))return null;throw n}return o==null?null:JSON.parse(o)}async set(e,r){re(e).setPassword(JSON.stringify(r))}async delete(e){let r=re(e);try{r.deletePassword()}catch(o){if(Me(o))return;throw o}}async listProfiles(){throw new Error("KeychainCredentialStore.listProfiles is not supported directly \u2014 use ProfileRegistry to track known profile IDs, then look up each one via get().")}};function Me(t){let e=String(t?.message??"");return/no such|not found|nosuchkeyring|nosuchitem/i.test(e)}var oe=class extends j{constructor(e){super(),this._keychain=new we,this._registry=e}async get(e){return this._keychain.get(e)}async set(e,r){await this._keychain.set(e,r),await this._registry.add(e)}async delete(e){await this._keychain.delete(e),await this._registry.remove(e)}async listProfiles(){return this._registry.list()}};import{promises as ne}from"node:fs";import ze from"node:path";import br from"node:os";function vr(){return ze.join(br.homedir(),".config","lixblogs","profiles.json")}var b=class{constructor(e=vr()){this._path=e}async list(){return(await this._read()).profiles}async getActive(){let e=await this._read();return e.activeProfile&&e.profiles.includes(e.activeProfile)?e.activeProfile:e.profiles[0]||null}async setActive(e){$(e);let r=await this._read();if(!r.profiles.includes(e))throw new Error(`Profile "${e}" does not exist. Log in with it first.`);await this._write(r.profiles,e)}async _read(){try{let e=await ne.readFile(this._path,"utf8"),r=JSON.parse(e);return{profiles:Array.isArray(r.profiles)?r.profiles.filter(n=>typeof n=="string"):[],activeProfile:typeof r.activeProfile=="string"?r.activeProfile:null}}catch(e){if(e.code==="ENOENT")return{profiles:[],activeProfile:null};throw e}}async add(e){$(e);let r=await this._read(),o=new Set(r.profiles);o.add(e),await this._write([...o],r.activeProfile||e)}async remove(e){let r=await this._read(),o=r.profiles.filter(i=>i!==e),n=r.activeProfile===e?o[0]||null:r.activeProfile;await this._write(o,n)}async _write(e,r=null){await ne.mkdir(ze.dirname(this._path),{recursive:!0});let o=`${this._path}.${process.pid}.tmp`;await ne.writeFile(o,JSON.stringify({activeProfile:r,profiles:e},null,2),{encoding:"utf8",mode:384}),await ne.rename(o,this._path)}};function $(t){if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(t||""))throw new Error("Profile names must be 1-64 characters using letters, numbers, dot, dash, or underscore.");return t}async function Ge({allowInsecureFallback:t=!1,profileRegistry:e}={}){let r=await Fe();if(!r.available){if(!t)throw new P(`OS keychain is unavailable: ${r.error}. Re-run with an explicit fallback flag if you want to opt in to a less secure storage method.`);return process.stderr.write(`warning: OS keychain unavailable (${r.error}); using in-memory fallback because --allow-insecure-fallback was passed. Credentials will NOT persist between CLI runs.
4
+ `),new ee}let o=e||new b,n=new oe(o);return new te(n)}var ve="[REDACTED]",xr=/token|refresh|secret|password|authorization/i,Ir=/^(mock-(access|refresh)-|Bearer\s+)\S+/i;function Ve(t){return typeof t=="string"&&Ir.test(t)?ve:t}function be(t){if(Array.isArray(t))return t.map(e=>be(e));if(t&&typeof t=="object"){let e={};for(let[r,o]of Object.entries(t))xr.test(r)?e[r]=ve:typeof o=="object"&&o!==null?e[r]=be(o):e[r]=Ve(o);return e}return Ve(t)}function ie(t,e){return JSON.stringify(be(t),null,e)}function D(t){return typeof t!="string"?t:t.replace(/(mock-(access|refresh)-\S+|Bearer\s+\S+)/gi,ve)}async function Je({provider:t,credentialStore:e,profileId:r,scopes:o,openBrowser:n,resolveProfileId:i,sleep:s=l=>new Promise(u=>setTimeout(u,l)),onStatus:a=()=>{}}){let l;try{l=await t.requestDeviceCode({scopes:o})}catch(c){return{ok:!1,reason:D(c.message)}}a({type:"verification_pending",verificationUri:l.verificationUri,verificationUriComplete:l.verificationUriComplete,userCode:l.userCode,expiresInSeconds:l.expiresInSeconds}),n&&await n(l.verificationUriComplete||l.verificationUri);let u=l.pollIntervalSeconds*1e3,d=Date.now()+l.expiresInSeconds*1e3;for(;Date.now()<d;){await s(u);let c;try{c=await t.pollDeviceCode({deviceCode:l.deviceCode})}catch(f){return{ok:!1,reason:D(f.message)}}if(c.status==="approved"){let f=r;if(i)try{f=await i({accessToken:c.token.accessToken,requestedProfileId:r})}catch(E){return{ok:!1,reason:D(E.message)}}return await e.set(f,{accessToken:c.token.accessToken,refreshToken:c.token.refreshToken,expiresAt:Date.now()+c.token.expiresInSeconds*1e3,scopes:c.token.scopes}),a({type:"approved"}),{ok:!0,profileId:f}}if(c.status==="denied")return a({type:"denied"}),{ok:!1,reason:"Login was denied."};if(c.status==="expired")return a({type:"expired"}),{ok:!1,reason:"Device code expired before login was approved."};if(c.status==="slow_down"){u+=c.pollIntervalIncreaseSeconds*1e3,a({type:"slow_down",newIntervalMs:u});continue}a({type:"pending"})}return{ok:!1,reason:"Device code expired before login was approved."}}async function He({credentialStore:t,profileId:e}){let r=e?[e]:await t.listProfiles(),o=[];for(let n of r){let i=await t.get(n);if(!i){o.push({profileId:n,loggedIn:!1});continue}o.push({profileId:n,loggedIn:!0,expired:Date.now()>=i.expiresAt,scopes:i.scopes})}return o}async function We({credentialStore:t,profileId:e}){return await t.delete(e),{ok:!0}}async function Ke({provider:t,credentialStore:e,profileId:r,confirmed:o}){if(o!==!0)return{ok:!1,reason:"Revoke was not confirmed. This is a destructive action and requires explicit confirmation (interactive prompt, or --yes in a non-interactive session)."};let n=await e.get(r);return n?(await t.revoke({token:n.refreshToken}),await e.delete(r),{ok:!0}):{ok:!1,reason:`No stored credentials for profile "${r}".`}}async function Xe({credentialStore:t,profileRegistry:e}){let r=await e.getActive(),o=await t.listProfiles(),n=[];for(let i of o){let s=await t.get(i);n.push({profileId:i,active:i===r,loggedIn:!!s,expired:s?Date.now()>=s.expiresAt:void 0,scopes:s?.scopes||[]})}return{activeProfile:r,profiles:n}}async function Ye({credentialStore:t,profileRegistry:e,profileId:r}){return await t.get(r)?(await e.setActive(r),{ok:!0,profileId:r}):{ok:!1,reason:`Profile "${r}" is not logged in.`}}async function Ze({accessToken:t,apiBaseUrl:e,fetchImpl:r=globalThis.fetch}){let o=new URL("/api/v1/me",e),n=await r(o,{headers:{accept:"application/json",authorization:`Bearer ${t}`}}),i;try{i=await n.json()}catch{throw new Error("LixBlogs could not resolve the signed-in username.")}if(!n.ok||typeof i?.data?.username!="string")throw new Error(i?.error?.message||"LixBlogs could not resolve the signed-in username.");return $(i.data.username)}var Er=6e4,Qe=new WeakMap;function _r(t){let e=Qe.get(t);return e||(e=new Map,Qe.set(t,e)),e}var se=class extends Error{constructor(e){super(`Profile "${e}" needs to log in again.`),this.name="LoginRequiredError",this.code="login_required"}},xe=class extends Error{constructor(e,r){super("The configured LixBlogs origin is not serving the API v1 JSON contract."),this.name="ApiContractUnavailableError",this.code="api_contract_unavailable",this.status=e,this.details={contentType:r||"unknown"},this.hint="Deploy the LixBlogs API v1 stack, or select an origin that exposes /api/v1."}},N=class{constructor({provider:e,credentialStore:r,profileId:o,apiBaseUrl:n="https://blogs.elixpo.com",fetchImpl:i=globalThis.fetch,refreshSkewMs:s=Er}){this.provider=e,this.credentialStore=r,this.profileId=o,this.apiBaseUrl=new URL(n),this.fetchImpl=i,this.refreshSkewMs=s}async _refresh(e,{force:r=!1}={}){let o=_r(this.credentialStore),n=o.get(this.profileId);if(n)return n;let i=(async()=>{let s=await this.credentialStore.get(this.profileId)||e;if(!r&&s.expiresAt-Date.now()>this.refreshSkewMs)return s;try{let a=await this.provider.refresh({refreshToken:s.refreshToken,scopes:s.scopes}),l={accessToken:a.accessToken,refreshToken:a.refreshToken,expiresAt:Date.now()+a.expiresInSeconds*1e3,scopes:a.scopes};return await this.credentialStore.set(this.profileId,l),l}catch(a){throw a instanceof _&&a.requiresLogin?(await this.credentialStore.delete(this.profileId),new se(this.profileId)):a}})();o.set(this.profileId,i);try{return await i}finally{o.get(this.profileId)===i&&o.delete(this.profileId)}}async credentials({forceRefresh:e=!1}={}){let r=await this.credentialStore.get(this.profileId);if(!r)throw new se(this.profileId);return e||r.expiresAt-Date.now()<=this.refreshSkewMs?this._refresh(r,{force:e}):r}async request(e,r={}){let o=await this.requestRaw(e,r),n=o.headers.get("content-type")||"";if(!n.toLowerCase().includes("application/json"))throw new xe(o.status,n);return o}async requestRaw(e,r={}){let o=new URL(e,this.apiBaseUrl);if(o.origin!==this.apiBaseUrl.origin||!o.pathname.startsWith("/api/v1/"))throw new Error("Authenticated CLI requests are restricted to the configured LixBlogs /api/v1 resource server.");let n=await this.credentials(),i=()=>this.fetchImpl(o.toString(),{...r,headers:{...r.headers,authorization:`Bearer ${n.accessToken}`}}),s=await i();return s.status===401&&(n=await this.credentials({forceRefresh:!0}),s=await i()),s}async requireScopes(e){let r=await this.credentials(),o=e.filter(n=>!r.scopes.includes(n));if(o.length){let n=new Error(`Login again with the required scope${o.length>1?"s":""}: ${o.join(", ")}`);throw n.name="InsufficientScopeError",n.code="insufficient_scope",n.missingScopes=o,n}}};import{randomUUID as et}from"node:crypto";var h=class extends Error{constructor(e,r,{status:o,requestId:n,details:i}={}){super(r),this.name="BlogApiError",this.code=e||"api_error",this.status=o||0,this.requestId=n||null,this.details=i||null}};async function kr(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new h(e?.error?.code||`http_${t.status}`,e?.error?.message||`LixBlogs returned HTTP ${t.status}.`,{status:t.status,requestId:e?.error?.requestId||t.headers.get("x-request-id"),details:e?.error?.details});return{payload:e,etag:t.headers.get("etag")}}var W=class{constructor(e,{sleep:r=o=>new Promise(n=>setTimeout(n,o))}={}){this.http=e,this.sleep=r}async request(e,r={}){let o={...r,headers:{accept:"application/json",...r.body?{"content-type":"application/json"}:{},...r.headers}},i=(o.method||"GET")==="GET"||!!o.headers["idempotency-key"];for(let s=0;s<2;s+=1)try{let a=await this.http.request(e,o);if(i&&s===0&&(a.status===429||a.status>=500)){let l=Math.min(2,Number.parseInt(a.headers.get("retry-after")||"1",10)||1);await this.sleep(l*1e3);continue}return kr(a)}catch(a){if(!i||s>0||a instanceof h||a?.code)throw a;await this.sleep(250)}throw new h("request_failed","The LixBlogs request failed after retrying.")}async requireScopes(e){typeof this.http.requireScopes=="function"&&await this.http.requireScopes(e)}async whoami(){return await this.requireScopes(["lixblogs:profile:read"]),(await this.request("/api/v1/me")).payload.data}async list({status:e="all",limit:r=20,cursor:o}={}){await this.requireScopes(["lixblogs:blog:read"]);let n=new URLSearchParams({status:e,limit:String(r)});return o&&n.set("cursor",o),(await this.request(`/api/v1/blogs?${n}`)).payload}async get(e){await this.requireScopes(["lixblogs:blog:read"]);let r=await this.request(`/api/v1/blogs/${encodeURIComponent(e)}`);return{...r.payload.data,etag:r.payload.data.etag||r.etag}}async create(e,{idempotencyKey:r=et()}={}){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request("/api/v1/blogs",{method:"POST",headers:{"idempotency-key":r},body:JSON.stringify(e)})).payload.data}async update(e,r,{etag:o}){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}`,{method:"PATCH",headers:{"if-match":o},body:JSON.stringify(r)})).payload.data}async publish(e,{etag:r,status:o="published",idempotencyKey:n=et()}){return await this.requireScopes(["lixblogs:blog:publish"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/publish`,{method:"POST",headers:{"if-match":r,"idempotency-key":n},body:JSON.stringify({status:o})})).payload.data}async unpublish(e,{etag:r}){return await this.requireScopes(["lixblogs:blog:publish"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/unpublish`,{method:"POST",headers:{"if-match":r}})).payload.data}async delete(e,{etag:r,permanent:o=!1}){return await this.requireScopes(["lixblogs:blog:delete",...o?["lixblogs:blog:delete:permanent"]:[]]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}${o?"?permanent=true":""}`,{method:"DELETE",headers:{"if-match":r,...o?{"x-confirm-permanent-delete":e}:{}}})).payload.data}async restore(e,{etag:r}){return await this.requireScopes(["lixblogs:blog:delete"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/restore`,{method:"POST",headers:{"if-match":r}})).payload.data}async versions(e){return await this.requireScopes(["lixblogs:blog:read"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/versions`)).payload.data}async restoreVersion(e,r,{etag:o}){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/versions`,{method:"POST",headers:{"if-match":o},body:JSON.stringify({versionId:r})})).payload.data}async comments(e){return await this.requireScopes(["lixblogs:blog:read"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/comments`)).payload.data}async comment(e,r,{parentId:o}={}){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/comments`,{method:"POST",body:JSON.stringify({content:r,parentId:o})})).payload.data}async deleteComment(e,r){return await this.requireScopes(["lixblogs:blog:write"]),(await this.request(`/api/v1/blogs/${encodeURIComponent(e)}/comments/${encodeURIComponent(r)}`,{method:"DELETE"})).payload.data}};async function Sr(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new h(e?.error?.code||`http_${t.status}`,e?.error?.message||`LixBlogs returned HTTP ${t.status}.`,{status:t.status,requestId:e?.error?.requestId||t.headers.get("x-request-id"),details:e?.error?.details});return{payload:e,etag:t.headers.get("etag")}}var ae=class{constructor(e,{sleep:r=o=>new Promise(n=>setTimeout(n,o))}={}){this.http=e,this.sleep=r}async request(e,r={}){let o={...r,headers:{accept:"application/json",...r.body?{"content-type":"application/json"}:{},...r.headers}},i=(o.method||"GET")==="GET";for(let s=0;s<2;s+=1)try{let a=await this.http.request(e,o);if(i&&s===0&&(a.status===429||a.status>=500)){let l=Math.min(2,Number.parseInt(a.headers.get("retry-after")||"1",10)||1);await this.sleep(l*1e3);continue}return Sr(a)}catch(a){if(!i||s>0||a instanceof h||a?.code)throw a;await this.sleep(250)}throw new h("request_failed","The LixBlogs request failed after retrying.")}async requireScopes(e){typeof this.http.requireScopes=="function"&&await this.http.requireScopes(e)}async list(){return await this.requireScopes(["lixblogs:organizations:read"]),(await this.request("/api/v1/orgs")).payload}async get(e){if(!e)throw new Error("An organization ID or handle is required.");return await this.requireScopes(["lixblogs:organizations:read"]),(await this.request(`/api/v1/orgs/${encodeURIComponent(e)}`)).payload.data}async collections(e){if(!e)throw new Error("An organization ID or handle is required.");return await this.requireScopes(["lixblogs:organizations:read"]),(await this.request(`/api/v1/orgs/${encodeURIComponent(e)}/collections`)).payload.data}async members(e){if(!e)throw new Error("An organization ID or handle is required.");return await this.requireScopes(["lixblogs:organizations:read"]),(await this.request(`/api/v1/orgs/${encodeURIComponent(e)}/members`)).payload.data}async targets(){await this.requireScopes(["lixblogs:organizations:read"]);let o=((await this.list())?.data||[]).filter(i=>i.canWrite),n=await Promise.all(o.map(async i=>{let s=[];try{s=await this.collections(i.id)}catch{s=[]}return{target:`org:${i.id}`,orgId:i.id,slug:i.slug,name:i.name,role:i.role,collections:s.map(a=>({id:a.id,slug:a.slug,name:a.name}))}}));return{personal:{target:"personal",name:"Personal Blog"},organizations:n}}};import{randomUUID as le}from"node:crypto";async function $r(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new h(e?.error?.code||`http_${t.status}`,e?.error?.message||`LixBlogs returned HTTP ${t.status}.`,{status:t.status,requestId:e?.error?.requestId||t.headers.get("x-request-id"),details:e?.error?.details});return e.data}var ce=class{constructor(e){this.http=e}async request(e,r={}){let o=await this.http.request(e,{...r,headers:{accept:"application/json",...r.body?{"content-type":"application/json"}:{},...r.headers}});return $r(o)}async list(e){return await this.http.requireScopes(["lixblogs:collaboration:read"]),this.request(`/api/v1/blogs/${encodeURIComponent(e)}/collaborators`)}async invitations(){return await this.http.requireScopes(["lixblogs:collaboration:read"]),this.request("/api/v1/collaboration/invitations")}async invite(e,{user:r,role:o,idempotencyKey:n=le()}){return await this.http.requireScopes(["lixblogs:collaboration:write"]),this.request(`/api/v1/blogs/${encodeURIComponent(e)}/collaborators`,{method:"POST",headers:{"idempotency-key":n},body:JSON.stringify({user:r,role:o})})}async role(e,{user:r,role:o,idempotencyKey:n=le()}){return await this.http.requireScopes(["lixblogs:collaboration:write"]),this.request(`/api/v1/blogs/${encodeURIComponent(e)}/collaborators`,{method:"PATCH",headers:{"idempotency-key":n},body:JSON.stringify({user:r,role:o})})}async remove(e,{user:r,idempotencyKey:o=le()}={}){return await this.http.requireScopes(["lixblogs:collaboration:write"]),this.request(`/api/v1/blogs/${encodeURIComponent(e)}/collaborators`,{method:"DELETE",headers:{"idempotency-key":o},body:JSON.stringify({...r?{user:r}:{}})})}async resolveInvitation(e,{action:r,showOnProfile:o=!0,idempotencyKey:n=le()}){return await this.http.requireScopes(["lixblogs:collaboration:write"]),this.request("/api/v1/collaboration/invitations",{method:"POST",headers:{"idempotency-key":n},body:JSON.stringify({blogId:e,action:r,showOnProfile:o})})}};async function qr(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new h(e?.error?.code||`http_${t.status}`,e?.error?.message||`LixBlogs returned HTTP ${t.status}.`,{status:t.status,requestId:e?.error?.requestId||t.headers.get("x-request-id"),details:e?.error?.details});return e}var ue=class{constructor(e){this.http=e}async query(e={}){let r=e.scope||"personal";await this.http.requireScopes(["lixblogs:analytics:read",...r.startsWith("org:")?["lixblogs:organizations:read"]:[]]);let o=new URLSearchParams({scope:r,range:e.range||(e.from||e.to?"custom":"30d"),dimension:e.dimension||"overview",limit:String(e.limit||20)});return e.from&&o.set("from",e.from),e.to&&o.set("to",e.to),e.cursor&&o.set("cursor",e.cursor),qr(await this.http.request(`/api/v1/analytics?${o}`,{headers:{accept:"application/json"}}))}};var de=class{constructor(e){this.http=e}async requireScopes(e){typeof this.http.requireScopes=="function"&&await this.http.requireScopes(e)}async _request(e,r={}){let o=await this.http.request(e,r),n;try{n=await o.json()}catch{n=null}if(!o.ok||n?.error){let i=new Error(n?.error?.message||`Request failed with HTTP ${o.status}`);throw i.code=n?.error?.code||`http_${o.status}`,i.status=o.status,i.requestId=n?.error?.requestId||o.headers.get("x-request-id")||null,i.details=n?.error?.details||null,i}return n.data}async cloudinaryStatus(){return await this.requireScopes(["lixblogs:integrations:cloudinary:read"]),this._request("/api/v1/integrations/cloudinary")}async cloudinaryDisconnect(){return await this.requireScopes(["lixblogs:integrations:cloudinary:disconnect"]),this._request("/api/v1/integrations/cloudinary",{method:"DELETE"})}async pollinationsStatus({refresh:e=!1}={}){return await this.requireScopes(["lixblogs:media:read"]),this._request(`/api/v1/integrations/pollinations${e?"?refresh=1":""}`)}async pollinationsDisconnect(){return await this.requireScopes(["lixblogs:media:write"]),this._request("/api/v1/integrations/pollinations",{method:"DELETE"})}};import{randomUUID as tt}from"node:crypto";var Ar=Object.freeze({"image/avif":"avif","image/bmp":"bmp","image/jpeg":"jpg","image/png":"png","image/svg+xml":"svg","image/webp":"webp"});async function Ie(t){let e=await t.json().catch(()=>({})),r=new Error(e.error?.message||e.error||`Media request failed with HTTP ${t.status}`);return r.code=e.error?.code||e.code||`http_${t.status}`,r.status=t.status,r}var pe=class{constructor(e){this.http=e}async generate({prompt:e,model:r="flux",seed:o,width:n,height:i,destination:s="inline",generationId:a=tt(),reference:l}){await this.http.requireScopes(["lixblogs:media:write"]);let u,d;if(l){u=new FormData;for(let[f,E]of Object.entries({prompt:e,model:r,seed:o,width:n,height:i,destination:s,generationId:a}))E!==void 0&&u.append(f,String(E));u.append("referenceImage",new Blob([l.bytes],{type:l.mimeType}),l.name||"reference-image"),d={accept:"image/*, application/json"}}else u=JSON.stringify({prompt:e,model:r,seed:o,width:n,height:i,destination:s,generationId:a}),d={"content-type":"application/json",accept:"image/*, application/json"};let c=await this.http.requestRaw("/api/v1/media/generate",{method:"POST",headers:d,body:u});if(!c.ok)throw await Ie(c);return{bytes:new Uint8Array(await c.arrayBuffer()),mimeType:c.headers.get("content-type")||"image/jpeg",generationId:a}}async upload({bytes:e,mimeType:r,blogId:o,mediaType:n="inline",uploadId:i=tt()}){await this.http.requireScopes(["lixblogs:media:write"]);let s=new FormData,a=Ar[r];if(!a)throw new Error(`Unsupported image MIME type: ${r}`);s.append("file",new Blob([e],{type:r}),`lixblogs-${i}.${a}`),s.append("type",n),s.append("uploadId",i),o&&s.append("blogId",o);let l=await this.http.requestRaw("/api/v1/media/upload",{method:"POST",body:s,headers:{accept:"application/json"}});if(!l.ok)throw await Ie(l);return l.json()}async delete(e){if(!e)throw new Error("A media ID is required.");await this.http.requireScopes(["lixblogs:media:write"]);let r=await this.http.request(`/api/v1/media/${encodeURIComponent(e)}`,{method:"DELETE"});if(!r.ok)throw await Ie(r);let o=await r.json();return o.data||o}};var g=Object.freeze({OK:0,ERROR:1,USAGE:2,CONFLICT:3,AUTH:4,CONFIRMATION:5}),Rr=Object.freeze({login:["auth","login"],logout:["auth","logout"],whoami:["auth","whoami"],profiles:["auth","profiles"],use:["auth","use"]});function rt(t){let[e,...r]=t,o=Rr[e];return o?[...o,...r]:t}function ot(t,e="cli_error"){if(t&&typeof t=="object"&&t.error&&!Array.isArray(t.error))return t;let r=t&&typeof t=="object"?t:{message:String(t||"Command failed.")};return{ok:!1,error:{code:r.code||e,message:r.message||"Command failed.",hint:r.hint||null,requestId:r.requestId||null,...r.details?{details:r.details}:{}}}}function y(t,e){if(t.yes)return;let r=new Error(`${e} requires --yes in non-interactive operation.`);throw r.code="confirmation_required",r.hint="Review the operation, then run it again with --yes.",r.exitCode=g.CONFIRMATION,r}var x=Object.freeze({reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",violet:"\x1B[38;5;141m",green:"\x1B[38;5;42m",yellow:"\x1B[38;5;220m",red:"\x1B[38;5;203m",gray:"\x1B[38;5;245m"});function p(t=process.stdout,e=process.env){return!!t.isTTY&&e.NO_COLOR===void 0&&e.TERM!=="dumb"}function k(t,e,r){return r?`${e}${t}${x.reset}`:t}function it({url:t,code:e,expiresInSeconds:r,profile:o,interactive:n,color:i=!1}){let s=`${k("\u25C6",x.violet,i)} ${k("LixBlogs",x.bold,i)}`,a=n?"Press Enter to open here, or use the URL on another device.":"Open the URL in any browser and approve this device.";return["",` ${s}`,` ${k("Device login",x.dim,i)}`," \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",` URL ${t}`,` Code ${k(e,x.bold,i)}`,` Expires ${Math.ceil(r/60)} min`,o?` Profile ${o} ${k("(local credential slot)",x.dim,i)}`:` Profile ${k("your Accounts username after approval",x.dim,i)}`,"",` ${a}`," No localhost callback or exposed port is required.",""].join(`
5
+ `)}function v(t,e=!1){return` ${k(`\u2713 ${t}`,x.green,e)}`}function T(t,e=!1){return` ${k(`! ${t}`,x.yellow,e)}`}function C(t,e=!1){return` ${k(`\u2022 ${t}`,x.gray,e)}`}function st(t,e=!1){return` ${k(`\u2715 ${t}`,x.red,e)}`}var nt=Object.freeze(["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"]);function Pr(t,{stream:e=process.stderr,enabled:r=!!e.isTTY,intervalMs:o=80}={}){if(!r)return{stop(){}};let n=0,i=()=>{e.write(`\r\x1B[2K${k(nt[n],x.violet,p(e))} ${t}`),n=(n+1)%nt.length};i();let s=setInterval(i,o);s.unref?.();let a=!1;return{stop(){a||(a=!0,clearInterval(s),e.write("\r\x1B[2K"))}}}async function q(t,e,r,o={}){let n=Pr(e,{...o,enabled:o.enabled??(!t?.json&&!t?.quiet&&!!(o.stream||process.stderr).isTTY)});try{return await r()}finally{n.stop()}}function at({input:t=process.stdin,open:e,url:r}){if(!t.isTTY||typeof e!="function")return()=>{};let o=()=>{Promise.resolve(e(r)).catch(()=>{})};return t.setEncoding?.("utf8"),t.once("data",o),t.resume?.(),()=>{t.off?.("data",o),t.pause?.()}}function B(t){return[{type:"text",text:t}]}function lt(t){let e=String(t||"").replace(/\r\n/g,`
6
6
  `).split(`
7
- `),r=[],o=[],i=()=>{o.length&&(r.push({type:"paragraph",content:C(o.join(" ").trim())}),o=[])};for(let n=0;n<e.length;n+=1){let s=e[n].trim();if(!s){i();continue}let l=s.match(/^```([\w+-]*)/);if(l){i();let ue=[];for(n+=1;n<e.length&&!/^```/.test(e[n].trim());)ue.push(e[n++]);r.push(l[1].toLowerCase()==="mermaid"?{type:"mermaidBlock",props:{diagram:ue.join(`
8
- `)}}:{type:"codeBlock",props:{language:l[1].toLowerCase()},content:C(ue.join(`
9
- `))});continue}let u=s.match(/^(#{1,3})\s+(.+)/);if(u){i(),r.push({type:"heading",props:{level:String(u[1].length)},content:C(u[2])});continue}let d=s.match(/^[-*]\s+(.+)/);if(d){i(),r.push({type:"bulletListItem",content:C(d[1])});continue}let c=s.match(/^\d+\.\s+(.+)/);if(c){i(),r.push({type:"numberedListItem",content:C(c[1])});continue}let p=s.match(/^>\s?(.*)/);if(p){i(),r.push({type:"quote",content:C(p[1])});continue}let v=s.match(/^!\[([^\]]*)\]\((https:\/\/[^)]+)\)$/);if(v){i(),r.push({type:"image",props:{url:v[2],caption:v[1]}});continue}if(/^([-*_])\1{2,}$/.test(s)){i(),r.push({type:"divider"});continue}o.push(s)}return i(),r}function Sr(t){return(t?.content||[]).map(e=>typeof e=="string"?e:e?.text||"").join("")}function le(t){return(t||[]).map(e=>{let r=Sr(e);return e.type==="heading"?`${"#".repeat(Number(e.props?.level)||1)} ${r}`:e.type==="bulletListItem"?`- ${r}`:e.type==="numberedListItem"?`1. ${r}`:e.type==="quote"?`> ${r}`:e.type==="codeBlock"?`\`\`\`${e.props?.language||""}
7
+ `),r=[],o=[],n=()=>{o.length&&(r.push({type:"paragraph",content:B(o.join(" ").trim())}),o=[])};for(let i=0;i<e.length;i+=1){let a=e[i].trim();if(!a){n();continue}let l=a.match(/^```([\w+-]*)/);if(l){n();let ge=[];for(i+=1;i<e.length&&!/^```/.test(e[i].trim());)ge.push(e[i++]);r.push(l[1].toLowerCase()==="mermaid"?{type:"mermaidBlock",props:{diagram:ge.join(`
8
+ `)}}:{type:"codeBlock",props:{language:l[1].toLowerCase()},content:B(ge.join(`
9
+ `))});continue}let u=a.match(/^(#{1,3})\s+(.+)/);if(u){n(),r.push({type:"heading",props:{level:String(u[1].length)},content:B(u[2])});continue}let d=a.match(/^[-*]\s+(.+)/);if(d){n(),r.push({type:"bulletListItem",content:B(d[1])});continue}let c=a.match(/^\d+\.\s+(.+)/);if(c){n(),r.push({type:"numberedListItem",content:B(c[1])});continue}let f=a.match(/^>\s?(.*)/);if(f){n(),r.push({type:"quote",content:B(f[1])});continue}let E=a.match(/^!\[([^\]]*)\]\((https:\/\/[^)]+)\)$/);if(E){n(),r.push({type:"image",props:{url:E[2],caption:E[1]}});continue}if(/^([-*_])\1{2,}$/.test(a)){n(),r.push({type:"divider"});continue}o.push(a)}return n(),r}function Tr(t){return(t?.content||[]).map(e=>typeof e=="string"?e:e?.text||"").join("")}function fe(t){return(t||[]).map(e=>{let r=Tr(e);return e.type==="heading"?`${"#".repeat(Number(e.props?.level)||1)} ${r}`:e.type==="bulletListItem"?`- ${r}`:e.type==="numberedListItem"?`1. ${r}`:e.type==="quote"?`> ${r}`:e.type==="codeBlock"?`\`\`\`${e.props?.language||""}
10
10
  ${r}
11
11
  \`\`\``:e.type==="mermaidBlock"?`\`\`\`mermaid
12
12
  ${e.props?.diagram||""}
13
13
  \`\`\``:e.type==="image"?`![${e.props?.caption||""}](${e.props?.url||""})`:e.type==="divider"?"---":r}).join(`
14
14
 
15
- `)}import{promises as G}from"node:fs";import{tmpdir as qr}from"node:os";import be from"node:path";import{spawn as $r}from"node:child_process";async function Ar(t){let e="";t.setEncoding("utf8");for await(let r of t)e+=r;return e}async function Rr(t="",e=process.env.EDITOR||process.env.VISUAL){if(!e)throw new Error("$EDITOR or $VISUAL must be set when using --editor.");let r=await G.mkdtemp(be.join(qr(),"lixblogs-")),o=be.join(r,"post.md");await G.writeFile(o,t,{mode:384});try{return await new Promise((i,n)=>{let a=$r(e,[o],{stdio:"inherit",shell:!0});a.once("error",n),a.once("exit",s=>s===0?i():n(new Error(`Editor exited with code ${s}.`)))}),await G.readFile(o,"utf8")}finally{await G.rm(r,{recursive:!0,force:!0})}}async function ve(t,{stdin:e=process.stdin,initial:r=""}={}){let o=[t.file!==void 0,t.stdin,t.content!==void 0,t.editor].filter(Boolean).length;if(o>1)throw new Error("Use only one of --file, --stdin, --content, or --editor.");if(!o)return null;let i;return t.file!==void 0?i=await G.readFile(be.resolve(t.file),"utf8"):t.stdin?i=await Ar(e):t.content!==void 0?i=t.content:i=await Rr(r),{markdown:i,blocks:ot(i)}}function xe(t){let e={},r={title:"title",subtitle:"subtitle",slug:"slug",emoji:"emoji",publication:"publishedAs",collection:"collectionId",cover:"coverUrl"};for(let[o,i]of Object.entries(r))t[o]!==void 0&&(e[i]=t[o]);return t.tag!==void 0&&(e.tags=t.tag),t["member-only"]&&(e.memberOnly=!0),t["no-member-only"]&&(e.memberOnly=!1),t.secret&&(e.secret=!0),t["not-secret"]&&(e.secret=!1),t["allow-comments"]&&(e.allowComments=!0),t["no-comments"]&&(e.allowComments=!1),(t["cover-x"]!==void 0||t["cover-y"]!==void 0)&&(e.coverPosition={x:Number(t["cover-x"]??50),y:Number(t["cover-y"]??50)}),t["cover-zoom"]!==void 0&&(e.coverZoom=Number(t["cover-zoom"])),e}function Pr(t){let e=[],r=o=>{for(let i of o||[]){for(let n of i?.content||[]){let a=typeof n=="string"?n:n?.text||"";e.push(...a.trim().split(/\s+/).filter(Boolean))}i?.children&&r(i.children)}};return r(t),e.length}function ce(t,{publishing:e=!1}={}){if(t.title!==void 0&&(typeof t.title!="string"||t.title.length>300))throw new Error("Title must be 300 characters or fewer.");if(t.subtitle!==void 0&&(typeof t.subtitle!="string"||t.subtitle.length>500))throw new Error("Subtitle must be 500 characters or fewer.");if(t.tags!==void 0&&(!Array.isArray(t.tags)||t.tags.length>5))throw new Error("Use at most five tags.");if(t.coverUrl&&!/^https:\/\//i.test(t.coverUrl))throw new Error("Cover URLs must use HTTPS.");if(t.publishedAs&&t.publishedAs!=="personal"&&!/^org:[^:]+$/.test(t.publishedAs))throw new Error("Publication must be personal or org:<id>.");if(t.content!==void 0){if(!Array.isArray(t.content))throw new Error("Blog content must be a block array.");if(Buffer.byteLength(JSON.stringify(t.content),"utf8")>15e5)throw new Error("Blog content exceeds the 1.5 MB limit.")}if(e){if(!t.title?.trim())throw new Error("A title is required before publishing.");if(Pr(t.content)<20)throw new Error("A post needs at least 20 words before publishing.")}return t}import{promises as Ie}from"node:fs";import Ee from"node:path";async function it({client:t,options:e}){return t.list({status:e.status,limit:e.limit,cursor:e.cursor})}async function _e({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");let r=await t.get(e);return{...r,markdown:le(r.content)}}async function nt({client:t,options:e,stdin:r}){let o=await ve(e,{stdin:r}),i={...xe(e),content:o?.blocks||[]};return ce(i),e["dry-run"]?{dryRun:!0,input:i,markdown:o?.markdown||""}:t.create(i,{idempotencyKey:e["idempotency-key"]})}async function st({client:t,id:e,options:r,stdin:o}){if(!e)throw new Error("A blog ID is required.");let i=await t.get(e),n=await ve(r,{stdin:o,initial:le(i.content)}),a={...xe(r),...n?{content:n.blocks}:{}};if(!Object.keys(a).length)throw new Error("No blog changes were provided.");if(ce(a),r["dry-run"])return{dryRun:!0,id:e,etag:i.etag,input:a,markdown:n?.markdown};try{return await t.update(e,a,{etag:r.etag||i.etag})}catch(s){if(!(s instanceof h)||s.code!=="revision_conflict")throw s;let l=await t.get(e),u=r.conflictDirectory||Ee.resolve(".lixblogs-conflicts");await Ie.mkdir(u,{recursive:!0});let d=e.replace(/[^A-Za-z0-9._-]/g,"_"),c=Ee.join(u,`${d}-local.json`),p=Ee.join(u,`${d}-server.md`);throw await Promise.all([Ie.writeFile(c,JSON.stringify(a,null,2),{mode:384}),Ie.writeFile(p,le(l.content),{mode:384})]),s.details={...s.details,localPath:c,serverPath:p,serverEtag:l.etag},s}}async function at({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");let o=await t.get(e);ce(o,{publishing:!0});let i=r.status||"published";if(!["published","unlisted"].includes(i))throw new Error("--status must be published or unlisted.");return r["dry-run"]?{dryRun:!0,id:e,from:o.status,to:i}:(g(r,"Publishing this blog"),t.publish(e,{etag:r.etag||o.etag,status:i,idempotencyKey:r["idempotency-key"]}))}async function lt({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");return{data:await t.versions(e)}}async function ct({client:t,id:e,options:r}){if(!e||!r.version)throw new Error("A blog ID and --version are required.");g(r,"Restoring this historical version");let o=await t.get(e);return t.restoreVersion(e,r.version,{etag:r.etag||o.etag})}async function ut({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");let o=await t.get(e);return r["dry-run"]?{dryRun:!0,id:e,from:o.status,to:"draft"}:(g(r,"Unpublishing this blog"),t.unpublish(e,{etag:r.etag||o.etag}))}async function ke({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");if(!r.yes)throw new Error("Deletion requires --yes. Trash is the default; add --permanent for irreversible deletion.");let o=await t.get(e);return r["dry-run"]?{dryRun:!0,id:e,permanent:r.permanent}:t.delete(e,{etag:r.etag||o.etag,permanent:r.permanent})}async function dt({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");let o=await t.get(e);return r["dry-run"]?{dryRun:!0,id:e,restoreTo:o.preDeleteStatus||"draft"}:(g(r,"Restoring this blog"),t.restore(e,{etag:r.etag||o.etag}))}async function pt({client:t}){return t.list()}async function ft({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.get(e)}async function mt({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.collections(e)}async function gt({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.members(e)}async function ht({client:t}){return t.targets()}function L(t){if(!t)throw new Error("A blog ID is required.")}async function yt({client:t,id:e}){return L(e),t.list(e)}async function wt({client:t}){return t.invitations()}async function bt({client:t,id:e,options:r}){if(L(e),!r.user)throw new Error("--user is required.");if(!["viewer","editor","admin"].includes(r.role))throw new Error("--role must be viewer, editor, or admin.");return r["dry-run"]?{dryRun:!0,action:"invite",blogId:e,user:r.user,role:r.role}:(g(r,"Inviting this collaborator"),t.invite(e,{user:r.user,role:r.role,idempotencyKey:r["idempotency-key"]}))}async function vt({client:t,id:e,options:r}){if(L(e),!r.user)throw new Error("--user is required.");if(!["viewer","editor","admin"].includes(r.role))throw new Error("--role must be viewer, editor, or admin.");return r["dry-run"]?{dryRun:!0,action:"role",blogId:e,user:r.user,role:r.role}:(g(r,"Changing this collaborator role"),t.role(e,{user:r.user,role:r.role,idempotencyKey:r["idempotency-key"]}))}async function xt({client:t,id:e,options:r}){return L(e),r["dry-run"]?{dryRun:!0,action:"remove",blogId:e,user:r.user||"self"}:(g(r,"Removing this collaborator or invitation"),t.remove(e,{user:r.user,idempotencyKey:r["idempotency-key"]}))}async function It({client:t,id:e,options:r}){return L(e),r["dry-run"]?{dryRun:!0,action:"accept",blogId:e,showOnProfile:!r["hide-on-profile"]}:(g(r,"Accepting this collaboration invitation"),t.resolveInvitation(e,{action:"accept",showOnProfile:!r["hide-on-profile"],idempotencyKey:r["idempotency-key"]}))}async function Et({client:t,id:e,options:r}){return L(e),r["dry-run"]?{dryRun:!0,action:"decline",blogId:e}:(g(r,"Declining this collaboration invitation"),t.resolveInvitation(e,{action:"decline",idempotencyKey:r["idempotency-key"]}))}import{access as Tr,cp as Or,readFile as jr,readdir as Cr}from"node:fs/promises";import b from"node:path";import{fileURLToPath as Lr}from"node:url";var Se=b.dirname(Lr(import.meta.url)),St=b.basename(Se)==="dist"?b.resolve(Se,".."):b.resolve(Se,"../../.."),_t=b.join(St,"skills"),kt=b.resolve(St,"../..",".agents","skills");async function U(t){try{return await Tr(t),!0}catch{return!1}}async function qe(){if(await U(_t))return _t;if(await U(kt))return kt;let t=new Error("No bundled LixBlogs skills were found. Reinstall @elixpo/lixblogs-cli.");throw t.code="skills_unavailable",t}function qt(t){if(!/^lixblogs-[a-z0-9-]+$/.test(t||"")){let e=new Error("A valid lixblogs-* skill name is required.");throw e.code="invalid_skill_name",e}return t}async function $t(t,e){let r=await jr(b.join(t,e,"SKILL.md"),"utf8"),o=r.match(/^description:\s*(.+)$/m)?.[1]||r.match(/^description:\s*>-\s*\n\s*(.+)$/m)?.[1]||"",i=r.match(/`@elixpo\/lixblogs-cli`\s+([0-9.]+)/)?.[1]||null;return{name:e,description:o.trim(),minimumCliVersion:i,content:r}}async function At(){let t=await qe(),e=await Cr(t,{withFileTypes:!0});return Promise.all(e.filter(r=>r.isDirectory()&&r.name.startsWith("lixblogs-")).map(r=>$t(t,r.name))).then(r=>r.map(({content:o,...i})=>i).sort((o,i)=>o.name.localeCompare(i.name)))}async function Rt({name:t}){let e=await qe(),r=qt(t);if(!await U(b.join(e,r,"SKILL.md"))){let o=new Error(`Skill "${r}" is not bundled.`);throw o.code="skill_not_found",o}return $t(e,r)}async function Pt({name:t,options:e}){let r=await qe(),o=qt(t),i=b.join(r,o);if(!await U(b.join(i,"SKILL.md"))){let s=new Error(`Skill "${o}" is not bundled.`);throw s.code="skill_not_found",s}let n=b.resolve(e.target||".agents/skills"),a=b.join(n,o);if(e["dry-run"])return{dryRun:!0,name:o,target:a,replace:await U(a)};if(await U(a)){if(!e.force){let s=new Error(`Skill already exists at ${a}.`);throw s.code="skill_exists",s.hint="Inspect the existing skill or re-run with --force --yes to replace it.",s}g(e,`Replacing ${a}`)}else g(e,`Installing ${o} into ${n}`);return await Or(i,a,{recursive:!0,force:!!e.force}),{installed:!0,name:o,target:a}}import{writeFile as Ur}from"node:fs/promises";var Dr=new Set(["overview","timeline","posts","sources","devices","countries"]),Nr=new Set(["7d","30d","90d","12m","custom"]);function jt(t={}){let e=t.dimension||"overview",r=t.range||(t.from||t.to?"custom":"30d");if(!Dr.has(e))throw new Error(`Unsupported analytics dimension: ${e}.`);if(!Nr.has(r))throw new Error(`Unsupported analytics range: ${r}.`);if(r==="custom"&&(!t.from||!t.to))throw new Error("Custom analytics ranges require --from and --to.");return{scope:t.scope?.[0]||t.publication||"personal",range:r,from:t.from,to:t.to,dimension:e,limit:t.limit,cursor:t.cursor}}async function Ct({client:t,options:e}){return t.query(jt(e))}function Tt(t){let e=t==null?"":typeof t=="object"?JSON.stringify(t):String(t);return/[",\n]/.test(e)?`"${e.replaceAll('"','""')}"`:e}function Ot(t){let e=t?.data?.values;return Array.isArray(e)?e:e?.labels&&Array.isArray(e.labels)?e.labels.map((r,o)=>({label:r,views:e.views?.[o]||0,reads:e.reads?.[o]||0})):e?.totals?Object.entries(e.totals).map(([r,o])=>({metric:r,value:o,previous:e.previous?.[r],change:e.changes?.[r]})):[]}async function Lt({client:t,options:e}){if(!e.output)throw new Error("Analytics export requires --output <file>.");let r=e.format||"json";if(!["json","csv"].includes(r))throw new Error("Analytics export format must be json or csv.");let o=await t.query(jt(e)),i;if(r==="json")i=`${JSON.stringify(o,null,2)}
16
- `;else{let n=Ot(o),a=[...new Set(n.flatMap(s=>Object.keys(s)))];i=`${a.map(Tt).join(",")}
17
- ${n.map(s=>a.map(l=>Tt(s[l])).join(",")).join(`
15
+ `)}import{promises as K}from"node:fs";import{tmpdir as Or}from"node:os";import Ee from"node:path";import{spawn as jr}from"node:child_process";async function Cr(t){let e="";t.setEncoding("utf8");for await(let r of t)e+=r;return e}async function Lr(t="",e=process.env.EDITOR||process.env.VISUAL){if(!e)throw new Error("$EDITOR or $VISUAL must be set when using --editor.");let r=await K.mkdtemp(Ee.join(Or(),"lixblogs-")),o=Ee.join(r,"post.md");await K.writeFile(o,t,{mode:384});try{return await new Promise((n,i)=>{let s=jr(e,[o],{stdio:"inherit",shell:!0});s.once("error",i),s.once("exit",a=>a===0?n():i(new Error(`Editor exited with code ${a}.`)))}),await K.readFile(o,"utf8")}finally{await K.rm(r,{recursive:!0,force:!0})}}async function _e(t,{stdin:e=process.stdin,initial:r=""}={}){let o=[t.file!==void 0,t.stdin,t.content!==void 0,t.editor].filter(Boolean).length;if(o>1)throw new Error("Use only one of --file, --stdin, --content, or --editor.");if(!o)return null;let n;return t.file!==void 0?n=await K.readFile(Ee.resolve(t.file),"utf8"):t.stdin?n=await Cr(e):t.content!==void 0?n=t.content:n=await Lr(r),{markdown:n,blocks:lt(n)}}function ke(t){let e={},r={title:"title",subtitle:"subtitle",slug:"slug",emoji:"emoji",publication:"publishedAs",collection:"collectionId",cover:"coverUrl"};for(let[o,n]of Object.entries(r))t[o]!==void 0&&(e[n]=t[o]);return t.tag!==void 0&&(e.tags=t.tag),t["member-only"]&&(e.memberOnly=!0),t["no-member-only"]&&(e.memberOnly=!1),t.secret&&(e.secret=!0),t["not-secret"]&&(e.secret=!1),t["allow-comments"]&&(e.allowComments=!0),t["no-comments"]&&(e.allowComments=!1),(t["cover-x"]!==void 0||t["cover-y"]!==void 0)&&(e.coverPosition={x:Number(t["cover-x"]??50),y:Number(t["cover-y"]??50)}),t["cover-zoom"]!==void 0&&(e.coverZoom=Number(t["cover-zoom"])),e}function Ur(t){let e=[],r=o=>{for(let n of o||[]){for(let i of n?.content||[]){let s=typeof i=="string"?i:i?.text||"";e.push(...s.trim().split(/\s+/).filter(Boolean))}n?.children&&r(n.children)}};return r(t),e.length}function me(t,{publishing:e=!1}={}){if(t.title!==void 0&&(typeof t.title!="string"||t.title.length>300))throw new Error("Title must be 300 characters or fewer.");if(t.subtitle!==void 0&&(typeof t.subtitle!="string"||t.subtitle.length>500))throw new Error("Subtitle must be 500 characters or fewer.");if(t.tags!==void 0&&(!Array.isArray(t.tags)||t.tags.length>5))throw new Error("Use at most five tags.");if(t.coverUrl&&!/^https:\/\//i.test(t.coverUrl))throw new Error("Cover URLs must use HTTPS.");if(t.publishedAs&&t.publishedAs!=="personal"&&!/^org:[^:]+$/.test(t.publishedAs))throw new Error("Publication must be personal or org:<id>.");if(t.content!==void 0){if(!Array.isArray(t.content))throw new Error("Blog content must be a block array.");if(Buffer.byteLength(JSON.stringify(t.content),"utf8")>15e5)throw new Error("Blog content exceeds the 1.5 MB limit.")}if(e){if(!t.title?.trim())throw new Error("A title is required before publishing.");if(Ur(t.content)<20)throw new Error("A post needs at least 20 words before publishing.")}return t}import{promises as Se}from"node:fs";import $e from"node:path";async function ct({client:t,options:e}){return t.list({status:e.status,limit:e.limit,cursor:e.cursor})}async function qe({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");let r=await t.get(e);return{...r,markdown:fe(r.content)}}async function ut({client:t,options:e,stdin:r}){let o=await _e(e,{stdin:r}),n={...ke(e),content:o?.blocks||[]};return me(n),e["dry-run"]?{dryRun:!0,input:n,markdown:o?.markdown||""}:t.create(n,{idempotencyKey:e["idempotency-key"]})}async function dt({client:t,id:e,options:r,stdin:o}){if(!e)throw new Error("A blog ID is required.");let n=await t.get(e),i=await _e(r,{stdin:o,initial:fe(n.content)}),s={...ke(r),...i?{content:i.blocks}:{}};if(!Object.keys(s).length)throw new Error("No blog changes were provided.");if(me(s),r["dry-run"])return{dryRun:!0,id:e,etag:n.etag,input:s,markdown:i?.markdown};try{return await t.update(e,s,{etag:r.etag||n.etag})}catch(a){if(!(a instanceof h)||a.code!=="revision_conflict")throw a;let l=await t.get(e),u=r.conflictDirectory||$e.resolve(".lixblogs-conflicts");await Se.mkdir(u,{recursive:!0});let d=e.replace(/[^A-Za-z0-9._-]/g,"_"),c=$e.join(u,`${d}-local.json`),f=$e.join(u,`${d}-server.md`);throw await Promise.all([Se.writeFile(c,JSON.stringify(s,null,2),{mode:384}),Se.writeFile(f,fe(l.content),{mode:384})]),a.details={...a.details,localPath:c,serverPath:f,serverEtag:l.etag},a}}async function pt({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");let o=await t.get(e);me(o,{publishing:!0});let n=r.status||"published";if(!["published","unlisted"].includes(n))throw new Error("--status must be published or unlisted.");return r["dry-run"]?{dryRun:!0,id:e,from:o.status,to:n}:(y(r,"Publishing this blog"),t.publish(e,{etag:r.etag||o.etag,status:n,idempotencyKey:r["idempotency-key"]}))}async function ft({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");return{data:await t.versions(e)}}async function mt({client:t,id:e,options:r}){if(!e||!r.version)throw new Error("A blog ID and --version are required.");y(r,"Restoring this historical version");let o=await t.get(e);return t.restoreVersion(e,r.version,{etag:r.etag||o.etag})}async function gt({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");let o=await t.get(e);return r["dry-run"]?{dryRun:!0,id:e,from:o.status,to:"draft"}:(y(r,"Unpublishing this blog"),t.unpublish(e,{etag:r.etag||o.etag}))}async function Ae({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");if(!r.yes)throw new Error("Deletion requires --yes. Trash is the default; add --permanent for irreversible deletion.");let o=await t.get(e);return r["dry-run"]?{dryRun:!0,id:e,permanent:r.permanent}:t.delete(e,{etag:r.etag||o.etag,permanent:r.permanent})}async function ht({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");let o=await t.get(e);return r["dry-run"]?{dryRun:!0,id:e,restoreTo:o.preDeleteStatus||"draft"}:(y(r,"Restoring this blog"),t.restore(e,{etag:r.etag||o.etag}))}async function yt({client:t}){return t.list()}async function wt({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.get(e)}async function bt({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.collections(e)}async function vt({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.members(e)}async function xt({client:t}){return t.targets()}function M(t){if(!t)throw new Error("A blog ID is required.")}async function It({client:t,id:e}){return M(e),t.list(e)}async function Et({client:t}){return t.invitations()}async function _t({client:t,id:e,options:r}){if(M(e),!r.user)throw new Error("--user is required.");if(!["viewer","editor","admin"].includes(r.role))throw new Error("--role must be viewer, editor, or admin.");return r["dry-run"]?{dryRun:!0,action:"invite",blogId:e,user:r.user,role:r.role}:(y(r,"Inviting this collaborator"),t.invite(e,{user:r.user,role:r.role,idempotencyKey:r["idempotency-key"]}))}async function kt({client:t,id:e,options:r}){if(M(e),!r.user)throw new Error("--user is required.");if(!["viewer","editor","admin"].includes(r.role))throw new Error("--role must be viewer, editor, or admin.");return r["dry-run"]?{dryRun:!0,action:"role",blogId:e,user:r.user,role:r.role}:(y(r,"Changing this collaborator role"),t.role(e,{user:r.user,role:r.role,idempotencyKey:r["idempotency-key"]}))}async function St({client:t,id:e,options:r}){return M(e),r["dry-run"]?{dryRun:!0,action:"remove",blogId:e,user:r.user||"self"}:(y(r,"Removing this collaborator or invitation"),t.remove(e,{user:r.user,idempotencyKey:r["idempotency-key"]}))}async function $t({client:t,id:e,options:r}){return M(e),r["dry-run"]?{dryRun:!0,action:"accept",blogId:e,showOnProfile:!r["hide-on-profile"]}:(y(r,"Accepting this collaboration invitation"),t.resolveInvitation(e,{action:"accept",showOnProfile:!r["hide-on-profile"],idempotencyKey:r["idempotency-key"]}))}async function qt({client:t,id:e,options:r}){return M(e),r["dry-run"]?{dryRun:!0,action:"decline",blogId:e}:(y(r,"Declining this collaboration invitation"),t.resolveInvitation(e,{action:"decline",idempotencyKey:r["idempotency-key"]}))}import{access as Dr,cp as Nr,readFile as Br,readdir as Mr}from"node:fs/promises";import I from"node:path";import{fileURLToPath as Fr}from"node:url";var Re=I.dirname(Fr(import.meta.url)),Pt=I.basename(Re)==="dist"?I.resolve(Re,".."):I.resolve(Re,"../../.."),At=I.join(Pt,"skills"),Rt=I.resolve(Pt,"../..",".agents","skills");async function F(t){try{return await Dr(t),!0}catch{return!1}}async function Pe(){if(await F(At))return At;if(await F(Rt))return Rt;let t=new Error("No bundled LixBlogs skills were found. Reinstall @elixpo/lixblogs-cli.");throw t.code="skills_unavailable",t}function Tt(t){if(!/^lixblogs-[a-z0-9-]+$/.test(t||"")){let e=new Error("A valid lixblogs-* skill name is required.");throw e.code="invalid_skill_name",e}return t}async function Ot(t,e){let r=await Br(I.join(t,e,"SKILL.md"),"utf8"),o=r.match(/^description:\s*(.+)$/m)?.[1]||r.match(/^description:\s*>-\s*\n\s*(.+)$/m)?.[1]||"",n=r.match(/`@elixpo\/lixblogs-cli`\s+([0-9.]+)/)?.[1]||null;return{name:e,description:o.trim(),minimumCliVersion:n,content:r}}async function jt(){let t=await Pe(),e=await Mr(t,{withFileTypes:!0});return Promise.all(e.filter(r=>r.isDirectory()&&r.name.startsWith("lixblogs-")).map(r=>Ot(t,r.name))).then(r=>r.map(({content:o,...n})=>n).sort((o,n)=>o.name.localeCompare(n.name)))}async function Ct({name:t}){let e=await Pe(),r=Tt(t);if(!await F(I.join(e,r,"SKILL.md"))){let o=new Error(`Skill "${r}" is not bundled.`);throw o.code="skill_not_found",o}return Ot(e,r)}async function Lt({name:t,options:e}){let r=await Pe(),o=Tt(t),n=I.join(r,o);if(!await F(I.join(n,"SKILL.md"))){let a=new Error(`Skill "${o}" is not bundled.`);throw a.code="skill_not_found",a}let i=I.resolve(e.target||".agents/skills"),s=I.join(i,o);if(e["dry-run"])return{dryRun:!0,name:o,target:s,replace:await F(s)};if(await F(s)){if(!e.force){let a=new Error(`Skill already exists at ${s}.`);throw a.code="skill_exists",a.hint="Inspect the existing skill or re-run with --force --yes to replace it.",a}y(e,`Replacing ${s}`)}else y(e,`Installing ${o} into ${i}`);return await Nr(n,s,{recursive:!0,force:!!e.force}),{installed:!0,name:o,target:s}}import{writeFile as zr}from"node:fs/promises";var Gr=new Set(["overview","timeline","posts","sources","devices","countries"]),Vr=new Set(["7d","30d","90d","12m","custom"]);function Nt(t={}){let e=t.dimension||"overview",r=t.range||(t.from||t.to?"custom":"30d");if(!Gr.has(e))throw new Error(`Unsupported analytics dimension: ${e}.`);if(!Vr.has(r))throw new Error(`Unsupported analytics range: ${r}.`);if(r==="custom"&&(!t.from||!t.to))throw new Error("Custom analytics ranges require --from and --to.");return{scope:t.scope?.[0]||t.publication||"personal",range:r,from:t.from,to:t.to,dimension:e,limit:t.limit,cursor:t.cursor}}async function Bt({client:t,options:e}){return t.query(Nt(e))}function Ut(t){let e=t==null?"":typeof t=="object"?JSON.stringify(t):String(t);return/[",\n]/.test(e)?`"${e.replaceAll('"','""')}"`:e}function Dt(t){let e=t?.data?.values;return Array.isArray(e)?e:e?.labels&&Array.isArray(e.labels)?e.labels.map((r,o)=>({label:r,views:e.views?.[o]||0,reads:e.reads?.[o]||0})):e?.totals?Object.entries(e.totals).map(([r,o])=>({metric:r,value:o,previous:e.previous?.[r],change:e.changes?.[r]})):[]}async function Mt({client:t,options:e}){if(!e.output)throw new Error("Analytics export requires --output <file>.");let r=e.format||"json";if(!["json","csv"].includes(r))throw new Error("Analytics export format must be json or csv.");let o=await t.query(Nt(e)),n;if(r==="json")n=`${JSON.stringify(o,null,2)}
16
+ `;else{let i=Dt(o),s=[...new Set(i.flatMap(a=>Object.keys(a)))];n=`${s.map(Ut).join(",")}
17
+ ${i.map(a=>s.map(l=>Ut(a[l])).join(",")).join(`
18
18
  `)}
19
- `}return await Ur(e.output,i,{encoding:"utf8",flag:"wx"}),{output:e.output,format:r,rows:Ot(o).length}}async function Ut({integrationsClient:t,confirmed:e}){if(e!==!0)return{ok:!1,reason:"Disconnect was not confirmed. This is a destructive action and requires explicit confirmation (interactive prompt, or --yes in a non-interactive session)."};try{return{ok:!0,data:await t.cloudinaryDisconnect()}}catch(r){return{ok:!1,error:r}}}async function Dt({integrationsClient:t}){try{return{ok:!0,data:await t.cloudinaryStatus()}}catch(e){return{ok:!1,error:e}}}import{randomUUID as Nt}from"node:crypto";import{promises as $e}from"node:fs";import D from"node:path";function Br(t){let e=r=>t[r]===void 0?void 0:Number.parseInt(t[r],10);return{width:e("width"),height:e("height"),seed:e("seed")}}var Bt=Object.freeze({".avif":"image/avif",".bmp":"image/bmp",".jpeg":"image/jpeg",".jpg":"image/jpeg",".png":"image/png",".svg":"image/svg+xml",".webp":"image/webp"});async function Mt({blogClient:t,blogId:e,media:r,type:o,caption:i}){if(!e)return null;let n=await t.get(e);if(o==="cover")return t.update(e,{coverUrl:r.url},{etag:n.etag});let a=[...n.content||[],{id:Nt(),type:"image",props:{url:r.url,caption:i||"",_mediaId:r.id||""},content:[],children:[]}];return t.update(e,{content:a},{etag:n.etag})}async function Ft({mediaClient:t,blogClient:e,options:r}){let o=r.prompt?.trim();if(!o)throw new Error("--prompt is required.");let i=r.type||"inline";if(!["inline","cover"].includes(i))throw new Error("--type must be inline or cover.");let n;if(r.reference){let c=D.resolve(r.reference),p=D.extname(c).toLowerCase(),v=Bt[p];if(!v)throw new Error("Unsupported reference image type. Use AVIF, BMP, JPEG, PNG, SVG, or WebP.");n={bytes:await $e.readFile(c),mimeType:v,name:D.basename(c)}}let a=await t.generate({prompt:o,model:r.model||"flux",destination:i,reference:n,...Br(r)}),s=a.mimeType.includes("png")?"png":a.mimeType.includes("webp")?"webp":"jpg",l=D.resolve(r.output||`lixblogs-${a.generationId}.${s}`);await $e.writeFile(l,a.bytes,{mode:384});let u=null,d=null;return r.blog&&(u=await t.upload({bytes:a.bytes,mimeType:a.mimeType,blogId:r.blog,mediaType:i,uploadId:a.generationId}),r.attach&&(d=await Mt({blogClient:e,blogId:r.blog,media:u,type:i,caption:r.caption}))),{generationId:a.generationId,output:l,mimeType:a.mimeType,media:u,blog:d}}async function zt({mediaClient:t,blogClient:e,options:r}){if(!r.file)throw new Error("--file is required.");if(!r.blog)throw new Error("--blog is required.");let o=r.type||"inline",i=D.resolve(r.file),n=await $e.readFile(i),a=D.extname(i).toLowerCase(),s=Bt[a];if(!s)throw new Error("Unsupported image type. Use AVIF, BMP, JPEG, PNG, SVG, or WebP.");let l=await t.upload({bytes:n,mimeType:s,blogId:r.blog,mediaType:o,uploadId:r["upload-id"]||Nt()}),u=r.attach?await Mt({blogClient:e,blogId:r.blog,media:l,type:o,caption:r.caption}):null;return{media:l,blog:u}}async function Gt({mediaClient:t,id:e,options:r}){if(!e)throw new Error("A media ID is required.");g(r,"Deleting this media asset from its storage provider");let o=await t.delete(e);return o?.data||o}async function Vt({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");return t.comments(e)}async function Jt({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");if(!r.content?.trim())throw new Error("--content is required.");return t.comment(e,r.content.trim())}async function Ht({client:t,id:e,options:r}){if(!e||!r.parent)throw new Error("A blog ID and --parent comment ID are required.");if(!r.content?.trim())throw new Error("--content is required.");return t.comment(e,r.content.trim(),{parentId:r.parent})}async function Wt({client:t,id:e,options:r}){if(!e||!r.comment)throw new Error("A blog ID and --comment ID are required.");return g(r,"Deleting this comment"),t.deleteComment(e,r.comment)}var zr={profile:{type:"string"},env:{type:"string"},json:{type:"boolean",default:!1},quiet:{type:"boolean",default:!1},yes:{type:"boolean",short:"y",default:!1},"allow-insecure-fallback":{type:"boolean",default:!1},"auth-provider":{type:"string"},"accounts-url":{type:"string"},"api-url":{type:"string"},"client-id":{type:"string"},audience:{type:"string"},scope:{type:"string",multiple:!0},open:{type:"boolean",default:!1},status:{type:"string"},limit:{type:"string"},cursor:{type:"string"},range:{type:"string"},from:{type:"string"},to:{type:"string"},dimension:{type:"string"},format:{type:"string"},output:{type:"string"},file:{type:"string"},stdin:{type:"boolean",default:!1},content:{type:"string"},editor:{type:"boolean",default:!1},title:{type:"string"},subtitle:{type:"string"},slug:{type:"string"},tag:{type:"string",multiple:!0},emoji:{type:"string"},publication:{type:"string"},collection:{type:"string"},cover:{type:"string"},"member-only":{type:"boolean",default:!1},"no-member-only":{type:"boolean",default:!1},secret:{type:"boolean",default:!1},"not-secret":{type:"boolean",default:!1},"dry-run":{type:"boolean",default:!1},"no-input":{type:"boolean",default:!1},etag:{type:"string"},permanent:{type:"boolean",default:!1},"idempotency-key":{type:"string"},user:{type:"string"},role:{type:"string"},"hide-on-profile":{type:"boolean",default:!1},target:{type:"string"},force:{type:"boolean",default:!1},prompt:{type:"string"},reference:{type:"string"},model:{type:"string"},seed:{type:"string"},width:{type:"string"},height:{type:"string"},blog:{type:"string"},type:{type:"string"},attach:{type:"boolean",default:!1},caption:{type:"string"},"upload-id":{type:"string"},version:{type:"string"},parent:{type:"string"},comment:{type:"string"},"allow-comments":{type:"boolean",default:!1},"no-comments":{type:"boolean",default:!1},"cover-x":{type:"string"},"cover-y":{type:"string"},"cover-zoom":{type:"string"},help:{type:"boolean",short:"h",default:!1}},Gr=`lixblogs \u2014 LixBlogs CLI
19
+ `}return await zr(e.output,n,{encoding:"utf8",flag:"wx"}),{output:e.output,format:r,rows:Dt(o).length}}async function Ft({integrationsClient:t,confirmed:e}){if(e!==!0)return{ok:!1,reason:"Disconnect was not confirmed. This is a destructive action and requires explicit confirmation (interactive prompt, or --yes in a non-interactive session)."};try{return{ok:!0,data:await t.cloudinaryDisconnect()}}catch(r){return{ok:!1,error:r}}}async function zt({integrationsClient:t}){try{return{ok:!0,data:await t.cloudinaryStatus()}}catch(e){return{ok:!1,error:e}}}import{randomUUID as Gt}from"node:crypto";import{promises as Te}from"node:fs";import z from"node:path";function Jr(t){let e=r=>t[r]===void 0?void 0:Number.parseInt(t[r],10);return{width:e("width"),height:e("height"),seed:e("seed")}}var Vt=Object.freeze({".avif":"image/avif",".bmp":"image/bmp",".jpeg":"image/jpeg",".jpg":"image/jpeg",".png":"image/png",".svg":"image/svg+xml",".webp":"image/webp"});async function Jt({blogClient:t,blogId:e,media:r,type:o,caption:n}){if(!e)return null;for(let i=0;i<3;i+=1){let s=await t.get(e),a=o==="cover"?{coverUrl:r.url}:{content:[...s.content||[],{id:Gt(),type:"image",props:{url:r.url,caption:n||"",_mediaId:r.id||""},content:[],children:[]}]};try{return await t.update(e,a,{etag:s.etag})}catch(l){if(!(l instanceof h&&l.code==="revision_conflict")||i===2)throw l}}return null}async function Ht({mediaClient:t,blogClient:e,options:r}){let o=r.prompt?.trim();if(!o)throw new Error("--prompt is required.");let n=r.type||"inline";if(!["inline","cover"].includes(n))throw new Error("--type must be inline or cover.");let i;if(r.reference){let c=z.resolve(r.reference),f=z.extname(c).toLowerCase(),E=Vt[f];if(!E)throw new Error("Unsupported reference image type. Use AVIF, BMP, JPEG, PNG, SVG, or WebP.");i={bytes:await Te.readFile(c),mimeType:E,name:z.basename(c)}}let s=await t.generate({prompt:o,model:r.model||"flux",destination:n,reference:i,...Jr(r)}),a=s.mimeType.includes("png")?"png":s.mimeType.includes("webp")?"webp":"jpg",l=z.resolve(r.output||`lixblogs-${s.generationId}.${a}`);await Te.writeFile(l,s.bytes,{mode:384});let u=null,d=null;return r.blog&&(u=await t.upload({bytes:s.bytes,mimeType:s.mimeType,blogId:r.blog,mediaType:n,uploadId:s.generationId}),r.attach&&(d=await Jt({blogClient:e,blogId:r.blog,media:u,type:n,caption:r.caption}))),{generationId:s.generationId,output:l,mimeType:s.mimeType,media:u,blog:d}}async function Wt({mediaClient:t,blogClient:e,options:r}){if(!r.file)throw new Error("--file is required.");if(!r.blog)throw new Error("--blog is required.");let o=r.type||"inline",n=z.resolve(r.file),i=await Te.readFile(n),s=z.extname(n).toLowerCase(),a=Vt[s];if(!a)throw new Error("Unsupported image type. Use AVIF, BMP, JPEG, PNG, SVG, or WebP.");let l=await t.upload({bytes:i,mimeType:a,blogId:r.blog,mediaType:o,uploadId:r["upload-id"]||Gt()}),u=r.attach?await Jt({blogClient:e,blogId:r.blog,media:l,type:o,caption:r.caption}):null;return{media:l,blog:u}}async function Kt({mediaClient:t,id:e,options:r}){if(!e)throw new Error("A media ID is required.");y(r,"Deleting this media asset from its storage provider");let o=await t.delete(e);return o?.data||o}async function Xt({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");return t.comments(e)}async function Yt({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");if(!r.content?.trim())throw new Error("--content is required.");return t.comment(e,r.content.trim())}async function Zt({client:t,id:e,options:r}){if(!e||!r.parent)throw new Error("A blog ID and --parent comment ID are required.");if(!r.content?.trim())throw new Error("--content is required.");return t.comment(e,r.content.trim(),{parentId:r.parent})}async function Qt({client:t,id:e,options:r}){if(!e||!r.comment)throw new Error("A blog ID and --comment ID are required.");return y(r,"Deleting this comment"),t.deleteComment(e,r.comment)}var Kr={profile:{type:"string"},env:{type:"string"},json:{type:"boolean",default:!1},quiet:{type:"boolean",default:!1},yes:{type:"boolean",short:"y",default:!1},"allow-insecure-fallback":{type:"boolean",default:!1},"auth-provider":{type:"string"},"accounts-url":{type:"string"},"api-url":{type:"string"},"client-id":{type:"string"},audience:{type:"string"},scope:{type:"string",multiple:!0},open:{type:"boolean",default:!1},status:{type:"string"},limit:{type:"string"},cursor:{type:"string"},range:{type:"string"},from:{type:"string"},to:{type:"string"},dimension:{type:"string"},format:{type:"string"},output:{type:"string"},file:{type:"string"},stdin:{type:"boolean",default:!1},content:{type:"string"},editor:{type:"boolean",default:!1},title:{type:"string"},subtitle:{type:"string"},slug:{type:"string"},tag:{type:"string",multiple:!0},emoji:{type:"string"},publication:{type:"string"},collection:{type:"string"},cover:{type:"string"},"member-only":{type:"boolean",default:!1},"no-member-only":{type:"boolean",default:!1},secret:{type:"boolean",default:!1},"not-secret":{type:"boolean",default:!1},"dry-run":{type:"boolean",default:!1},"no-input":{type:"boolean",default:!1},etag:{type:"string"},permanent:{type:"boolean",default:!1},"idempotency-key":{type:"string"},user:{type:"string"},role:{type:"string"},"hide-on-profile":{type:"boolean",default:!1},target:{type:"string"},force:{type:"boolean",default:!1},prompt:{type:"string"},reference:{type:"string"},model:{type:"string"},seed:{type:"string"},width:{type:"string"},height:{type:"string"},blog:{type:"string"},type:{type:"string"},attach:{type:"boolean",default:!1},caption:{type:"string"},"upload-id":{type:"string"},version:{type:"string"},parent:{type:"string"},comment:{type:"string"},"allow-comments":{type:"boolean",default:!1},"no-comments":{type:"boolean",default:!1},"cover-x":{type:"string"},"cover-y":{type:"string"},"cover-zoom":{type:"string"},help:{type:"boolean",short:"h",default:!1}},Xr=`lixblogs \u2014 LixBlogs CLI
20
20
 
21
21
  Usage:
22
22
  lixblogs login [--profile <name>] [--open]
@@ -102,16 +102,16 @@ Global flags:
102
102
  Machine mode:
103
103
  --json --no-input produces stable JSON on stdout, diagnostics on stderr, and
104
104
  never prompts. Publishing and destructive state changes require --yes.
105
- `,Vr=["openid","profile","email","lixblogs:profile:read","lixblogs:profile:write","lixblogs:blog:read","lixblogs:blog:write","lixblogs:blog:publish","lixblogs:blog:delete","lixblogs:media:read","lixblogs:media:write","lixblogs:organizations:read","lixblogs:organizations:write","lixblogs:collaboration:read","lixblogs:collaboration:write","lixblogs:analytics:read","lixblogs:notifications:read"];function $(t){return{profile:t.profile,env:t.env,authProvider:t["auth-provider"],accountsUrl:t["accounts-url"],apiUrl:t["api-url"],clientId:t["client-id"],audience:t.audience}}async function B(t,e){return t.profileExplicit?E(t.profile):await e.getActive()||E(t.profile)}async function Ae(t){let e=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",r=process.platform==="win32"?["/c","start","",t]:[t],o=Fr(e,r,{detached:!0,stdio:"ignore"});o.on("error",()=>{}),o.unref()}function y(t,e){t.json&&process.stdout.write(ee(e)+`
106
- `)}function f(t,e,r=m.ERROR){let o=e&&typeof e=="object"?e:{message:String(e)},i=O(o.message),n=Qe({...o,message:i});t.json?process.stdout.write(ee(n)+`
107
- `):t.quiet||(process.stderr.write(`Error: ${i}
108
- `),o.hint&&process.stderr.write(`Hint: ${o.hint}
109
- `),o.requestId&&process.stderr.write(`Request: ${o.requestId}
110
- `)),process.exitCode=o.exitCode||r}async function _(t,e){try{return await Be({allowInsecureFallback:t["allow-insecure-fallback"],profileRegistry:e})}catch(r){return f(t,`${r.message}${t["allow-insecure-fallback"]?"":" Re-run with --allow-insecure-fallback to opt in to non-persistent storage instead."}`),null}}async function Kt(t){let e=I({flags:$(t)}),r=new w,o=E(e.profile),i=t.scope?.length?[...t.scope]:[...Vr];!e.profileExplicit&&!i.includes("lixblogs:profile:read")&&i.push("lixblogs:profile:read");let n;try{n=T(e)}catch(u){return f(t,u.message)}let a=await _(t,r);if(!a)return;let s=()=>{},l;try{l=await Fe({provider:n,credentialStore:a,profileId:o,scopes:i,openBrowser:t.open?Ae:void 0,resolveProfileId:e.profileExplicit?void 0:({accessToken:u})=>We({accessToken:u,apiBaseUrl:e.apiBaseUrl}),onStatus:u=>{if(t.json){u.type!=="pending"&&y(t,{event:u.type,...u});return}if(!t.quiet)if(u.type==="verification_pending"){let d=u.verificationUriComplete||u.verificationUri,c=!!process.stdin.isTTY&&!t["no-input"];process.stdout.write(et({url:d,code:u.userCode,expiresInSeconds:u.expiresInSeconds,profile:e.profileExplicit?o:null,interactive:c,color:we()})),c&&!t.open&&(s=rt({input:process.stdin,open:Ae,url:d}))}else u.type==="approved"?console.log(tt("Access approved by Elixpo Accounts.",we())):u.type==="denied"?console.log(" Access denied."):u.type==="expired"&&console.log(" Device code expired.")}})}finally{s()}if(!l.ok)return f(t,l.reason);await r.add(l.profileId),await r.setActive(l.profileId),y(t,{ok:!0,profile:l.profileId}),!t.json&&!t.quiet&&(console.log(` Credentials saved to local profile "${l.profileId}".`),console.log(" Tip: add another account with `lixblogs login`, list accounts with `lixblogs profiles`,"),console.log(" and switch with `lixblogs use <username>`."))}async function Jr(t){let e=I({flags:$(t)}),r=new w,o=await B(e,r),i=await _(t,r);if(!i)return;let n=await ze({credentialStore:i,profileId:o});if(y(t,n),!t.json)for(let a of n)a.loggedIn?console.log(`${a.profileId}: logged in${a.expired?" (expired)":""} \u2014 scopes: ${a.scopes.join(", ")}`):console.log(`${a.profileId}: not logged in`)}async function M(t){let e=I({flags:$(t)}),r=new w,o=await B(e,r),i=await _(t,r);if(!i)return null;let n;try{n=T(e)}catch(s){return f(t,s),null}let a=new j({provider:n,credentialStore:i,profileId:o,apiBaseUrl:e.apiBaseUrl});return{client:new z(a),http:a,config:e,credentialStore:i,profileId:o}}async function Hr(t){let e=await M(t);if(e)try{let[r,o]=await Promise.all([e.client.whoami(),e.credentialStore.get(e.profileId)]),i={ok:!0,profile:e.profileId,environment:e.config.environment,identity:r,scopes:o?.scopes||[],expiresAt:o?.expiresAt?new Date(o.expiresAt).toISOString():null,expired:o?Date.now()>=o.expiresAt:!0};y(t,i),!t.json&&!t.quiet&&(console.log(`${r.displayName||r.username} (@${r.username})`),console.log(`Profile: ${e.profileId} \xB7 ${i.environment}`),console.log(`Scopes: ${i.scopes.join(", ")||"none"}`),console.log(`Expires: ${i.expiresAt||"unknown"}`))}catch(r){f(t,r,r.status===401||r.status===403?m.AUTH:m.ERROR)}}async function Wr(t){let e=I({flags:$(t)}),r=new URL("/register",e.accountsBaseUrl).toString();if(t["no-input"]){y(t,{ok:!0,registrationUrl:r,next:"lixblogs login"}),!t.json&&!t.quiet&&console.log(r);return}await Ae(r),t.quiet||console.log(`Create your account at ${r}, then approve the device login.`),await Kt(t)}async function Xr(t){let e=I({flags:$(t)}),r=new w,o=await B(e,r),i=await _(t,r);if(!i)return;let n=await Ge({credentialStore:i,profileId:o});y(t,n),!t.json&&!t.quiet&&console.log(`Logged out profile "${o}".`)}async function Kr(t){let e=I({flags:$(t)}),r=new w,o=await B(e,r);if(!t.yes)return f(t,"This is a destructive action. Re-run with --yes to confirm (interactive confirmation prompt not yet implemented).");let i;try{i=T(e)}catch(s){return f(t,s.message)}let n=await _(t,r);if(!n)return;let a=await Ve({provider:i,credentialStore:n,profileId:o,confirmed:!0});if(!a.ok)return f(t,a.reason);y(t,a),!t.json&&!t.quiet&&console.log(`Revoked and logged out profile "${o}".`)}async function N(t,e,r){let o=I({flags:$(t)}),i=new w,n=await B(o,i);if((r==="cloudinary-disconnect"||r==="pollinations-disconnect")&&!t.yes)return f(t,"This is a destructive action. Re-run with --yes to confirm (interactive confirmation prompt not yet implemented).");let a;try{a=T(o)}catch(c){return f(t,c.message)}let s=await _(t,i);if(!s)return;let l=new j({provider:a,credentialStore:s,profileId:n,apiBaseUrl:o.apiBaseUrl}),u=new se(l),d;if(r==="cloudinary-status")d=await Dt({integrationsClient:u});else if(r==="cloudinary-disconnect")d=await Ut({integrationsClient:u,confirmed:!0});else try{d={ok:!0,data:r==="pollinations-status"?await u.pollinationsStatus({refresh:t.force}):await u.pollinationsDisconnect()}}catch(c){d={ok:!1,error:c}}if(!d.ok)return f(t,d.error||d.reason);y(t,d),!t.json&&!t.quiet&&console.log(r==="cloudinary-status"?`Cloudinary: ${d.data.connected?`connected (${d.data.cloudName})`:"not connected"}`:r==="pollinations-status"?`Pollinations: ${d.data.connected?`connected${d.data.handle?` as ${d.data.handle}`:""} \xB7 ${d.data.balance??"unknown"} Pollen`:`${d.data.status}. Connect at ${d.data.connectUrl||"https://blogs.elixpo.com/settings?tab=integrations"}`}`:`${r.startsWith("pollinations")?"Pollinations":"Cloudinary"} connection disconnected.`)}async function Yr(t){let e=new w,r=await _(t,e);if(!r)return;let o=await Je({credentialStore:r,profileRegistry:e});if(y(t,o),!t.json){o.profiles.length||console.log("No profiles. Run `lixblogs auth login` first.");for(let i of o.profiles)console.log(`${i.active?"*":" "} ${i.profileId}${i.expired?" (expired)":""}`)}}async function Zr(t,e){let r;try{r=E(e[0])}catch(a){return f(t,a.message)}let o=new w,i=await _(t,o);if(!i)return;let n=await He({credentialStore:i,profileRegistry:o,profileId:r});if(!n.ok)return f(t,n.reason);y(t,n),!t.json&&!t.quiet&&console.log(`Using profile "${r}".`)}var Yt={list:it,get:_e,preview:_e,create:nt,edit:st,publish:at,unpublish:ut,delete:ke,trash:ke,restore:dt,history:lt,"restore-version":ct},Zt={list:pt,get:ft,collections:mt,members:gt,targets:ht},Qt={list:yt,invitations:wt,invite:bt,role:vt,remove:xt,accept:It,decline:Et},er={list:({options:t})=>At(t),inspect:({id:t})=>Rt({name:t}),install:({id:t,options:e})=>Pt({name:t,options:e})},tr={query:Ct,export:Lt},rr={generate:Ft,upload:zt,delete:Gt},or={list:Vt,add:Jt,reply:Ht,delete:Wt};async function Qr(t,e,r){let o=I({flags:$(t)}),i=new w,n=await B(o,i),a=await _(t,i);if(!a)return;let s;try{s=T(o)}catch(c){return f(t,c.message)}let l=new j({provider:s,credentialStore:a,profileId:n,apiBaseUrl:o.apiBaseUrl}),u=new z(l),d={...t,limit:t.limit===void 0?void 0:Number.parseInt(t.limit,10)};try{let c=await Yt[r]({client:u,id:e[0],options:d,stdin:process.stdin});if(y(t,{ok:!0,...c}),!t.json&&!t.quiet)if(r==="list"){for(let p of c.data||[])console.log(`${p.id} ${p.status} ${p.title||"(untitled)"}`);c.meta?.nextCursor&&console.log(`Next cursor: ${c.meta.nextCursor}`)}else if(r==="get")console.log(`${c.title||"(untitled)"} [${c.status}]
111
- ${c.markdown||""}`);else if(r==="history")for(let p of c.data||[])console.log(`${p.id} ${p.label||"snapshot"} ${p.created_at} ${p.username||"system"}`);else c.dryRun?console.log(`Dry run: ${r} validated; no changes sent.`):console.log(c.url||`${r} completed for ${c.id}.`)}catch(c){if(t.json&&c instanceof h){process.stdout.write(ee({ok:!1,error:{code:c.code,message:c.message,requestId:c.requestId,details:c.details}})+`
112
- `),process.exitCode=c.status===412?3:1;return}return f(t,c,c.status===412?m.CONFLICT:m.ERROR)}}async function eo(t,e,r){let o=await M(t);if(!o)return;let i=new re(o.http);try{let n=await Zt[r]({client:i,id:e[0],options:t});if(y(t,{ok:!0,data:n}),t.json||t.quiet)return;if(r==="targets"){console.log("personal Personal Blog");for(let s of n.organizations||[]){console.log(`${s.target} ${s.role} ${s.name}`);for(let l of s.collections||[])console.log(` collection:${l.id} ${l.name}`)}return}let a=r==="list"?n.data||[]:Array.isArray(n)?n:[n];for(let s of a)console.log([s.id||s.userId||s.orgId,s.role,s.slug||s.username,s.name||s.displayName].filter(Boolean).join(" "))}catch(n){f(t,n,n.status===401||n.status===403?m.AUTH:m.ERROR)}}async function to(t,e,r){let o=await M(t);if(o)try{let i=await rr[r]({mediaClient:new ae(o.http),blogClient:o.client,id:e[0],options:t});y(t,{ok:!0,data:i}),!t.json&&!t.quiet&&(console.log(r==="delete"?`Deleted media ${i.id}.`:`${r==="generate"?"Generated":"Uploaded"} image: ${i.media?.url||i.output||i.media?.publicId}`),i.blog&&console.log(`Attached to blog ${t.blog}.`))}catch(i){f(t,i,i.status===401||i.status===403?m.AUTH:m.ERROR)}}async function ro(t,e,r){let o=await M(t);if(o)try{let i=await or[r]({client:o.client,id:e[0],options:t});if(y(t,{ok:!0,data:i}),!t.json&&!t.quiet)if(r==="list")for(let n of i)console.log(`${n.id} ${n.parent_id?"reply":"comment"} ${n.display_name||n.username||"Anonymous"} ${n.content}`);else console.log(`${r} completed for ${i.id}.`)}catch(i){f(t,i,i.status===401||i.status===403?m.AUTH:m.ERROR)}}async function oo(t,e,r){let o=await M(t);if(!o)return;let i=new ie(o.http);try{let n=await Qt[r]({client:i,id:e[0],options:t});if(y(t,{ok:!0,data:n}),t.json||t.quiet)return;if(n.dryRun){console.log(`Dry run: ${n.action} validated; no changes sent.`);return}let a=r==="invitations"?n:r==="list"?n.collaborators||[]:[n];for(let s of a)console.log([s.blogId||s.userId,s.status,s.role,s.username||s.title,s.notificationState].filter(Boolean).join(" "))}catch(n){f(t,n,n.status===401||n.status===403?m.AUTH:m.ERROR)}}async function io(t,e,r){try{let o=await er[r]({id:e[0],options:t});if(y(t,{ok:!0,data:o}),t.json||t.quiet)return;if(r==="list")for(let i of o)console.log(`${i.name} CLI >= ${i.minimumCliVersion||"unknown"} ${i.description}`);else r==="inspect"?process.stdout.write(o.content):o.dryRun?console.log(`Dry run: install ${o.name} to ${o.target}${o.replace?" (replace)":""}.`):console.log(`Installed ${o.name} at ${o.target}.`)}catch(o){f(t,o)}}async function no(t,e,r){let o=await M(t);if(!o)return;let i=new ne(o.http),n={...t,limit:t.limit===void 0?void 0:Number.parseInt(t.limit,10)};try{let a=await tr[r]({client:i,options:n});if(y(t,{ok:!0,data:a}),t.json||t.quiet)return;if(r==="export"){console.log(`Exported ${a.rows} rows to ${a.output}.`);return}let s=a.data;if(console.log(`${s.scope.label} \xB7 ${s.dimension} \xB7 ${s.range.key}`),s.dimension==="overview")for(let[l,u]of Object.entries(s.values.totals))console.log(`${l} ${u} ${s.values.changes[l]}%`);else if(s.dimension==="timeline")s.values.labels.forEach((l,u)=>console.log(`${l} ${s.values.views[u]} ${s.values.reads[u]}`));else{for(let l of s.values)console.log(Object.values(l).join(" "));a.meta?.nextCursor&&console.log(`Next cursor: ${a.meta.nextCursor}`)}}catch(a){f(t,a,a.status===401||a.status===403?m.AUTH:m.ERROR)}}var Xt={auth:{login:Kt,status:Jr,whoami:Hr,logout:Xr,revoke:Kr,profiles:Yr,use:Zr},blog:Object.fromEntries(Object.keys(Yt).map(t=>[t,(e,r)=>Qr(e,r,t)])),org:Object.fromEntries(Object.keys(Zt).map(t=>[t,(e,r)=>eo(e,r,t)])),collab:Object.fromEntries(Object.keys(Qt).map(t=>[t,(e,r)=>oo(e,r,t)])),skill:Object.fromEntries(Object.keys(er).map(t=>[t,(e,r)=>io(e,r,t)])),analytics:Object.fromEntries(Object.keys(tr).map(t=>[t,(e,r)=>no(e,r,t)])),media:Object.fromEntries(Object.keys(rr).map(t=>[t,(e,r)=>to(e,r,t)])),comment:Object.fromEntries(Object.keys(or).map(t=>[t,(e,r)=>ro(e,r,t)])),integrations:{"cloudinary-status":(t,e)=>N(t,e,"cloudinary-status"),"cloudinary-disconnect":(t,e)=>N(t,e,"cloudinary-disconnect"),"pollinations-status":(t,e)=>N(t,e,"pollinations-status"),"pollinations-disconnect":(t,e)=>N(t,e,"pollinations-disconnect")},disconnect:{cloudinary:(t,e)=>N(t,e,"cloudinary-disconnect"),pollinations:(t,e)=>N(t,e,"pollinations-disconnect")}};async function so(){let t,e;try{({values:t,positionals:e}=Mr({args:process.argv.slice(2),options:zr,allowPositionals:!0,strict:!0}))}catch(a){process.stderr.write(`Error: Invalid flag. ${a.message}
113
- `),process.exitCode=m.USAGE;return}if(t.help||e.length===0){process.stdout.write(Gr);return}if(e[0]==="register"){await Wr(t);return}e=Ze(e);let[r,o]=e,i=Xt[r];if(!i){process.stderr.write(`Error: Unknown command category "${r}".
114
- `),process.stderr.write(`Available categories: ${Object.keys(Xt).join(", ")}
115
- `),process.exitCode=m.USAGE;return}let n=i[o];if(!n){process.stderr.write(`Error: Unknown ${r} command "${o}".
116
- `),process.stderr.write(`Available commands: ${Object.keys(i).map(a=>`${r} ${a}`).join(", ")}
117
- `),process.exitCode=m.USAGE;return}await n(t,e.slice(2))}so();
105
+ `,Yr=["openid","profile","email","lixblogs:profile:read","lixblogs:profile:write","lixblogs:blog:read","lixblogs:blog:write","lixblogs:blog:publish","lixblogs:blog:delete","lixblogs:media:read","lixblogs:media:write","lixblogs:organizations:read","lixblogs:organizations:write","lixblogs:collaboration:read","lixblogs:collaboration:write","lixblogs:analytics:read","lixblogs:notifications:read"];function O(t){return{profile:t.profile,env:t.env,authProvider:t["auth-provider"],accountsUrl:t["accounts-url"],apiUrl:t["api-url"],clientId:t["client-id"],audience:t.audience}}async function V(t,e){return t.profileExplicit?$(t.profile):await e.getActive()||$(t.profile)}async function Oe(t){let e=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",r=process.platform==="win32"?["/c","start","",t]:[t],o=Wr(e,r,{detached:!0,stdio:"ignore"});o.on("error",()=>{}),o.unref()}function w(t,e){t.json&&process.stdout.write(ie(e)+`
106
+ `)}function m(t,e,r=g.ERROR){let o=e&&typeof e=="object"?e:{message:String(e)},n=D(o.message),i=ot({...o,message:n});if(t.json)process.stdout.write(ie(i)+`
107
+ `);else if(!t.quiet){let s=p(process.stderr);process.stderr.write(`${st(n,s)}
108
+ `),o.hint&&process.stderr.write(`${T(`Hint: ${o.hint}`,s)}
109
+ `),o.requestId&&process.stderr.write(`${C(`Request: ${o.requestId}`,s)}
110
+ `)}process.exitCode=o.exitCode||r}async function A(t,e){try{return await Ge({allowInsecureFallback:t["allow-insecure-fallback"],profileRegistry:e})}catch(r){return m(t,`${r.message}${t["allow-insecure-fallback"]?"":" Re-run with --allow-insecure-fallback to opt in to non-persistent storage instead."}`),null}}async function tr(t){let e=S({flags:O(t)}),r=new b,o=$(e.profile),n=t.scope?.length?[...t.scope]:[...Yr];!e.profileExplicit&&!n.includes("lixblogs:profile:read")&&n.push("lixblogs:profile:read");let i;try{i=U(e)}catch(u){return m(t,u.message)}let s=await A(t,r);if(!s)return;let a=()=>{},l;try{l=await Je({provider:i,credentialStore:s,profileId:o,scopes:n,openBrowser:t.open?Oe:void 0,resolveProfileId:e.profileExplicit?void 0:({accessToken:u})=>Ze({accessToken:u,apiBaseUrl:e.apiBaseUrl}),onStatus:u=>{if(t.json){u.type!=="pending"&&w(t,{event:u.type,...u});return}if(!t.quiet)if(u.type==="verification_pending"){let d=u.verificationUriComplete||u.verificationUri,c=!!process.stdin.isTTY&&!t["no-input"];process.stdout.write(it({url:d,code:u.userCode,expiresInSeconds:u.expiresInSeconds,profile:e.profileExplicit?o:null,interactive:c,color:p()})),c&&!t.open&&(a=at({input:process.stdin,open:Oe,url:d}))}else u.type==="approved"?console.log(v("Access approved by Elixpo Accounts.",p())):u.type==="denied"?console.log(T("Access denied.",p())):u.type==="expired"&&console.log(T("Device code expired.",p()))}})}finally{a()}if(!l.ok)return m(t,l.reason);await r.add(l.profileId),await r.setActive(l.profileId),w(t,{ok:!0,profile:l.profileId}),!t.json&&!t.quiet&&(console.log(v(`Credentials saved to local profile "${l.profileId}".`,p())),console.log(C("Add another account with `lixblogs login`; switch with `lixblogs use <username>`.",p())))}async function Zr(t){let e=S({flags:O(t)}),r=new b,o=await V(e,r),n=await A(t,r);if(!n)return;let i=await He({credentialStore:n,profileId:o});if(w(t,i),!t.json)for(let s of i)s.loggedIn?console.log(`${s.profileId}: logged in${s.expired?" (expired)":""} \u2014 scopes: ${s.scopes.join(", ")}`):console.log(`${s.profileId}: not logged in`)}async function J(t){let e=S({flags:O(t)}),r=new b,o=await V(e,r),n=await A(t,r);if(!n)return null;let i;try{i=U(e)}catch(a){return m(t,a),null}let s=new N({provider:i,credentialStore:n,profileId:o,apiBaseUrl:e.apiBaseUrl});return{client:new W(s),http:s,config:e,credentialStore:n,profileId:o}}async function Qr(t){let e=await J(t);if(e)try{let[r,o]=await q(t,"Loading account\u2026",()=>Promise.all([e.client.whoami(),e.credentialStore.get(e.profileId)])),n={ok:!0,profile:e.profileId,environment:e.config.environment,identity:r,scopes:o?.scopes||[],expiresAt:o?.expiresAt?new Date(o.expiresAt).toISOString():null,expired:o?Date.now()>=o.expiresAt:!0};w(t,n),!t.json&&!t.quiet&&(console.log(`${r.displayName||r.username} (@${r.username})`),console.log(`Profile: ${e.profileId} \xB7 ${n.environment}`),console.log(`Scopes: ${n.scopes.join(", ")||"none"}`),console.log(`Expires: ${n.expiresAt||"unknown"}`))}catch(r){m(t,r,r.status===401||r.status===403?g.AUTH:g.ERROR)}}async function eo(t){let e=S({flags:O(t)}),r=new URL("/register",e.accountsBaseUrl).toString();if(t["no-input"]){w(t,{ok:!0,registrationUrl:r,next:"lixblogs login"}),!t.json&&!t.quiet&&console.log(r);return}await Oe(r),t.quiet||console.log(C(`Create your account at ${r}, then approve the device login.`,p())),await tr(t)}async function to(t){let e=S({flags:O(t)}),r=new b,o=await V(e,r),n=await A(t,r);if(!n)return;let i=await We({credentialStore:n,profileId:o});w(t,i),!t.json&&!t.quiet&&console.log(v(`Logged out profile "${o}".`,p()))}async function ro(t){let e=S({flags:O(t)}),r=new b,o=await V(e,r);if(!t.yes)return m(t,"This is a destructive action. Re-run with --yes to confirm (interactive confirmation prompt not yet implemented).");let n;try{n=U(e)}catch(a){return m(t,a.message)}let i=await A(t,r);if(!i)return;let s=await Ke({provider:n,credentialStore:i,profileId:o,confirmed:!0});if(!s.ok)return m(t,s.reason);w(t,s),!t.json&&!t.quiet&&console.log(v(`Revoked and logged out profile "${o}".`,p()))}async function G(t,e,r){let o=S({flags:O(t)}),n=new b,i=await V(o,n);if((r==="cloudinary-disconnect"||r==="pollinations-disconnect")&&!t.yes)return m(t,"This is a destructive action. Re-run with --yes to confirm (interactive confirmation prompt not yet implemented).");let s;try{s=U(o)}catch(c){return m(t,c.message)}let a=await A(t,n);if(!a)return;let l=new N({provider:s,credentialStore:a,profileId:i,apiBaseUrl:o.apiBaseUrl}),u=new de(l),d=await q(t,r.endsWith("status")?"Checking integration\u2026":"Disconnecting integration\u2026",async()=>{if(r==="cloudinary-status")return zt({integrationsClient:u});if(r==="cloudinary-disconnect")return Ft({integrationsClient:u,confirmed:!0});try{return{ok:!0,data:r==="pollinations-status"?await u.pollinationsStatus({refresh:t.force}):await u.pollinationsDisconnect()}}catch(c){return{ok:!1,error:c}}});if(!d.ok)return m(t,d.error||d.reason);w(t,d),!t.json&&!t.quiet&&console.log(r==="cloudinary-status"?C(`Cloudinary: ${d.data.connected?`connected (${d.data.cloudName})`:"not connected"}`,p()):r==="pollinations-status"?C(`Pollinations: ${d.data.connected?`connected${d.data.handle?` as ${d.data.handle}`:""} \xB7 ${d.data.balance??"unknown"} Pollen`:`${d.data.status}. Connect at ${d.data.connectUrl||"https://blogs.elixpo.com/settings?tab=integrations"}`}`,p()):v(`${r.startsWith("pollinations")?"Pollinations":"Cloudinary"} connection disconnected.`,p()))}async function oo(t){let e=new b,r=await A(t,e);if(!r)return;let o=await Xe({credentialStore:r,profileRegistry:e});if(w(t,o),!t.json){o.profiles.length||console.log(T("No profiles. Run `lixblogs auth login` first.",p()));for(let n of o.profiles)console.log(`${n.active?"*":" "} ${n.profileId}${n.expired?" (expired)":""}`)}}async function no(t,e){let r;try{r=$(e[0])}catch(s){return m(t,s.message)}let o=new b,n=await A(t,o);if(!n)return;let i=await Ye({credentialStore:n,profileRegistry:o,profileId:r});if(!i.ok)return m(t,i.reason);w(t,i),!t.json&&!t.quiet&&console.log(v(`Using profile "${r}".`,p()))}var rr={list:ct,get:qe,preview:qe,create:ut,edit:dt,publish:pt,unpublish:gt,delete:Ae,trash:Ae,restore:ht,history:ft,"restore-version":mt},or={list:yt,get:wt,collections:bt,members:vt,targets:xt},nr={list:It,invitations:Et,invite:_t,role:kt,remove:St,accept:$t,decline:qt},ir={list:({options:t})=>jt(t),inspect:({id:t})=>Ct({name:t}),install:({id:t,options:e})=>Lt({name:t,options:e})},sr={query:Bt,export:Mt},ar={generate:Ht,upload:Wt,delete:Kt},lr={list:Xt,add:Yt,reply:Zt,delete:Qt};async function io(t,e,r){let o=S({flags:O(t)}),n=new b,i=await V(o,n),s=await A(t,n);if(!s)return;let a;try{a=U(o)}catch(c){return m(t,c.message)}let l=new N({provider:a,credentialStore:s,profileId:i,apiBaseUrl:o.apiBaseUrl}),u=new W(l),d={...t,limit:t.limit===void 0?void 0:Number.parseInt(t.limit,10)};try{let c=await q(t,`${r==="list"?"Loading":r==="get"||r==="preview"?"Opening":"Updating"} blog\u2026`,()=>rr[r]({client:u,id:e[0],options:d,stdin:process.stdin}));if(w(t,{ok:!0,...c}),!t.json&&!t.quiet)if(r==="list"){for(let f of c.data||[])console.log(`${f.id} ${f.status} ${f.title||"(untitled)"}`);c.meta?.nextCursor&&console.log(C(`Next cursor: ${c.meta.nextCursor}`,p()))}else if(r==="get")console.log(`${c.title||"(untitled)"} [${c.status}]
111
+ ${c.markdown||""}`);else if(r==="history")for(let f of c.data||[])console.log(`${f.id} ${f.label||"snapshot"} ${f.created_at} ${f.username||"system"}`);else c.dryRun?console.log(T(`Dry run: ${r} validated; no changes sent.`,p())):console.log(v(c.url||`${r} completed for ${c.id}.`,p()))}catch(c){if(t.json&&c instanceof h){process.stdout.write(ie({ok:!1,error:{code:c.code,message:c.message,requestId:c.requestId,details:c.details}})+`
112
+ `),process.exitCode=c.status===412?3:1;return}return m(t,c,c.status===412?g.CONFLICT:g.ERROR)}}async function so(t,e,r){let o=await J(t);if(!o)return;let n=new ae(o.http);try{let i=await q(t,"Loading organization\u2026",()=>or[r]({client:n,id:e[0],options:t}));if(w(t,{ok:!0,data:i}),t.json||t.quiet)return;if(r==="targets"){console.log("personal Personal Blog");for(let a of i.organizations||[]){console.log(`${a.target} ${a.role} ${a.name}`);for(let l of a.collections||[])console.log(` collection:${l.id} ${l.name}`)}return}let s=r==="list"?i.data||[]:Array.isArray(i)?i:[i];for(let a of s)console.log([a.id||a.userId||a.orgId,a.role,a.slug||a.username,a.name||a.displayName].filter(Boolean).join(" "))}catch(i){m(t,i,i.status===401||i.status===403?g.AUTH:g.ERROR)}}async function ao(t,e,r){let o=await J(t);if(o)try{let i=await q(t,r==="generate"?"Generating image\u2026":r==="upload"?"Uploading image\u2026":"Deleting media\u2026",()=>ar[r]({mediaClient:new pe(o.http),blogClient:o.client,id:e[0],options:t}));if(w(t,{ok:!0,data:i}),!t.json&&!t.quiet)if(r==="delete")console.log(v(`Deleted media ${i.id}.`,p()));else{let s=r==="generate"?"Generated":"Uploaded",a=i.blog?` and attached to blog ${t.blog}`:"";console.log(v(`${s}${a}: ${i.media?.url||i.output||i.media?.publicId}`,p()))}}catch(n){m(t,n,n.status===401||n.status===403?g.AUTH:g.ERROR)}}async function lo(t,e,r){let o=await J(t);if(o)try{let n=await q(t,r==="list"?"Loading comments\u2026":"Updating comments\u2026",()=>lr[r]({client:o.client,id:e[0],options:t}));if(w(t,{ok:!0,data:n}),!t.json&&!t.quiet)if(r==="list")for(let i of n)console.log(`${i.id} ${i.parent_id?"reply":"comment"} ${i.display_name||i.username||"Anonymous"} ${i.content}`);else console.log(v(`${r} completed for ${n.id}.`,p()))}catch(n){m(t,n,n.status===401||n.status===403?g.AUTH:g.ERROR)}}async function co(t,e,r){let o=await J(t);if(!o)return;let n=new ce(o.http);try{let i=await q(t,"Updating collaborators\u2026",()=>nr[r]({client:n,id:e[0],options:t}));if(w(t,{ok:!0,data:i}),t.json||t.quiet)return;if(i.dryRun){console.log(T(`Dry run: ${i.action} validated; no changes sent.`,p()));return}if(r!=="list"&&r!=="invitations"){console.log(v(`${r} completed for ${i.blogId||i.userId||e[0]}.`,p()));return}let s=r==="invitations"?i:r==="list"?i.collaborators||[]:[i];for(let a of s)console.log([a.blogId||a.userId,a.status,a.role,a.username||a.title,a.notificationState].filter(Boolean).join(" "))}catch(i){m(t,i,i.status===401||i.status===403?g.AUTH:g.ERROR)}}async function uo(t,e,r){try{let o=await ir[r]({id:e[0],options:t});if(w(t,{ok:!0,data:o}),t.json||t.quiet)return;if(r==="list")for(let n of o)console.log(`${n.name} CLI >= ${n.minimumCliVersion||"unknown"} ${n.description}`);else r==="inspect"?process.stdout.write(o.content):o.dryRun?console.log(T(`Dry run: install ${o.name} to ${o.target}${o.replace?" (replace)":""}.`,p())):console.log(v(`Installed ${o.name} at ${o.target}.`,p()))}catch(o){m(t,o)}}async function po(t,e,r){let o=await J(t);if(!o)return;let n=new ue(o.http),i={...t,limit:t.limit===void 0?void 0:Number.parseInt(t.limit,10)};try{let s=await q(t,r==="export"?"Exporting analytics\u2026":"Loading analytics\u2026",()=>sr[r]({client:n,options:i}));if(w(t,{ok:!0,data:s}),t.json||t.quiet)return;if(r==="export"){console.log(v(`Exported ${s.rows} rows to ${s.output}.`,p()));return}let a=s.data;if(console.log(`${a.scope.label} \xB7 ${a.dimension} \xB7 ${a.range.key}`),a.dimension==="overview")for(let[l,u]of Object.entries(a.values.totals))console.log(`${l} ${u} ${a.values.changes[l]}%`);else if(a.dimension==="timeline")a.values.labels.forEach((l,u)=>console.log(`${l} ${a.values.views[u]} ${a.values.reads[u]}`));else{for(let l of a.values)console.log(Object.values(l).join(" "));s.meta?.nextCursor&&console.log(`Next cursor: ${s.meta.nextCursor}`)}}catch(s){m(t,s,s.status===401||s.status===403?g.AUTH:g.ERROR)}}var er={auth:{login:tr,status:Zr,whoami:Qr,logout:to,revoke:ro,profiles:oo,use:no},blog:Object.fromEntries(Object.keys(rr).map(t=>[t,(e,r)=>io(e,r,t)])),org:Object.fromEntries(Object.keys(or).map(t=>[t,(e,r)=>so(e,r,t)])),collab:Object.fromEntries(Object.keys(nr).map(t=>[t,(e,r)=>co(e,r,t)])),skill:Object.fromEntries(Object.keys(ir).map(t=>[t,(e,r)=>uo(e,r,t)])),analytics:Object.fromEntries(Object.keys(sr).map(t=>[t,(e,r)=>po(e,r,t)])),media:Object.fromEntries(Object.keys(ar).map(t=>[t,(e,r)=>ao(e,r,t)])),comment:Object.fromEntries(Object.keys(lr).map(t=>[t,(e,r)=>lo(e,r,t)])),integrations:{"cloudinary-status":(t,e)=>G(t,e,"cloudinary-status"),"cloudinary-disconnect":(t,e)=>G(t,e,"cloudinary-disconnect"),"pollinations-status":(t,e)=>G(t,e,"pollinations-status"),"pollinations-disconnect":(t,e)=>G(t,e,"pollinations-disconnect")},disconnect:{cloudinary:(t,e)=>G(t,e,"cloudinary-disconnect"),pollinations:(t,e)=>G(t,e,"pollinations-disconnect")}};async function fo(){let t,e;try{({values:t,positionals:e}=Hr({args:process.argv.slice(2),options:Kr,allowPositionals:!0,strict:!0}))}catch(s){process.stderr.write(`Error: Invalid flag. ${s.message}
113
+ `),process.exitCode=g.USAGE;return}if(t.help||e.length===0){process.stdout.write(Xr);return}if(e[0]==="register"){await eo(t);return}e=rt(e);let[r,o]=e,n=er[r];if(!n){process.stderr.write(`Error: Unknown command category "${r}".
114
+ `),process.stderr.write(`Available categories: ${Object.keys(er).join(", ")}
115
+ `),process.exitCode=g.USAGE;return}let i=n[o];if(!i){process.stderr.write(`Error: Unknown ${r} command "${o}".
116
+ `),process.stderr.write(`Available commands: ${Object.keys(n).map(s=>`${r} ${s}`).join(", ")}
117
+ `),process.exitCode=g.USAGE;return}await i(t,e.slice(2))}fo();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elixpo/lixblogs-cli",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "Official CLI for LixBlogs — publish, manage, and inspect blogs through the supported API. Built for creators and agent automation.",
5
5
  "author": {
6
6
  "name": "Elixpo",
@@ -5,13 +5,14 @@ description: Generate, upload, and attach LixBlogs images through the supported
5
5
 
6
6
  # LixBlogs media
7
7
 
8
- Use `@elixpo/lixblogs-cli` 1.5.0 or newer with `--json --no-input`. Never read integration keys, call Pollinations or Cloudinary directly, persist provider URLs, or use session cookies.
8
+ Use `@elixpo/lixblogs-cli` 1.5.0 or newer with `--json --no-input`. Never read integration keys, call Pollinations generation/account endpoints or Cloudinary directly, persist provider URLs, or use session cookies. The unauthenticated Pollinations image-model catalog is the only direct provider request allowed.
9
9
 
10
10
  ## Access and cost
11
11
 
12
12
  - Inspect connection: `lixblogs:media:read`
13
13
  - Generate or upload: `lixblogs:media:write`; attaching also needs `lixblogs:blog:read` and `lixblogs:blog:write`
14
14
  - Pollinations generation spends the creator's approved Pollen budget. Generate only when the current user request explicitly authorizes that image. One command is one billable attempt; never automatically retry a failed generation.
15
+ - The approved models are `gptimage`, `flux`, and `klein`. Respect an explicit model choice. Otherwise choose the least costly approved model that supports the requested operation; do not assume that today's cheapest model will stay cheapest.
15
16
  - A disconnected, expired, or revoked connection must be repaired by the user at `https://blogs.elixpo.com/settings?tab=integrations`.
16
17
 
17
18
  Check the connection before offering generation:
@@ -20,6 +21,14 @@ Check the connection before offering generation:
20
21
  lixblogs integrations pollinations-status --json --no-input
21
22
  ```
22
23
 
24
+ When no model was requested, inspect current public pricing and capabilities:
25
+
26
+ ```bash
27
+ curl -fsSL https://gen.pollinations.ai/image/models
28
+ ```
29
+
30
+ Compare only `gptimage`, `flux`, and `klein` using the catalog's declared image price. Exclude models that do not support the requested reference-image or sizing capability, then pass the cheapest remaining model explicitly with `--model`. This catalog request needs no key. If pricing or required capability metadata cannot be established, stop and ask the user to choose rather than initiating an uncertain billable request.
31
+
23
32
  ## Generate and attach
24
33
 
25
34
  Keep `--output`: it retains a local recovery copy if Cloudinary persistence fails, so retry `media upload` instead of paying for another generation.