dunsocial 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,19 +1,18 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{mkdir as $n,readFile as Ge,writeFile as Nn,chmod as Un,unlink as Dn}from"node:fs/promises";import{dirname as bn,join as ue}from"node:path";import{homedir as Je}from"node:os";var y={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class t extends Error{code;details;constructor(e,n=y.USAGE,u){super(e);this.name="CliError",this.code=n,this.details=u}}function ne(e){if(e===401)return y.AUTH;if(e===403)return y.FORBIDDEN;if(e===404)return y.NOT_FOUND;if(e===429)return y.RATE_LIMITED;if(e>=400&&e<500)return y.VALIDATION;return y.NETWORK}var _e="https://api.dunsocial.com",M="0.3.1";function He(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let e=process.env.XDG_CONFIG_HOME,n=e?e:ue(Je(),".config");return ue(n,"dunsocial","config.json")}async function ze(){let e=He();try{let n=await Ge(e,"utf8");return JSON.parse(n)}catch(n){if(n.code==="ENOENT")return{};throw new t(`Failed to read config at ${e}: ${n.message}`,y.USAGE)}}async function de(e){let n=await ze(),u=(e.apiUrl||process.env.DUN_API_URL||n.apiUrl||_e).replace(/\/$/,""),r=e.token||process.env.DUN_TOKEN||n.token,w=e.workspace||process.env.DUN_WORKSPACE_ID||n.defaultWorkspaceId||void 0,d=process.env.DUN_JSON==="0",m=!process.stdout.isTTY,i=d?!1:e.json===!0||process.env.DUN_JSON==="1"||process.env.CI==="true"||process.env.CI==="1"||m;return{apiUrl:u,token:r,workspaceId:w,json:i,debug:e.debug===!0||process.env.DUN_DEBUG==="1",quiet:e.quiet===!0,yes:e.yes===!0||process.env.CI==="true"||process.env.CI==="1",config:n}}function re(e){let n={},u=[],r=0,w=!0;while(r<e.length){let O=e[r];if(w&&O==="--"){w=!1,r+=1;continue}if(w&&O.startsWith("--")){let N=O.indexOf("=");if(N!==-1){n[O.slice(2,N)]=O.slice(N+1),r+=1;continue}let T=O.slice(2),D=e[r+1];if(D!==void 0&&!D.startsWith("-"))n[T]=D,r+=2;else n[T]=!0,r+=1;continue}if(w&&O.startsWith("-")&&O.length>1&&O!=="-"){let N=O.slice(1);if(N.length>1&&!N.includes("=")){for(let W of N)n[W]=!0;r+=1;continue}let T=N,D=e[r+1];if(D!==void 0&&!D.startsWith("-"))n[T]=D,r+=2;else n[T]=!0,r+=1;continue}u.push(O),r+=1}let d=u[0],m,i,o=1;if(d==="memory"&&(u[1]==="collections"||u[1]==="collection"))i="collections",m=u[2],o=m?3:2;else m=u[1],o=m?2:1;return{group:d,action:m,nest:i,flags:n,positionals:u.slice(o)}}function Q(e,...n){for(let u of n){let r=e[u];if(typeof r==="string"&&r.length>0)return r}return}function G(e,...n){for(let u of n){let r=e[u];if(r===!0)return!0;if(typeof r==="string"){let w=r.toLowerCase();if(w==="1"||w==="true"||w==="yes")return!0}}return!1}function B(e){return G(e,"help","h")}function P(e){return G(e,"version","v","V")}function f(e,n){if(n.quiet&&!n.json)return;if(n.json||!process.stdout.isTTY){process.stdout.write(`${JSON.stringify({ok:!0,data:e},null,2)}
4
- `);return}if(e===void 0||e===null){process.stdout.write(`ok
5
- `);return}if(typeof e==="string"){process.stdout.write(`${e}
6
- `);return}process.stdout.write(`${Me(e)}
7
- `)}function me(e,n){let{message:u,code:r,details:w}=Ye(e);if(n.json||!process.stdout.isTTY)process.stdout.write(`${JSON.stringify({ok:!1,error:{code:We(r),message:u,details:w??null}},null,2)}
8
- `);else if(process.stderr.write(`error: ${u}
9
- `),w!==void 0&&process.env.DUN_DEBUG==="1")process.stderr.write(`${JSON.stringify(w,null,2)}
10
- `);return r}function We(e){switch(e){case y.AUTH:return"auth";case y.FORBIDDEN:return"forbidden";case y.VALIDATION:return"validation";case y.NOT_FOUND:return"not_found";case y.RATE_LIMITED:return"rate_limited";case y.NETWORK:return"network";default:return"usage"}}function Ye(e){if(e instanceof t)return{message:e.message,code:e.code,details:e.details};if(e instanceof Error)return{message:e.message,code:y.USAGE};return{message:String(e),code:y.USAGE}}function Me(e){if(Array.isArray(e)){if(e.length===0)return"(empty)";if(e.every((n)=>n&&typeof n==="object"))return ie(e);return e.map((n)=>`- ${s(n)}`).join(`
11
- `)}if(e&&typeof e==="object"){let n=e;if(Array.isArray(n.items)){let u=typeof n.total==="number"?`total=${n.total} showing=${n.items.length}`:void 0,r=ie(n.items);return u?`${u}
12
- ${r}`:r}return Object.entries(n).map(([u,r])=>`${u}: ${s(r)}`).join(`
13
- `)}return String(e)}function ie(e){if(e.length===0)return"(empty)";let n=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],u=new Set;for(let o of e)for(let O of Object.keys(o))u.add(O);let r=[...n.filter((o)=>u.has(o)),...[...u].filter((o)=>!n.includes(o)).slice(0,6)].slice(0,8),w=e.map((o)=>r.map((O)=>Qe(s(o[O]),O==="content"||O==="text"?48:28))),d=r.map((o,O)=>Math.max(o.length,...w.map((N)=>N[O]?.length??0))),m=r.map((o,O)=>o.padEnd(d[O])).join(" "),i=w.map((o)=>o.map((O,N)=>O.padEnd(d[N])).join(" ")).join(`
14
- `);return`${m}
15
- ${i}`}function s(e){if(e===null||e===void 0)return"";if(typeof e==="string")return e.replace(/\s+/g," ").trim();if(typeof e==="number"||typeof e==="boolean")return String(e);if(e instanceof Date)return e.toISOString();if(typeof e==="object"){let n=e;if(typeof n.id==="string"&&typeof n.name==="string")return`${n.name} (${n.id})`;if(typeof n.status==="string")return n.status;return JSON.stringify(e)}return String(e)}function Qe(e,n){if(e.length<=n)return e;return`${e.slice(0,Math.max(0,n-1))}…`}var K={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class h extends Error{code;details;constructor(e,n=K.USAGE,u){super(e);this.name="CliError",this.code=n,this.details=u}}import{mkdir as Xe,readFile as Gn,writeFile as Ze,chmod as Le,unlink as Fe}from"node:fs/promises";import{dirname as qe,join as oe}from"node:path";import{homedir as Be}from"node:os";var X="0.3.1";function S(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let e=process.env.XDG_CONFIG_HOME,n=e?e:oe(Be(),".config");return oe(n,"dunsocial","config.json")}function J(){return S()}async function _(e){let n=S();await Xe(qe(n),{recursive:!0});let u={...e,updatedAt:new Date().toISOString()};await Ze(n,`${JSON.stringify(u,null,2)}
16
- `,"utf8");try{await Le(n,384)}catch{}}async function ye(){let e=S();try{await Fe(e)}catch(n){if(n.code!=="ENOENT")throw new t(`Failed to clear config: ${n.message}`,y.USAGE)}}function R(e){if(!e.token)throw new t("Not authenticated. Run `dun auth login --token <token>` or set DUN_TOKEN.",y.AUTH);return e.token}function p(e){if(!e.workspaceId)throw new t("No workspace selected. Run `dun workspace use <id|slug>` or pass --workspace / DUN_WORKSPACE_ID.",y.VALIDATION);return e.workspaceId}var Z=`dun — DunSocial agent-first CLI v${X}
3
+ import{mkdir as L0,readFile as EU,writeFile as k0,chmod as M0,unlink as j0}from"node:fs/promises";import{dirname as d0,join as KU}from"node:path";import{homedir as CU}from"node:os";var z={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class Q extends Error{code;details;constructor(U,D=z.USAGE,O){super(U);this.name="CliError",this.code=D,this.details=O}}function $U(U){if(U===401)return z.AUTH;if(U===403)return z.FORBIDDEN;if(U===404)return z.NOT_FOUND;if(U===429)return z.RATE_LIMITED;if(U>=400&&U<500)return z.VALIDATION;return z.NETWORK}var pU="https://api.dunsocial.com",v="0.4.0";function nU(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let U=process.env.XDG_CONFIG_HOME,D=U?U:KU(CU(),".config");return KU(D,"dunsocial","config.json")}async function fU(){let U=nU();try{let D=await EU(U,"utf8");return JSON.parse(D)}catch(D){if(D.code==="ENOENT")return{};throw new Q(`Failed to read config at ${U}: ${D.message}`,z.USAGE)}}async function VU(U){let D=await fU(),O=(U.apiUrl||process.env.DUN_API_URL||D.apiUrl||pU).replace(/\/$/,""),K=U.token||process.env.DUN_TOKEN||D.token,G=U.workspace||process.env.DUN_WORKSPACE_ID||D.defaultWorkspaceId||void 0,$=process.env.DUN_JSON==="0",A=!process.stdout.isTTY,V=$?!1:U.json===!0||process.env.DUN_JSON==="1"||process.env.CI==="true"||process.env.CI==="1"||A;return{apiUrl:O,token:K,workspaceId:G,json:V,debug:U.debug===!0||process.env.DUN_DEBUG==="1",quiet:U.quiet===!0,yes:U.yes===!0||process.env.CI==="true"||process.env.CI==="1",config:D}}function AU(U){let D={},O=[],K=0,G=!0;while(K<U.length){let Z=U[K];if(G&&Z==="--"){G=!1,K+=1;continue}if(G&&Z.startsWith("--")){let J=Z.indexOf("=");if(J!==-1){D[Z.slice(2,J)]=Z.slice(J+1),K+=1;continue}let N=Z.slice(2),q=U[K+1];if(q!==void 0&&!q.startsWith("-"))D[N]=q,K+=2;else D[N]=!0,K+=1;continue}if(G&&Z.startsWith("-")&&Z.length>1&&Z!=="-"){let J=Z.slice(1);if(J.length>1&&!J.includes("=")){for(let I of J)D[I]=!0;K+=1;continue}let N=J,q=U[K+1];if(q!==void 0&&!q.startsWith("-"))D[N]=q,K+=2;else D[N]=!0,K+=1;continue}O.push(Z),K+=1}let $=O[0],A,V,X=1;if($==="memory"&&(O[1]==="collections"||O[1]==="collection"))V="collections",A=O[2],X=A?3:2;else A=O[1],X=A?2:1;return{group:$,action:A,nest:V,flags:D,positionals:O.slice(X)}}function m(U,...D){for(let O of D){let K=U[O];if(typeof K==="string"&&K.length>0)return K}return}function y(U,...D){for(let O of D){let K=U[O];if(K===!0)return!0;if(typeof K==="string"){let G=K.toLowerCase();if(G==="1"||G==="true"||G==="yes")return!0}}return!1}function n(U){return y(U,"help","h")}function f(U){return y(U,"version","v","V")}function x(U,D){if(D.quiet&&!D.json)return;if(D.json||!process.stdout.isTTY){process.stdout.write(`${JSON.stringify({ok:!0,data:U},null,2)}
4
+ `);return}if(U===void 0||U===null){process.stdout.write(`ok
5
+ `);return}if(typeof U==="string"){process.stdout.write(`${U}
6
+ `);return}process.stdout.write(`${cU(U,D.emptyMessage)}
7
+ `)}function zU(U,D){let{message:O,code:K,details:G}=xU(U);if(D.json||!process.stdout.isTTY)process.stdout.write(`${JSON.stringify({ok:!1,error:{code:iU(K),message:O,details:G??null}},null,2)}
8
+ `);else if(process.stderr.write(`error: ${O}
9
+ `),G!==void 0&&process.env.DUN_DEBUG==="1")process.stderr.write(`${JSON.stringify(G,null,2)}
10
+ `);return K}function iU(U){switch(U){case z.AUTH:return"auth";case z.FORBIDDEN:return"forbidden";case z.VALIDATION:return"validation";case z.NOT_FOUND:return"not_found";case z.RATE_LIMITED:return"rate_limited";case z.NETWORK:return"network";default:return"usage"}}function xU(U){if(U instanceof Q)return{message:U.message,code:U.code,details:U.details};if(U instanceof Error)return{message:U.message,code:z.USAGE};return{message:String(U),code:z.USAGE}}function cU(U,D){if(Array.isArray(U)){if(U.length===0)return D??"(empty)";if(U.every((O)=>O&&typeof O==="object"))return GU(U,D);return U.map((O)=>`- ${i(O)}`).join(`
11
+ `)}if(U&&typeof U==="object"){let O=U;if(Array.isArray(O.items)){if(O.items.length===0&&D)return D;let K=typeof O.total==="number"?`total=${O.total} showing=${O.items.length}`:void 0,G=GU(O.items,D);return K?`${K}
12
+ ${G}`:G}return Object.entries(O).map(([K,G])=>`${K}: ${i(G)}`).join(`
13
+ `)}return String(U)}function GU(U,D){if(U.length===0)return D??"(empty)";let O=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],K=new Set;for(let Z of U)for(let J of Object.keys(Z))K.add(J);let G=[...O.filter((Z)=>K.has(Z)),...[...K].filter((Z)=>!O.includes(Z)).slice(0,6)].slice(0,8),$=U.map((Z)=>G.map((J)=>gU(i(Z[J]),J==="content"||J==="text"?48:28))),A=G.map((Z,J)=>Math.max(Z.length,...$.map((N)=>N[J]?.length??0))),V=G.map((Z,J)=>Z.padEnd(A[J])).join(" "),X=$.map((Z)=>Z.map((J,N)=>J.padEnd(A[N])).join(" ")).join(`
14
+ `);return`${V}
15
+ ${X}`}function i(U){if(U===null||U===void 0)return"";if(typeof U==="string")return U.replace(/\s+/g," ").trim();if(typeof U==="number"||typeof U==="boolean")return String(U);if(U instanceof Date)return U.toISOString();if(typeof U==="object"){let D=U;if(typeof D.id==="string"&&typeof D.name==="string")return`${D.name} (${D.id})`;if(typeof D.status==="string")return D.status;return JSON.stringify(U)}return String(U)}function gU(U,D){if(U.length<=D)return U;return`${U.slice(0,Math.max(0,D-1))}…`}var _={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class c extends Error{code;details;constructor(U,D=_.USAGE,O){super(U);this.name="CliError",this.code=D,this.details=O}}var QU=`dun schedule & publish social posts
17
16
 
18
17
  Usage:
19
18
  dun <command> [flags]
@@ -89,8 +88,8 @@ Env:
89
88
  Exit codes:
90
89
  0 ok · 1 usage · 2 auth · 3 forbidden · 4 validation · 5 not found · 6 rate limit · 7 network
91
90
 
92
- Docs: https://dunsocial.com · MCP remains the remote-host path; CLI is local/CI.
93
- `;function E(e){return{auth:`dun auth
91
+ Docs: https://dunsocial.com
92
+ `;function g(U){return{auth:`dun auth
94
93
 
95
94
  login [--token] Device-code browser login (default) or --token for CI
96
95
  logout Clear local config
@@ -114,37 +113,129 @@ Docs: https://dunsocial.com · MCP remains the remote-host path; CLI is local/CI
114
113
  `,drafts:"dun drafts list|get|create|update|delete",media:"dun media list|get|upload|delete",memory:`dun memory collections list|create
115
114
  dun memory list|save|search|delete`,skill:`dun skill install <claude|codex|cursor|pi|opencode|agents|copilot|windsurf|goose|gemini|amp|grok|continue|antigravity|all>
116
115
  dun skill list
117
- dun skill path`}[e]??Z}function $(e,n){if(n.quiet&&!n.json)return;if(n.json||!process.stdout.isTTY){process.stdout.write(`${JSON.stringify({ok:!0,data:e},null,2)}
118
- `);return}if(e===void 0||e===null){process.stdout.write(`ok
119
- `);return}if(typeof e==="string"){process.stdout.write(`${e}
120
- `);return}process.stdout.write(`${Pe(e)}
121
- `)}function Pe(e){if(Array.isArray(e)){if(e.length===0)return"(empty)";if(e.every((n)=>n&&typeof n==="object"))return we(e);return e.map((n)=>`- ${v(n)}`).join(`
122
- `)}if(e&&typeof e==="object"){let n=e;if(Array.isArray(n.items)){let u=typeof n.total==="number"?`total=${n.total} showing=${n.items.length}`:void 0,r=we(n.items);return u?`${u}
123
- ${r}`:r}return Object.entries(n).map(([u,r])=>`${u}: ${v(r)}`).join(`
124
- `)}return String(e)}function we(e){if(e.length===0)return"(empty)";let n=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],u=new Set;for(let o of e)for(let O of Object.keys(o))u.add(O);let r=[...n.filter((o)=>u.has(o)),...[...u].filter((o)=>!n.includes(o)).slice(0,6)].slice(0,8),w=e.map((o)=>r.map((O)=>se(v(o[O]),O==="content"||O==="text"?48:28))),d=r.map((o,O)=>Math.max(o.length,...w.map((N)=>N[O]?.length??0))),m=r.map((o,O)=>o.padEnd(d[O])).join(" "),i=w.map((o)=>o.map((O,N)=>O.padEnd(d[N])).join(" ")).join(`
125
- `);return`${m}
126
- ${i}`}function v(e){if(e===null||e===void 0)return"";if(typeof e==="string")return e.replace(/\s+/g," ").trim();if(typeof e==="number"||typeof e==="boolean")return String(e);if(e instanceof Date)return e.toISOString();if(typeof e==="object"){let n=e;if(typeof n.id==="string"&&typeof n.name==="string")return`${n.name} (${n.id})`;if(typeof n.status==="string")return n.status;return JSON.stringify(e)}return String(e)}function se(e,n){if(e.length<=n)return e;return`${e.slice(0,Math.max(0,n-1))}…`}function H(e,n,u){if(!e)return;if(u!==void 0){process.stderr.write(`[debug] ${n} ${JSON.stringify(u)}
127
- `);return}process.stderr.write(`[debug] ${n}
128
- `)}class te{ctx;constructor(e){this.ctx=e}async request(e){let n=e.method??"GET",u=new URL(e.path.startsWith("http")?e.path:`${this.ctx.apiUrl}${e.path}`);if(e.query)for(let[o,O]of Object.entries(e.query)){if(O===void 0||O===null||O==="")continue;u.searchParams.set(o,String(O))}let r={Accept:"application/json","User-Agent":`dunsocial-cli/${X}`,...e.headers??{}};if(e.auth!==!1)r.Authorization=`Bearer ${R(this.ctx)}`;if(e.workspace)r["X-Workspace-Id"]=p(this.ctx);let w;if(e.body!==void 0)r["Content-Type"]="application/json",w=JSON.stringify(e.body);H(this.ctx.debug,`${n} ${u.toString()}`);let d;try{d=await fetch(u,{method:n,headers:r,body:w})}catch(o){throw new t(`Network error: ${o.message}`,y.NETWORK,{cause:String(o)})}let m=await d.text(),i=null;if(m)try{i=JSON.parse(m)}catch{i=null}if(H(this.ctx.debug,`← ${d.status}`,{ok:d.ok,bodyPreview:m.slice(0,300)}),!d.ok){let o=i?.error||i?.message||(m?m.slice(0,300):`HTTP ${d.status}`);throw new t(o,ne(d.status),{status:d.status,body:i??m})}if(i&&typeof i==="object"&&"success"in i){if(i.success===!1)throw new t(i.error||i.message||"Request failed",y.VALIDATION,i);return i.data}return i??void 0}get(e,n){return this.request({...n,method:"GET",path:e})}post(e,n,u){return this.request({...u,method:"POST",path:e,body:n})}patch(e,n,u){return this.request({...u,method:"PATCH",path:e,body:n})}delete(e,n,u){return this.request({...u,method:"DELETE",path:e,body:n})}}function A(e){return new te(e)}function U(e,...n){for(let u of n){let r=e[u];if(typeof r==="string"&&r.length>0)return r}return}function k(e,...n){for(let u of n){let r=e[u];if(r===!0)return!0;if(typeof r==="string"){let w=r.toLowerCase();if(w==="1"||w==="true"||w==="yes")return!0}}return!1}function V(e,...n){let u=U(e,...n);if(u===void 0)return;let r=Number(u);if(!Number.isFinite(r))throw Error(`Invalid number for --${n[0]}: ${u}`);return r}function I(e,...n){let u=U(e,...n);if(u===void 0)return;return u.split(",").map((r)=>r.trim()).filter(Boolean)}async function Oe(e,n,u,r){switch(n){case"login":return fe(e,u);case"logout":return Ee(e);case"whoami":return ve(e);case"status":return ce(e);default:throw new t(`Unknown auth command "${n??""}". Try: login, logout, whoami, status`,y.USAGE)}}async function fe(e,n){let u=U(n,"token")||process.env.DUN_TOKEN;if(u)return he(e,u);return Se(e,n)}async function he(e,n){let u={...e,token:n},w=await A(u).get("/api/user/profile");await _({...e.config,apiUrl:e.apiUrl,token:n,defaultWorkspaceId:e.config.defaultWorkspaceId,defaultWorkspaceSlug:e.config.defaultWorkspaceSlug,defaultWorkspaceName:e.config.defaultWorkspaceName}),$({user:{id:w.id,name:w.name,email:w.email},configPath:J(),method:"token",message:"Logged in. Next: dun workspace list && dun workspace use <id|slug>"},e)}async function Se(e,n){let u=A({...e,token:void 0}),r=await u.post("/api/cli/auth/start",{clientName:"dun-cli"},{auth:!1}),w=!k(n,"no-browser");if(!e.json)process.stderr.write(`
116
+ dun skill path`}[U]??`dun — schedule & publish social posts
117
+
118
+ Usage:
119
+ dun <command> [flags]
120
+
121
+ Auth:
122
+ dun auth login # device-code browser flow
123
+ dun auth login --token <token> # CI / headless
124
+ dun auth login --no-browser
125
+ dun auth logout
126
+ dun auth whoami
127
+ dun auth status
128
+
129
+ Workspaces:
130
+ dun workspace list
131
+ dun workspace use <id|slug>
132
+ dun workspace current
133
+
134
+ Accounts:
135
+ dun accounts list [--platform x]
136
+
137
+ Posts:
138
+ dun posts list [--status scheduled|published|failed|draft|cancelled]
139
+ dun posts get <id>
140
+ dun posts schedule --text "..." --accounts <id,id> (--at ISO | --in 2h) [--media id,id]
141
+ dun posts publish --text "..." --accounts <id,id> [--media id,id] [--natural]
142
+ dun posts reschedule <id> --at ISO|--in 2h [--text "..."]
143
+ dun posts cancel <id>
144
+ dun posts delete <id> [--yes]
145
+ dun posts x-cap
146
+
147
+ Drafts:
148
+ dun drafts list
149
+ dun drafts get <id>
150
+ dun drafts create --text "..." [--platforms x,linkedin] [--accounts id,id]
151
+ dun drafts update <id> --text "..."
152
+ dun drafts delete <id> [--yes]
153
+
154
+ Media:
155
+ dun media list
156
+ dun media get <id>
157
+ dun media upload <file> [--alt "..."]
158
+ dun media delete <id> [--yes]
159
+
160
+ Memory:
161
+ dun memory collections list
162
+ dun memory collections create --name "..." [--color blue] [--private]
163
+ dun memory list --collection <id>
164
+ dun memory save --collection <id> --text "..."
165
+ dun memory search --prompt "..." --collections <id,id> [--top-k 5]
166
+ dun memory delete <id> --collection <id> [--yes]
167
+
168
+ Agent skills:
169
+ dun skill install <agent|all>
170
+ dun skill list
171
+ dun skill path
172
+ # agents: claude codex cursor pi opencode agents copilot windsurf
173
+ # goose gemini amp grok continue antigravity
174
+
175
+ Global flags:
176
+ --json Machine-readable output (default when non-TTY / CI)
177
+ --workspace <id> Override workspace for this command
178
+ --api-url <url> Override API base (default https://api.dunsocial.com)
179
+ --token <token> Override bearer token for this command
180
+ --debug Log HTTP method/path/status to stderr
181
+ --quiet Suppress human success output
182
+ --yes Skip destructive confirmations
183
+ --help, -h Show help
184
+ --version, -v Show version
185
+
186
+ Env:
187
+ DUN_TOKEN DUN_WORKSPACE_ID DUN_API_URL DUN_CONFIG DUN_JSON=1 DUN_DEBUG=1
188
+
189
+ Exit codes:
190
+ 0 ok · 1 usage · 2 auth · 3 forbidden · 4 validation · 5 not found · 6 rate limit · 7 network
191
+
192
+ Docs: https://dunsocial.com
193
+ `}import{mkdir as oU,readFile as i0,writeFile as eU,chmod as rU,unlink as lU}from"node:fs/promises";import{dirname as sU,join as XU}from"node:path";import{homedir as tU}from"node:os";var M="0.4.0";function o(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let U=process.env.XDG_CONFIG_HOME,D=U?U:XU(tU(),".config");return XU(D,"dunsocial","config.json")}function j(){return o()}async function b(U){let D=o();await oU(sU(D),{recursive:!0});let O={...U,updatedAt:new Date().toISOString()};await eU(D,`${JSON.stringify(O,null,2)}
194
+ `,"utf8");try{await rU(D,384)}catch{}}async function ZU(){let U=o();try{await lU(U)}catch(D){if(D.code!=="ENOENT")throw new Q(`Failed to clear config: ${D.message}`,z.USAGE)}}function w(U){if(!U.token)throw new Q("Not authenticated. Run `dun auth login` (or `dun auth login --token <token>` / set DUN_TOKEN).",z.AUTH);return U.token}function H(U){if(!U.workspaceId)throw new Q("No workspace selected. Run `dun workspace use <id|slug>` or pass --workspace / DUN_WORKSPACE_ID.",z.VALIDATION);return U.workspaceId}var JU=["█▀▀▄ █ █ █▀▀▄ █▀▀▀ █▀▀█ █▀▀▀ ▀█▀ █▀▀█ █","█ █ █ █ █ █ ▀▀▀█ █ █ █ █ █▀▀█ █","▀▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀"].join(`
195
+ `),RU=["█▀▀▄ █ █ █▀▀▄","█ █ █ █ █ █","▀▀▀▀ ▀▀▀▀ ▀ ▀",""," DunSocial"].join(`
196
+ `),aU="DunSocial",U0=YU(JU),O0=YU(RU);function YU(U){return Math.max(...U.split(`
197
+ `).map((D)=>D.length))}function e(U=process.stderr){if(!U.isTTY)return!1;if(process.env.CI==="true"||process.env.CI==="1")return!1;if(process.env.GITHUB_ACTIONS||process.env.TF_BUILD||process.env.GITLAB_CI)return!1;if(process.env.DUN_JSON==="1")return!1;return!0}function qU(U=process.stdout.columns??80){let D=U>0?U:80;if(D>=U0)return JU;if(D>=O0)return RU;return aU}function D0(U=process.stdout.columns??80){return`${qU(U)}
198
+
199
+ Schedule & publish from the terminal · v${M}`}function NU(U=process.stderr,D={}){if(!e(U))return;let O=D.columns??U.columns??process.stdout.columns??80,K=D.tagline?D0(O):qU(O);U.write(`
200
+ ${K}
201
+
202
+ `)}function R(U,D){if(D.quiet&&!D.json)return;if(D.json||!process.stdout.isTTY){process.stdout.write(`${JSON.stringify({ok:!0,data:U},null,2)}
203
+ `);return}if(U===void 0||U===null){process.stdout.write(`ok
204
+ `);return}if(typeof U==="string"){process.stdout.write(`${U}
205
+ `);return}process.stdout.write(`${$0(U,D.emptyMessage)}
206
+ `)}function $0(U,D){if(Array.isArray(U)){if(U.length===0)return D??"(empty)";if(U.every((O)=>O&&typeof O==="object"))return TU(U,D);return U.map((O)=>`- ${r(O)}`).join(`
207
+ `)}if(U&&typeof U==="object"){let O=U;if(Array.isArray(O.items)){if(O.items.length===0&&D)return D;let K=typeof O.total==="number"?`total=${O.total} showing=${O.items.length}`:void 0,G=TU(O.items,D);return K?`${K}
208
+ ${G}`:G}return Object.entries(O).map(([K,G])=>`${K}: ${r(G)}`).join(`
209
+ `)}return String(U)}function TU(U,D){if(U.length===0)return D??"(empty)";let O=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],K=new Set;for(let Z of U)for(let J of Object.keys(Z))K.add(J);let G=[...O.filter((Z)=>K.has(Z)),...[...K].filter((Z)=>!O.includes(Z)).slice(0,6)].slice(0,8),$=U.map((Z)=>G.map((J)=>K0(r(Z[J]),J==="content"||J==="text"?48:28))),A=G.map((Z,J)=>Math.max(Z.length,...$.map((N)=>N[J]?.length??0))),V=G.map((Z,J)=>Z.padEnd(A[J])).join(" "),X=$.map((Z)=>Z.map((J,N)=>J.padEnd(A[N])).join(" ")).join(`
210
+ `);return`${V}
211
+ ${X}`}function r(U){if(U===null||U===void 0)return"";if(typeof U==="string")return U.replace(/\s+/g," ").trim();if(typeof U==="number"||typeof U==="boolean")return String(U);if(U instanceof Date)return U.toISOString();if(typeof U==="object"){let D=U;if(typeof D.id==="string"&&typeof D.name==="string")return`${D.name} (${D.id})`;if(typeof D.status==="string")return D.status;return JSON.stringify(U)}return String(U)}function K0(U,D){if(U.length<=D)return U;return`${U.slice(0,Math.max(0,D-1))}…`}function d(U,D,O){if(!U)return;if(O!==void 0){process.stderr.write(`[debug] ${D} ${JSON.stringify(O)}
212
+ `);return}process.stderr.write(`[debug] ${D}
213
+ `)}class WU{ctx;constructor(U){this.ctx=U}async request(U){let D=U.method??"GET",O=new URL(U.path.startsWith("http")?U.path:`${this.ctx.apiUrl}${U.path}`);if(U.query)for(let[X,Z]of Object.entries(U.query)){if(Z===void 0||Z===null||Z==="")continue;O.searchParams.set(X,String(Z))}let K={Accept:"application/json","User-Agent":`dunsocial-cli/${M}`,...U.headers??{}};if(U.auth!==!1)K.Authorization=`Bearer ${w(this.ctx)}`;if(U.workspace)K["X-Workspace-Id"]=H(this.ctx);let G;if(U.body!==void 0)K["Content-Type"]="application/json",G=JSON.stringify(U.body);d(this.ctx.debug,`${D} ${O.toString()}`);let $;try{$=await fetch(O,{method:D,headers:K,body:G})}catch(X){throw new Q(`Network error: ${X.message}`,z.NETWORK,{cause:String(X)})}let A=await $.text(),V=null;if(A)try{V=JSON.parse(A)}catch{V=null}if(d(this.ctx.debug,`← ${$.status}`,{ok:$.ok,bodyPreview:A.slice(0,300)}),!$.ok){let X=V?.error||V?.message||(A?A.slice(0,300):`HTTP ${$.status}`);throw new Q(X,$U($.status),{status:$.status,body:V??A})}if(V&&typeof V==="object"&&"success"in V){if(V.success===!1)throw new Q(V.error||V.message||"Request failed",z.VALIDATION,V);return V.data}return V??void 0}get(U,D){return this.request({...D,method:"GET",path:U})}post(U,D,O){return this.request({...O,method:"POST",path:U,body:D})}patch(U,D,O){return this.request({...O,method:"PATCH",path:U,body:D})}delete(U,D,O){return this.request({...O,method:"DELETE",path:U,body:D})}}function T(U){return new WU(U)}function Y(U,...D){for(let O of D){let K=U[O];if(typeof K==="string"&&K.length>0)return K}return}function F(U,...D){for(let O of D){let K=U[O];if(K===!0)return!0;if(typeof K==="string"){let G=K.toLowerCase();if(G==="1"||G==="true"||G==="yes")return!0}}return!1}function P(U,...D){let O=Y(U,...D);if(O===void 0)return;let K=Number(O);if(!Number.isFinite(K))throw Error(`Invalid number for --${D[0]}: ${O}`);return K}function u(U,...D){let O=Y(U,...D);if(O===void 0)return;return O.split(",").map((K)=>K.trim()).filter(Boolean)}var V0=["█▀▀▄ █ █ █▀▀▄ █▀▀▀ █▀▀█ █▀▀▀ ▀█▀ █▀▀█ █","█ █ █ █ █ █ ▀▀▀█ █ █ █ █ █▀▀█ █","▀▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀"].join(`
214
+ `),wU=["█▀▀▄ █ █ █▀▀▄","█ █ █ █ █ █","▀▀▀▀ ▀▀▀▀ ▀ ▀",""," DunSocial"].join(`
215
+ `);var KO=HU(V0),VO=HU(wU);function HU(U){return Math.max(...U.split(`
216
+ `).map((D)=>D.length))}function S(U=process.stderr){if(!U.isTTY)return!1;if(process.env.CI==="true"||process.env.CI==="1")return!1;if(process.env.GITHUB_ACTIONS||process.env.TF_BUILD||process.env.GITLAB_CI)return!1;if(process.env.DUN_JSON==="1")return!1;return!0}function FU(){return wU}async function PU(U,D,O,K){switch(D){case"login":return A0(U,O);case"logout":return Q0(U);case"whoami":return X0(U);case"status":return Z0(U);default:throw new Q(`Unknown auth command "${D??""}". Try: login, logout, whoami, status`,z.USAGE)}}async function A0(U,D){let O=Y(D,"token")||process.env.DUN_TOKEN;if(O)return G0(U,O);return z0(U,D)}function E(U){if(U)return"Logged in.";return`Logged in.
217
+ Next: dun workspace list && dun workspace use <id|slug>`}async function G0(U,D){let O={...U,token:D},G=await T(O).get("/api/user/profile");if(await b({...U.config,apiUrl:U.apiUrl,token:D,defaultWorkspaceId:U.config.defaultWorkspaceId,defaultWorkspaceSlug:U.config.defaultWorkspaceSlug,defaultWorkspaceName:U.config.defaultWorkspaceName}),!U.json&&S(process.stdout)){R(E(Boolean(U.config.defaultWorkspaceId)),U);return}R({user:{id:G.id,name:G.name,email:G.email},configPath:j(),method:"token",message:E(Boolean(U.config.defaultWorkspaceId))},U)}async function z0(U,D){let O=T({...U,token:void 0}),K=await O.post("/api/cli/auth/start",{clientName:"dun-cli"},{auth:!1}),G=!F(D,"no-browser");if(!U.json){if(S(process.stderr))process.stderr.write(`
218
+ ${FU()}
219
+
220
+ `);else process.stderr.write(`
129
221
  DunSocial CLI login
130
222
  `),process.stderr.write(`───────────────────
131
- `),process.stderr.write(`In your browser, open:
132
- ${r.verificationUrlComplete}
223
+ `);process.stderr.write(`In your browser, open:
224
+ ${K.verificationUrlComplete}
133
225
 
134
- `),process.stderr.write(`Or go to ${r.verificationUrl} and enter code:
135
- `),process.stderr.write(` ${r.userCode}
226
+ `),process.stderr.write(`Or go to ${K.verificationUrl} and enter code:
227
+ `),process.stderr.write(` ${K.userCode}
136
228
 
137
229
  `),process.stderr.write(`Waiting for approval…
138
- `);else process.stderr.write(JSON.stringify({ok:!0,phase:"waiting",userCode:r.userCode,verificationUrlComplete:r.verificationUrlComplete})+`
139
- `);if(w&&process.stdout.isTTY)try{await ge(r.verificationUrlComplete)}catch{}let d=Math.max(3,r.interval||5)*1000,m=Date.now()+(r.expiresIn||900)*1000;while(Date.now()<m){await Ce(d);let i=await u.post("/api/cli/auth/poll",{deviceCode:r.deviceCode},{auth:!1});if(i.status==="pending")continue;if(i.status==="expired")throw new t("Login expired. Run dun auth login again.",y.AUTH);if(i.status==="denied")throw new t("Login was denied in the browser.",y.AUTH);if(i.status==="approved"){let o=i.token,O={...e,token:o},N=A(O),T=await N.get("/api/user/profile"),D={...e.config,apiUrl:e.apiUrl,token:o,defaultWorkspaceId:i.workspaceId||e.config.defaultWorkspaceId,defaultWorkspaceSlug:e.config.defaultWorkspaceSlug,defaultWorkspaceName:e.config.defaultWorkspaceName};if(i.workspaceId)try{let j=(await N.get("/api/workspaces")).find((q)=>q.id===i.workspaceId);if(j)D.defaultWorkspaceId=j.id,D.defaultWorkspaceSlug=j.slug,D.defaultWorkspaceName=j.name}catch{}await _(D),$({user:{id:T.id,name:T.name,email:T.email},workspaceId:D.defaultWorkspaceId??null,configPath:J(),method:"device",message:D.defaultWorkspaceId?"Logged in.":"Logged in. Next: dun workspace list && dun workspace use <id|slug>"},e);return}}throw new t("Login timed out. Run dun auth login again.",y.AUTH)}async function Ee(e){await ye(),$({message:"Logged out. Config cleared.",configPath:J()},e)}async function ve(e){R(e);let u=await A(e).get("/api/user/profile");$({id:u.id,name:u.name,email:u.email,image:u.image??null,apiUrl:e.apiUrl,workspaceId:e.workspaceId??null},e)}async function ce(e){let n=Boolean(e.token),u=null,r=null;if(n)try{u=await A(e).get("/api/user/profile")}catch(w){r=w instanceof Error?w.message:String(w)}$({authenticated:Boolean(u),tokenPresent:n,apiUrl:e.apiUrl,workspaceId:e.workspaceId??null,workspaceSlug:e.config.defaultWorkspaceSlug??null,workspaceName:e.config.defaultWorkspaceName??null,configPath:J(),user:u?{id:u.id,name:u.name,email:u.email}:null,error:r},e)}function Ce(e){return new Promise((n)=>setTimeout(n,e))}async function ge(e){let n=process.platform,u=n==="darwin"?["open",e]:n==="win32"?["cmd","/c","start","",e]:["xdg-open",e];await Bun.spawn(u,{stdout:"ignore",stderr:"ignore"}).exited}async function $e(e,n,u,r){R(e);let w=A(e);switch(n){case"list":case"ls":{let d=await w.get("/api/workspaces");$(d.map((m)=>({id:m.id,name:m.name,slug:m.slug,role:m.role,timezone:m.timezone,plan:m.subscription?.planName??null,status:m.subscription?.status??null})),e);return}case"use":{let d=r[0];if(!d)throw new t("Usage: dun workspace use <id|slug>",y.USAGE);let i=(await w.get("/api/workspaces")).find((o)=>o.id===d||o.slug===d);if(!i)throw new t(`Workspace not found: ${d}. Run dun workspace list`,y.NOT_FOUND);await _({...e.config,apiUrl:e.apiUrl,token:e.token,defaultWorkspaceId:i.id,defaultWorkspaceSlug:i.slug,defaultWorkspaceName:i.name}),$({id:i.id,name:i.name,slug:i.slug,role:i.role,message:`Default workspace set to ${i.name}`},e);return}case"current":{if(!e.workspaceId&&!e.config.defaultWorkspaceId)throw new t("No workspace selected. Run dun workspace use <id|slug>",y.VALIDATION);let d=e.workspaceId||e.config.defaultWorkspaceId;try{let i=(await w.get("/api/workspaces")).find((o)=>o.id===d||o.slug===d);if(i){$({id:i.id,name:i.name,slug:i.slug,role:i.role,timezone:i.timezone},e);return}}catch{}$({id:d,name:e.config.defaultWorkspaceName??null,slug:e.config.defaultWorkspaceSlug??null},e);return}default:throw new t(`Unknown workspace command "${n??""}". Try: list, use, current`,y.USAGE)}}async function Ne(e,n,u,r){R(e),p(e);let w=A(e);switch(n){case void 0:case"list":case"ls":{let d=await w.get("/api/social-accounts",{workspace:!0}),m=U(u,"platform","p")?.toLowerCase(),i=m?d.filter((o)=>{let O=(o.providerName||"").toLowerCase();return({x:["x","twitter"],twitter:["x","twitter"],linkedin:["linkedin"],"linkedin-business":["linkedin business","linkedin-business","linkedin_business"],bluesky:["bluesky"],threads:["threads"],reddit:["reddit"],pinterest:["pinterest"],instagram:["instagram"],youtube:["youtube"]}[m]??[m]).some((D)=>O.includes(D))}):d;$(i.map((o)=>({id:o.id,providerName:o.providerName,username:o.username??null,displayName:o.displayName??null,isConnected:o.isConnected,expiresAt:o.expiresAt??null})),e);return}default:throw new t(`Unknown accounts command "${n}". Try: list`,y.USAGE)}}var le=/^(\d+)\s*(s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days|w|week|weeks)$/i;function c(e,n=new Date){let u=e.trim();if(!u)throw new t("Empty time value",y.VALIDATION);let r=u.match(le);if(r){let m=Number(r[1]),i=r[2].toLowerCase(),o=xe(i)*m;return new Date(n.getTime()+o)}let w=u.match(/^in\s+(.+)$/i);if(w)return c(w[1],n);let d=new Date(u);if(Number.isNaN(d.getTime()))throw new t(`Invalid time "${e}". Use ISO-8601 (2026-06-01T09:00:00Z) or relative (2h, 30m, 1d).`,y.VALIDATION);return d}function xe(e){switch(e){case"s":case"sec":case"secs":case"second":case"seconds":return 1000;case"m":case"min":case"mins":case"minute":case"minutes":return 60000;case"h":case"hr":case"hrs":case"hour":case"hours":return 3600000;case"d":case"day":case"days":return 86400000;case"w":case"week":case"weeks":return 604800000;default:throw new t(`Unknown time unit: ${e}`,y.VALIDATION)}}function L(e){if(e.at&&e.in)throw new t("Pass only one of --at or --in",y.VALIDATION);if(e.at)return c(e.at).toISOString();if(e.in)return c(e.in).toISOString();throw new t("Schedule time required. Pass --at <iso> or --in <duration> (e.g. 2h).",y.VALIDATION)}function De(e){if(!e)return[];return e.split(",").map((n)=>n.trim()).filter(Boolean)}function Ue(e){let n=U(e,"text","content","c","t");if(!n)throw new t("Missing --text",y.VALIDATION);return n}function C(e){let n=I(e,"accounts","account","a")??De(U(e,"accounts","account","a"));if(!n||n.length===0)throw new t("Missing --accounts <id,id>",y.VALIDATION);return n}function g(e){return I(e,"media","media-urls")??De(U(e,"media","media-urls"))}function z(e){let n=U(e,"meta","metadata","meta-json");if(!n)return;try{let u=JSON.parse(n);if(!u||typeof u!=="object"||Array.isArray(u))throw Error("metadata must be a JSON object");return u}catch(u){throw new t(`Invalid --meta JSON: ${u.message}`,y.VALIDATION)}}async function Ae(e,n,u,r){R(e),p(e);let w=A(e);switch(n){case"list":case"ls":{let d=await w.get("/api/posts",{workspace:!0,query:{status:U(u,"status","s"),socialAccountId:U(u,"account","social-account"),groupId:U(u,"group"),limit:V(u,"limit")??50,offset:V(u,"offset")??0}});$(d,e);return}case"get":{let d=r[0];if(!d)throw new t("Usage: dun posts get <id>",y.USAGE);let m=await w.get(`/api/posts/${d}`,{workspace:!0});$(m,e);return}case"schedule":{let d=Ue(u),m=C(u),i=L({at:U(u,"at"),in:U(u,"in")}),o=await w.post("/api/posts/schedule",{content:d,socialAccountIds:m,scheduledAt:i,mediaUrls:g(u),metadata:z(u)??{}},{workspace:!0});$(o,e);return}case"publish":case"publish-now":{let d=Ue(u),m=C(u),i=await w.post("/api/posts/publish-now",{content:d,socialAccountIds:m,mediaUrls:g(u),metadata:z(u)??{},naturalPosting:k(u,"natural","natural-posting")},{workspace:!0});$(i,e);return}case"reschedule":{let d=r[0];if(!d)throw new t("Usage: dun posts reschedule <id> --at|--in",y.USAGE);let i={scheduledAt:L({at:U(u,"at"),in:U(u,"in")})},o=U(u,"text","content","c","t");if(o)i.content=o;let O=g(u);if(O.length)i.mediaUrls=O;let N=z(u);if(N)i.metadata=N;if(!i.content){let D=await w.get(`/api/posts/${d}`,{workspace:!0});if(i.content=D.content,!i.metadata&&D.metadata)i.metadata=D.metadata;if(!i.mediaUrls&&D.mediaUrls)i.mediaUrls=D.mediaUrls}let T=await w.post(`/api/posts/${d}/reschedule`,i,{workspace:!0});$(T,e);return}case"cancel":{let d=r[0];if(!d)throw new t("Usage: dun posts cancel <id>",y.USAGE);let m=await w.post(`/api/posts/${d}/cancel`,void 0,{workspace:!0});$(m??{id:d,status:"cancelled"},e);return}case"delete":case"rm":{let d=r[0];if(!d)throw new t("Usage: dun posts delete <id> [--yes]",y.USAGE);if(!e.yes&&!k(u,"yes","y"))throw new t("Refusing to delete without --yes (or CI/yes mode)",y.VALIDATION);let m=await w.delete(`/api/posts/${d}`,void 0,{workspace:!0});$(m??{id:d,deleted:!0},e);return}case"x-cap":case"xcap":{let d=await w.get("/api/posts/x-cap-usage",{workspace:!0});$(d,e);return}case"schedule-thread":case"publish-thread":case"publish-thread-now":{let d=U(u,"file","f"),m=U(u,"account","accounts","a")??C(u)[0];if(!m)throw new t("Missing --account <xAccountId>",y.VALIDATION);let i;if(d){let O=await Bun.file(d).text(),N=JSON.parse(O);if(Array.isArray(N))i=N;else if(N&&typeof N==="object"&&Array.isArray(N.tweets))i=N.tweets;else throw new t("Thread file must be an array of tweets or { tweets: [] }",y.VALIDATION)}else{let O=U(u,"text","content");if(!O)throw new t("Provide --file thread.json (array of {content}) or informal --text with || separators is not enough alone for threads — use --file",y.VALIDATION);i=O.split("||").map((N)=>({content:N.trim()})).filter((N)=>N.content)}if(i.length<2)throw new t("Threads require at least 2 tweets",y.VALIDATION);if(n==="schedule-thread"){let O=L({at:U(u,"at"),in:U(u,"in")}),N=await w.post("/api/posts/schedule-thread",{socialAccountId:m,tweets:i,scheduledAt:O,metadata:z(u)??{}},{workspace:!0});$(N,e);return}let o=await w.post("/api/posts/publish-thread-now",{socialAccountId:m,tweets:i,metadata:z(u)??{}},{workspace:!0});$(o,e);return}default:throw new t(`Unknown posts command "${n??""}". Try: list, get, schedule, publish, reschedule, cancel, delete, x-cap, schedule-thread, publish-thread`,y.USAGE)}}async function be(e,n,u,r){R(e),p(e);let w=A(e);switch(n){case"list":case"ls":{let d=await w.get("/api/drafts",{workspace:!0,query:{limit:V(u,"limit")??50,offset:V(u,"offset")??0}});$(d,e);return}case"get":{let d=r[0];if(!d)throw new t("Usage: dun drafts get <id>",y.USAGE);let m=await w.get(`/api/drafts/${d}`,{workspace:!0});$(m,e);return}case"create":{let d=U(u,"text","content","c","t");if(!d)throw new t("Missing --text",y.VALIDATION);let m=await w.post("/api/drafts",{content:d,name:U(u,"name","n"),socialAccountIds:I(u,"accounts","account","a")??[],selectedPlatforms:I(u,"platforms","platform","p")??[],mediaUrls:I(u,"media")??[]},{workspace:!0});$(m,e);return}case"update":{let d=r[0];if(!d)throw new t("Usage: dun drafts update <id> --text ...",y.USAGE);let m={id:d},i=U(u,"text","content","c","t");if(i)m.content=i;let o=U(u,"name","n");if(o!==void 0)m.name=o;let O=I(u,"accounts","account","a");if(O)m.socialAccountIds=O;let N=I(u,"platforms","platform","p");if(N)m.selectedPlatforms=N;let T=I(u,"media");if(T)m.mediaUrls=T;let D=await w.patch(`/api/drafts/${d}`,m,{workspace:!0});$(D,e);return}case"delete":case"rm":{let d=r[0];if(!d)throw new t("Usage: dun drafts delete <id> [--yes]",y.USAGE);if(!e.yes&&!k(u,"yes","y"))throw new t("Refusing to delete without --yes",y.VALIDATION);let m=await w.delete(`/api/drafts/${d}`,void 0,{workspace:!0});$(m??{id:d,deleted:!0},e);return}default:throw new t(`Unknown drafts command "${n??""}". Try: list, get, create, update, delete`,y.USAGE)}}import{basename as ae}from"node:path";function en(e){let n=e.split(".").pop()?.toLowerCase()??"";return{jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",mp4:"video/mp4",mov:"video/quicktime",webm:"video/webm"}[n]??"application/octet-stream"}async function Te(e,n,u,r){R(e),p(e);let w=A(e);switch(n){case"list":case"ls":{let d=await w.get("/api/media",{workspace:!0,query:{limit:V(u,"limit")??50,offset:V(u,"offset")??0}});$(d,e);return}case"get":{let d=r[0];if(!d)throw new t("Usage: dun media get <id>",y.USAGE);let m=await w.get(`/api/media/${d}`,{workspace:!0});$(m,e);return}case"upload":{let d=r[0]||U(u,"file","f");if(!d)throw new t('Usage: dun media upload <file> [--alt "..."]',y.USAGE);let m=Bun.file(d);if(!await m.exists())throw new t(`File not found: ${d}`,y.VALIDATION);let o=ae(d),O=U(u,"mime","type","content-type")||m.type||en(o),N=m.size,T=U(u,"alt","alt-text"),D=await w.post("/api/media/upload-url",{filename:o,mimeType:O,fileSize:N},{workspace:!0});H(e.debug,`PUT ${D.uploadUrl}`,{storageKey:D.storageKey,fileSize:N});let W=await m.arrayBuffer(),j;try{j=await fetch(D.uploadUrl,{method:"PUT",headers:{"Content-Type":O,"Content-Length":String(N)},body:W})}catch(Y){throw new t(`Upload PUT failed: ${Y.message}`,y.NETWORK)}if(!j.ok){let Y=await j.text().catch(()=>"");throw new t(`Upload PUT failed with HTTP ${j.status}${Y?`: ${Y.slice(0,200)}`:""}`,y.NETWORK)}let q=await w.post("/api/media/complete",{storageKey:D.storageKey,originalFilename:o,mimeType:O,fileSize:N,altText:T},{workspace:!0});$(q,e);return}case"delete":case"rm":{let d=r[0];if(!d)throw new t("Usage: dun media delete <id> [--yes]",y.USAGE);if(!e.yes&&!k(u,"yes","y"))throw new t("Refusing to delete without --yes",y.VALIDATION);let m=await w.delete(`/api/media/${d}`,void 0,{workspace:!0});$(m??{id:d,deleted:!0},e);return}default:throw new t(`Unknown media command "${n??""}". Try: list, get, upload, delete`,y.USAGE)}}async function Re(e,n,u,r){R(e),p(e);let w=A(e),d=n[0];if(d==="collections"||d==="collection"){let m=n[1]||"list";switch(m){case"list":case"ls":{let i=await w.get("/api/memory/collections",{workspace:!0});$(i,e);return}case"create":{let i=U(u,"name","n");if(!i)throw new t("Missing --name",y.VALIDATION);let o=await w.post("/api/memory/collections",{name:i,color:U(u,"color")??"blue",isPrivate:k(u,"private")},{workspace:!0});$(o,e);return}case"get":{let i=n[2]||r[0];if(!i)throw new t("Usage: dun memory collections get <id>",y.USAGE);let o=await w.get(`/api/memory/collections/${i}`,{workspace:!0});$(o,e);return}default:throw new t(`Unknown memory collections command "${m}". Try: list, create, get`,y.USAGE)}}switch(d){case"list":case"ls":{let m=U(u,"collection","collection-id","c");if(!m)throw new t("Missing --collection <id>",y.VALIDATION);let i=await w.get("/api/memory",{workspace:!0,query:{collectionId:m,limit:V(u,"limit")??100}});$(i,e);return}case"save":case"add":{let m=U(u,"collection","collection-id","c"),i=U(u,"text","t");if(!m)throw new t("Missing --collection <id>",y.VALIDATION);if(!i)throw new t("Missing --text",y.VALIDATION);let o=await w.post("/api/memory",{collectionId:m,text:i},{workspace:!0});$(o,e);return}case"search":{let m=U(u,"prompt","q","query","text"),i=I(u,"collections","collection","collection-ids","c")??[];if(!m)throw new t("Missing --prompt",y.VALIDATION);if(i.length===0)throw new t("Missing --collections <id,id>",y.VALIDATION);let o=await w.post("/api/memory/search",{prompt:m,collectionIds:i,topK:V(u,"top-k","topk","k")??5},{workspace:!0});$(o,e);return}case"delete":case"rm":{let m=n[1]||r[0],i=U(u,"collection","collection-id","c");if(!m)throw new t("Usage: dun memory delete <id> --collection <id>",y.USAGE);if(!i)throw new t("Missing --collection <id>",y.VALIDATION);if(!e.yes&&!k(u,"yes","y"))throw new t("Refusing to delete without --yes",y.VALIDATION);let o=await w.delete(`/api/memory/${m}`,{collectionId:i,memoryId:m},{workspace:!0});$(o??{id:m,deleted:!0},e);return}default:throw new t(`Unknown memory command "${d??""}". Try: collections, list, save, search, delete`,y.USAGE)}}import{existsSync as nn}from"node:fs";import{mkdir as Ve,writeFile as Ie,readFile as je}from"node:fs/promises";import{dirname as a,join as b}from"node:path";import{homedir as ee}from"node:os";import{fileURLToPath as un}from"node:url";var F=[{id:"claude",label:"Claude Code",aliases:["claude-code"],dest:(e)=>b(e,".claude","skills","dunsocial","SKILL.md")},{id:"codex",label:"Codex",dest:(e)=>b(e,".codex","skills","dunsocial","SKILL.md"),afterInstall:async(e)=>{return{agentsPath:await on(e)}}},{id:"cursor",label:"Cursor",dest:(e)=>b(e,".cursor","skills","dunsocial","SKILL.md")},{id:"pi",label:"Pi",dest:(e)=>b(e,".pi","agent","skills","dunsocial","SKILL.md")},{id:"opencode",label:"OpenCode",dest:(e)=>b(e,".config","opencode","skills","dunsocial","SKILL.md")},{id:"agents",label:"Universal (.agents)",aliases:["universal"],dest:(e)=>b(e,".agents","skills","dunsocial","SKILL.md")},{id:"copilot",label:"GitHub Copilot",aliases:["github-copilot","github"],dest:(e)=>b(e,".copilot","skills","dunsocial","SKILL.md")},{id:"windsurf",label:"Windsurf",dest:(e)=>b(e,".codeium","windsurf","skills","dunsocial","SKILL.md")},{id:"goose",label:"Goose",dest:(e)=>b(e,".config","goose","skills","dunsocial","SKILL.md")},{id:"gemini",label:"Gemini CLI",aliases:["gemini-cli"],dest:(e)=>b(e,".gemini","skills","dunsocial","SKILL.md")},{id:"amp",label:"Amp",dest:(e)=>b(e,".config","agents","skills","dunsocial","SKILL.md")},{id:"grok",label:"Grok Build",dest:(e)=>b(e,".grok","skills","dunsocial","SKILL.md")},{id:"continue",label:"Continue",dest:(e)=>b(e,".continue","skills","dunsocial","SKILL.md")},{id:"antigravity",label:"Antigravity",dest:(e)=>b(e,".gemini","antigravity","skills","dunsocial","SKILL.md")}],l=new Map;for(let e of F){l.set(e.id,e);for(let n of e.aliases??[])l.set(n,e)}var pe=`Usage: dun skill install <${F.map((e)=>e.id).join("|")}|all>`;function x(){let e=a(un(import.meta.url)),n=b("skills","dunsocial","SKILL.md"),u=[b(e,"..",n),b(e,"..","..",n)];return u.find(nn)??u[0]}function dn(){let e=ee();return F.map((n)=>({id:n.id,label:n.label,path:n.dest(e),aliases:n.aliases??[]}))}async function rn(){try{return await je(x(),"utf8")}catch{throw new t(`Bundled skill not found at ${x()}. Reinstall dunsocial.`,y.USAGE)}}async function mn(e,n){await Ve(a(e),{recursive:!0}),await Ie(e,n,"utf8")}async function on(e){let n=b(ee(),".codex","AGENTS.md");await Ve(a(n),{recursive:!0});let u="";try{u=await je(n,"utf8")}catch{u=""}let r=["<!-- BEGIN DUNSOCIAL CLI SKILL -->","## DunSocial CLI","","Use the `dun` CLI for DunSocial scheduling from the shell. Prefer `--json` output.",`Skill details: ${e}`,"","Common flow:","1. `dun auth status --json`","2. `dun workspace list --json` / `dun workspace use <id>`","3. `dun accounts list --json`",'4. `dun posts schedule --text "..." --accounts <id> --in 1h --json`',"<!-- END DUNSOCIAL CLI SKILL -->"].join(`
140
- `),w="<!-- BEGIN DUNSOCIAL CLI SKILL -->",d="<!-- END DUNSOCIAL CLI SKILL -->",m,i=u.indexOf(w),o=u.indexOf(d);if(i!==-1&&o!==-1&&o>i)m=u.slice(0,i)+r+u.slice(o+d.length);else if(u.trim())m=`${u.trimEnd()}
141
-
142
- ${r}
143
- `;else m=`${r}
144
- `;return await Ie(n,m,"utf8"),n}async function ke(e,n){let u=e.dest(ee());await mn(u,n);let r=e.afterInstall?await e.afterInstall(u):{};return{target:e.id,path:u,message:`Installed DunSocial skill for ${e.label}. Restart or open a new session if needed.`,...r}}async function Ke(e,n,u,r){switch(n){case"path":{$({path:x()},e);return}case"list":{$({targets:dn(),hint:"Install with: dun skill install <target|all>"},e);return}case"install":{let w=(r[0]||"").toLowerCase();if(!w)throw new t(pe,y.USAGE);let d=await rn();if(w==="all"){let o=[];for(let O of F){let N=await ke(O,d);o.push({target:N.target,path:N.path})}$({target:"all",count:o.length,installed:o,message:`Installed DunSocial skill for ${o.length} agents.`},e);return}let m=l.get(w);if(!m)throw new t(`Unknown agent "${w}". ${pe}
145
- Try: dun skill list`,y.USAGE);let i=await ke(m,d);$(i,e);return}default:throw new t(`Unknown skill command "${n??""}". Try: install, list, path`,y.USAGE)}}async function yn(e){let n=re(e),{group:u,action:r,nest:w,flags:d,positionals:m}=n;if(P(d)&&!u)return process.stdout.write(`${M}
146
- `),K.OK;if(u==="help"||!u&&B(d)){let o=u==="help"?r||m[0]:void 0;return process.stdout.write(o?`${E(o)}
147
- `:`${Z}
148
- `),K.OK}if(!u)return process.stdout.write(`${Z}
149
- `),K.OK;if(B(d))return process.stdout.write(`${E(u)}
150
- `),K.OK;let i=await de({apiUrl:Q(d,"api-url","apiUrl"),token:Q(d,"token"),workspace:Q(d,"workspace","w"),json:G(d,"json","j")?!0:void 0,debug:G(d,"debug"),quiet:G(d,"quiet","q"),yes:G(d,"yes","y")});if(P(d))return f({version:M},i),K.OK;try{switch(u){case"auth":await Oe(i,r,d,m);break;case"workspace":case"workspaces":await $e(i,r,d,m);break;case"account":case"accounts":await Ne(i,r??"list",d,m);break;case"post":case"posts":await Ae(i,r,d,m);break;case"draft":case"drafts":await be(i,r,d,m);break;case"media":await Te(i,r,d,m);break;case"memory":{let o=w?[w,r].filter(Boolean):r?[r]:[];await Re(i,o,d,m);break}case"skill":case"skills":await Ke(i,r,d,m);break;case"version":f({version:M},i);break;default:throw new h(`Unknown command "${u}". Run dun --help`,K.USAGE)}return K.OK}catch(o){return me(o,i)}}var wn=await yn(process.argv.slice(2));process.exit(wn);
230
+ `)}else process.stderr.write(JSON.stringify({ok:!0,phase:"waiting",userCode:K.userCode,verificationUrlComplete:K.verificationUrlComplete})+`
231
+ `);if(G&&process.stdout.isTTY)try{await R0(K.verificationUrlComplete)}catch{}let $=Math.max(3,K.interval||5)*1000,A=Date.now()+(K.expiresIn||900)*1000;while(Date.now()<A){await J0($);let V=await O.post("/api/cli/auth/poll",{deviceCode:K.deviceCode},{auth:!1});if(V.status==="pending")continue;if(V.status==="expired")throw new Q("Login expired. Run dun auth login again.",z.AUTH);if(V.status==="denied")throw new Q("Login was denied in the browser.",z.AUTH);if(V.status==="approved"){let X=V.token,Z={...U,token:X},J=T(Z),N=await J.get("/api/user/profile"),q={...U.config,apiUrl:U.apiUrl,token:X,defaultWorkspaceId:V.workspaceId||U.config.defaultWorkspaceId,defaultWorkspaceSlug:U.config.defaultWorkspaceSlug,defaultWorkspaceName:U.config.defaultWorkspaceName};if(V.workspaceId)try{let B=(await J.get("/api/workspaces")).find((L)=>L.id===V.workspaceId);if(B)q.defaultWorkspaceId=B.id,q.defaultWorkspaceSlug=B.slug,q.defaultWorkspaceName=B.name}catch{}await b(q);let I=Boolean(q.defaultWorkspaceId);if(!U.json&&S(process.stdout)){R(E(I),U);return}R({user:{id:N.id,name:N.name,email:N.email},workspaceId:q.defaultWorkspaceId??null,configPath:j(),method:"device",message:E(I)},U);return}}throw new Q("Login timed out. Run dun auth login again.",z.AUTH)}async function Q0(U){if(await ZU(),!U.json&&S(process.stdout)){R("Logged out. Config cleared.",U);return}R({message:"Logged out. Config cleared.",configPath:j()},U)}async function X0(U){w(U);let O=await T(U).get("/api/user/profile");R({id:O.id,name:O.name,email:O.email,image:O.image??null,apiUrl:U.apiUrl,workspaceId:U.workspaceId??null},U)}async function Z0(U){let D=Boolean(U.token),O=null,K=null;if(D)try{O=await T(U).get("/api/user/profile")}catch(G){K=G instanceof Error?G.message:String(G)}R({authenticated:Boolean(O),tokenPresent:D,apiUrl:U.apiUrl,workspaceId:U.workspaceId??null,workspaceSlug:U.config.defaultWorkspaceSlug??null,workspaceName:U.config.defaultWorkspaceName??null,configPath:j(),user:O?{id:O.id,name:O.name,email:O.email}:null,error:K},U)}function J0(U){return new Promise((D)=>setTimeout(D,U))}async function R0(U){let D=process.platform,O=D==="darwin"?["open",U]:D==="win32"?["cmd","/c","start","",U]:["xdg-open",U];await Bun.spawn(O,{stdout:"ignore",stderr:"ignore"}).exited}async function uU(U,D,O,K){w(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/workspaces");R($.map((A)=>({id:A.id,name:A.name,slug:A.slug,role:A.role,timezone:A.timezone,plan:A.subscription?.planName??null,status:A.subscription?.status??null})),{...U,emptyMessage:"No workspaces yet."});return}case"use":{let $=K[0];if(!$)throw new Q("Usage: dun workspace use <id|slug>",z.USAGE);let V=(await G.get("/api/workspaces")).find((X)=>X.id===$||X.slug===$);if(!V)throw new Q(`Workspace not found: ${$}. Run dun workspace list`,z.NOT_FOUND);await b({...U.config,apiUrl:U.apiUrl,token:U.token,defaultWorkspaceId:V.id,defaultWorkspaceSlug:V.slug,defaultWorkspaceName:V.name}),R({id:V.id,name:V.name,slug:V.slug,role:V.role,message:`Default workspace set to ${V.name}`},U);return}case"current":{if(!U.workspaceId&&!U.config.defaultWorkspaceId)throw new Q("No workspace selected. Run dun workspace use <id|slug>",z.VALIDATION);let $=U.workspaceId||U.config.defaultWorkspaceId;try{let V=(await G.get("/api/workspaces")).find((X)=>X.id===$||X.slug===$);if(V){R({id:V.id,name:V.name,slug:V.slug,role:V.role,timezone:V.timezone},U);return}}catch{}R({id:$,name:U.config.defaultWorkspaceName??null,slug:U.config.defaultWorkspaceSlug??null},U);return}default:throw new Q(`Unknown workspace command "${D??""}". Try: list, use, current`,z.USAGE)}}async function _U(U,D,O,K){w(U),H(U);let G=T(U);switch(D){case void 0:case"list":case"ls":{let $=await G.get("/api/social-accounts",{workspace:!0}),A=Y(O,"platform","p")?.toLowerCase(),V=A?$.filter((X)=>{let Z=(X.providerName||"").toLowerCase();return({x:["x","twitter"],twitter:["x","twitter"],linkedin:["linkedin"],"linkedin-business":["linkedin business","linkedin-business","linkedin_business"],bluesky:["bluesky"],threads:["threads"],reddit:["reddit"],pinterest:["pinterest"],instagram:["instagram"],youtube:["youtube"]}[A]??[A]).some((q)=>Z.includes(q))}):$;R(V.map((X)=>({id:X.id,providerName:X.providerName,username:X.username??null,displayName:X.displayName??null,isConnected:X.isConnected,expiresAt:X.expiresAt??null})),{...U,emptyMessage:A?`No ${A} accounts connected. Connect one in the DunSocial dashboard.`:"No accounts connected. Connect one in the DunSocial dashboard."});return}default:throw new Q(`Unknown accounts command "${D}". Try: list`,z.USAGE)}}var Y0=/^(\d+)\s*(s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days|w|week|weeks)$/i;function l(U,D=new Date){let O=U.trim();if(!O)throw new Q("Empty time value",z.VALIDATION);let K=O.match(Y0);if(K){let A=Number(K[1]),V=K[2].toLowerCase(),X=q0(V)*A;return new Date(D.getTime()+X)}let G=O.match(/^in\s+(.+)$/i);if(G)return l(G[1],D);let $=new Date(O);if(Number.isNaN($.getTime()))throw new Q(`Invalid time "${U}". Use ISO-8601 (2026-06-01T09:00:00Z) or relative (2h, 30m, 1d).`,z.VALIDATION);return $}function q0(U){switch(U){case"s":case"sec":case"secs":case"second":case"seconds":return 1000;case"m":case"min":case"mins":case"minute":case"minutes":return 60000;case"h":case"hr":case"hrs":case"hour":case"hours":return 3600000;case"d":case"day":case"days":return 86400000;case"w":case"week":case"weeks":return 604800000;default:throw new Q(`Unknown time unit: ${U}`,z.VALIDATION)}}function C(U){if(U.at&&U.in)throw new Q("Pass only one of --at or --in",z.VALIDATION);if(U.at)return l(U.at).toISOString();if(U.in)return l(U.in).toISOString();throw new Q("Schedule time required. Pass --at <iso> or --in <duration> (e.g. 2h).",z.VALIDATION)}function IU(U){if(!U)return[];return U.split(",").map((D)=>D.trim()).filter(Boolean)}function yU(U){let D=Y(U,"text","content","c","t");if(!D)throw new Q("Missing --text",z.VALIDATION);return D}function s(U){let D=u(U,"accounts","account","a")??IU(Y(U,"accounts","account","a"));if(!D||D.length===0)throw new Q("Missing --accounts <id,id>",z.VALIDATION);return D}function t(U){return u(U,"media","media-urls")??IU(Y(U,"media","media-urls"))}function h(U){let D=Y(U,"meta","metadata","meta-json");if(!D)return;try{let O=JSON.parse(D);if(!O||typeof O!=="object"||Array.isArray(O))throw Error("metadata must be a JSON object");return O}catch(O){throw new Q(`Invalid --meta JSON: ${O.message}`,z.VALIDATION)}}async function BU(U,D,O,K){w(U),H(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/posts",{workspace:!0,query:{status:Y(O,"status","s"),socialAccountId:Y(O,"account","social-account"),groupId:Y(O,"group"),limit:P(O,"limit")??50,offset:P(O,"offset")??0}});R($,{...U,emptyMessage:"No posts yet. Schedule one with dun posts schedule."});return}case"get":{let $=K[0];if(!$)throw new Q("Usage: dun posts get <id>",z.USAGE);let A=await G.get(`/api/posts/${$}`,{workspace:!0});R(A,U);return}case"schedule":{let $=yU(O),A=s(O),V=C({at:Y(O,"at"),in:Y(O,"in")}),X=await G.post("/api/posts/schedule",{content:$,socialAccountIds:A,scheduledAt:V,mediaUrls:t(O),metadata:h(O)??{}},{workspace:!0});R(X,U);return}case"publish":case"publish-now":{let $=yU(O),A=s(O),V=await G.post("/api/posts/publish-now",{content:$,socialAccountIds:A,mediaUrls:t(O),metadata:h(O)??{},naturalPosting:F(O,"natural","natural-posting")},{workspace:!0});R(V,U);return}case"reschedule":{let $=K[0];if(!$)throw new Q("Usage: dun posts reschedule <id> --at|--in",z.USAGE);let V={scheduledAt:C({at:Y(O,"at"),in:Y(O,"in")})},X=Y(O,"text","content","c","t");if(X)V.content=X;let Z=t(O);if(Z.length)V.mediaUrls=Z;let J=h(O);if(J)V.metadata=J;if(!V.content){let q=await G.get(`/api/posts/${$}`,{workspace:!0});if(V.content=q.content,!V.metadata&&q.metadata)V.metadata=q.metadata;if(!V.mediaUrls&&q.mediaUrls)V.mediaUrls=q.mediaUrls}let N=await G.post(`/api/posts/${$}/reschedule`,V,{workspace:!0});R(N,U);return}case"cancel":{let $=K[0];if(!$)throw new Q("Usage: dun posts cancel <id>",z.USAGE);let A=await G.post(`/api/posts/${$}/cancel`,void 0,{workspace:!0});R(A??{id:$,status:"cancelled"},U);return}case"delete":case"rm":{let $=K[0];if(!$)throw new Q("Usage: dun posts delete <id> [--yes]",z.USAGE);if(!U.yes&&!F(O,"yes","y"))throw new Q("Refusing to delete without --yes (or CI/yes mode)",z.VALIDATION);let A=await G.delete(`/api/posts/${$}`,void 0,{workspace:!0});R(A??{id:$,deleted:!0},U);return}case"x-cap":case"xcap":{let $=await G.get("/api/posts/x-cap-usage",{workspace:!0});R($,U);return}case"schedule-thread":case"publish-thread":case"publish-thread-now":{let $=Y(O,"file","f"),A=Y(O,"account","accounts","a")??s(O)[0];if(!A)throw new Q("Missing --account <xAccountId>",z.VALIDATION);let V;if($){let Z=await Bun.file($).text(),J=JSON.parse(Z);if(Array.isArray(J))V=J;else if(J&&typeof J==="object"&&Array.isArray(J.tweets))V=J.tweets;else throw new Q("Thread file must be an array of tweets or { tweets: [] }",z.VALIDATION)}else{let Z=Y(O,"text","content");if(!Z)throw new Q("Provide --file thread.json (array of {content}) or informal --text with || separators is not enough alone for threads — use --file",z.VALIDATION);V=Z.split("||").map((J)=>({content:J.trim()})).filter((J)=>J.content)}if(V.length<2)throw new Q("Threads require at least 2 tweets",z.VALIDATION);if(D==="schedule-thread"){let Z=C({at:Y(O,"at"),in:Y(O,"in")}),J=await G.post("/api/posts/schedule-thread",{socialAccountId:A,tweets:V,scheduledAt:Z,metadata:h(O)??{}},{workspace:!0});R(J,U);return}let X=await G.post("/api/posts/publish-thread-now",{socialAccountId:A,tweets:V,metadata:h(O)??{}},{workspace:!0});R(X,U);return}default:throw new Q(`Unknown posts command "${D??""}". Try: list, get, schedule, publish, reschedule, cancel, delete, x-cap, schedule-thread, publish-thread`,z.USAGE)}}async function LU(U,D,O,K){w(U),H(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/drafts",{workspace:!0,query:{limit:P(O,"limit")??50,offset:P(O,"offset")??0}});R($,{...U,emptyMessage:'No drafts yet. Create one with dun drafts create --text "..."'});return}case"get":{let $=K[0];if(!$)throw new Q("Usage: dun drafts get <id>",z.USAGE);let A=await G.get(`/api/drafts/${$}`,{workspace:!0});R(A,U);return}case"create":{let $=Y(O,"text","content","c","t");if(!$)throw new Q("Missing --text",z.VALIDATION);let A=await G.post("/api/drafts",{content:$,name:Y(O,"name","n"),socialAccountIds:u(O,"accounts","account","a")??[],selectedPlatforms:u(O,"platforms","platform","p")??[],mediaUrls:u(O,"media")??[]},{workspace:!0});R(A,U);return}case"update":{let $=K[0];if(!$)throw new Q("Usage: dun drafts update <id> --text ...",z.USAGE);let A={id:$},V=Y(O,"text","content","c","t");if(V)A.content=V;let X=Y(O,"name","n");if(X!==void 0)A.name=X;let Z=u(O,"accounts","account","a");if(Z)A.socialAccountIds=Z;let J=u(O,"platforms","platform","p");if(J)A.selectedPlatforms=J;let N=u(O,"media");if(N)A.mediaUrls=N;let q=await G.patch(`/api/drafts/${$}`,A,{workspace:!0});R(q,U);return}case"delete":case"rm":{let $=K[0];if(!$)throw new Q("Usage: dun drafts delete <id> [--yes]",z.USAGE);if(!U.yes&&!F(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let A=await G.delete(`/api/drafts/${$}`,void 0,{workspace:!0});R(A??{id:$,deleted:!0},U);return}default:throw new Q(`Unknown drafts command "${D??""}". Try: list, get, create, update, delete`,z.USAGE)}}import{basename as N0}from"node:path";function T0(U){let D=U.split(".").pop()?.toLowerCase()??"";return{jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",mp4:"video/mp4",mov:"video/quicktime",webm:"video/webm"}[D]??"application/octet-stream"}async function kU(U,D,O,K){w(U),H(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/media",{workspace:!0,query:{limit:P(O,"limit")??50,offset:P(O,"offset")??0}});R($,{...U,emptyMessage:"No media yet. Upload with dun media upload <file>."});return}case"get":{let $=K[0];if(!$)throw new Q("Usage: dun media get <id>",z.USAGE);let A=await G.get(`/api/media/${$}`,{workspace:!0});R(A,U);return}case"upload":{let $=K[0]||Y(O,"file","f");if(!$)throw new Q('Usage: dun media upload <file> [--alt "..."]',z.USAGE);let A=Bun.file($);if(!await A.exists())throw new Q(`File not found: ${$}`,z.VALIDATION);let X=N0($),Z=Y(O,"mime","type","content-type")||A.type||T0(X),J=A.size,N=Y(O,"alt","alt-text"),q=await G.post("/api/media/upload-url",{filename:X,mimeType:Z,fileSize:J},{workspace:!0});d(U.debug,`PUT ${q.uploadUrl}`,{storageKey:q.storageKey,fileSize:J});let I=await A.arrayBuffer(),k;try{k=await fetch(q.uploadUrl,{method:"PUT",headers:{"Content-Type":Z,"Content-Length":String(J)},body:I})}catch(L){throw new Q(`Upload PUT failed: ${L.message}`,z.NETWORK)}if(!k.ok){let L=await k.text().catch(()=>"");throw new Q(`Upload PUT failed with HTTP ${k.status}${L?`: ${L.slice(0,200)}`:""}`,z.NETWORK)}let B=await G.post("/api/media/complete",{storageKey:q.storageKey,originalFilename:X,mimeType:Z,fileSize:J,altText:N},{workspace:!0});R(B,U);return}case"delete":case"rm":{let $=K[0];if(!$)throw new Q("Usage: dun media delete <id> [--yes]",z.USAGE);if(!U.yes&&!F(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let A=await G.delete(`/api/media/${$}`,void 0,{workspace:!0});R(A??{id:$,deleted:!0},U);return}default:throw new Q(`Unknown media command "${D??""}". Try: list, get, upload, delete`,z.USAGE)}}async function MU(U,D,O,K){w(U),H(U);let G=T(U),$=D[0];if($==="collections"||$==="collection"){let A=D[1]||"list";switch(A){case"list":case"ls":{let V=await G.get("/api/memory/collections",{workspace:!0});R(V,{...U,emptyMessage:'No memory collections yet. Create one with dun memory collections create --name "..."'});return}case"create":{let V=Y(O,"name","n");if(!V)throw new Q("Missing --name",z.VALIDATION);let X=await G.post("/api/memory/collections",{name:V,color:Y(O,"color")??"blue",isPrivate:F(O,"private")},{workspace:!0});R(X,U);return}case"get":{let V=D[2]||K[0];if(!V)throw new Q("Usage: dun memory collections get <id>",z.USAGE);let X=await G.get(`/api/memory/collections/${V}`,{workspace:!0});R(X,U);return}default:throw new Q(`Unknown memory collections command "${A}". Try: list, create, get`,z.USAGE)}}switch($){case"list":case"ls":{let A=Y(O,"collection","collection-id","c");if(!A)throw new Q("Missing --collection <id>",z.VALIDATION);let V=await G.get("/api/memory",{workspace:!0,query:{collectionId:A,limit:P(O,"limit")??100}});R(V,{...U,emptyMessage:"No memories in this collection."});return}case"save":case"add":{let A=Y(O,"collection","collection-id","c"),V=Y(O,"text","t");if(!A)throw new Q("Missing --collection <id>",z.VALIDATION);if(!V)throw new Q("Missing --text",z.VALIDATION);let X=await G.post("/api/memory",{collectionId:A,text:V},{workspace:!0});R(X,U);return}case"search":{let A=Y(O,"prompt","q","query","text"),V=u(O,"collections","collection","collection-ids","c")??[];if(!A)throw new Q("Missing --prompt",z.VALIDATION);if(V.length===0)throw new Q("Missing --collections <id,id>",z.VALIDATION);let X=await G.post("/api/memory/search",{prompt:A,collectionIds:V,topK:P(O,"top-k","topk","k")??5},{workspace:!0});R(X,U);return}case"delete":case"rm":{let A=D[1]||K[0],V=Y(O,"collection","collection-id","c");if(!A)throw new Q("Usage: dun memory delete <id> --collection <id>",z.USAGE);if(!V)throw new Q("Missing --collection <id>",z.VALIDATION);if(!U.yes&&!F(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let X=await G.delete(`/api/memory/${A}`,{collectionId:V,memoryId:A},{workspace:!0});R(X??{id:A,deleted:!0},U);return}default:throw new Q(`Unknown memory command "${$??""}". Try: collections, list, save, search, delete`,z.USAGE)}}import{existsSync as W0}from"node:fs";import{mkdir as dU,writeFile as SU,readFile as hU}from"node:fs/promises";import{dirname as OU,join as W}from"node:path";import{homedir as DU}from"node:os";import{fileURLToPath as w0}from"node:url";var p=[{id:"claude",label:"Claude Code",aliases:["claude-code"],dest:(U)=>W(U,".claude","skills","dunsocial","SKILL.md")},{id:"codex",label:"Codex",dest:(U)=>W(U,".codex","skills","dunsocial","SKILL.md"),afterInstall:async(U)=>{return{agentsPath:await u0(U)}}},{id:"cursor",label:"Cursor",dest:(U)=>W(U,".cursor","skills","dunsocial","SKILL.md")},{id:"pi",label:"Pi",dest:(U)=>W(U,".pi","agent","skills","dunsocial","SKILL.md")},{id:"opencode",label:"OpenCode",dest:(U)=>W(U,".config","opencode","skills","dunsocial","SKILL.md")},{id:"agents",label:"Universal (.agents)",aliases:["universal"],dest:(U)=>W(U,".agents","skills","dunsocial","SKILL.md")},{id:"copilot",label:"GitHub Copilot",aliases:["github-copilot","github"],dest:(U)=>W(U,".copilot","skills","dunsocial","SKILL.md")},{id:"windsurf",label:"Windsurf",dest:(U)=>W(U,".codeium","windsurf","skills","dunsocial","SKILL.md")},{id:"goose",label:"Goose",dest:(U)=>W(U,".config","goose","skills","dunsocial","SKILL.md")},{id:"gemini",label:"Gemini CLI",aliases:["gemini-cli"],dest:(U)=>W(U,".gemini","skills","dunsocial","SKILL.md")},{id:"amp",label:"Amp",dest:(U)=>W(U,".config","agents","skills","dunsocial","SKILL.md")},{id:"grok",label:"Grok Build",dest:(U)=>W(U,".grok","skills","dunsocial","SKILL.md")},{id:"continue",label:"Continue",dest:(U)=>W(U,".continue","skills","dunsocial","SKILL.md")},{id:"antigravity",label:"Antigravity",dest:(U)=>W(U,".gemini","antigravity","skills","dunsocial","SKILL.md")}],a=new Map;for(let U of p){a.set(U.id,U);for(let D of U.aliases??[])a.set(D,U)}var jU=`Usage: dun skill install <${p.map((U)=>U.id).join("|")}|all>`;function UU(){let U=OU(w0(import.meta.url)),D=W("skills","dunsocial","SKILL.md"),O=[W(U,"..",D),W(U,"..","..",D)];return O.find(W0)??O[0]}function H0(){let U=DU();return p.map((D)=>({id:D.id,label:D.label,path:D.dest(U),aliases:D.aliases??[]}))}async function F0(){try{return await hU(UU(),"utf8")}catch{throw new Q(`Bundled skill not found at ${UU()}. Reinstall dunsocial.`,z.USAGE)}}async function P0(U,D){await dU(OU(U),{recursive:!0}),await SU(U,D,"utf8")}async function u0(U){let D=W(DU(),".codex","AGENTS.md");await dU(OU(D),{recursive:!0});let O="";try{O=await hU(D,"utf8")}catch{O=""}let K=["<!-- BEGIN DUNSOCIAL CLI SKILL -->","## DunSocial CLI","","Use the `dun` CLI for DunSocial scheduling from the shell. Prefer `--json` output.",`Skill details: ${U}`,"","Common flow:","1. `dun auth status --json`","2. `dun workspace list --json` / `dun workspace use <id>`","3. `dun accounts list --json`",'4. `dun posts schedule --text "..." --accounts <id> --in 1h --json`',"<!-- END DUNSOCIAL CLI SKILL -->"].join(`
232
+ `),G="<!-- BEGIN DUNSOCIAL CLI SKILL -->",$="<!-- END DUNSOCIAL CLI SKILL -->",A,V=O.indexOf(G),X=O.indexOf($);if(V!==-1&&X!==-1&&X>V)A=O.slice(0,V)+K+O.slice(X+$.length);else if(O.trim())A=`${O.trimEnd()}
233
+
234
+ ${K}
235
+ `;else A=`${K}
236
+ `;return await SU(D,A,"utf8"),D}async function bU(U,D){let O=U.dest(DU());await P0(O,D);let K=U.afterInstall?await U.afterInstall(O):{};return{target:U.id,path:O,message:`Installed DunSocial skill for ${U.label}. Restart or open a new session if needed.`,...K}}async function vU(U,D,O,K){switch(D){case"path":{R({path:UU()},U);return}case"list":{R({targets:H0(),hint:"Install with: dun skill install <target|all>"},U);return}case"install":{let G=(K[0]||"").toLowerCase();if(!G)throw new Q(jU,z.USAGE);let $=await F0();if(G==="all"){let X=[];for(let Z of p){let J=await bU(Z,$);X.push({target:J.target,path:J.path})}R({target:"all",count:X.length,installed:X,message:`Installed DunSocial skill for ${X.length} agents.`},U);return}let A=a.get(G);if(!A)throw new Q(`Unknown agent "${G}". ${jU}
237
+ Try: dun skill list`,z.USAGE);let V=await bU(A,$);R(V,U);return}default:throw new Q(`Unknown skill command "${D??""}". Try: install, list, path`,z.USAGE)}}function mU(){if(e(process.stdout))NU(process.stdout,{tagline:!0});process.stdout.write(`${QU}
238
+ `)}async function _0(U){let D=AU(U),{group:O,action:K,nest:G,flags:$,positionals:A}=D;if(f($)&&!O)return process.stdout.write(`${v}
239
+ `),_.OK;if(O==="help"||!O&&n($)){let X=O==="help"?K||A[0]:void 0;if(X)process.stdout.write(`${g(X)}
240
+ `);else mU();return _.OK}if(!O)return mU(),_.OK;if(n($))return process.stdout.write(`${g(O)}
241
+ `),_.OK;let V=await VU({apiUrl:m($,"api-url","apiUrl"),token:m($,"token"),workspace:m($,"workspace","w"),json:y($,"json","j")?!0:void 0,debug:y($,"debug"),quiet:y($,"quiet","q"),yes:y($,"yes","y")});if(f($))return x({version:v},V),_.OK;try{switch(O){case"auth":await PU(V,K,$,A);break;case"workspace":case"workspaces":await uU(V,K,$,A);break;case"account":case"accounts":await _U(V,K??"list",$,A);break;case"post":case"posts":await BU(V,K,$,A);break;case"draft":case"drafts":await LU(V,K,$,A);break;case"media":await kU(V,K,$,A);break;case"memory":{let X=G?[G,K].filter(Boolean):K?[K]:[];await MU(V,X,$,A);break}case"skill":case"skills":await vU(V,K,$,A);break;case"version":x({version:v},V);break;default:throw new c(`Unknown command "${O}". Run dun --help`,_.USAGE)}return _.OK}catch(X){return zU(X,V)}}var y0=await _0(process.argv.slice(2));process.exit(y0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dunsocial",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "DunSocial agent-first CLI — schedule and publish social posts from the terminal, CI, and coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,6 +28,7 @@
28
28
  "files": [
29
29
  "dist",
30
30
  "skills",
31
+ "scripts/postinstall.js",
31
32
  "README.md",
32
33
  "LICENSE"
33
34
  ],
@@ -45,7 +46,8 @@
45
46
  "type-check": "tsc --noEmit",
46
47
  "test": "bun test",
47
48
  "release-check": "bun run scripts/release-check.ts",
48
- "prepack": "bun run build && bun run release-check"
49
+ "prepack": "bun run build && bun run release-check",
50
+ "postinstall": "node ./scripts/postinstall.js"
49
51
  },
50
52
  "devDependencies": {
51
53
  "@types/bun": "latest",
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Gated install tip — silent in CI / non-TTY / agent installs.
4
+ * Keep in sync with apps/cli/src/brand.ts OpenCode-style block art.
5
+ */
6
+ 'use strict';
7
+
8
+ const BANNER_FULL = [
9
+ '█▀▀▄ █ █ █▀▀▄ █▀▀▀ █▀▀█ █▀▀▀ ▀█▀ █▀▀█ █',
10
+ '█ █ █ █ █ █ ▀▀▀█ █ █ █ █ █▀▀█ █',
11
+ '▀▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀'
12
+ ].join('\n');
13
+
14
+ const BANNER_MINI = [
15
+ '█▀▀▄ █ █ █▀▀▄',
16
+ '█ █ █ █ █ █',
17
+ '▀▀▀▀ ▀▀▀▀ ▀ ▀',
18
+ '',
19
+ ' DunSocial'
20
+ ].join('\n');
21
+
22
+ function isCi() {
23
+ const env = process.env;
24
+ return (
25
+ env.CI === 'true' ||
26
+ env.CI === '1' ||
27
+ Boolean(env.GITHUB_ACTIONS) ||
28
+ Boolean(env.TF_BUILD) ||
29
+ Boolean(env.GITLAB_CI) ||
30
+ Boolean(env.CIRCLECI) ||
31
+ Boolean(env.BUILDKITE)
32
+ );
33
+ }
34
+
35
+ function maxWidth(art) {
36
+ return Math.max(...art.split('\n').map((line) => line.length));
37
+ }
38
+
39
+ function renderBanner(columns) {
40
+ const cols = columns > 0 ? columns : 80;
41
+ if (cols >= maxWidth(BANNER_FULL)) return BANNER_FULL;
42
+ if (cols >= maxWidth(BANNER_MINI)) return BANNER_MINI;
43
+ return 'DunSocial';
44
+ }
45
+
46
+ function main() {
47
+ if (!process.stderr.isTTY || isCi()) return;
48
+
49
+ // Quiet for monorepo / local deps; greet on `npm install -g dunsocial`
50
+ const globalInstall =
51
+ process.env.npm_config_global === 'true' || process.env.DUN_POSTINSTALL_FORCE === '1';
52
+ if (!globalInstall) return;
53
+
54
+ const columns = process.stderr.columns || process.stdout.columns || 80;
55
+ const banner = renderBanner(columns);
56
+
57
+ process.stderr.write(`\n${banner}\n\n`);
58
+ process.stderr.write(' dun auth login\n');
59
+ process.stderr.write(' dun --help\n');
60
+ process.stderr.write(' Docs: https://dunsocial.com\n\n');
61
+ }
62
+
63
+ try {
64
+ main();
65
+ } catch {
66
+ // never fail install
67
+ }