@elixpo/lixblogs-cli 1.5.4 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lixblogs.mjs +25 -25
- package/package.json +1 -1
package/dist/lixblogs.mjs
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{parseArgs as Gr}from"node:util";import{spawn as Vr}from"node:child_process";var J={environment:"production",profile:"default",accountsBaseUrl:"https://accounts.elixpo.com",apiBaseUrl:"https://blogs.elixpo.com"},Pe={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 E({flags:t={},env:e=process.env}={}){let r=t.env??e.LIXBLOGS_ENV??J.environment,o=t.profile??e.LIXBLOGS_PROFILE??J.profile,i=Pe[r]||Pe.production,n=t.authProvider??e.LIXBLOGS_AUTH_PROVIDER??(r==="production"?"elixpo":"mock"),a=t.accountsUrl??e.LIXBLOGS_ACCOUNTS_URL??J.accountsBaseUrl,s=t.apiUrl??e.LIXBLOGS_API_URL??J.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 T=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 Te={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-"},sr=5,Oe=0;function ar(t){return Oe+=1,`${t}${Oe}`}var H=class extends T{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=Te[r]??Te.APPROVE_IMMEDIATELY,i=ar(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:sr}:{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 je="urn:ietf:params:oauth:grant-type:device_code",lr=1,cr=15e3,ur={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(ur[e]||"Authentication failed."),this.name="AuthProviderError",this.code=e||"authentication_failed",this.status=r,this.requiresLogin=o}},q=class extends Error{constructor(e){super(e),this.name="CompatibilityError",this.code="incompatible_accounts_contract"}};function fe(t){return String(t||"0.0.0").split(".").slice(0,3).map(e=>Number.parseInt(e,10)||0)}function dr(t,e){let r=fe(t),o=fe(e);for(let i=0;i<3;i+=1)if(r[i]!==o[i])return r[i]>o[i];return!0}function pr(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 q("Accounts must use HTTPS outside local development.");return e.toString().replace(/\/$/,"")}async function z(t){try{return await t.json()}catch{throw new x("server_error",{status:t.status})}}function W(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 Ce(t,e){if(!e.ok)throw W(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 K=class extends T{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=cr}={}){if(super(),typeof n!="function")throw new TypeError("A fetch implementation is required.");this.accountsBaseUrl=pr(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 z(e);if(!e.ok)throw new x("temporarily_unavailable",{status:e.status});if(fe(r.elixpo_contract_version)[0]!==lr)throw new q("Accounts uses an unsupported device-flow contract version.");if(!dr(this.cliVersion,r.elixpo_min_compatible_cli_version))throw new q(`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(je))throw new q("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 q(`Accounts discovery is missing ${n}.`);let a=new URL(r[n]),s=new URL(this.accountsBaseUrl);if(a.origin!==s.origin)throw new q(`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 z(o);if(!o.ok)throw W(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:je,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 z(i);if(i.ok)return{status:"approved",token:Ce(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 W(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 z(n);return Ce(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 z(o);throw W(i,o)}}};var Le="elixpo",me=class extends Error{constructor(e){super(e),this.name="ProductionAuthGateError"}};function Ue({providerId:t,environment:e}){if(e==="production"&&t!==Le)throw new me(`Provider "${t}" is not approved for production. Only "${Le}" may be used in production.`)}function O(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 H:new K({accountsBaseUrl:t.accountsBaseUrl,clientId:t.clientId,audience:t.audience,cliVersion:t.cliVersion||"1.2.0",fetchImpl:t.fetchImpl});return Ue({providerId:e.providerId,environment:t.environment}),e}var $=class extends Error{constructor(e){super(e),this.name="CredentialStoreUnavailableError"}},P=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 P{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()]}},Y=class extends P{constructor(e){super(),this._realStore=e}async get(e){try{return await this._realStore.get(e)}catch(r){throw new $(`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 $(`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 $(`OS keychain is unavailable: ${r.message}`)}}async listProfiles(){try{return await this._realStore.listProfiles()}catch(e){throw new $(`OS keychain is unavailable: ${e.message}`)}}};import{Entry as fr}from"@napi-rs/keyring";var mr="lixblogs-cli",gr="__lixblogs_availability_probe__";function Z(t){return new fr(mr,t)}async function Ne(){let t=Z(gr);try{return t.setPassword("probe"),t.deletePassword(),{available:!0}}catch(e){return{available:!1,error:String(e.message??e).split(`
|
|
3
|
-
`)[0].trim()}}}var
|
|
4
|
-
`),new X}let o=e||new w,i=new Q(o);return new Y(i)}var ye="[REDACTED]",wr=/token|refresh|secret|password|authorization/i,br=/^(mock-(access|refresh)-|Bearer\s+)\S+/i;function Fe(t){return typeof t=="string"&&br.test(t)?ye:t}function he(t){if(Array.isArray(t))return t.map(e=>he(e));if(t&&typeof t=="object"){let e={};for(let[r,o]of Object.entries(t))wr.test(r)?e[r]=ye:typeof o=="object"&&o!==null?e[r]=he(o):e[r]=Fe(o);return e}return Fe(t)}function te(t,e){return JSON.stringify(he(t),null,e)}function j(t){return typeof t!="string"?t:t.replace(/(mock-(access|refresh)-\S+|Bearer\s+\S+)/gi,ye)}async function ze({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:j(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:j(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:j(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 Ge({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 Ve({credentialStore:t,profileId:e}){return await t.delete(e),{ok:!0}}async function Je({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 He({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 We({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 Ke({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 I(n.data.username)}var vr=6e4,Xe=new WeakMap;function xr(t){let e=Xe.get(t);return e||(e=new Map,Xe.set(t,e)),e}var re=class extends Error{constructor(e){super(`Profile "${e}" needs to log in again.`),this.name="LoginRequiredError",this.code="login_required"}},we=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."}},C=class{constructor({provider:e,credentialStore:r,profileId:o,apiBaseUrl:i="https://blogs.elixpo.com",fetchImpl:n=globalThis.fetch,refreshSkewMs:a=vr}){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=xr(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 re(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 re(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 we(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 Ye}from"node:crypto";var g=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 Er(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new g(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 G=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 Er(s)}catch(s){if(!n||a>0||s instanceof g||s?.code)throw s;await this.sleep(250)}throw new g("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.payload.data.etag||r.etag}}async create(e,{idempotencyKey:r=Ye()}={}){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=Ye()}){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 Ir(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new g(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 oe=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 Ir(s)}catch(s){if(!n||a>0||s instanceof g||s?.code)throw s;await this.sleep(250)}throw new g("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 ie}from"node:crypto";async function _r(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new g(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 ne=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:i=ie()}){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=ie()}){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=ie()}={}){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=ie()}){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 kr(t){let e;try{e=await t.json()}catch{e=null}if(!t.ok||e?.error)throw new g(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 se=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),kr(await this.http.request(`/api/v1/analytics?${o}`,{headers:{accept:"application/json"}}))}};var ae=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 Ze}from"node:crypto";var Sr=Object.freeze({"image/avif":"avif","image/bmp":"bmp","image/jpeg":"jpg","image/png":"png","image/svg+xml":"svg","image/webp":"webp"});async function be(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 le=class{constructor(e){this.http=e}async generate({prompt:e,model:r="flux",seed:o,width:i,height:n,destination:a="inline",generationId:s=Ze(),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 be(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=Ze()}){await this.http.requireScopes(["lixblogs:media:write"]);let a=new FormData,s=Sr[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 be(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 be(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}),qr=Object.freeze({login:["auth","login"],logout:["auth","logout"],whoami:["auth","whoami"],profiles:["auth","profiles"],use:["auth","use"]});function Qe(t){let[e,...r]=t,o=qr[e];return o?[...o,...r]:t}function et(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 h(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 _=Object.freeze({reset:"\x1B[0m",bold:"\x1B[1m",dim:"\x1B[2m",violet:"\x1B[38;5;141m",green:"\x1B[38;5;42m"});function ce(t=process.stdout,e=process.env){return!!t.isTTY&&e.NO_COLOR===void 0&&e.TERM!=="dumb"}function A(t,e,r){return r?`${e}${t}${_.reset}`:t}function rt({url:t,code:e,expiresInSeconds:r,profile:o,interactive:i,color:n=!1}){let a=`${A("\u25C6",_.violet,n)} ${A("LixBlogs",_.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}`,` ${A("Device login",_.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 ${A(e,_.bold,n)}`,` Expires ${Math.ceil(r/60)} min`,o?` Profile ${o} ${A("(local credential slot)",_.dim,n)}`:` Profile ${A("your Accounts username after approval",_.dim,n)}`,"",` ${s}`," No localhost callback or exposed port is required.",""].join(`
|
|
5
|
-
`)}function
|
|
2
|
+
import{parseArgs as to}from"node:util";import{spawn as ro}from"node:child_process";var X={environment:"production",profile:"default",accountsBaseUrl:"https://accounts.elixpo.com",apiBaseUrl:"https://blogs.elixpo.com"},Ce={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=Ce[r]||Ce.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,c=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:c}}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 Le={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-"},hr=5,Ue=0;function yr(t){return Ue+=1,`${t}${Ue}`}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=Le[r]??Le.APPROVE_IMMEDIATELY,n=yr(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:hr}:{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 De="urn:ietf:params:oauth:grant-type:device_code",wr=1,br=15e3,vr={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."},E=class extends Error{constructor(e,{status:r=0,requiresLogin:o=!1}={}){super(vr[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 ye(t){return String(t||"0.0.0").split(".").slice(0,3).map(e=>Number.parseInt(e,10)||0)}function xr(t,e){let r=ye(t),o=ye(e);for(let n=0;n<3;n+=1)if(r[n]!==o[n])return r[n]>o[n];return!0}function Ir(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 E("server_error",{status:t.status})}}function Z(t,e){let r=typeof t?.error=="string"?t.error:"server_error";return new E(r,{status:e.status,requiresLogin:r==="invalid_grant"||r==="access_denied"||r==="expired_token"})}function Ne(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 E("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=br}={}){if(super(),typeof i!="function")throw new TypeError("A fetch implementation is required.");this.accountsBaseUrl=Ir(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 E("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 E("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 E("temporarily_unavailable",{status:e.status});if(ye(r.elixpo_contract_version)[0]!==wr)throw new R("Accounts uses an unsupported device-flow contract version.");if(!xr(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(De))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 E("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:De,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:Ne(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 Ne(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 Be="elixpo",we=class extends Error{constructor(e){super(e),this.name="ProductionAuthGateError"}};function Me({providerId:t,environment:e}){if(e==="production"&&t!==Be)throw new we(`Provider "${t}" is not approved for production. Only "${Be}" 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 Me({providerId:e.providerId,environment:t.environment}),e}var T=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 T(`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 T(`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 T(`OS keychain is unavailable: ${r.message}`)}}async listProfiles(){try{return await this._realStore.listProfiles()}catch(e){throw new T(`OS keychain is unavailable: ${e.message}`)}}};import{Entry as _r}from"@napi-rs/keyring";var Er="lixblogs-cli",kr="__lixblogs_availability_probe__";function re(t){return new _r(Er,t)}async function ze(){let t=re(kr);try{return t.setPassword("probe"),t.deletePassword(),{available:!0}}catch(e){return{available:!1,error:String(e.message??e).split(`
|
|
3
|
+
`)[0].trim()}}}var be=class extends j{async get(e){let r=re(e),o;try{o=r.getPassword()}catch(n){if(Fe(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(Fe(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 Fe(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 be,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 Ge from"node:path";import Sr from"node:os";function $r(){return Ge.join(Sr.homedir(),".config","lixblogs","profiles.json")}var b=class{constructor(e=$r()){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(Ge.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 Ve({allowInsecureFallback:t=!1,profileRegistry:e}={}){let r=await ze();if(!r.available){if(!t)throw new T(`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 xe="[REDACTED]",qr=/token|refresh|secret|password|authorization/i,Ar=/^(mock-(access|refresh)-|Bearer\s+)\S+/i;function Je(t){return typeof t=="string"&&Ar.test(t)?xe:t}function ve(t){if(Array.isArray(t))return t.map(e=>ve(e));if(t&&typeof t=="object"){let e={};for(let[r,o]of Object.entries(t))qr.test(r)?e[r]=xe:typeof o=="object"&&o!==null?e[r]=ve(o):e[r]=Je(o);return e}return Je(t)}function ie(t,e){return JSON.stringify(ve(t),null,e)}function D(t){return typeof t!="string"?t:t.replace(/(mock-(access|refresh)-\S+|Bearer\s+\S+)/gi,xe)}async function He({provider:t,credentialStore:e,profileId:r,scopes:o,openBrowser:n,resolveProfileId:i,sleep:s=l=>new Promise(c=>setTimeout(c,l)),onStatus:a=()=>{}}){let l;try{l=await t.requestDeviceCode({scopes:o})}catch(u){return{ok:!1,reason:D(u.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 c=l.pollIntervalSeconds*1e3,d=Date.now()+l.expiresInSeconds*1e3;for(;Date.now()<d;){await s(c);let u;try{u=await t.pollDeviceCode({deviceCode:l.deviceCode})}catch(f){return{ok:!1,reason:D(f.message)}}if(u.status==="approved"){let f=r;if(i)try{f=await i({accessToken:u.token.accessToken,requestedProfileId:r})}catch(k){return{ok:!1,reason:D(k.message)}}return await e.set(f,{accessToken:u.token.accessToken,refreshToken:u.token.refreshToken,expiresAt:Date.now()+u.token.expiresInSeconds*1e3,scopes:u.token.scopes}),a({type:"approved"}),{ok:!0,profileId:f}}if(u.status==="denied")return a({type:"denied"}),{ok:!1,reason:"Login was denied."};if(u.status==="expired")return a({type:"expired"}),{ok:!1,reason:"Device code expired before login was approved."};if(u.status==="slow_down"){c+=u.pollIntervalIncreaseSeconds*1e3,a({type:"slow_down",newIntervalMs:c});continue}a({type:"pending"})}return{ok:!1,reason:"Device code expired before login was approved."}}async function We({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 Ke({credentialStore:t,profileId:e}){return await t.delete(e),{ok:!0}}async function Xe({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 Ye({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 Ze({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 Qe({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 Rr=6e4,et=new WeakMap;function Tr(t){let e=et.get(t);return e||(e=new Map,et.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"}},Ie=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=Rr}){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=Tr(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 E&&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 Ie(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 tt}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 Pr(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 Pr(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=tt()}={}){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=tt()}){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 Or(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 Or(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 jr(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 jr(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 Cr(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),Cr(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 rt}from"node:crypto";var Lr=Object.freeze({"image/avif":"avif","image/bmp":"bmp","image/jpeg":"jpg","image/png":"png","image/svg+xml":"svg","image/webp":"webp"});async function _e(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=rt(),reference:l}){await this.http.requireScopes(["lixblogs:media:write"]);let c,d;if(l){c=new FormData;for(let[f,k]of Object.entries({prompt:e,model:r,seed:o,width:n,height:i,destination:s,generationId:a}))k!==void 0&&c.append(f,String(k));c.append("referenceImage",new Blob([l.bytes],{type:l.mimeType}),l.name||"reference-image"),d={accept:"image/*, application/json"}}else c=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 u=await this.http.requestRaw("/api/v1/media/generate",{method:"POST",headers:d,body:c});if(!u.ok)throw await _e(u);return{bytes:new Uint8Array(await u.arrayBuffer()),mimeType:u.headers.get("content-type")||"image/jpeg",generationId:a}}async upload({bytes:e,mimeType:r,blogId:o,mediaType:n="inline",uploadId:i=rt()}){await this.http.requireScopes(["lixblogs:media:write"]);let s=new FormData,a=Lr[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 _e(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 _e(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}),Ur=Object.freeze({login:["auth","login"],logout:["auth","logout"],whoami:["auth","whoami"],profiles:["auth","profiles"],use:["auth","use"]});function ot(t){let[e,...r]=t,o=Ur[e];return o?[...o,...r]:t}function nt(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 v=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 I(t,e,r){return r?`${e}${t}${v.reset}`:t}function st({url:t,code:e,expiresInSeconds:r,profile:o,interactive:n,color:i=!1}){let s=`${I("\u25C6",v.violet,i)} ${I("LixBlogs",v.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}`,` ${I("Device login",v.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 ${I(e,v.bold,i)}`,` Expires ${Math.ceil(r/60)} min`,o?` Profile ${o} ${I("(local credential slot)",v.dim,i)}`:` Profile ${I("your Accounts username after approval",v.dim,i)}`,"",` ${a}`," No localhost callback or exposed port is required.",""].join(`
|
|
5
|
+
`)}function x(t,e=!1){return` ${I(`\u2713 ${t}`,v.green,e)}`}function P(t,e=!1){return` ${I(`! ${t}`,v.yellow,e)}`}function C(t,e=!1){return` ${I(`\u2022 ${t}`,v.gray,e)}`}function at(t,e=!1){return` ${I(`\u2715 ${t}`,v.red,e)}`}var it=Object.freeze(["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"]);function Dr(t,{stream:e=process.stderr,enabled:r=!!e.isTTY,intervalMs:o=80,color:n=p(e)}={}){if(!r)return{stop(){}};let i=0,s=()=>{e.write(`\r\x1B[2K${I(it[i],v.violet,n)} ${I(t,v.gray,n)}`),i=(i+1)%it.length};s();let a=setInterval(s,o);a.unref?.();let l=!1;return{stop(){l||(l=!0,clearInterval(a),e.write("\r\x1B[2K"))}}}async function q(t,e,r,o={}){let n=Dr(e,{...o,enabled:o.enabled??(!t?.json&&!t?.quiet&&!!(o.stream||process.stderr).isTTY)});try{return await r()}finally{n.stop()}}function lt({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 ct(t){return[{type:"text",text:t}]}function B(t){let e=String(t||""),r=[],o=/(\*\*([^*]+)\*\*|\*([^*]+)\*|`([^`]+)`|\[([^\]]+)\]\((https:\/\/[^)]+)\))/g,n=0;for(let i of e.matchAll(o))i.index>n&&r.push({type:"text",text:e.slice(n,i.index)}),i[2]!==void 0?r.push({type:"text",text:i[2],styles:{bold:!0}}):i[3]!==void 0?r.push({type:"text",text:i[3],styles:{italic:!0}}):i[4]!==void 0?r.push({type:"text",text:i[4],styles:{code:!0}}):r.push({type:"link",href:i[6],content:[{type:"text",text:i[5],styles:{}}]}),n=i.index+i[0].length;return n<e.length&&r.push({type:"text",text:e.slice(n)}),r.length?r:ct(e)}function ut(t){let e=String(t||"").replace(/\r\n/g,`
|
|
6
6
|
`).split(`
|
|
7
|
-
`),r=[],o=[],
|
|
8
|
-
`)}}:{type:"codeBlock",props:{language:l[1].toLowerCase()},content:
|
|
9
|
-
`))});continue}let
|
|
10
|
-
${
|
|
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 he=[];for(i+=1;i<e.length&&!/^```/.test(e[i].trim());)he.push(e[i++]);r.push(l[1].toLowerCase()==="mermaid"?{type:"mermaidBlock",props:{diagram:he.join(`
|
|
8
|
+
`)}}:{type:"codeBlock",props:{language:l[1].toLowerCase()},content:ct(he.join(`
|
|
9
|
+
`))});continue}let c=a.match(/^(#{1,3})\s+(.+)/);if(c){n(),r.push({type:"heading",props:{level:String(c[1].length)},content:B(c[2])});continue}let d=a.match(/^[-*]\s+\[([ xX])\](?:\s+(.*))?$/);if(d){n(),r.push({type:"checkListItem",props:{checked:d[1].toLowerCase()==="x"},content:B(d[2]||"")});continue}let u=a.match(/^[-*]\s+(.+)/);if(u){n(),r.push({type:"bulletListItem",content:B(u[1])});continue}let f=a.match(/^\d+\.\s+(.+)/);if(f){n(),r.push({type:"numberedListItem",content:B(f[1])});continue}let k=a.match(/^>\s?(.*)/);if(k){n(),r.push({type:"quote",content:B(k[1])});continue}let ge=a.match(/^!\[([^\]]*)\]\((https:\/\/[^)]+)\)$/);if(ge){n(),r.push({type:"image",props:{url:ge[2],caption:ge[1]}});continue}if(/^([-*_])\1{2,}$/.test(a)){n(),r.push({type:"divider"});continue}o.push(a)}return n(),r}function dt(t){return(t?.content||[]).map(e=>typeof e=="string"?e:e?.type==="link"?dt(e):e?.text||"").join("")}function pt(t){return(t?.content||[]).map(e=>{if(typeof e=="string")return e;if(e?.type==="link")return`[${pt(e)}](${e.href})`;let r=e?.text||"";return e?.styles?.code&&(r=`\`${r}\``),e?.styles?.italic&&(r=`*${r}*`),e?.styles?.bold&&(r=`**${r}**`),r}).join("")}function fe(t){return(t||[]).map(e=>{let r=pt(e);return e.type==="heading"?`${"#".repeat(Number(e.props?.level)||1)} ${r}`:e.type==="checkListItem"?`- [${e.props?.checked?"x":" "}] ${r}`:e.type==="bulletListItem"?`- ${r}`:e.type==="numberedListItem"?`1. ${r}`:e.type==="quote"?`> ${r}`:e.type==="codeBlock"?`\`\`\`${e.props?.language||""}
|
|
10
|
+
${dt(e)}
|
|
11
11
|
\`\`\``:e.type==="mermaidBlock"?`\`\`\`mermaid
|
|
12
12
|
${e.props?.diagram||""}
|
|
13
13
|
\`\`\``:e.type==="image"?``:e.type==="divider"?"---":r}).join(`
|
|
14
14
|
|
|
15
|
-
`)}import{promises as V}from"node:fs";import{tmpdir as Rr}from"node:os";import ve from"node:path";import{spawn as Pr}from"node:child_process";async function Tr(t){let e="";t.setEncoding("utf8");for await(let r of t)e+=r;return e}async function Or(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 V.mkdtemp(ve.join(Rr(),"lixblogs-")),o=ve.join(r,"post.md");await V.writeFile(o,t,{mode:384});try{return await new Promise((i,n)=>{let a=Pr(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 V.readFile(o,"utf8")}finally{await V.rm(r,{recursive:!0,force:!0})}}async function xe(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 V.readFile(ve.resolve(t.file),"utf8"):t.stdin?i=await Tr(e):t.content!==void 0?i=t.content:i=await Or(r),{markdown:i,blocks:nt(i)}}function Ee(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 jr(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 de(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(jr(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 _e from"node:path";async function st({client:t,options:e}){return t.list({status:e.status,limit:e.limit,cursor:e.cursor})}async function ke({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");let r=await t.get(e);return{...r,markdown:ue(r.content)}}async function at({client:t,options:e,stdin:r}){let o=await xe(e,{stdin:r}),i={...Ee(e),content:o?.blocks||[]};return de(i),e["dry-run"]?{dryRun:!0,input:i,markdown:o?.markdown||""}:t.create(i,{idempotencyKey:e["idempotency-key"]})}async function lt({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 xe(r,{stdin:o,initial:ue(i.content)}),a={...Ee(r),...n?{content:n.blocks}:{}};if(!Object.keys(a).length)throw new Error("No blog changes were provided.");if(de(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 g)||s.code!=="revision_conflict")throw s;let l=await t.get(e),u=r.conflictDirectory||_e.resolve(".lixblogs-conflicts");await Ie.mkdir(u,{recursive:!0});let d=e.replace(/[^A-Za-z0-9._-]/g,"_"),c=_e.join(u,`${d}-local.json`),p=_e.join(u,`${d}-server.md`);throw await Promise.all([Ie.writeFile(c,JSON.stringify(a,null,2),{mode:384}),Ie.writeFile(p,ue(l.content),{mode:384})]),s.details={...s.details,localPath:c,serverPath:p,serverEtag:l.etag},s}}async function ct({client:t,id:e,options:r}){if(!e)throw new Error("A blog ID is required.");let o=await t.get(e);de(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}:(h(r,"Publishing this blog"),t.publish(e,{etag:r.etag||o.etag,status:i,idempotencyKey:r["idempotency-key"]}))}async function ut({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");return{data:await t.versions(e)}}async function dt({client:t,id:e,options:r}){if(!e||!r.version)throw new Error("A blog ID and --version are required.");h(r,"Restoring this historical version");let o=await t.get(e);return t.restoreVersion(e,r.version,{etag:r.etag||o.etag})}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);return r["dry-run"]?{dryRun:!0,id:e,from:o.status,to:"draft"}:(h(r,"Unpublishing this blog"),t.unpublish(e,{etag:r.etag||o.etag}))}async function Se({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 ft({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"}:(h(r,"Restoring this blog"),t.restore(e,{etag:r.etag||o.etag}))}async function mt({client:t}){return t.list()}async function gt({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.get(e)}async function ht({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.collections(e)}async function yt({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.members(e)}async function wt({client:t}){return t.targets()}function U(t){if(!t)throw new Error("A blog ID is required.")}async function bt({client:t,id:e}){return U(e),t.list(e)}async function vt({client:t}){return t.invitations()}async function xt({client:t,id:e,options:r}){if(U(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}:(h(r,"Inviting this collaborator"),t.invite(e,{user:r.user,role:r.role,idempotencyKey:r["idempotency-key"]}))}async function Et({client:t,id:e,options:r}){if(U(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}:(h(r,"Changing this collaborator role"),t.role(e,{user:r.user,role:r.role,idempotencyKey:r["idempotency-key"]}))}async function It({client:t,id:e,options:r}){return U(e),r["dry-run"]?{dryRun:!0,action:"remove",blogId:e,user:r.user||"self"}:(h(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 U(e),r["dry-run"]?{dryRun:!0,action:"accept",blogId:e,showOnProfile:!r["hide-on-profile"]}:(h(r,"Accepting this collaboration invitation"),t.resolveInvitation(e,{action:"accept",showOnProfile:!r["hide-on-profile"],idempotencyKey:r["idempotency-key"]}))}async function kt({client:t,id:e,options:r}){return U(e),r["dry-run"]?{dryRun:!0,action:"decline",blogId:e}:(h(r,"Declining this collaboration invitation"),t.resolveInvitation(e,{action:"decline",idempotencyKey:r["idempotency-key"]}))}import{access as Cr,cp as Lr,readFile as Ur,readdir as Dr}from"node:fs/promises";import b from"node:path";import{fileURLToPath as Nr}from"node:url";var qe=b.dirname(Nr(import.meta.url)),$t=b.basename(qe)==="dist"?b.resolve(qe,".."):b.resolve(qe,"../../.."),St=b.join($t,"skills"),qt=b.resolve($t,"../..",".agents","skills");async function D(t){try{return await Cr(t),!0}catch{return!1}}async function $e(){if(await D(St))return St;if(await D(qt))return qt;let t=new Error("No bundled LixBlogs skills were found. Reinstall @elixpo/lixblogs-cli.");throw t.code="skills_unavailable",t}function At(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 Rt(t,e){let r=await Ur(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 Pt(){let t=await $e(),e=await Dr(t,{withFileTypes:!0});return Promise.all(e.filter(r=>r.isDirectory()&&r.name.startsWith("lixblogs-")).map(r=>Rt(t,r.name))).then(r=>r.map(({content:o,...i})=>i).sort((o,i)=>o.name.localeCompare(i.name)))}async function Tt({name:t}){let e=await $e(),r=At(t);if(!await D(b.join(e,r,"SKILL.md"))){let o=new Error(`Skill "${r}" is not bundled.`);throw o.code="skill_not_found",o}return Rt(e,r)}async function Ot({name:t,options:e}){let r=await $e(),o=At(t),i=b.join(r,o);if(!await D(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 D(a)};if(await D(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}h(e,`Replacing ${a}`)}else h(e,`Installing ${o} into ${n}`);return await Lr(i,a,{recursive:!0,force:!!e.force}),{installed:!0,name:o,target:a}}import{writeFile as Br}from"node:fs/promises";var Mr=new Set(["overview","timeline","posts","sources","devices","countries"]),Fr=new Set(["7d","30d","90d","12m","custom"]);function Lt(t={}){let e=t.dimension||"overview",r=t.range||(t.from||t.to?"custom":"30d");if(!Mr.has(e))throw new Error(`Unsupported analytics dimension: ${e}.`);if(!Fr.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 Ut({client:t,options:e}){return t.query(Lt(e))}function jt(t){let e=t==null?"":typeof t=="object"?JSON.stringify(t):String(t);return/[",\n]/.test(e)?`"${e.replaceAll('"','""')}"`:e}function Ct(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 Dt({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(Lt(e)),i;if(r==="json")i=`${JSON.stringify(o,null,2)}
|
|
16
|
-
`;else{let
|
|
17
|
-
${
|
|
15
|
+
`)}import{promises as K}from"node:fs";import{tmpdir as Nr}from"node:os";import Ee from"node:path";import{spawn as Br}from"node:child_process";async function Mr(t){let e="";t.setEncoding("utf8");for await(let r of t)e+=r;return e}async function Fr(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(Nr(),"lixblogs-")),o=Ee.join(r,"post.md");await K.writeFile(o,t,{mode:384});try{return await new Promise((n,i)=>{let s=Br(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 ke(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 Mr(e):t.content!==void 0?n=t.content:n=await Fr(r),{markdown:n,blocks:ut(n)}}function Se(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 zr(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(zr(t.content)<20)throw new Error("A post needs at least 20 words before publishing.")}return t}import{promises as $e}from"node:fs";import qe from"node:path";var Gr=new Set(["create","edit","publish","unpublish","delete","trash","restore","restore-version"]);async function ft({client:t,action:e,result:r}){if(!Gr.has(e)||r?.dryRun||!r?.id||r.url&&r.status)return r;try{let o=await t.get(r.id);return{...r,status:o.status||r.status,url:o.url||r.url}}catch{return r}}async function mt({client:t,options:e}){return t.list({status:e.status,limit:e.limit,cursor:e.cursor})}async function Ae({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 gt({client:t,options:e,stdin:r}){let o=await ke(e,{stdin:r}),n={...Se(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 ht({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 ke(r,{stdin:o,initial:fe(n.content)}),s={...Se(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),c=r.conflictDirectory||qe.resolve(".lixblogs-conflicts");await $e.mkdir(c,{recursive:!0});let d=e.replace(/[^A-Za-z0-9._-]/g,"_"),u=qe.join(c,`${d}-local.json`),f=qe.join(c,`${d}-server.md`);throw await Promise.all([$e.writeFile(u,JSON.stringify(s,null,2),{mode:384}),$e.writeFile(f,fe(l.content),{mode:384})]),a.details={...a.details,localPath:u,serverPath:f,serverEtag:l.etag},a}}async function yt({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 wt({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");return{data:await t.versions(e)}}async function bt({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 vt({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 Re({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);if(r["dry-run"])return{dryRun:!0,id:e,permanent:r.permanent};let n=await t.delete(e,{etag:r.etag||o.etag,permanent:r.permanent});return{...n,status:r.permanent?"deleted":"trashed",url:o.url||n.url}}async function xt({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}))}var Vr={create:"Blog created",edit:"Blog updated",publish:"Blog published",unpublish:"Blog unpublished",delete:"Blog deleted",trash:"Blog moved to trash",restore:"Blog restored","restore-version":"Blog version restored"};function It(t,e){let r=t==="delete"&&e?.status==="trashed"?"Blog moved to trash":Vr[t]||"Blog updated",o=e?.status?` [${e.status}]`:"",n=e?.url?` ${e.url}`:"";return`${r}${o}${n}`}function _t(t,e,r){if(t==="delete")return`Media ${e.id} deleted.`;let o=t==="generate"?"generated":"uploaded";return e.blog?`Image ${o} and attached to blog ${r}.`:`Image ${o} and stored.`}async function Et({client:t}){return t.list()}async function kt({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.get(e)}async function St({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.collections(e)}async function $t({client:t,id:e}){if(!e)throw new Error("An organization ID or handle is required.");return t.members(e)}async function qt({client:t}){return t.targets()}function M(t){if(!t)throw new Error("A blog ID is required.")}async function At({client:t,id:e}){return M(e),t.list(e)}async function Rt({client:t}){return t.invitations()}async function Tt({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 Pt({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 Ot({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 jt({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 Ct({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 Jr,cp as Hr,readFile as Wr,readdir as Kr}from"node:fs/promises";import _ from"node:path";import{fileURLToPath as Xr}from"node:url";var Te=_.dirname(Xr(import.meta.url)),Dt=_.basename(Te)==="dist"?_.resolve(Te,".."):_.resolve(Te,"../../.."),Lt=_.join(Dt,"skills"),Ut=_.resolve(Dt,"../..",".agents","skills");async function F(t){try{return await Jr(t),!0}catch{return!1}}async function Pe(){if(await F(Lt))return Lt;if(await F(Ut))return Ut;let t=new Error("No bundled LixBlogs skills were found. Reinstall @elixpo/lixblogs-cli.");throw t.code="skills_unavailable",t}function Nt(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 Bt(t,e){let r=await Wr(_.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 Mt(){let t=await Pe(),e=await Kr(t,{withFileTypes:!0});return Promise.all(e.filter(r=>r.isDirectory()&&r.name.startsWith("lixblogs-")).map(r=>Bt(t,r.name))).then(r=>r.map(({content:o,...n})=>n).sort((o,n)=>o.name.localeCompare(n.name)))}async function Ft({name:t}){let e=await Pe(),r=Nt(t);if(!await F(_.join(e,r,"SKILL.md"))){let o=new Error(`Skill "${r}" is not bundled.`);throw o.code="skill_not_found",o}return Bt(e,r)}async function zt({name:t,options:e}){let r=await Pe(),o=Nt(t),n=_.join(r,o);if(!await F(_.join(n,"SKILL.md"))){let a=new Error(`Skill "${o}" is not bundled.`);throw a.code="skill_not_found",a}let i=_.resolve(e.target||".agents/skills"),s=_.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 Hr(n,s,{recursive:!0,force:!!e.force}),{installed:!0,name:o,target:s}}import{writeFile as Yr}from"node:fs/promises";var Zr=new Set(["overview","timeline","posts","sources","devices","countries"]),Qr=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(!Zr.has(e))throw new Error(`Unsupported analytics dimension: ${e}.`);if(!Qr.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 Ht({client:t,options:e}){return t.query(Jt(e))}function Gt(t){let e=t==null?"":typeof t=="object"?JSON.stringify(t):String(t);return/[",\n]/.test(e)?`"${e.replaceAll('"','""')}"`:e}function Vt(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 Wt({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)),n;if(r==="json")n=`${JSON.stringify(o,null,2)}
|
|
16
|
+
`;else{let i=Vt(o),s=[...new Set(i.flatMap(a=>Object.keys(a)))];n=`${s.map(Gt).join(",")}
|
|
17
|
+
${i.map(a=>s.map(l=>Gt(a[l])).join(",")).join(`
|
|
18
18
|
`)}
|
|
19
|
-
`}return await
|
|
19
|
+
`}return await Yr(e.output,n,{encoding:"utf8",flag:"wx"}),{output:e.output,format:r,rows:Vt(o).length}}async function Kt({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 Xt({integrationsClient:t}){try{return{ok:!0,data:await t.cloudinaryStatus()}}catch(e){return{ok:!1,error:e}}}import{randomUUID as Yt}from"node:crypto";import{promises as Oe}from"node:fs";import z from"node:path";function eo(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 Zt=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 Qt({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:Yt(),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 er({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 u=z.resolve(r.reference),f=z.extname(u).toLowerCase(),k=Zt[f];if(!k)throw new Error("Unsupported reference image type. Use AVIF, BMP, JPEG, PNG, SVG, or WebP.");i={bytes:await Oe.readFile(u),mimeType:k,name:z.basename(u)}}let s=await t.generate({prompt:o,model:r.model||"flux",destination:n,reference:i,...eo(r)}),a=s.mimeType.includes("png")?"png":s.mimeType.includes("webp")?"webp":"jpg",l=z.resolve(r.output||`lixblogs-${s.generationId}.${a}`);await Oe.writeFile(l,s.bytes,{mode:384});let c=null,d=null;return r.blog&&(c=await t.upload({bytes:s.bytes,mimeType:s.mimeType,blogId:r.blog,mediaType:n,uploadId:s.generationId}),r.attach&&(d=await Qt({blogClient:e,blogId:r.blog,media:c,type:n,caption:r.caption}))),{generationId:s.generationId,output:l,mimeType:s.mimeType,media:c,blog:d}}async function tr({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 Oe.readFile(n),s=z.extname(n).toLowerCase(),a=Zt[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"]||Yt()}),c=r.attach?await Qt({blogClient:e,blogId:r.blog,media:l,type:o,caption:r.caption}):null;return{media:l,blog:c}}async function rr({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 or({client:t,id:e}){if(!e)throw new Error("A blog ID is required.");return t.comments(e)}async function nr({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 ir({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 sr({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 oo={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}},no=`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
|
-
`,
|
|
106
|
-
`)}function
|
|
107
|
-
`)
|
|
108
|
-
`),o.hint&&process.stderr.write(`Hint: ${o.hint}
|
|
109
|
-
`),o.requestId&&process.stderr.write(`Request: ${o.requestId}
|
|
110
|
-
`)
|
|
111
|
-
${
|
|
112
|
-
`),process.exitCode=
|
|
113
|
-
`),process.exitCode=
|
|
114
|
-
`),process.stderr.write(`Available categories: ${Object.keys(
|
|
115
|
-
`),process.exitCode=
|
|
116
|
-
`),process.stderr.write(`Available commands: ${Object.keys(
|
|
117
|
-
`),process.exitCode=
|
|
105
|
+
`,io=["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 je(t){let e=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",r=process.platform==="win32"?["/c","start","",t]:[t],o=ro(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=nt({...o,message:n});if(t.json)process.stdout.write(ie(i)+`
|
|
107
|
+
`);else if(!t.quiet){let s=p(process.stderr);process.stderr.write(`${at(n,s)}
|
|
108
|
+
`),o.hint&&process.stderr.write(`${P(`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 Ve({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 lr(t){let e=S({flags:O(t)}),r=new b,o=$(e.profile),n=t.scope?.length?[...t.scope]:[...io];!e.profileExplicit&&!n.includes("lixblogs:profile:read")&&n.push("lixblogs:profile:read");let i;try{i=U(e)}catch(c){return m(t,c.message)}let s=await A(t,r);if(!s)return;let a=()=>{},l;try{l=await He({provider:i,credentialStore:s,profileId:o,scopes:n,openBrowser:t.open?je:void 0,resolveProfileId:e.profileExplicit?void 0:({accessToken:c})=>Qe({accessToken:c,apiBaseUrl:e.apiBaseUrl}),onStatus:c=>{if(t.json){c.type!=="pending"&&w(t,{event:c.type,...c});return}if(!t.quiet)if(c.type==="verification_pending"){let d=c.verificationUriComplete||c.verificationUri,u=!!process.stdin.isTTY&&!t["no-input"];process.stdout.write(st({url:d,code:c.userCode,expiresInSeconds:c.expiresInSeconds,profile:e.profileExplicit?o:null,interactive:u,color:p()})),u&&!t.open&&(a=lt({input:process.stdin,open:je,url:d}))}else c.type==="approved"?console.log(x("Access approved by Elixpo Accounts.",p())):c.type==="denied"?console.log(P("Access denied.",p())):c.type==="expired"&&console.log(P("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(x(`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 so(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});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 ao(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 lo(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 je(r),t.quiet||console.log(C(`Create your account at ${r}, then approve the device login.`,p())),await lr(t)}async function co(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 Ke({credentialStore:n,profileId:o});w(t,i),!t.json&&!t.quiet&&console.log(x(`Logged out profile "${o}".`,p()))}async function uo(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 Xe({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(x(`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(u){return m(t,u.message)}let a=await A(t,n);if(!a)return;let l=new N({provider:s,credentialStore:a,profileId:i,apiBaseUrl:o.apiBaseUrl}),c=new de(l),d=await q(t,r.endsWith("status")?"Checking integration\u2026":"Disconnecting integration\u2026",async()=>{if(r==="cloudinary-status")return Xt({integrationsClient:c});if(r==="cloudinary-disconnect")return Kt({integrationsClient:c,confirmed:!0});try{return{ok:!0,data:r==="pollinations-status"?await c.pollinationsStatus({refresh:t.force}):await c.pollinationsDisconnect()}}catch(u){return{ok:!1,error:u}}});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()):x(`${r.startsWith("pollinations")?"Pollinations":"Cloudinary"} connection disconnected.`,p()))}async function po(t){let e=new b,r=await A(t,e);if(!r)return;let o=await Ye({credentialStore:r,profileRegistry:e});if(w(t,o),!t.json){o.profiles.length||console.log(P("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 fo(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 Ze({credentialStore:n,profileRegistry:o,profileId:r});if(!i.ok)return m(t,i.reason);w(t,i),!t.json&&!t.quiet&&console.log(x(`Using profile "${r}".`,p()))}var cr={list:mt,get:Ae,preview:Ae,create:gt,edit:ht,publish:yt,unpublish:vt,delete:Re,trash:Re,restore:xt,history:wt,"restore-version":bt},ur={list:Et,get:kt,collections:St,members:$t,targets:qt},dr={list:At,invitations:Rt,invite:Tt,role:Pt,remove:Ot,accept:jt,decline:Ct},pr={list:({options:t})=>Mt(t),inspect:({id:t})=>Ft({name:t}),install:({id:t,options:e})=>zt({name:t,options:e})},fr={query:Ht,export:Wt},mr={generate:er,upload:tr,delete:rr},gr={list:or,add:nr,reply:ir,delete:sr};async function mo(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(u){return m(t,u.message)}let l=new N({provider:a,credentialStore:s,profileId:i,apiBaseUrl:o.apiBaseUrl}),c=new W(l),d={...t,limit:t.limit===void 0?void 0:Number.parseInt(t.limit,10)};try{let u=await q(t,`${r==="list"?"Loading":r==="get"||r==="preview"?"Opening":"Updating"} blog\u2026`,async()=>{let f=await cr[r]({client:c,id:e[0],options:d,stdin:process.stdin});return ft({client:c,action:r,result:f})});if(w(t,{ok:!0,...u}),!t.json&&!t.quiet)if(r==="list"){for(let f of u.data||[])console.log(`${f.id} ${f.status} ${f.title||"(untitled)"}`);u.meta?.nextCursor&&console.log(C(`Next cursor: ${u.meta.nextCursor}`,p()))}else if(r==="get"||r==="preview")console.log(`${u.title||"(untitled)"} [${u.status}]
|
|
111
|
+
${u.markdown||""}`);else if(r==="history")for(let f of u.data||[])console.log(`${f.id} ${f.label||"snapshot"} ${f.created_at} ${f.username||"system"}`);else u.dryRun?console.log(P(`Dry run: ${r} validated; no changes sent.`,p())):console.log(x(It(r,u),p()))}catch(u){if(t.json&&u instanceof h){process.stdout.write(ie({ok:!1,error:{code:u.code,message:u.message,requestId:u.requestId,details:u.details}})+`
|
|
112
|
+
`),process.exitCode=u.status===412?3:1;return}return m(t,u,u.status===412?g.CONFLICT:g.ERROR)}}async function go(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",()=>ur[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 ho(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",()=>mr[r]({mediaClient:new pe(o.http),blogClient:o.client,id:e[0],options:t}));w(t,{ok:!0,data:i}),!t.json&&!t.quiet&&console.log(x(_t(r,i,t.blog),p()))}catch(n){m(t,n,n.status===401||n.status===403?g.AUTH:g.ERROR)}}async function yo(t,e,r){let o=await J(t);if(o)try{let n=await q(t,r==="list"?"Loading comments\u2026":"Updating comments\u2026",()=>gr[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(x(`${r} completed for ${n.id}.`,p()))}catch(n){m(t,n,n.status===401||n.status===403?g.AUTH:g.ERROR)}}async function wo(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",()=>dr[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(P(`Dry run: ${i.action} validated; no changes sent.`,p()));return}if(r!=="list"&&r!=="invitations"){console.log(x(`${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 bo(t,e,r){try{let o=await pr[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(P(`Dry run: install ${o.name} to ${o.target}${o.replace?" (replace)":""}.`,p())):console.log(x(`Installed ${o.name} at ${o.target}.`,p()))}catch(o){m(t,o)}}async function vo(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",()=>fr[r]({client:n,options:i}));if(w(t,{ok:!0,data:s}),t.json||t.quiet)return;if(r==="export"){console.log(x(`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,c]of Object.entries(a.values.totals))console.log(`${l} ${c} ${a.values.changes[l]}%`);else if(a.dimension==="timeline")a.values.labels.forEach((l,c)=>console.log(`${l} ${a.values.views[c]} ${a.values.reads[c]}`));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 ar={auth:{login:lr,status:so,whoami:ao,logout:co,revoke:uo,profiles:po,use:fo},blog:Object.fromEntries(Object.keys(cr).map(t=>[t,(e,r)=>mo(e,r,t)])),org:Object.fromEntries(Object.keys(ur).map(t=>[t,(e,r)=>go(e,r,t)])),collab:Object.fromEntries(Object.keys(dr).map(t=>[t,(e,r)=>wo(e,r,t)])),skill:Object.fromEntries(Object.keys(pr).map(t=>[t,(e,r)=>bo(e,r,t)])),analytics:Object.fromEntries(Object.keys(fr).map(t=>[t,(e,r)=>vo(e,r,t)])),media:Object.fromEntries(Object.keys(mr).map(t=>[t,(e,r)=>ho(e,r,t)])),comment:Object.fromEntries(Object.keys(gr).map(t=>[t,(e,r)=>yo(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 xo(){let t,e;try{({values:t,positionals:e}=to({args:process.argv.slice(2),options:oo,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(no);return}if(e[0]==="register"){await lo(t);return}e=ot(e);let[r,o]=e,n=ar[r];if(!n){process.stderr.write(`Error: Unknown command category "${r}".
|
|
114
|
+
`),process.stderr.write(`Available categories: ${Object.keys(ar).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))}xo();
|
package/package.json
CHANGED