dunsocial 0.10.0 → 0.12.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/README.md CHANGED
@@ -167,6 +167,8 @@ dun media upload ./photo.png
167
167
  dun reddit flairs some-subreddit
168
168
  dun posts validate --accounts ACCOUNT_ID --meta '{"reddit":{…}}'
169
169
  dun drafts create --text "Idea for later"
170
+ dun posts schedule-thread --account ACCOUNT_ID --file ./thread.json --in 2h
171
+ dun memory collections update COLLECTION_ID --name "Renamed"
170
172
  dun personalization get
171
173
  dun personalization set --description "Concise, direct, no fluff."
172
174
  ```
@@ -176,7 +178,7 @@ Tips:
176
178
  - Help for any command: `dun posts --help` / `dun reddit --help`
177
179
  - Structured output for scripts: add `--json`
178
180
  - Platform-specific options (Reddit, etc.): `--meta '{…}'`
179
- - Prefer ChatGPT or Claude **in the browser**? Use [MCP](https://docs.dunsocial.com/mcp) instead of the CLI
181
+ - Prefer ChatGPT or Claude **in the browser**? Use [MCP](https://www.dunsocial.com/mcp) instead of the CLI (`com.dunsocial/dunsocial` on the official registry, or `https://api.dunsocial.com/api/mcp`)
180
182
  - Personalization is workspace-wide AI voice (Settings → Personalization). Updating it needs a token with the `workspace:write` scope
181
183
 
182
184
  ---
package/dist/index.js CHANGED
@@ -1,26 +1,26 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as eu}from"node:fs";import{mkdir as Su,readFile as hu,writeFile as pu,chmod as fu,unlink as iu}from"node:fs/promises";import{dirname as Eu,join as f}from"node:path";import{homedir as Cu}from"node:os";var O={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class z extends Error{code;details;constructor(u,w=O.USAGE,d){super(u);this.name="CliError",this.code=w,this.details=d}}function uu(u){if(u===401)return O.AUTH;if(u===403)return O.FORBIDDEN;if(u===404)return O.NOT_FOUND;if(u===429)return O.RATE_LIMITED;if(u>=400&&u<500)return O.VALIDATION;return O.NETWORK}var ou="https://api.dunsocial.com",du="0.10.0";function gu(){if(!du.includes("@@"))return du;try{let u=JSON.parse(eu(f(import.meta.dir,"..","package.json"),"utf8"));if(typeof u.version==="string"&&u.version.length>0)return u.version}catch{}return"0.0.0"}var T=gu();function L(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let u=process.env.XDG_CONFIG_HOME,w=u?u:f(Cu(),".config");return f(w,"dunsocial","config.json")}function _(){return L()}async function i(){let u=L();try{let w=await hu(u,"utf8");return JSON.parse(w)}catch(w){if(w.code==="ENOENT")return{};throw new z(`Failed to read config at ${u}: ${w.message}`,O.USAGE)}}async function j(u){let w=L();await Su(Eu(w),{recursive:!0});let d={...u,updatedAt:new Date().toISOString()};await pu(w,`${JSON.stringify(d,null,2)}
3
- `,"utf8");try{await fu(w,384)}catch{}}async function wu(){let u=L();try{await iu(u)}catch(w){if(w.code!=="ENOENT")throw new z(`Failed to clear config: ${w.message}`,O.USAGE)}}async function mu(u){let w=await i(),d=(u.apiUrl||process.env.DUN_API_URL||w.apiUrl||ou).replace(/\/$/,""),D=u.token||process.env.DUN_TOKEN||w.token,y=u.workspace||process.env.DUN_WORKSPACE_ID||w.defaultWorkspaceId||void 0,m=process.env.DUN_JSON==="0",U=!process.stdout.isTTY,$=m?!1:u.json===!0||process.env.DUN_JSON==="1"||process.env.CI==="true"||process.env.CI==="1"||U;return{apiUrl:d,token:D,workspaceId:y,json:$,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:w}}function Y(u){if(!u.token)throw new z("Not authenticated. Run `dun auth login` (or `dun auth login --token <token>` / set DUN_TOKEN).",O.AUTH);return u.token}function F(u){if(!u.workspaceId)throw new z("No workspace selected. Run `dun workspace use <id|slug>` or pass --workspace / DUN_WORKSPACE_ID.",O.VALIDATION);return u.workspaceId}function $u(u){let w={},d=[],D=0,y=!0;while(D<u.length){let X=u[D];if(y&&X==="--"){y=!1,D+=1;continue}if(y&&X.startsWith("--")){let Z=X.indexOf("=");if(Z!==-1){w[X.slice(2,Z)]=X.slice(Z+1),D+=1;continue}let Q=X.slice(2),J=u[D+1];if(J!==void 0&&!J.startsWith("-"))w[Q]=J,D+=2;else w[Q]=!0,D+=1;continue}if(y&&X.startsWith("-")&&X.length>1&&X!=="-"){let Z=X.slice(1);if(Z.length>1&&!Z.includes("=")){for(let G of Z)w[G]=!0;D+=1;continue}let Q=Z,J=u[D+1];if(J!==void 0&&!J.startsWith("-"))w[Q]=J,D+=2;else w[Q]=!0,D+=1;continue}d.push(X),D+=1}let m=d[0],U,$,q=1;if(m==="memory"&&(d[1]==="collections"||d[1]==="collection"))$="collections",U=d[2],q=U?3:2;else U=d[1],q=U?2:1;return{group:m,action:U,nest:$,flags:w,positionals:d.slice(q)}}function V(u,...w){for(let d of w){let D=u[d];if(typeof D==="string"&&D.length>0)return D}return}function R(u,...w){for(let d of w){let D=u[d];if(D===!0)return!0;if(typeof D==="string"){let y=D.toLowerCase();if(y==="1"||y==="true"||y==="yes")return!0}}return!1}function B(u,...w){let d=V(u,...w);if(d===void 0)return;let D=Number(d);if(!Number.isFinite(D))throw Error(`Invalid number for --${w[0]}: ${d}`);return D}function I(u,...w){let d=V(u,...w);if(d===void 0)return;return d.split(",").map((D)=>D.trim()).filter(Boolean)}function E(u){return R(u,"help","h")}function C(u){return R(u,"version","v","V")}function k(u,w){if(w.quiet&&!w.json)return;if(w.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,w.emptyMessage)}
7
- `)}function Du(u,w){let{message:d,code:D,details:y}=ru(u);if(w.json||!process.stdout.isTTY)process.stdout.write(`${JSON.stringify({ok:!1,error:{code:su(D),message:d,details:y??null}},null,2)}
8
- `);else if(process.stderr.write(`error: ${d}
9
- `),y!==void 0&&process.env.DUN_DEBUG==="1")process.stderr.write(`${JSON.stringify(y,null,2)}
10
- `);return D}function su(u){switch(u){case O.AUTH:return"auth";case O.FORBIDDEN:return"forbidden";case O.VALIDATION:return"validation";case O.NOT_FOUND:return"not_found";case O.RATE_LIMITED:return"rate_limited";case O.NETWORK:return"network";default:return"usage"}}function ru(u){if(u instanceof z)return{message:u.message,code:u.code,details:u.details};if(u instanceof Error)return{message:u.message,code:O.USAGE};return{message:String(u),code:O.USAGE}}function cu(u,w){if(Array.isArray(u)){if(u.length===0)return w??"(empty)";if(u.every((d)=>d&&typeof d==="object"))return Uu(u,w);return u.map((d)=>`- ${o(d)}`).join(`
11
- `)}if(u&&typeof u==="object"){let d=u;if(Array.isArray(d.items)){if(d.items.length===0&&w)return w;let D=typeof d.total==="number"?`total=${d.total} showing=${d.items.length}`:void 0,y=Uu(d.items,w);return D?`${D}
12
- ${y}`:y}return Object.entries(d).map(([D,y])=>`${D}: ${o(y)}`).join(`
13
- `)}return String(u)}function Uu(u,w){if(u.length===0)return w??"(empty)";let d=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],D=new Set;for(let X of u)for(let Z of Object.keys(X))D.add(Z);let y=[...d.filter((X)=>D.has(X)),...[...D].filter((X)=>!d.includes(X)).slice(0,6)].slice(0,8),m=u.map((X)=>y.map((Z)=>xu(o(X[Z]),Z==="content"||Z==="text"?48:28))),U=y.map((X,Z)=>Math.max(X.length,...m.map((Q)=>Q[Z]?.length??0))),$=y.map((X,Z)=>X.padEnd(U[Z])).join(" "),q=m.map((X)=>X.map((Z,Q)=>Z.padEnd(U[Q])).join(" ")).join(`
14
- `);return`${$}
15
- ${q}`}function o(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 w=u;if(typeof w.id==="string"&&typeof w.name==="string")return`${w.name} (${w.id})`;if(typeof w.status==="string")return w.status;return JSON.stringify(u)}return String(u)}function xu(u,w){if(u.length<=w)return u;return`${u.slice(0,Math.max(0,w-1))}...`}function P(u,w,d){if(!u)return;if(d!==void 0){process.stderr.write(`[debug] ${w} ${JSON.stringify(d)}
16
- `);return}process.stderr.write(`[debug] ${w}
17
- `)}function W(u,w){let d=`${u} ${w}`,D="-".repeat(Math.max(12,d.length+4));return`${d}
18
- ${D}`}var g=`dun - schedule & publish social posts
2
+ import{readFileSync as Me}from"node:fs";import{mkdir as qe,readFile as je,writeFile as We,chmod as Ve,unlink as Ge}from"node:fs/promises";import{dirname as Be,join as V}from"node:path";import{homedir as Ke}from"node:os";var a={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class d extends Error{code;details;constructor(e,n=a.USAGE,t){super(e);this.name="CliError",this.code=n,this.details=t}}function te(e){if(e===401)return a.AUTH;if(e===403)return a.FORBIDDEN;if(e===404)return a.NOT_FOUND;if(e===429)return a.RATE_LIMITED;if(e>=400&&e<500)return a.VALIDATION;return a.NETWORK}var Fe="https://api.dunsocial.com",ne="0.12.0";function He(){if(!ne.includes("@@"))return ne;try{let e=JSON.parse(Me(V(import.meta.dir,"..","package.json"),"utf8"));if(typeof e.version==="string"&&e.version.length>0)return e.version}catch{}return"0.0.0"}var N=He();function _(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let e=process.env.XDG_CONFIG_HOME,n=e?e:V(Ke(),".config");return V(n,"dunsocial","config.json")}function L(){return _()}async function G(){let e=_();try{let n=await je(e,"utf8");return JSON.parse(n)}catch(n){if(n.code==="ENOENT")return{};throw new d(`Failed to read config at ${e}: ${n.message}`,a.USAGE)}}async function D(e){let n=_();await qe(Be(n),{recursive:!0});let t={...e,updatedAt:new Date().toISOString()};await We(n,`${JSON.stringify(t,null,2)}
3
+ `,"utf8");try{await Ve(n,384)}catch{}}async function re(){let e=_();try{await Ge(e)}catch(n){if(n.code!=="ENOENT")throw new d(`Failed to clear config: ${n.message}`,a.USAGE)}}async function ie(e){let n=await G(),t=(e.apiUrl||process.env.DUN_API_URL||n.apiUrl||Fe).replace(/\/$/,""),s=e.token||process.env.DUN_TOKEN||n.token,c=e.workspace||process.env.DUN_WORKSPACE_ID||n.defaultWorkspaceId||void 0,r=process.env.DUN_JSON==="0",o=!process.stdout.isTTY,i=r?!1:e.json===!0||process.env.DUN_JSON==="1"||process.env.CI==="true"||process.env.CI==="1"||o;return{apiUrl:t,token:s,workspaceId:c,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 I(e){if(!e.token)throw new d("Not authenticated. Run `dun auth login` (or `dun auth login --token <token>` / set DUN_TOKEN).",a.AUTH);return e.token}function A(e){if(!e.workspaceId)throw new d("No workspace selected. Run `dun workspace use <id|slug>` or pass --workspace / DUN_WORKSPACE_ID.",a.VALIDATION);return e.workspaceId}function oe(e){let n={},t=[],s=0,c=!0;while(s<e.length){let u=e[s];if(c&&u==="--"){c=!1,s+=1;continue}if(c&&u.startsWith("--")){let p=u.indexOf("=");if(p!==-1){n[u.slice(2,p)]=u.slice(p+1),s+=1;continue}let w=u.slice(2),g=e[s+1];if(g!==void 0&&!g.startsWith("-"))n[w]=g,s+=2;else n[w]=!0,s+=1;continue}if(c&&u.startsWith("-")&&u.length>1&&u!=="-"){let p=u.slice(1);if(p.length>1&&!p.includes("=")){for(let k of p)n[k]=!0;s+=1;continue}let w=p,g=e[s+1];if(g!==void 0&&!g.startsWith("-"))n[w]=g,s+=2;else n[w]=!0,s+=1;continue}t.push(u),s+=1}let r=t[0],o,i,l=1;if(r==="memory"&&(t[1]==="collections"||t[1]==="collection"))i="collections",o=t[2],l=o?3:2;else o=t[1],l=o?2:1;return{group:r,action:o,nest:i,flags:n,positionals:t.slice(l)}}function f(e,...n){for(let t of n){let s=e[t];if(typeof s==="string"&&s.length>0)return s}return}function h(e,...n){for(let t of n){let s=e[t];if(s===!0)return!0;if(typeof s==="string"){let c=s.toLowerCase();if(c==="1"||c==="true"||c==="yes")return!0}}return!1}function E(e,...n){let t=f(e,...n);if(t===void 0)return;let s=Number(t);if(!Number.isFinite(s))throw Error(`Invalid number for --${n[0]}: ${t}`);return s}function C(e,...n){let t=f(e,...n);if(t===void 0)return;return t.split(",").map((s)=>s.trim()).filter(Boolean)}function B(e){return h(e,"help","h")}function K(e){return h(e,"version","v","V")}function m(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(`${Ye(e,n.emptyMessage)}
7
+ `)}function ae(e,n){let{message:t,code:s,details:c}=Je(e);if(n.json||!process.stdout.isTTY)process.stdout.write(`${JSON.stringify({ok:!1,error:{code:ze(s),message:t,details:c??null}},null,2)}
8
+ `);else if(process.stderr.write(`error: ${t}
9
+ `),c!==void 0&&process.env.DUN_DEBUG==="1")process.stderr.write(`${JSON.stringify(c,null,2)}
10
+ `);return s}function ze(e){switch(e){case a.AUTH:return"auth";case a.FORBIDDEN:return"forbidden";case a.VALIDATION:return"validation";case a.NOT_FOUND:return"not_found";case a.RATE_LIMITED:return"rate_limited";case a.NETWORK:return"network";default:return"usage"}}function Je(e){if(e instanceof d)return{message:e.message,code:e.code,details:e.details};if(e instanceof Error)return{message:e.message,code:a.USAGE};return{message:String(e),code:a.USAGE}}function Ye(e,n){if(Array.isArray(e)){if(e.length===0)return n??"(empty)";if(e.every((t)=>t&&typeof t==="object"))return se(e,n);return e.map((t)=>`- ${F(t)}`).join(`
11
+ `)}if(e&&typeof e==="object"){let t=e;if(Array.isArray(t.items)){if(t.items.length===0&&n)return n;let s=typeof t.total==="number"?`total=${t.total} showing=${t.items.length}`:void 0,c=se(t.items,n);return s?`${s}
12
+ ${c}`:c}return Object.entries(t).map(([s,c])=>`${s}: ${F(c)}`).join(`
13
+ `)}return String(e)}function se(e,n){if(e.length===0)return n??"(empty)";let t=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],s=new Set;for(let u of e)for(let p of Object.keys(u))s.add(p);let c=[...t.filter((u)=>s.has(u)),...[...s].filter((u)=>!t.includes(u)).slice(0,6)].slice(0,8),r=e.map((u)=>c.map((p)=>Xe(F(u[p]),p==="content"||p==="text"?48:28))),o=c.map((u,p)=>Math.max(u.length,...r.map((w)=>w[p]?.length??0))),i=c.map((u,p)=>u.padEnd(o[p])).join(" "),l=r.map((u)=>u.map((p,w)=>p.padEnd(o[w])).join(" ")).join(`
14
+ `);return`${i}
15
+ ${l}`}function F(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 Xe(e,n){if(e.length<=n)return e;return`${e.slice(0,Math.max(0,n-1))}...`}function R(e,n,t){if(!e)return;if(t!==void 0){process.stderr.write(`[debug] ${n} ${JSON.stringify(t)}
16
+ `);return}process.stderr.write(`[debug] ${n}
17
+ `)}function S(e,n){let t=`${e} ${n}`,s="-".repeat(Math.max(12,t.length+4));return`${t}
18
+ ${s}`}var H=`dun - schedule & publish social posts
19
19
 
20
20
  Usage:
21
21
  dun <command> [flags]
22
22
 
23
- ${W("\uD83D\uDD10","Auth")}
23
+ ${S("\uD83D\uDD10","Auth")}
24
24
  dun auth login # device-code browser flow
25
25
  dun auth login --token <token> # CI / headless
26
26
  dun auth login --no-browser
@@ -28,20 +28,20 @@ ${W("\uD83D\uDD10","Auth")}
28
28
  dun auth whoami
29
29
  dun auth status
30
30
 
31
- ${W("\uD83D\uDCC1","Workspaces")}
31
+ ${S("\uD83D\uDCC1","Workspaces")}
32
32
  dun workspace list
33
33
  dun workspace use <id|slug>
34
34
  dun workspace current
35
35
 
36
- ${W("\uD83D\uDD17","Accounts")}
36
+ ${S("\uD83D\uDD17","Accounts")}
37
37
  dun accounts list [--platform x|reddit|...]
38
38
 
39
- ${W("\uD83D\uDFE0","Reddit")}
39
+ ${S("\uD83D\uDFE0","Reddit")}
40
40
  dun reddit flairs <subreddit> # template id + text + textEditable
41
41
  dun reddit requirements <subreddit> # live post rules (flair required, ...)
42
42
  # alias: dun accounts reddit flairs <subreddit>
43
43
 
44
- ${W("\uD83D\uDCDD","Posts")}
44
+ ${S("\uD83D\uDCDD","Posts")}
45
45
  dun posts list [--status scheduled|published|failed|draft|cancelled]
46
46
  dun posts get <id>
47
47
  dun posts validate --accounts <id> [--media id] --meta '{...}'
@@ -51,6 +51,9 @@ ${W("\uD83D\uDCDD","Posts")}
51
51
  dun posts cancel <id>
52
52
  dun posts delete <id> [--yes]
53
53
  dun posts x-cap
54
+ dun posts schedule-thread --account <id> --file thread.json (--at ISO | --in 2h)
55
+ dun posts publish-thread --account <id> --file thread.json
56
+ # thread.json: [{"content":"1"},{"content":"2"}] or --text "t1||t2"
54
57
 
55
58
  # Reddit --meta example (video):
56
59
  # --meta '{"reddit":{"subreddit":"tamilyapping","kind":"video","title":"...","flairId":"..."}}'
@@ -59,28 +62,31 @@ ${W("\uD83D\uDCDD","Posts")}
59
62
  # Company Pages stay images-only. Do not mix video + images on LinkedIn.
60
63
  # publish queues async work; without --wait, status scheduled means queued
61
64
 
62
- ${W("\uD83D\uDCC4","Drafts")}
65
+ ${S("\uD83D\uDCC4","Drafts")}
63
66
  dun drafts list
64
67
  dun drafts get <id>
65
68
  dun drafts create --text "..." [--platforms x,linkedin] [--accounts id,id]
66
69
  dun drafts update <id> --text "..."
67
70
  dun drafts delete <id> [--yes]
68
71
 
69
- ${W("\uD83D\uDDBC️","Media")}
72
+ ${S("\uD83D\uDDBC️","Media")}
70
73
  dun media list
71
74
  dun media get <id>
72
75
  dun media upload <file> [--alt "..."] # local preflight: images <=8MB, video <=256MB
73
76
  dun media delete <id> [--yes]
74
77
 
75
- ${W("\uD83E\uDDE0","Memory")}
78
+ ${S("\uD83E\uDDE0","Memory")}
76
79
  dun memory collections list
77
80
  dun memory collections create --name "..." [--color blue] [--private]
81
+ dun memory collections get <id>
82
+ dun memory collections update <id> [--name] [--color] [--private|--public]
83
+ dun memory collections delete <id> [--yes]
78
84
  dun memory list --collection <id>
79
85
  dun memory save --collection <id> --text "..."
80
86
  dun memory search --prompt "..." --collections <id,id> [--top-k 5]
81
87
  dun memory delete <id> --collection <id> [--yes]
82
88
 
83
- ${W("\uD83C\uDF99️","Personalization")}
89
+ ${S("\uD83C\uDF99️","Personalization")}
84
90
  # workspace AI voice
85
91
  dun personalization get
86
92
  dun personalization set --description "..."
@@ -88,21 +94,21 @@ ${W("\uD83C\uDF99️","Personalization")}
88
94
  dun personalization set --clear
89
95
  # requires PAT scope workspace:write for set (owner/admin)
90
96
 
91
- ${W("\uD83E\uDDE9","Agent skills")}
97
+ ${S("\uD83E\uDDE9","Agent skills")}
92
98
  dun skill install <agent|all>
93
99
  dun skill list
94
100
  dun skill path
95
101
  # agents: claude codex cursor pi opencode agents copilot windsurf
96
102
  # goose gemini amp grok continue antigravity
97
103
 
98
- ${W("⬆️","Update")}
104
+ ${S("⬆️","Update")}
99
105
  dun update # upgrade to latest from npm
100
106
  dun update --check # report only
101
107
  dun update --force # reinstall latest even if current
102
108
  # aliases: upgrade, self-update
103
109
  # detects npm / bun / pnpm / yarn; prints command for npx
104
110
 
105
- ${W("⚙️","Global flags")}
111
+ ${S("⚙️","Global flags")}
106
112
  --json Machine-readable output (default when non-TTY / CI)
107
113
  --workspace <id> Override workspace for this command
108
114
  --api-url <url> Override API base (default https://api.dunsocial.com)
@@ -113,14 +119,14 @@ ${W("⚙️","Global flags")}
113
119
  --help, -h Show help
114
120
  --version, -v Show version
115
121
 
116
- ${W("\uD83C\uDF31","Env")}
122
+ ${S("\uD83C\uDF31","Env")}
117
123
  DUN_TOKEN DUN_WORKSPACE_ID DUN_API_URL DUN_CONFIG DUN_JSON=1 DUN_DEBUG=1
118
124
 
119
- ${W("\uD83D\uDEAA","Exit codes")}
125
+ ${S("\uD83D\uDEAA","Exit codes")}
120
126
  0 ok | 1 usage | 2 auth | 3 forbidden | 4 validation | 5 not found | 6 rate limit | 7 network
121
127
 
122
128
  Docs: https://dunsocial.com/docs/cli
123
- `;function s(u){return{auth:`dun auth
129
+ `;function z(e){return{auth:`dun auth
124
130
 
125
131
  login [--token] Device-code browser login (default) or --token for CI
126
132
  logout Clear local config
@@ -153,6 +159,10 @@ Example:
153
159
  cancel <id>
154
160
  delete <id> [--yes]
155
161
  x-cap
162
+ schedule-thread --account --file thread.json (--at|--in)
163
+ publish-thread --account --file thread.json
164
+ # alias: publish-thread-now
165
+ # thread.json: [{"content":"1"},{"content":"2"}] or --text "t1||t2"
156
166
 
157
167
  Platform metadata (--meta / --metadata / --meta-json):
158
168
  Reddit (required when publishing to a Reddit account):
@@ -183,8 +193,14 @@ Local preflight before upload-url:
183
193
  images <= 8MB (jpeg/png/gif/webp)
184
194
  video <= 256MB (mp4/mov/webm)
185
195
  Oversized video error includes an ffmpeg compress one-liner.
186
- `,memory:`dun memory collections list|create
187
- dun memory list|save|search|delete`,personalization:`dun personalization
196
+ `,memory:`dun memory collections list|create|get|update|delete
197
+ dun memory list|save|search|delete
198
+
199
+ collections update <id> [--name] [--color] [--private|--public]
200
+ collections delete <id> [--yes]
201
+ # only the creator can update or delete
202
+ # default collections cannot be deleted or have visibility changed
203
+ `,personalization:`dun personalization
188
204
 
189
205
  get Show workspace AI voice profile
190
206
  set --description "..." Set voice from a paragraph
@@ -202,43 +218,43 @@ dun skill path`,update:`dun update
202
218
  --force Reinstall latest even if versions match
203
219
 
204
220
  Aliases: dun upgrade, dun self-update
205
- `,upgrade:"Alias of update","self-update":"Alias of update"}[u]??g}var au=["DunSocial","---------"].join(`
206
- `),Ou="DunSocial";var xd=yu(au),ad=yu(Ou);function yu(u){return Math.max(...u.split(`
207
- `).map((w)=>w.length))}function H(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 zu(){return`DunSocial CLI v${T}`}function qu(){return`${Ou}
208
- CLI v${T}`}var lu="https://dunsocial.com/docs/cli";function tu(u){if(!u.token)return"signed_out";if(!u.workspaceId)return"needs_workspace";return"ready"}function Xu(u){let w=tu(u),d=[`DunSocial CLI v${T}`,""];if(w==="signed_out")d.push("Not signed in.",""," Next: dun auth login");else if(w==="needs_workspace")d.push("Signed in — pick a workspace.",""," Next: dun workspace list"," dun workspace use <slug>");else{let D=u.config.defaultWorkspaceSlug||u.config.defaultWorkspaceName||u.workspaceId||"workspace";d.push(`Ready | workspace: ${D}`,""," Tip: dun accounts list"," dun posts list")}return d.push(""," dun --help all commands",` Docs ${lu}`,""),d.join(`
209
- `)}import{spawn as ud}from"node:child_process";class Zu{ctx;constructor(u){this.ctx=u}async request(u){let w=u.method??"GET",d=new URL(u.path.startsWith("http")?u.path:`${this.ctx.apiUrl}${u.path}`);if(u.query)for(let[q,X]of Object.entries(u.query)){if(X===void 0||X===null||X==="")continue;d.searchParams.set(q,String(X))}let D={Accept:"application/json","User-Agent":`dunsocial-cli/${T}`,...u.headers??{}};if(u.auth!==!1)D.Authorization=`Bearer ${Y(this.ctx)}`;if(u.workspace)D["X-Workspace-Id"]=F(this.ctx);let y;if(u.body!==void 0)D["Content-Type"]="application/json",y=JSON.stringify(u.body);P(this.ctx.debug,`${w} ${d.toString()}`);let m;try{m=await fetch(d,{method:w,headers:D,body:y})}catch(q){throw new z(`Network error: ${q.message}`,O.NETWORK,{cause:String(q)})}let U=await m.text(),$=null;if(U)try{$=JSON.parse(U)}catch{$=null}if(P(this.ctx.debug,`<- ${m.status}`,{ok:m.ok,bodyPreview:U.slice(0,300)}),!m.ok){let q=$?.error||$?.message||(U?U.slice(0,300):`HTTP ${m.status}`);throw new z(q,uu(m.status),{status:m.status,body:$??U})}if($&&typeof $==="object"&&"success"in $){if($.success===!1)throw new z($.error||$.message||"Request failed",O.VALIDATION,$);return $.data}return $??void 0}get(u,w){return this.request({...w,method:"GET",path:u})}post(u,w,d){return this.request({...d,method:"POST",path:u,body:w})}patch(u,w,d){return this.request({...d,method:"PATCH",path:u,body:w})}delete(u,w,d){return this.request({...d,method:"DELETE",path:u,body:w})}}function b(u){return new Zu(u)}async function ku(u,w,d,D){switch(w){case"login":return dd(u,d);case"logout":return $d(u);case"whoami":return Ud(u);case"status":return Dd(u);default:throw new z(`Unknown auth command "${w??""}". Try: login, logout, whoami, status`,O.USAGE)}}async function dd(u,w){let d=V(w,"token")||process.env.DUN_TOKEN;if(d)return wd(u,d);return md(u,w)}function v(u){if(u)return"Logged in.";return`Logged in.
210
- Next: dun workspace list && dun workspace use <id|slug>`}async function wd(u,w){let d={...u,token:w},y=await b(d).get("/api/user/profile");if(await j({...u.config,apiUrl:u.apiUrl,token:w,defaultWorkspaceId:u.config.defaultWorkspaceId,defaultWorkspaceSlug:u.config.defaultWorkspaceSlug,defaultWorkspaceName:u.config.defaultWorkspaceName}),!u.json&&H(process.stdout)){k(v(Boolean(u.config.defaultWorkspaceId)),u);return}k({user:{id:y.id,name:y.name,email:y.email},configPath:_(),method:"token",message:v(Boolean(u.config.defaultWorkspaceId))},u)}async function md(u,w){let d=b({...u,token:void 0}),D=await d.post("/api/cli/auth/start",{clientName:"dun-cli"},{auth:!1}),y=!R(w,"no-browser");if(!u.json){if(H(process.stderr))process.stderr.write(`
211
- ${qu()}
221
+ `,upgrade:"Alias of update","self-update":"Alias of update"}[e]??H}var Ze=["DunSocial","---------"].join(`
222
+ `),ce="DunSocial";var Xt=de(Ze),Zt=de(ce);function de(e){return Math.max(...e.split(`
223
+ `).map((n)=>n.length))}function U(e=process.stderr){if(!e.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 le(){return`DunSocial CLI v${N}`}function ue(){return`${ce}
224
+ CLI v${N}`}var Qe="https://dunsocial.com/docs/cli";function et(e){if(!e.token)return"signed_out";if(!e.workspaceId)return"needs_workspace";return"ready"}function pe(e){let n=et(e),t=[`DunSocial CLI v${N}`,""];if(n==="signed_out")t.push("Not signed in.",""," Next: dun auth login");else if(n==="needs_workspace")t.push("Signed in — pick a workspace.",""," Next: dun workspace list"," dun workspace use <slug>");else{let s=e.config.defaultWorkspaceSlug||e.config.defaultWorkspaceName||e.workspaceId||"workspace";t.push(`Ready | workspace: ${s}`,""," Tip: dun accounts list"," dun posts list")}return t.push(""," dun --help all commands",` Docs ${Qe}`,""),t.join(`
225
+ `)}import{spawn as tt}from"node:child_process";class me{ctx;constructor(e){this.ctx=e}async request(e){let n=e.method??"GET",t=new URL(e.path.startsWith("http")?e.path:`${this.ctx.apiUrl}${e.path}`);if(e.query)for(let[l,u]of Object.entries(e.query)){if(u===void 0||u===null||u==="")continue;t.searchParams.set(l,String(u))}let s={Accept:"application/json","User-Agent":`dunsocial-cli/${N}`,...e.headers??{}};if(e.auth!==!1)s.Authorization=`Bearer ${I(this.ctx)}`;if(e.workspace)s["X-Workspace-Id"]=A(this.ctx);let c;if(e.body!==void 0)s["Content-Type"]="application/json",c=JSON.stringify(e.body);R(this.ctx.debug,`${n} ${t.toString()}`);let r;try{r=await fetch(t,{method:n,headers:s,body:c})}catch(l){throw new d(`Network error: ${l.message}`,a.NETWORK,{cause:String(l)})}let o=await r.text(),i=null;if(o)try{i=JSON.parse(o)}catch{i=null}if(R(this.ctx.debug,`<- ${r.status}`,{ok:r.ok,bodyPreview:o.slice(0,300)}),!r.ok){let l=i?.error||i?.message||(o?o.slice(0,300):`HTTP ${r.status}`);throw new d(l,te(r.status),{status:r.status,body:i??o})}if(i&&typeof i==="object"&&"success"in i){if(i.success===!1)throw new d(i.error||i.message||"Request failed",a.VALIDATION,i);return i.data}return i??void 0}get(e,n){return this.request({...n,method:"GET",path:e})}post(e,n,t){return this.request({...t,method:"POST",path:e,body:n})}patch(e,n,t){return this.request({...t,method:"PATCH",path:e,body:n})}delete(e,n,t){return this.request({...t,method:"DELETE",path:e,body:n})}}function y(e){return new me(e)}async function fe(e,n,t,s){switch(n){case"login":return nt(e,t);case"logout":return ot(e);case"whoami":return st(e);case"status":return at(e);default:throw new d(`Unknown auth command "${n??""}". Try: login, logout, whoami, status`,a.USAGE)}}async function nt(e,n){let t=f(n,"token")||process.env.DUN_TOKEN;if(t)return rt(e,t);return it(e,n)}function P(e){if(e)return"Logged in.";return`Logged in.
226
+ Next: dun workspace list && dun workspace use <id|slug>`}async function rt(e,n){let t={...e,token:n},c=await y(t).get("/api/user/profile");if(await D({...e.config,apiUrl:e.apiUrl,token:n,defaultWorkspaceId:e.config.defaultWorkspaceId,defaultWorkspaceSlug:e.config.defaultWorkspaceSlug,defaultWorkspaceName:e.config.defaultWorkspaceName}),!e.json&&U(process.stdout)){m(P(Boolean(e.config.defaultWorkspaceId)),e);return}m({user:{id:c.id,name:c.name,email:c.email},configPath:L(),method:"token",message:P(Boolean(e.config.defaultWorkspaceId))},e)}async function it(e,n){let t=y({...e,token:void 0}),s=await t.post("/api/cli/auth/start",{clientName:"dun-cli"},{auth:!1}),c=!h(n,"no-browser");if(!e.json){if(U(process.stderr))process.stderr.write(`
227
+ ${ue()}
212
228
 
213
229
  `);else process.stderr.write(`
214
230
  DunSocial CLI login
215
231
  `),process.stderr.write(`───────────────────
216
232
  `);process.stderr.write(`In your browser, open:
217
- ${D.verificationUrlComplete}
233
+ ${s.verificationUrlComplete}
218
234
 
219
- `),process.stderr.write(`Or go to ${D.verificationUrl} and enter code:
220
- `),process.stderr.write(` ${D.userCode}
235
+ `),process.stderr.write(`Or go to ${s.verificationUrl} and enter code:
236
+ `),process.stderr.write(` ${s.userCode}
221
237
 
222
238
  `),process.stderr.write(`Waiting for approval...
223
- `)}else process.stderr.write(JSON.stringify({ok:!0,phase:"waiting",userCode:D.userCode,verificationUrlComplete:D.verificationUrlComplete})+`
224
- `);if(y&&process.stdout.isTTY)try{await yd(D.verificationUrlComplete)}catch{}let m=Math.max(3,D.interval||5)*1000,U=Date.now()+(D.expiresIn||900)*1000;while(Date.now()<U){await Od(m);let $=await d.post("/api/cli/auth/poll",{deviceCode:D.deviceCode},{auth:!1});if($.status==="pending")continue;if($.status==="expired")throw new z("Login expired. Run dun auth login again.",O.AUTH);if($.status==="denied")throw new z("Login was denied in the browser.",O.AUTH);if($.status==="approved"){let q=$.token,X={...u,token:q},Z=b(X),Q=await Z.get("/api/user/profile"),J={...u.config,apiUrl:u.apiUrl,token:q,defaultWorkspaceId:$.workspaceId||u.config.defaultWorkspaceId,defaultWorkspaceSlug:u.config.defaultWorkspaceSlug,defaultWorkspaceName:u.config.defaultWorkspaceName};if($.workspaceId)try{let A=(await Z.get("/api/workspaces")).find((vu)=>vu.id===$.workspaceId);if(A)J.defaultWorkspaceId=A.id,J.defaultWorkspaceSlug=A.slug,J.defaultWorkspaceName=A.name}catch{}await j(J);let G=Boolean(J.defaultWorkspaceId);if(!u.json&&H(process.stdout)){k(v(G),u);return}k({user:{id:Q.id,name:Q.name,email:Q.email},workspaceId:J.defaultWorkspaceId??null,configPath:_(),method:"device",message:v(G)},u);return}}throw new z("Login timed out. Run dun auth login again.",O.AUTH)}async function $d(u){if(await wu(),!u.json&&H(process.stdout)){k("Logged out. Config cleared.",u);return}k({message:"Logged out. Config cleared.",configPath:_()},u)}async function Ud(u){Y(u);let d=await b(u).get("/api/user/profile");k({id:d.id,name:d.name,email:d.email,image:d.image??null,apiUrl:u.apiUrl,workspaceId:u.workspaceId??null},u)}async function Dd(u){let w=Boolean(u.token),d=null,D=null;if(w)try{d=await b(u).get("/api/user/profile")}catch(y){D=y instanceof Error?y.message:String(y)}k({authenticated:Boolean(d),tokenPresent:w,apiUrl:u.apiUrl,workspaceId:u.workspaceId??null,workspaceSlug:u.config.defaultWorkspaceSlug??null,workspaceName:u.config.defaultWorkspaceName??null,configPath:_(),user:d?{id:d.id,name:d.name,email:d.email}:null,error:D},u)}function Od(u){return new Promise((w)=>setTimeout(w,u))}async function yd(u){let w=process.platform,d=w==="darwin"?["open",u]:w==="win32"?["cmd","/c","start","",u]:["xdg-open",u];await new Promise((D,y)=>{let m=ud(d[0],d.slice(1),{stdio:"ignore",windowsHide:!0});m.on("error",y),m.on("close",()=>D())})}async function Vu(u,w,d,D){Y(u);let y=b(u);switch(w){case"list":case"ls":{let m=await y.get("/api/workspaces");k(m.map((U)=>({id:U.id,name:U.name,slug:U.slug,role:U.role,timezone:U.timezone,plan:U.subscription?.planName??null,status:U.subscription?.status??null})),{...u,emptyMessage:"No workspaces yet."});return}case"use":{let m=D[0];if(!m)throw new z("Usage: dun workspace use <id|slug>",O.USAGE);let $=(await y.get("/api/workspaces")).find((q)=>q.id===m||q.slug===m);if(!$)throw new z(`Workspace not found: ${m}. Run dun workspace list`,O.NOT_FOUND);await j({...u.config,apiUrl:u.apiUrl,token:u.token,defaultWorkspaceId:$.id,defaultWorkspaceSlug:$.slug,defaultWorkspaceName:$.name}),k({id:$.id,name:$.name,slug:$.slug,role:$.role,message:`Default workspace set to ${$.name}`},u);return}case"current":{if(!u.workspaceId&&!u.config.defaultWorkspaceId)throw new z("No workspace selected. Run dun workspace use <id|slug>",O.VALIDATION);let m=u.workspaceId||u.config.defaultWorkspaceId;try{let $=(await y.get("/api/workspaces")).find((q)=>q.id===m||q.slug===m);if($){k({id:$.id,name:$.name,slug:$.slug,role:$.role,timezone:$.timezone},u);return}}catch{}k({id:m,name:u.config.defaultWorkspaceName??null,slug:u.config.defaultWorkspaceSlug??null},u);return}default:throw new z(`Unknown workspace command "${w??""}". Try: list, use, current`,O.USAGE)}}async function Qu(u,w,d,D){Y(u),F(u);let y=b(u);switch(w){case void 0:case"list":case"ls":{let m=await y.get("/api/social-accounts",{workspace:!0}),U=V(d,"platform","p")?.toLowerCase(),$=U?m.filter((q)=>{let X=(q.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"],tiktok:["tiktok"]}[U]??[U]).some((J)=>X.includes(J))}):m;k($.map((q)=>({id:q.id,providerName:q.providerName,username:q.username??null,displayName:q.displayName??null,isConnected:q.isConnected,expiresAt:q.expiresAt??null})),{...u,emptyMessage:U?`No ${U} accounts connected. Connect one in the DunSocial dashboard.`:"No accounts connected. Connect one in the DunSocial dashboard."});return}default:throw new z(`Unknown accounts command "${w}". Try: list (or: dun accounts reddit flairs <subreddit>)`,O.USAGE)}}import{readFileSync as Xd}from"node:fs";var zd=/^(\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 r(u,w=new Date){let d=u.trim();if(!d)throw new z("Empty time value",O.VALIDATION);let D=d.match(zd);if(D){let U=Number(D[1]),$=D[2].toLowerCase(),q=qd($)*U;return new Date(w.getTime()+q)}let y=d.match(/^in\s+(.+)$/i);if(y)return r(y[1],w);let m=new Date(d);if(Number.isNaN(m.getTime()))throw new z(`Invalid time "${u}". Use ISO-8601 (2026-06-01T09:00:00Z) or relative (2h, 30m, 1d).`,O.VALIDATION);return m}function qd(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 z(`Unknown time unit: ${u}`,O.VALIDATION)}}function e(u){if(u.at&&u.in)throw new z("Pass only one of --at or --in",O.VALIDATION);if(u.at)return r(u.at).toISOString();if(u.in)return r(u.in).toISOString();throw new z("Schedule time required. Pass --at <iso> or --in <duration> (e.g. 2h).",O.VALIDATION)}function Ru(u){if(!u)return[];return u.split(",").map((w)=>w.trim()).filter(Boolean)}function Ju(u,w){let d=V(u,"text","content","c","t");if(d)return d;let D=w?.reddit;if(D&&typeof D==="object"&&!Array.isArray(D)&&typeof D.title==="string"&&D.title.trim())return D.title.trim();throw new z("Missing --text",O.VALIDATION)}function S(u){let w=I(u,"accounts","account","a")??Ru(V(u,"accounts","account","a"));if(!w||w.length===0)throw new z("Missing --accounts <id,id>",O.VALIDATION);return w}function h(u){return I(u,"media","media-urls")??Ru(V(u,"media","media-urls"))}function N(u){let w=V(u,"meta","metadata","meta-json");if(!w)return;try{let d=JSON.parse(w);if(!d||typeof d!=="object"||Array.isArray(d))throw Error("metadata must be a JSON object");return d}catch(d){throw new z(`Invalid --meta JSON: ${d.message}`,O.VALIDATION)}}var Zd=new Set(["published","failed","cancelled"]);async function kd(u){await new Promise((w)=>setTimeout(w,u))}async function Vd(u,w,d,D){let y=Date.now()+D*1000,m=new Map,U=1500;while(m.size<d.length){if(Date.now()>y){let X=d.filter((Z)=>!m.has(Z.id)).map((Z)=>Z.id);throw new z(`Timed out waiting for publish after ${D}s. Still pending: ${X.join(", ")}. Poll with: dun posts get <id> --json`,O.NETWORK,{pending:X})}for(let X of d){if(m.has(X.id))continue;let Z=await u.get(`/api/posts/${X.id}`,{workspace:!0});if(P(w.debug,`wait status ${X.id}=${Z.status}`),Zd.has(Z.status))m.set(X.id,Z)}if(m.size>=d.length)break;await kd(U),U=Math.min(U*1.4,8000)}let $=d.map((X)=>m.get(X.id)),q=$.filter((X)=>X.status==="failed");if(q.length>0){let X=q.map((Z)=>`${Z.id}: ${Z.error||"publish failed"}`).join("; ");throw new z(`Publish failed — ${X}`,O.VALIDATION,{posts:$})}return $}async function Qd(u,w){let d=[],D=w.meta?.reddit??null;if(!D||typeof D!=="object")throw new z("Reddit accounts require --meta with a reddit object, e.g. "+`--meta '{"reddit":{"subreddit":"…","kind":"self","title":"…","flairId":"…"}}'`,O.VALIDATION);let y=(D.subreddit??"").trim().replace(/^\/?r\//i,""),m=(D.title??"").trim(),U=(D.kind??"").trim(),$=(D.flairId??"").trim(),q=(D.flairText??"").trim();if(!y)throw new z("metadata.reddit.subreddit is required",O.VALIDATION);if(!m)throw new z("metadata.reddit.title is required",O.VALIDATION);if(m.length>300)throw new z(`Reddit title exceeds 300 characters: ${m.length}/300`,O.VALIDATION);if(!["self","link","image","video"].includes(U))throw new z("metadata.reddit.kind must be one of: self, link, image, video",O.VALIDATION);if(d.push(`kind=${U}`),d.push(`titleLength=${m.length}`),q&&!$)throw new z(`Can't set flairText without flairId for r/${y}. Run: dun reddit flairs ${y} --json`,O.VALIDATION);if(U==="link"){if(!D.url?.trim())throw new z("Reddit link posts require metadata.reddit.url",O.VALIDATION);try{new URL(D.url)}catch{throw new z("metadata.reddit.url is invalid",O.VALIDATION)}if(w.media.length>0)throw new z("Reddit link posts do not accept --media",O.VALIDATION)}else if(U==="image"||U==="video"){if(w.media.length!==1)throw new z(`Reddit ${U} posts require exactly one --media asset id`,O.VALIDATION)}else if(w.media.length>0)throw new z("Reddit self posts do not accept --media. Use kind image or video.",O.VALIDATION);let X=await u.get("/api/social-accounts/reddit/post-requirements",{workspace:!0,query:{subreddit:y}});if(d.push("fetched post-requirements"),X?.is_flair_required&&!$)throw new z(`r/${y} requires post flair. Discover ids with: dun reddit flairs ${y} --json`,O.VALIDATION);let Z=X?.title_text_max_length??300;if(m.length>Z)throw new z(`Reddit title too long for r/${y}: ${m.length}/${Z}`,O.VALIDATION);if(X?.title_text_min_length&&m.length<X.title_text_min_length)throw new z(`Reddit title must be at least ${X.title_text_min_length} characters`,O.VALIDATION);if($){let Q=await u.get("/api/social-accounts/reddit/flair-options",{workspace:!0,query:{subreddit:y}});d.push(`fetched ${Q.choices?.length??0} flairs`);let J=(Q.choices??[]).find((G)=>G.id===$);if(!J)throw new z(`Unknown flairId "${$}" for r/${y}. Run: dun reddit flairs ${y} --json`,O.VALIDATION);if(J.textEditable===!1&&q&&q!==J.text)throw new z(`Customization not allowed for this flair template. Use flairId "${J.id}" alone`+(J.text?` (template text: "${J.text}")`:""),O.VALIDATION);d.push(`flairId=${J.id}`)}return{ok:!0,checks:d,reddit:{subreddit:y,kind:U,title:m,flairId:$||null,accounts:w.accounts,media:w.media,textPreview:w.text?.slice(0,120)??null,requirements:{is_flair_required:X?.is_flair_required??!1,title_text_max_length:Z}}}}async function Gu(u,w,d,D){Y(u),F(u);let y=b(u);switch(w){case"list":case"ls":{let m=await y.get("/api/posts",{workspace:!0,query:{status:V(d,"status","s"),socialAccountId:V(d,"account","social-account"),groupId:V(d,"group"),limit:B(d,"limit")??50,offset:B(d,"offset")??0}});k(m,{...u,emptyMessage:"No posts yet. Schedule one with dun posts schedule."});return}case"get":{let m=D[0];if(!m)throw new z("Usage: dun posts get <id>",O.USAGE);let U=await y.get(`/api/posts/${m}`,{workspace:!0});k(U,u);return}case"validate":{let m=S(d),U=h(d),$=N(d)??{},q=V(d,"text","content","c","t"),X=Boolean($.reddit),Z=X;if(!X){let J=await y.get("/api/social-accounts",{workspace:!0});Z=m.some((G)=>{return(J.find((A)=>A.id===G)?.providerName||"").toLowerCase().includes("reddit")})}if(!Z){k({ok:!0,checks:["no reddit-specific rules for selected accounts"],accounts:m,media:U},u);return}let Q=await Qd(y,{accounts:m,text:q,media:U,meta:$});k(Q,u);return}case"schedule":{let m=N(d)??{},U=Ju(d,m),$=S(d),q=e({at:V(d,"at"),in:V(d,"in")}),X=await y.post("/api/posts/schedule",{content:U,socialAccountIds:$,scheduledAt:q,mediaUrls:h(d),metadata:m},{workspace:!0});k(X,u);return}case"publish":case"publish-now":{let m=N(d)??{},U=Ju(d,m),$=S(d),q=await y.post("/api/posts/publish-now",{content:U,socialAccountIds:$,mediaUrls:h(d),metadata:m,naturalPosting:R(d,"natural","natural-posting")},{workspace:!0});if(!R(d,"wait")){k(q,u);return}let Z=Array.isArray(q)?q:[];if(Z.length===0||!Z.every((G)=>typeof G?.id==="string"))throw new z("publish --wait expected an array of created posts with ids",O.NETWORK,{data:q});let Q=B(d,"wait-timeout","timeout")??120,J=await Vd(y,u,Z,Q);k({queued:Z,posts:J,status:J.every((G)=>G.status==="published")?"published":J[0]?.status},u);return}case"reschedule":{let m=D[0];if(!m)throw new z("Usage: dun posts reschedule <id> --at|--in",O.USAGE);let $={scheduledAt:e({at:V(d,"at"),in:V(d,"in")})},q=V(d,"text","content","c","t");if(q)$.content=q;let X=h(d);if(X.length)$.mediaUrls=X;let Z=N(d);if(Z)$.metadata=Z;if(!$.content){let J=await y.get(`/api/posts/${m}`,{workspace:!0});if($.content=J.content,!$.metadata&&J.metadata)$.metadata=J.metadata;if(!$.mediaUrls&&J.mediaUrls)$.mediaUrls=J.mediaUrls}let Q=await y.post(`/api/posts/${m}/reschedule`,$,{workspace:!0});k(Q,u);return}case"cancel":{let m=D[0];if(!m)throw new z("Usage: dun posts cancel <id>",O.USAGE);let U=await y.post(`/api/posts/${m}/cancel`,void 0,{workspace:!0});k(U??{id:m,status:"cancelled"},u);return}case"delete":case"rm":{let m=D[0];if(!m)throw new z("Usage: dun posts delete <id> [--yes]",O.USAGE);if(!u.yes&&!R(d,"yes","y"))throw new z("Refusing to delete without --yes (or CI/yes mode)",O.VALIDATION);let U=await y.delete(`/api/posts/${m}`,void 0,{workspace:!0});k(U??{id:m,deleted:!0},u);return}case"x-cap":case"xcap":{let m=await y.get("/api/posts/x-cap-usage",{workspace:!0});k(m,u);return}case"schedule-thread":case"publish-thread":case"publish-thread-now":{let m=V(d,"file","f"),U=V(d,"account","accounts","a")??S(d)[0];if(!U)throw new z("Missing --account <xAccountId>",O.VALIDATION);let $;if(m){let X;try{X=Xd(m,"utf8")}catch(Q){throw new z(`Could not read file: ${Q.message}`,O.VALIDATION)}let Z=JSON.parse(X);if(Array.isArray(Z))$=Z;else if(Z&&typeof Z==="object"&&Array.isArray(Z.tweets))$=Z.tweets;else throw new z("Thread file must be an array of tweets or { tweets: [] }",O.VALIDATION)}else{let X=V(d,"text","content");if(!X)throw new z("Provide --file thread.json (array of {content}) or informal --text with || separators is not enough alone for threads - use --file",O.VALIDATION);$=X.split("||").map((Z)=>({content:Z.trim()})).filter((Z)=>Z.content)}if($.length<2)throw new z("Threads require at least 2 tweets",O.VALIDATION);if(w==="schedule-thread"){let X=e({at:V(d,"at"),in:V(d,"in")}),Z=await y.post("/api/posts/schedule-thread",{socialAccountId:U,tweets:$,scheduledAt:X,metadata:N(d)??{}},{workspace:!0});k(Z,u);return}let q=await y.post("/api/posts/publish-thread-now",{socialAccountId:U,tweets:$,metadata:N(d)??{}},{workspace:!0});k(q,u);return}default:throw new z(`Unknown posts command "${w??""}". Try: list, get, validate, schedule, publish, reschedule, cancel, delete, x-cap, schedule-thread, publish-thread`,O.USAGE)}}async function bu(u,w,d,D){Y(u),F(u);let y=b(u);switch(w){case"list":case"ls":{let m=await y.get("/api/drafts",{workspace:!0,query:{limit:B(d,"limit")??50,offset:B(d,"offset")??0}});k(m,{...u,emptyMessage:'No drafts yet. Create one with dun drafts create --text "..."'});return}case"get":{let m=D[0];if(!m)throw new z("Usage: dun drafts get <id>",O.USAGE);let U=await y.get(`/api/drafts/${m}`,{workspace:!0});k(U,u);return}case"create":{let m=V(d,"text","content","c","t");if(!m)throw new z("Missing --text",O.VALIDATION);let U=await y.post("/api/drafts",{content:m,name:V(d,"name","n"),socialAccountIds:I(d,"accounts","account","a")??[],selectedPlatforms:I(d,"platforms","platform","p")??[],mediaUrls:I(d,"media")??[]},{workspace:!0});k(U,u);return}case"update":{let m=D[0];if(!m)throw new z("Usage: dun drafts update <id> --text ...",O.USAGE);let U={id:m},$=V(d,"text","content","c","t");if($)U.content=$;let q=V(d,"name","n");if(q!==void 0)U.name=q;let X=I(d,"accounts","account","a");if(X)U.socialAccountIds=X;let Z=I(d,"platforms","platform","p");if(Z)U.selectedPlatforms=Z;let Q=I(d,"media");if(Q)U.mediaUrls=Q;let J=await y.patch(`/api/drafts/${m}`,U,{workspace:!0});k(J,u);return}case"delete":case"rm":{let m=D[0];if(!m)throw new z("Usage: dun drafts delete <id> [--yes]",O.USAGE);if(!u.yes&&!R(d,"yes","y"))throw new z("Refusing to delete without --yes",O.VALIDATION);let U=await y.delete(`/api/drafts/${m}`,void 0,{workspace:!0});k(U??{id:m,deleted:!0},u);return}default:throw new z(`Unknown drafts command "${w??""}". Try: list, get, create, update, delete`,O.USAGE)}}import{readFileSync as Jd,existsSync as Rd,statSync as Gd}from"node:fs";import{basename as bd}from"node:path";var Yu=["image/jpeg","image/png","image/gif","image/webp"],nu=["video/mp4","video/quicktime","video/webm"];function Fu(u){if(u<1024)return`${u}B`;if(u<1048576)return`${(u/1024).toFixed(1)}KB`;return`${(u/1048576).toFixed(1)}MB`}function Wu(u){let{mimeType:w,fileSize:d}=u,D=Yu.includes(w),y=nu.includes(w);if(!D&&!y)return{ok:!1,message:`Unsupported MIME type "${w}". Allowed images: ${Yu.join(", ")}; videos: ${nu.join(", ")}`};if(y&&d>268435456)return{ok:!1,message:`Video files must be less than 256MB (got ${Fu(d)}). Compress first, e.g.:
225
- ffmpeg -i input.mp4 -vf scale=-2:1080 -c:v libx264 -crf 23 -c:a aac output.mp4`};if(D&&d>8388608)return{ok:!1,message:`Image files must be less than 8MB (got ${Fu(d)}).`};return{ok:!0}}function Yd(u){let w=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"}[w]??"application/octet-stream"}async function Ku(u,w,d,D){Y(u),F(u);let y=b(u);switch(w){case"list":case"ls":{let m=await y.get("/api/media",{workspace:!0,query:{limit:B(d,"limit")??50,offset:B(d,"offset")??0}});k(m,{...u,emptyMessage:"No media yet. Upload with dun media upload <file>."});return}case"get":{let m=D[0];if(!m)throw new z("Usage: dun media get <id>",O.USAGE);let U=await y.get(`/api/media/${m}`,{workspace:!0});k(U,u);return}case"upload":{let m=D[0]||V(d,"file","f");if(!m)throw new z('Usage: dun media upload <file> [--alt "..."]',O.USAGE);if(!Rd(m))throw new z(`File not found: ${m}`,O.VALIDATION);let U=bd(m),$=Gd(m).size,q=V(d,"mime","type","content-type")||Yd(U),X=V(d,"alt","alt-text"),Z=Wu({mimeType:q,fileSize:$});if(!Z.ok)throw new z(Z.message,O.VALIDATION);let Q=await y.post("/api/media/upload-url",{filename:U,mimeType:q,fileSize:$},{workspace:!0});P(u.debug,`PUT ${Q.uploadUrl}`,{storageKey:Q.storageKey,fileSize:$});let J=Jd(m),G;try{G=await fetch(Q.uploadUrl,{method:"PUT",headers:{"Content-Type":q,"Content-Length":String($)},body:J})}catch(A){throw new z(`Upload PUT failed: ${A.message}`,O.NETWORK)}if(!G.ok){let A=await G.text().catch(()=>"");throw new z(`Upload PUT failed with HTTP ${G.status}${A?`: ${A.slice(0,200)}`:""}`,O.NETWORK)}let M=await y.post("/api/media/complete",{storageKey:Q.storageKey,originalFilename:U,mimeType:q,fileSize:$,altText:X},{workspace:!0});k(M,u);return}case"delete":case"rm":{let m=D[0];if(!m)throw new z("Usage: dun media delete <id> [--yes]",O.USAGE);if(!u.yes&&!R(d,"yes","y"))throw new z("Refusing to delete without --yes",O.VALIDATION);let U=await y.delete(`/api/media/${m}`,void 0,{workspace:!0});k(U??{id:m,deleted:!0},u);return}default:throw new z(`Unknown media command "${w??""}". Try: list, get, upload, delete`,O.USAGE)}}async function Tu(u,w,d,D){Y(u),F(u);let y=b(u),m=w[0];if(m==="collections"||m==="collection"){let U=w[1]||"list";switch(U){case"list":case"ls":{let $=await y.get("/api/memory/collections",{workspace:!0});k($,{...u,emptyMessage:'No memory collections yet. Create one with dun memory collections create --name "..."'});return}case"create":{let $=V(d,"name","n");if(!$)throw new z("Missing --name",O.VALIDATION);let q=await y.post("/api/memory/collections",{name:$,color:V(d,"color")??"blue",isPrivate:R(d,"private")},{workspace:!0});k(q,u);return}case"get":{let $=w[2]||D[0];if(!$)throw new z("Usage: dun memory collections get <id>",O.USAGE);let q=await y.get(`/api/memory/collections/${$}`,{workspace:!0});k(q,u);return}default:throw new z(`Unknown memory collections command "${U}". Try: list, create, get`,O.USAGE)}}switch(m){case"list":case"ls":{let U=V(d,"collection","collection-id","c");if(!U)throw new z("Missing --collection <id>",O.VALIDATION);let $=await y.get("/api/memory",{workspace:!0,query:{collectionId:U,limit:B(d,"limit")??100}});k($,{...u,emptyMessage:"No memories in this collection."});return}case"save":case"add":{let U=V(d,"collection","collection-id","c"),$=V(d,"text","t");if(!U)throw new z("Missing --collection <id>",O.VALIDATION);if(!$)throw new z("Missing --text",O.VALIDATION);let q=await y.post("/api/memory",{collectionId:U,text:$},{workspace:!0});k(q,u);return}case"search":{let U=V(d,"prompt","q","query","text"),$=I(d,"collections","collection","collection-ids","c")??[];if(!U)throw new z("Missing --prompt",O.VALIDATION);if($.length===0)throw new z("Missing --collections <id,id>",O.VALIDATION);let q=await y.post("/api/memory/search",{prompt:U,collectionIds:$,topK:B(d,"top-k","topk","k")??5},{workspace:!0});k(q,u);return}case"delete":case"rm":{let U=w[1]||D[0],$=V(d,"collection","collection-id","c");if(!U)throw new z("Usage: dun memory delete <id> --collection <id>",O.USAGE);if(!$)throw new z("Missing --collection <id>",O.VALIDATION);if(!u.yes&&!R(d,"yes","y"))throw new z("Refusing to delete without --yes",O.VALIDATION);let q=await y.delete(`/api/memory/${U}`,{collectionId:$,memoryId:U},{workspace:!0});k(q??{id:U,deleted:!0},u);return}default:throw new z(`Unknown memory command "${m??""}". Try: collections, list, save, search, delete`,O.USAGE)}}import{readFileSync as nd}from"node:fs";async function Bu(u,w,d,D){Y(u);let y=F(u),m=b(u),U=`/api/workspaces/${y}/ai-voice-settings`;switch(w){case"get":case"show":case void 0:{let $=await m.get(U,{workspace:!0});k($??{personalization:null,hint:'No personalization set. Use dun personalization set --description "..."'},u);return}case"set":case"update":{let $=R(d,"clear"),q=V(d,"description","d"),X=V(d,"file","f");if([$,Boolean(q),Boolean(X)].filter(Boolean).length!==1)throw new z("Use exactly one of --description, --file, or --clear",O.VALIDATION);let Q;if($)Q=null;else if(X){let G;try{G=nd(X,"utf8").trim()}catch(M){throw new z(`Could not read file: ${M.message}`,O.VALIDATION)}if(!G)throw new z("File is empty",O.VALIDATION);Q={description:G}}else{let G=q.trim();if(!G)throw new z("--description cannot be empty",O.VALIDATION);Q={description:G}}let J=await m.patch(U,Q,{workspace:!0});k(J,u);return}default:throw new z(`Unknown personalization command "${w}". Try: get, set`,O.USAGE)}}function Iu(u){return u.trim().replace(/^\/?r\//i,"").replace(/^@/,"")}async function c(u,w,d,D){Y(u),F(u);let y=b(u);switch(w){case"flairs":case"flair":case"flair-options":{let m=D[0]||V(d,"subreddit","sub","r");if(!m)throw new z(`Usage: dun reddit flairs <subreddit>
226
- Example: dun reddit flairs tamilyapping --json`,O.USAGE);let U=Iu(m),q=((await y.get("/api/social-accounts/reddit/flair-options",{workspace:!0,query:{subreddit:U}})).choices??[]).map((X)=>({id:X.id,text:X.text,textEditable:X.textEditable!==!1}));k({subreddit:U,choices:q},{...u,emptyMessage:`No flairs returned for r/${U}. The subreddit may not use post flair.`});return}case"requirements":case"post-requirements":{let m=D[0]||V(d,"subreddit","sub","r");if(!m)throw new z("Usage: dun reddit requirements <subreddit>",O.USAGE);let U=Iu(m),$=await y.get("/api/social-accounts/reddit/post-requirements",{workspace:!0,query:{subreddit:U}});k({subreddit:U,requirements:$},u);return}default:throw new z(`Unknown reddit command "${w??""}". Try: flairs, requirements`,O.USAGE)}}import{existsSync as Fd}from"node:fs";import{mkdir as Pu,writeFile as Nu,readFile as Mu}from"node:fs/promises";import{dirname as l,join as n}from"node:path";import{homedir as t}from"node:os";import{fileURLToPath as Wd}from"node:url";var p=[{id:"claude",label:"Claude Code",aliases:["claude-code"],dest:(u)=>n(u,".claude","skills","dunsocial","SKILL.md")},{id:"codex",label:"Codex",dest:(u)=>n(u,".codex","skills","dunsocial","SKILL.md"),afterInstall:async(u)=>{return{agentsPath:await Id(u)}}},{id:"cursor",label:"Cursor",dest:(u)=>n(u,".cursor","skills","dunsocial","SKILL.md")},{id:"pi",label:"Pi",dest:(u)=>n(u,".pi","agent","skills","dunsocial","SKILL.md")},{id:"opencode",label:"OpenCode",dest:(u)=>n(u,".config","opencode","skills","dunsocial","SKILL.md")},{id:"agents",label:"Universal (.agents)",aliases:["universal"],dest:(u)=>n(u,".agents","skills","dunsocial","SKILL.md")},{id:"copilot",label:"GitHub Copilot",aliases:["github-copilot","github"],dest:(u)=>n(u,".copilot","skills","dunsocial","SKILL.md")},{id:"windsurf",label:"Windsurf",dest:(u)=>n(u,".codeium","windsurf","skills","dunsocial","SKILL.md")},{id:"goose",label:"Goose",dest:(u)=>n(u,".config","goose","skills","dunsocial","SKILL.md")},{id:"gemini",label:"Gemini CLI",aliases:["gemini-cli"],dest:(u)=>n(u,".gemini","skills","dunsocial","SKILL.md")},{id:"amp",label:"Amp",dest:(u)=>n(u,".config","agents","skills","dunsocial","SKILL.md")},{id:"grok",label:"Grok Build",dest:(u)=>n(u,".grok","skills","dunsocial","SKILL.md")},{id:"continue",label:"Continue",dest:(u)=>n(u,".continue","skills","dunsocial","SKILL.md")},{id:"antigravity",label:"Antigravity",dest:(u)=>n(u,".gemini","antigravity","skills","dunsocial","SKILL.md")}],x=new Map;for(let u of p){x.set(u.id,u);for(let w of u.aliases??[])x.set(w,u)}var Au=`Usage: dun skill install <${p.map((u)=>u.id).join("|")}|all>`;function a(){let u=l(Wd(import.meta.url)),w=n("skills","dunsocial","SKILL.md"),d=[n(u,"..",w),n(u,"..","..",w)];return d.find(Fd)??d[0]}function Kd(){let u=t();return p.map((w)=>({id:w.id,label:w.label,path:w.dest(u),aliases:w.aliases??[]}))}async function Td(){try{return await Mu(a(),"utf8")}catch{throw new z(`Bundled skill not found at ${a()}. Reinstall dunsocial.`,O.USAGE)}}async function Bd(u,w){await Pu(l(u),{recursive:!0}),await Nu(u,w,"utf8")}async function Id(u){let w=n(t(),".codex","AGENTS.md");await Pu(l(w),{recursive:!0});let d="";try{d=await Mu(w,"utf8")}catch{d=""}let D=["<!-- 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(`
227
- `),y="<!-- BEGIN DUNSOCIAL CLI SKILL -->",m="<!-- END DUNSOCIAL CLI SKILL -->",U,$=d.indexOf(y),q=d.indexOf(m);if($!==-1&&q!==-1&&q>$)U=d.slice(0,$)+D+d.slice(q+m.length);else if(d.trim())U=`${d.trimEnd()}
228
-
229
- ${D}
230
- `;else U=`${D}
231
- `;return await Nu(w,U,"utf8"),w}async function Hu(u,w){let d=u.dest(t());await Bd(d,w);let D=u.afterInstall?await u.afterInstall(d):{};return{target:u.id,path:d,message:`Installed DunSocial skill for ${u.label}. Restart or open a new session if needed.`,...D}}async function _u(u,w,d,D){switch(w){case"path":{k({path:a()},u);return}case"list":{k({targets:Kd(),hint:"Install with: dun skill install <target|all>"},u);return}case"install":{let y=(D[0]||"").toLowerCase();if(!y)throw new z(Au,O.USAGE);let m=await Td();if(y==="all"){let q=[];for(let X of p){let Z=await Hu(X,m);q.push({target:Z.target,path:Z.path})}k({target:"all",count:q.length,installed:q,message:`Installed DunSocial skill for ${q.length} agents.`},u);return}let U=x.get(y);if(!U)throw new z(`Unknown agent "${y}". ${Au}
232
- Try: dun skill list`,O.USAGE);let $=await Hu(U,m);k($,u);return}default:throw new z(`Unknown skill command "${w??""}". Try: install, list, path`,O.USAGE)}}import{spawn as Ad}from"node:child_process";import{realpathSync as Hd}from"node:fs";var K="dunsocial",Pd=`https://registry.npmjs.org/${K}/latest`;function Nd(u=process.argv[1]??""){let w=u.replaceAll("\\","/");try{w=Hd(u).replaceAll("\\","/")}catch{}if(w.includes("/.npm/_npx")||w.includes("/npm/_npx")||w.includes("/_npx/"))return{packageManager:"npx",manual:`npm install -g ${K}@latest`};if(w.includes("/.pnpm/")||w.includes("/pnpm/"))return{packageManager:"pnpm",command:"pnpm",args:["add","-g",`${K}@latest`],manual:`pnpm add -g ${K}@latest`};if(w.includes("/.yarn/")||w.includes("/yarn/global"))return{packageManager:"yarn",command:"yarn",args:["global","add",`${K}@latest`],manual:`yarn global add ${K}@latest`};if(w.includes("/.bun/")||w.includes("/bun/install/global"))return{packageManager:"bun",command:"bun",args:["add","-g",`${K}@latest`],manual:`bun add -g ${K}@latest`};if(w.includes(`/node_modules/${K}/`))return{packageManager:"npm",command:"npm",args:["install","-g",`${K}@latest`],manual:`npm install -g ${K}@latest`};return{packageManager:"unknown",manual:`npm install -g ${K}@latest`}}function Md(u,w){let d=u.replace(/^v/,"").split(".").map((m)=>Number.parseInt(m,10)||0),D=w.replace(/^v/,"").split(".").map((m)=>Number.parseInt(m,10)||0),y=Math.max(d.length,D.length);for(let m=0;m<y;m++){let U=d[m]??0,$=D[m]??0;if(U>$)return 1;if(U<$)return-1}return 0}async function _d(u=fetch){let w=await u(Pd,{headers:{Accept:"application/json"}});if(!w.ok)throw new z(`Could not check npm for updates (HTTP ${w.status}). Try: npm install -g ${K}@latest`,O.NETWORK);let d=await w.json();if(!d.version)throw new z("npm registry response missing version",O.NETWORK);return d.version}function jd(u,w){return new Promise((d,D)=>{let y=Ad(u,w,{stdio:"inherit",env:process.env,windowsHide:!0,shell:process.platform==="win32"});y.on("error",(m)=>{D(new z(`Failed to run ${u}: ${m.message}. Try manually: ${u} ${w.join(" ")}`,O.NETWORK))}),y.on("close",(m)=>d(m??1))})}async function ju(u,w,d){let D=R(d,"check"),y=R(d,"force"),m=T,U=Nd(),$;try{$=await _d()}catch(Q){if(Q instanceof z)throw Q;throw new z(`Could not check npm for updates: ${Q.message}`,O.NETWORK)}let q=Md($,m)>0,X={current:m,latest:$,upToDate:!q,packageManager:U.packageManager,command:U.manual};if(!q&&!y){k(u.json?X:`Already on the latest version (${m}).`,u);return}if(D){k(u.json?{...X,updateAvailable:q}:`Update available: ${m} → ${$}
239
+ `)}else process.stderr.write(JSON.stringify({ok:!0,phase:"waiting",userCode:s.userCode,verificationUrlComplete:s.verificationUrlComplete})+`
240
+ `);if(c&&process.stdout.isTTY)try{await dt(s.verificationUrlComplete)}catch{}let r=Math.max(3,s.interval||5)*1000,o=Date.now()+(s.expiresIn||900)*1000;while(Date.now()<o){await ct(r);let i=await t.post("/api/cli/auth/poll",{deviceCode:s.deviceCode},{auth:!1});if(i.status==="pending")continue;if(i.status==="expired")throw new d("Login expired. Run dun auth login again.",a.AUTH);if(i.status==="denied")throw new d("Login was denied in the browser.",a.AUTH);if(i.status==="approved"){let l=i.token,u={...e,token:l},p=y(u),w=await p.get("/api/user/profile"),g={...e.config,apiUrl:e.apiUrl,token:l,defaultWorkspaceId:i.workspaceId||e.config.defaultWorkspaceId,defaultWorkspaceSlug:e.config.defaultWorkspaceSlug,defaultWorkspaceName:e.config.defaultWorkspaceName};if(i.workspaceId)try{let v=(await p.get("/api/workspaces")).find((Pe)=>Pe.id===i.workspaceId);if(v)g.defaultWorkspaceId=v.id,g.defaultWorkspaceSlug=v.slug,g.defaultWorkspaceName=v.name}catch{}await D(g);let k=Boolean(g.defaultWorkspaceId);if(!e.json&&U(process.stdout)){m(P(k),e);return}m({user:{id:w.id,name:w.name,email:w.email},workspaceId:g.defaultWorkspaceId??null,configPath:L(),method:"device",message:P(k)},e);return}}throw new d("Login timed out. Run dun auth login again.",a.AUTH)}async function ot(e){if(await re(),!e.json&&U(process.stdout)){m("Logged out. Config cleared.",e);return}m({message:"Logged out. Config cleared.",configPath:L()},e)}async function st(e){I(e);let t=await y(e).get("/api/user/profile");m({id:t.id,name:t.name,email:t.email,image:t.image??null,apiUrl:e.apiUrl,workspaceId:e.workspaceId??null},e)}async function at(e){let n=Boolean(e.token),t=null,s=null;if(n)try{t=await y(e).get("/api/user/profile")}catch(c){s=c instanceof Error?c.message:String(c)}m({authenticated:Boolean(t),tokenPresent:n,apiUrl:e.apiUrl,workspaceId:e.workspaceId??null,workspaceSlug:e.config.defaultWorkspaceSlug??null,workspaceName:e.config.defaultWorkspaceName??null,configPath:L(),user:t?{id:t.id,name:t.name,email:t.email}:null,error:s},e)}function ct(e){return new Promise((n)=>setTimeout(n,e))}async function dt(e){let n=process.platform,t=n==="darwin"?["open",e]:n==="win32"?["cmd","/c","start","",e]:["xdg-open",e];await new Promise((s,c)=>{let r=tt(t[0],t.slice(1),{stdio:"ignore",windowsHide:!0});r.on("error",c),r.on("close",()=>s())})}async function ge(e,n,t,s){I(e);let c=y(e);switch(n){case"list":case"ls":{let r=await c.get("/api/workspaces");m(r.map((o)=>({id:o.id,name:o.name,slug:o.slug,role:o.role,timezone:o.timezone,plan:o.subscription?.planName??null,status:o.subscription?.status??null})),{...e,emptyMessage:"No workspaces yet."});return}case"use":{let r=s[0];if(!r)throw new d("Usage: dun workspace use <id|slug>",a.USAGE);let i=(await c.get("/api/workspaces")).find((l)=>l.id===r||l.slug===r);if(!i)throw new d(`Workspace not found: ${r}. Run dun workspace list`,a.NOT_FOUND);await D({...e.config,apiUrl:e.apiUrl,token:e.token,defaultWorkspaceId:i.id,defaultWorkspaceSlug:i.slug,defaultWorkspaceName:i.name}),m({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 d("No workspace selected. Run dun workspace use <id|slug>",a.VALIDATION);let r=e.workspaceId||e.config.defaultWorkspaceId;try{let i=(await c.get("/api/workspaces")).find((l)=>l.id===r||l.slug===r);if(i){m({id:i.id,name:i.name,slug:i.slug,role:i.role,timezone:i.timezone},e);return}}catch{}m({id:r,name:e.config.defaultWorkspaceName??null,slug:e.config.defaultWorkspaceSlug??null},e);return}default:throw new d(`Unknown workspace command "${n??""}". Try: list, use, current`,a.USAGE)}}async function we(e,n,t,s){I(e),A(e);let c=y(e);switch(n){case void 0:case"list":case"ls":{let r=await c.get("/api/social-accounts",{workspace:!0}),o=f(t,"platform","p")?.toLowerCase(),i=o?r.filter((l)=>{let u=(l.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"],tiktok:["tiktok"]}[o]??[o]).some((g)=>u.includes(g))}):r;m(i.map((l)=>({id:l.id,providerName:l.providerName,username:l.username??null,displayName:l.displayName??null,isConnected:l.isConnected,expiresAt:l.expiresAt??null})),{...e,emptyMessage:o?`No ${o} accounts connected. Connect one in the DunSocial dashboard.`:"No accounts connected. Connect one in the DunSocial dashboard."});return}default:throw new d(`Unknown accounts command "${n}". Try: list (or: dun accounts reddit flairs <subreddit>)`,a.USAGE)}}import{readFileSync as pt}from"node:fs";var lt=/^(\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 J(e,n=new Date){let t=e.trim();if(!t)throw new d("Empty time value",a.VALIDATION);let s=t.match(lt);if(s){let o=Number(s[1]),i=s[2].toLowerCase(),l=ut(i)*o;return new Date(n.getTime()+l)}let c=t.match(/^in\s+(.+)$/i);if(c)return J(c[1],n);let r=new Date(t);if(Number.isNaN(r.getTime()))throw new d(`Invalid time "${e}". Use ISO-8601 (2026-06-01T09:00:00Z) or relative (2h, 30m, 1d).`,a.VALIDATION);return r}function ut(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 d(`Unknown time unit: ${e}`,a.VALIDATION)}}function M(e){if(e.at&&e.in)throw new d("Pass only one of --at or --in",a.VALIDATION);if(e.at)return J(e.at).toISOString();if(e.in)return J(e.in).toISOString();throw new d("Schedule time required. Pass --at <iso> or --in <duration> (e.g. 2h).",a.VALIDATION)}function ke(e){if(!e)return[];return e.split(",").map((n)=>n.trim()).filter(Boolean)}function he(e,n){let t=f(e,"text","content","c","t");if(t)return t;let s=n?.reddit;if(s&&typeof s==="object"&&!Array.isArray(s)&&typeof s.title==="string"&&s.title.trim())return s.title.trim();throw new d("Missing --text",a.VALIDATION)}function q(e){let n=C(e,"accounts","account","a")??ke(f(e,"accounts","account","a"));if(!n||n.length===0)throw new d("Missing --accounts <id,id>",a.VALIDATION);return n}function j(e){return C(e,"media","media-urls")??ke(f(e,"media","media-urls"))}function O(e){let n=f(e,"meta","metadata","meta-json");if(!n)return;try{let t=JSON.parse(n);if(!t||typeof t!=="object"||Array.isArray(t))throw Error("metadata must be a JSON object");return t}catch(t){throw new d(`Invalid --meta JSON: ${t.message}`,a.VALIDATION)}}var mt=new Set(["published","failed","cancelled"]);async function ft(e){await new Promise((n)=>setTimeout(n,e))}async function gt(e,n,t,s){let c=Date.now()+s*1000,r=new Map,o=1500;while(r.size<t.length){if(Date.now()>c){let u=t.filter((p)=>!r.has(p.id)).map((p)=>p.id);throw new d(`Timed out waiting for publish after ${s}s. Still pending: ${u.join(", ")}. Poll with: dun posts get <id> --json`,a.NETWORK,{pending:u})}for(let u of t){if(r.has(u.id))continue;let p=await e.get(`/api/posts/${u.id}`,{workspace:!0});if(R(n.debug,`wait status ${u.id}=${p.status}`),mt.has(p.status))r.set(u.id,p)}if(r.size>=t.length)break;await ft(o),o=Math.min(o*1.4,8000)}let i=t.map((u)=>r.get(u.id)),l=i.filter((u)=>u.status==="failed");if(l.length>0){let u=l.map((p)=>`${p.id}: ${p.error||"publish failed"}`).join("; ");throw new d(`Publish failed — ${u}`,a.VALIDATION,{posts:i})}return i}async function wt(e,n){let t=[],s=n.meta?.reddit??null;if(!s||typeof s!=="object")throw new d("Reddit accounts require --meta with a reddit object, e.g. "+`--meta '{"reddit":{"subreddit":"…","kind":"self","title":"…","flairId":"…"}}'`,a.VALIDATION);let c=(s.subreddit??"").trim().replace(/^\/?r\//i,""),r=(s.title??"").trim(),o=(s.kind??"").trim(),i=(s.flairId??"").trim(),l=(s.flairText??"").trim();if(!c)throw new d("metadata.reddit.subreddit is required",a.VALIDATION);if(!r)throw new d("metadata.reddit.title is required",a.VALIDATION);if(r.length>300)throw new d(`Reddit title exceeds 300 characters: ${r.length}/300`,a.VALIDATION);if(!["self","link","image","video"].includes(o))throw new d("metadata.reddit.kind must be one of: self, link, image, video",a.VALIDATION);if(t.push(`kind=${o}`),t.push(`titleLength=${r.length}`),l&&!i)throw new d(`Can't set flairText without flairId for r/${c}. Run: dun reddit flairs ${c} --json`,a.VALIDATION);if(o==="link"){if(!s.url?.trim())throw new d("Reddit link posts require metadata.reddit.url",a.VALIDATION);try{new URL(s.url)}catch{throw new d("metadata.reddit.url is invalid",a.VALIDATION)}if(n.media.length>0)throw new d("Reddit link posts do not accept --media",a.VALIDATION)}else if(o==="image"||o==="video"){if(n.media.length!==1)throw new d(`Reddit ${o} posts require exactly one --media asset id`,a.VALIDATION)}else if(n.media.length>0)throw new d("Reddit self posts do not accept --media. Use kind image or video.",a.VALIDATION);let u=await e.get("/api/social-accounts/reddit/post-requirements",{workspace:!0,query:{subreddit:c}});if(t.push("fetched post-requirements"),u?.is_flair_required&&!i)throw new d(`r/${c} requires post flair. Discover ids with: dun reddit flairs ${c} --json`,a.VALIDATION);let p=u?.title_text_max_length??300;if(r.length>p)throw new d(`Reddit title too long for r/${c}: ${r.length}/${p}`,a.VALIDATION);if(u?.title_text_min_length&&r.length<u.title_text_min_length)throw new d(`Reddit title must be at least ${u.title_text_min_length} characters`,a.VALIDATION);if(i){let w=await e.get("/api/social-accounts/reddit/flair-options",{workspace:!0,query:{subreddit:c}});t.push(`fetched ${w.choices?.length??0} flairs`);let g=(w.choices??[]).find((k)=>k.id===i);if(!g)throw new d(`Unknown flairId "${i}" for r/${c}. Run: dun reddit flairs ${c} --json`,a.VALIDATION);if(g.textEditable===!1&&l&&l!==g.text)throw new d(`Customization not allowed for this flair template. Use flairId "${g.id}" alone`+(g.text?` (template text: "${g.text}")`:""),a.VALIDATION);t.push(`flairId=${g.id}`)}return{ok:!0,checks:t,reddit:{subreddit:c,kind:o,title:r,flairId:i||null,accounts:n.accounts,media:n.media,textPreview:n.text?.slice(0,120)??null,requirements:{is_flair_required:u?.is_flair_required??!1,title_text_max_length:p}}}}async function ye(e,n,t,s){I(e),A(e);let c=y(e);switch(n){case"list":case"ls":{let r=await c.get("/api/posts",{workspace:!0,query:{status:f(t,"status","s"),socialAccountId:f(t,"account","social-account"),groupId:f(t,"group"),limit:E(t,"limit")??50,offset:E(t,"offset")??0}});m(r,{...e,emptyMessage:"No posts yet. Schedule one with dun posts schedule."});return}case"get":{let r=s[0];if(!r)throw new d("Usage: dun posts get <id>",a.USAGE);let o=await c.get(`/api/posts/${r}`,{workspace:!0});m(o,e);return}case"validate":{let r=q(t),o=j(t),i=O(t)??{},l=f(t,"text","content","c","t"),u=Boolean(i.reddit),p=u;if(!u){let g=await c.get("/api/social-accounts",{workspace:!0});p=r.some((k)=>(g.find((v)=>v.id===k)?.providerName||"").toLowerCase().includes("reddit"))}if(!p){m({ok:!0,checks:["no reddit-specific rules for selected accounts"],accounts:r,media:o},e);return}let w=await wt(c,{accounts:r,text:l,media:o,meta:i});m(w,e);return}case"schedule":{let r=O(t)??{},o=he(t,r),i=q(t),l=M({at:f(t,"at"),in:f(t,"in")}),u=await c.post("/api/posts/schedule",{content:o,socialAccountIds:i,scheduledAt:l,mediaUrls:j(t),metadata:r},{workspace:!0});m(u,e);return}case"publish":case"publish-now":{let r=O(t)??{},o=he(t,r),i=q(t),l=await c.post("/api/posts/publish-now",{content:o,socialAccountIds:i,mediaUrls:j(t),metadata:r,naturalPosting:h(t,"natural","natural-posting")},{workspace:!0});if(!h(t,"wait")){m(l,e);return}let p=Array.isArray(l)?l:[];if(p.length===0||!p.every((k)=>typeof k?.id==="string"))throw new d("publish --wait expected an array of created posts with ids",a.NETWORK,{data:l});let w=E(t,"wait-timeout","timeout")??120,g=await gt(c,e,p,w);m({queued:p,posts:g,status:g.every((k)=>k.status==="published")?"published":g[0]?.status},e);return}case"reschedule":{let r=s[0];if(!r)throw new d("Usage: dun posts reschedule <id> --at|--in",a.USAGE);let i={scheduledAt:M({at:f(t,"at"),in:f(t,"in")})},l=f(t,"text","content","c","t");if(l)i.content=l;let u=j(t);if(u.length)i.mediaUrls=u;let p=O(t);if(p)i.metadata=p;if(!i.content){let g=await c.get(`/api/posts/${r}`,{workspace:!0});if(i.content=g.content,!i.metadata&&g.metadata)i.metadata=g.metadata;if(!i.mediaUrls&&g.mediaUrls)i.mediaUrls=g.mediaUrls}let w=await c.post(`/api/posts/${r}/reschedule`,i,{workspace:!0});m(w,e);return}case"cancel":{let r=s[0];if(!r)throw new d("Usage: dun posts cancel <id>",a.USAGE);let o=await c.post(`/api/posts/${r}/cancel`,void 0,{workspace:!0});m(o??{id:r,status:"cancelled"},e);return}case"delete":case"rm":{let r=s[0];if(!r)throw new d("Usage: dun posts delete <id> [--yes]",a.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes (or CI/yes mode)",a.VALIDATION);let o=await c.delete(`/api/posts/${r}`,void 0,{workspace:!0});m(o??{id:r,deleted:!0},e);return}case"x-cap":case"xcap":{let r=await c.get("/api/posts/x-cap-usage",{workspace:!0});m(r,e);return}case"schedule-thread":case"publish-thread":case"publish-thread-now":{let r=f(t,"file","f"),o=f(t,"account","accounts","a")??q(t)[0];if(!o)throw new d("Missing --account <xAccountId>",a.VALIDATION);let i;if(r){let u;try{u=pt(r,"utf8")}catch(w){throw new d(`Could not read file: ${w.message}`,a.VALIDATION)}let p=JSON.parse(u);if(Array.isArray(p))i=p;else if(p&&typeof p==="object"&&Array.isArray(p.tweets))i=p.tweets;else throw new d("Thread file must be an array of tweets or { tweets: [] }",a.VALIDATION)}else{let u=f(t,"text","content");if(!u)throw new d('Provide --file thread.json (array of {content}) or --text "tweet1||tweet2"',a.VALIDATION);i=u.split("||").map((p)=>({content:p.trim()})).filter((p)=>p.content)}if(i.length<2)throw new d("Threads require at least 2 tweets",a.VALIDATION);if(n==="schedule-thread"){let u=M({at:f(t,"at"),in:f(t,"in")}),p=await c.post("/api/posts/schedule-thread",{socialAccountId:o,tweets:i,scheduledAt:u,metadata:O(t)??{}},{workspace:!0});m(p,e);return}let l=await c.post("/api/posts/publish-thread-now",{socialAccountId:o,tweets:i,metadata:O(t)??{}},{workspace:!0});m(l,e);return}default:throw new d(`Unknown posts command "${n??""}". Try: list, get, validate, schedule, publish, reschedule, cancel, delete, x-cap, schedule-thread, publish-thread`,a.USAGE)}}async function Ie(e,n,t,s){I(e),A(e);let c=y(e);switch(n){case"list":case"ls":{let r=await c.get("/api/drafts",{workspace:!0,query:{limit:E(t,"limit")??50,offset:E(t,"offset")??0}});m(r,{...e,emptyMessage:'No drafts yet. Create one with dun drafts create --text "..."'});return}case"get":{let r=s[0];if(!r)throw new d("Usage: dun drafts get <id>",a.USAGE);let o=await c.get(`/api/drafts/${r}`,{workspace:!0});m(o,e);return}case"create":{let r=f(t,"text","content","c","t");if(!r)throw new d("Missing --text",a.VALIDATION);let o=await c.post("/api/drafts",{content:r,name:f(t,"name","n"),socialAccountIds:C(t,"accounts","account","a")??[],selectedPlatforms:C(t,"platforms","platform","p")??[],mediaUrls:C(t,"media")??[]},{workspace:!0});m(o,e);return}case"update":{let r=s[0];if(!r)throw new d("Usage: dun drafts update <id> --text ...",a.USAGE);let o={id:r},i=f(t,"text","content","c","t");if(i)o.content=i;let l=f(t,"name","n");if(l!==void 0)o.name=l;let u=C(t,"accounts","account","a");if(u)o.socialAccountIds=u;let p=C(t,"platforms","platform","p");if(p)o.selectedPlatforms=p;let w=C(t,"media");if(w)o.mediaUrls=w;let g=await c.patch(`/api/drafts/${r}`,o,{workspace:!0});m(g,e);return}case"delete":case"rm":{let r=s[0];if(!r)throw new d("Usage: dun drafts delete <id> [--yes]",a.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",a.VALIDATION);let o=await c.delete(`/api/drafts/${r}`,void 0,{workspace:!0});m(o??{id:r,deleted:!0},e);return}default:throw new d(`Unknown drafts command "${n??""}". Try: list, get, create, update, delete`,a.USAGE)}}import{readFileSync as ht,existsSync as kt,statSync as yt}from"node:fs";import{basename as It}from"node:path";var be=["image/jpeg","image/png","image/gif","image/webp"],Ae=["video/mp4","video/quicktime","video/webm"];function Se(e){if(e<1024)return`${e}B`;if(e<1048576)return`${(e/1024).toFixed(1)}KB`;return`${(e/1048576).toFixed(1)}MB`}function Te(e){let{mimeType:n,fileSize:t}=e,s=be.includes(n),c=Ae.includes(n);if(!s&&!c)return{ok:!1,message:`Unsupported MIME type "${n}". Allowed images: ${be.join(", ")}; videos: ${Ae.join(", ")}`};if(c&&t>268435456)return{ok:!1,message:`Video files must be less than 256MB (got ${Se(t)}). Compress first, e.g.:
241
+ ffmpeg -i input.mp4 -vf scale=-2:1080 -c:v libx264 -crf 23 -c:a aac output.mp4`};if(s&&t>8388608)return{ok:!1,message:`Image files must be less than 8MB (got ${Se(t)}).`};return{ok:!0}}function bt(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 Ne(e,n,t,s){I(e),A(e);let c=y(e);switch(n){case"list":case"ls":{let r=await c.get("/api/media",{workspace:!0,query:{limit:E(t,"limit")??50,offset:E(t,"offset")??0}});m(r,{...e,emptyMessage:"No media yet. Upload with dun media upload <file>."});return}case"get":{let r=s[0];if(!r)throw new d("Usage: dun media get <id>",a.USAGE);let o=await c.get(`/api/media/${r}`,{workspace:!0});m(o,e);return}case"upload":{let r=s[0]||f(t,"file","f");if(!r)throw new d('Usage: dun media upload <file> [--alt "..."]',a.USAGE);if(!kt(r))throw new d(`File not found: ${r}`,a.VALIDATION);let o=It(r),i=yt(r).size,l=f(t,"mime","type","content-type")||bt(o),u=f(t,"alt","alt-text"),p=Te({mimeType:l,fileSize:i});if(!p.ok)throw new d(p.message,a.VALIDATION);let w=await c.post("/api/media/upload-url",{filename:o,mimeType:l,fileSize:i},{workspace:!0});R(e.debug,`PUT ${w.uploadUrl}`,{storageKey:w.storageKey,fileSize:i});let g=ht(r),k;try{k=await fetch(w.uploadUrl,{method:"PUT",headers:{"Content-Type":l,"Content-Length":String(i)},body:g})}catch(v){throw new d(`Upload PUT failed: ${v.message}`,a.NETWORK)}if(!k.ok){let v=await k.text().catch(()=>"");throw new d(`Upload PUT failed with HTTP ${k.status}${v?`: ${v.slice(0,200)}`:""}`,a.NETWORK)}let x=await c.post("/api/media/complete",{storageKey:w.storageKey,originalFilename:o,mimeType:l,fileSize:i,altText:u},{workspace:!0});m(x,e);return}case"delete":case"rm":{let r=s[0];if(!r)throw new d("Usage: dun media delete <id> [--yes]",a.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",a.VALIDATION);let o=await c.delete(`/api/media/${r}`,void 0,{workspace:!0});m(o??{id:r,deleted:!0},e);return}default:throw new d(`Unknown media command "${n??""}". Try: list, get, upload, delete`,a.USAGE)}}async function Ee(e,n,t,s){I(e),A(e);let c=y(e),r=n[0];if(r==="collections"||r==="collection"){let o=n[1]||"list";switch(o){case"list":case"ls":{let i=await c.get("/api/memory/collections",{workspace:!0});m(i,{...e,emptyMessage:'No memory collections yet. Create one with dun memory collections create --name "..."'});return}case"create":{let i=f(t,"name","n");if(!i)throw new d("Missing --name",a.VALIDATION);let l=await c.post("/api/memory/collections",{name:i,color:f(t,"color")??"blue",isPrivate:h(t,"private")},{workspace:!0});m(l,e);return}case"get":{let i=n[2]||s[0];if(!i)throw new d("Usage: dun memory collections get <id>",a.USAGE);let l=await c.get(`/api/memory/collections/${i}`,{workspace:!0});m(l,e);return}case"update":{let i=n[2]||s[0];if(!i)throw new d("Usage: dun memory collections update <id> [--name] [--color] [--private|--public]",a.USAGE);let l=f(t,"name","n"),u=f(t,"color"),p=h(t,"private"),w=h(t,"public");if(p&&w)throw new d("Use either --private or --public, not both",a.VALIDATION);let g={};if(l)g.name=l;if(u)g.color=u;if(p)g.isPrivate=!0;if(w)g.isPrivate=!1;if(Object.keys(g).length===0)throw new d("Provide at least one of --name, --color, --private, or --public",a.VALIDATION);let k=await c.patch(`/api/memory/collections/${i}`,g,{workspace:!0});m(k,e);return}case"delete":case"rm":{let i=n[2]||s[0];if(!i)throw new d("Usage: dun memory collections delete <id> [--yes]",a.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",a.VALIDATION);let l=await c.delete(`/api/memory/collections/${i}`,void 0,{workspace:!0});m(l??{id:i,deleted:!0},e);return}default:throw new d(`Unknown memory collections command "${o}". Try: list, create, get, update, delete`,a.USAGE)}}switch(r){case"list":case"ls":{let o=f(t,"collection","collection-id","c");if(!o)throw new d("Missing --collection <id>",a.VALIDATION);let i=await c.get("/api/memory",{workspace:!0,query:{collectionId:o,limit:E(t,"limit")??100}});m(i,{...e,emptyMessage:"No memories in this collection."});return}case"save":case"add":{let o=f(t,"collection","collection-id","c"),i=f(t,"text","t");if(!o)throw new d("Missing --collection <id>",a.VALIDATION);if(!i)throw new d("Missing --text",a.VALIDATION);let l=await c.post("/api/memory",{collectionId:o,text:i},{workspace:!0});m(l,e);return}case"search":{let o=f(t,"prompt","q","query","text"),i=C(t,"collections","collection","collection-ids","c")??[];if(!o)throw new d("Missing --prompt",a.VALIDATION);if(i.length===0)throw new d("Missing --collections <id,id>",a.VALIDATION);let l=await c.post("/api/memory/search",{prompt:o,collectionIds:i,topK:E(t,"top-k","topk","k")??5},{workspace:!0});m(l,e);return}case"delete":case"rm":{let o=n[1]||s[0],i=f(t,"collection","collection-id","c");if(!o)throw new d("Usage: dun memory delete <id> --collection <id>",a.USAGE);if(!i)throw new d("Missing --collection <id>",a.VALIDATION);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",a.VALIDATION);let l=await c.delete(`/api/memory/${o}`,{collectionId:i,memoryId:o},{workspace:!0});m(l??{id:o,deleted:!0},e);return}default:throw new d(`Unknown memory command "${r??""}". Try: collections, list, save, search, delete`,a.USAGE)}}import{readFileSync as At}from"node:fs";async function Ce(e,n,t,s){I(e);let c=A(e),r=y(e),o=`/api/workspaces/${c}/ai-voice-settings`;switch(n){case"get":case"show":case void 0:{let i=await r.get(o,{workspace:!0});m(i??{personalization:null,hint:'No personalization set. Use dun personalization set --description "..."'},e);return}case"set":case"update":{let i=h(t,"clear"),l=f(t,"description","d"),u=f(t,"file","f");if([i,Boolean(l),Boolean(u)].filter(Boolean).length!==1)throw new d("Use exactly one of --description, --file, or --clear",a.VALIDATION);let w;if(i)w=null;else if(u){let k;try{k=At(u,"utf8").trim()}catch(x){throw new d(`Could not read file: ${x.message}`,a.VALIDATION)}if(!k)throw new d("File is empty",a.VALIDATION);w={description:k}}else{let k=l.trim();if(!k)throw new d("--description cannot be empty",a.VALIDATION);w={description:k}}let g=await r.patch(o,w,{workspace:!0});m(g,e);return}default:throw new d(`Unknown personalization command "${n}". Try: get, set`,a.USAGE)}}function ve(e){return e.trim().replace(/^\/?r\//i,"").replace(/^@/,"")}async function Y(e,n,t,s){I(e),A(e);let c=y(e);switch(n){case"flairs":case"flair":case"flair-options":{let r=s[0]||f(t,"subreddit","sub","r");if(!r)throw new d(`Usage: dun reddit flairs <subreddit>
242
+ Example: dun reddit flairs tamilyapping --json`,a.USAGE);let o=ve(r),l=((await c.get("/api/social-accounts/reddit/flair-options",{workspace:!0,query:{subreddit:o}})).choices??[]).map((u)=>({id:u.id,text:u.text,textEditable:u.textEditable!==!1}));m({subreddit:o,choices:l},{...e,emptyMessage:`No flairs returned for r/${o}. The subreddit may not use post flair.`});return}case"requirements":case"post-requirements":{let r=s[0]||f(t,"subreddit","sub","r");if(!r)throw new d("Usage: dun reddit requirements <subreddit>",a.USAGE);let o=ve(r),i=await c.get("/api/social-accounts/reddit/post-requirements",{workspace:!0,query:{subreddit:o}});m({subreddit:o,requirements:i},e);return}default:throw new d(`Unknown reddit command "${n??""}". Try: flairs, requirements`,a.USAGE)}}import{existsSync as St}from"node:fs";import{mkdir as Oe,writeFile as $e,readFile as xe}from"node:fs/promises";import{dirname as Q,join as b}from"node:path";import{homedir as ee}from"node:os";import{fileURLToPath as Tt}from"node:url";var W=[{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)=>({agentsPath:await vt(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")}],X=new Map;for(let e of W){X.set(e.id,e);for(let n of e.aliases??[])X.set(n,e)}var Ue=`Usage: dun skill install <${W.map((e)=>e.id).join("|")}|all>`;function Z(){let e=Q(Tt(import.meta.url)),n=b("skills","dunsocial","SKILL.md"),t=[b(e,"..",n),b(e,"..","..",n)];return t.find(St)??t[0]}function Nt(){let e=ee();return W.map((n)=>({id:n.id,label:n.label,path:n.dest(e),aliases:n.aliases??[]}))}async function Et(){try{return await xe(Z(),"utf8")}catch{throw new d(`Bundled skill not found at ${Z()}. Reinstall dunsocial.`,a.USAGE)}}async function Ct(e,n){await Oe(Q(e),{recursive:!0}),await $e(e,n,"utf8")}async function vt(e){let n=b(ee(),".codex","AGENTS.md");await Oe(Q(n),{recursive:!0});let t="";try{t=await xe(n,"utf8")}catch{t=""}let s=["<!-- 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(`
243
+ `),c="<!-- BEGIN DUNSOCIAL CLI SKILL -->",r="<!-- END DUNSOCIAL CLI SKILL -->",o,i=t.indexOf(c),l=t.indexOf(r);if(i!==-1&&l!==-1&&l>i)o=t.slice(0,i)+s+t.slice(l+r.length);else if(t.trim())o=`${t.trimEnd()}
244
+
245
+ ${s}
246
+ `;else o=`${s}
247
+ `;return await $e(n,o,"utf8"),n}async function Re(e,n){let t=e.dest(ee());await Ct(t,n);let s=e.afterInstall?await e.afterInstall(t):{};return{target:e.id,path:t,message:`Installed DunSocial skill for ${e.label}. Restart or open a new session if needed.`,...s}}async function Le(e,n,t,s){switch(n){case"path":{m({path:Z()},e);return}case"list":{m({targets:Nt(),hint:"Install with: dun skill install <target|all>"},e);return}case"install":{let c=(s[0]||"").toLowerCase();if(!c)throw new d(Ue,a.USAGE);let r=await Et();if(c==="all"){let l=[];for(let u of W){let p=await Re(u,r);l.push({target:p.target,path:p.path})}m({target:"all",count:l.length,installed:l,message:`Installed DunSocial skill for ${l.length} agents.`},e);return}let o=X.get(c);if(!o)throw new d(`Unknown agent "${c}". ${Ue}
248
+ Try: dun skill list`,a.USAGE);let i=await Re(o,r);m(i,e);return}default:throw new d(`Unknown skill command "${n??""}". Try: install, list, path`,a.USAGE)}}import{spawn as Ut}from"node:child_process";import{realpathSync as Rt}from"node:fs";var T="dunsocial",Ot=`https://registry.npmjs.org/${T}/latest`;function $t(e=process.argv[1]??""){let n=e.replaceAll("\\","/");try{n=Rt(e).replaceAll("\\","/")}catch{}if(n.includes("/.npm/_npx")||n.includes("/npm/_npx")||n.includes("/_npx/"))return{packageManager:"npx",manual:`npm install -g ${T}@latest`};if(n.includes("/.pnpm/")||n.includes("/pnpm/"))return{packageManager:"pnpm",command:"pnpm",args:["add","-g",`${T}@latest`],manual:`pnpm add -g ${T}@latest`};if(n.includes("/.yarn/")||n.includes("/yarn/global"))return{packageManager:"yarn",command:"yarn",args:["global","add",`${T}@latest`],manual:`yarn global add ${T}@latest`};if(n.includes("/.bun/")||n.includes("/bun/install/global"))return{packageManager:"bun",command:"bun",args:["add","-g",`${T}@latest`],manual:`bun add -g ${T}@latest`};if(n.includes(`/node_modules/${T}/`))return{packageManager:"npm",command:"npm",args:["install","-g",`${T}@latest`],manual:`npm install -g ${T}@latest`};return{packageManager:"unknown",manual:`npm install -g ${T}@latest`}}function xt(e,n){let t=e.replace(/^v/,"").split(".").map((r)=>Number.parseInt(r,10)||0),s=n.replace(/^v/,"").split(".").map((r)=>Number.parseInt(r,10)||0),c=Math.max(t.length,s.length);for(let r=0;r<c;r++){let o=t[r]??0,i=s[r]??0;if(o>i)return 1;if(o<i)return-1}return 0}async function Lt(e=fetch){let n=await e(Ot,{headers:{Accept:"application/json"}});if(!n.ok)throw new d(`Could not check npm for updates (HTTP ${n.status}). Try: npm install -g ${T}@latest`,a.NETWORK);let t=await n.json();if(!t.version)throw new d("npm registry response missing version",a.NETWORK);return t.version}function Dt(e,n){return new Promise((t,s)=>{let c=Ut(e,n,{stdio:"inherit",env:process.env,windowsHide:!0,shell:process.platform==="win32"});c.on("error",(r)=>{s(new d(`Failed to run ${e}: ${r.message}. Try manually: ${e} ${n.join(" ")}`,a.NETWORK))}),c.on("close",(r)=>t(r??1))})}async function De(e,n,t){let s=h(t,"check"),c=h(t,"force"),r=N,o=$t(),i;try{i=await Lt()}catch(w){if(w instanceof d)throw w;throw new d(`Could not check npm for updates: ${w.message}`,a.NETWORK)}let l=xt(i,r)>0,u={current:r,latest:i,upToDate:!l,packageManager:o.packageManager,command:o.manual};if(!l&&!c){m(e.json?u:`Already on the latest version (${r}).`,e);return}if(s){m(e.json?{...u,updateAvailable:l}:`Update available: ${r} → ${i}
233
249
  Run: dun update
234
- Or: ${U.manual}`,u);return}if(!U.command||!U.args){k(u.json?{...X,applied:!1,hint:U.manual}:`Update available: ${m} → ${$}
235
- Cannot auto-update this install (${U.packageManager}).
236
- Run: ${U.manual}`,u);return}if(!u.json&&!u.quiet)process.stderr.write(`Updating ${K}: ${m} → ${$} via ${U.packageManager}...
237
- `);let Z=await jd(U.command,U.args);if(Z!==0)throw new z(`Update command failed (exit ${Z}). Try: ${U.manual}`,O.NETWORK);k(u.json?{...X,applied:!0}:`Updated via ${U.packageManager}. Run dun --version to confirm.`,u)}function Lu(){if(H(process.stdout))process.stdout.write(`${zu()}
238
-
239
- `);process.stdout.write(`${g}
240
- `)}async function Ld(){let u=await i(),w=process.env.DUN_TOKEN||u.token,d=process.env.DUN_WORKSPACE_ID||u.defaultWorkspaceId;process.stdout.write(`${Xu({token:w,workspaceId:d,config:u})}
241
- `)}async function vd(u){let w=$u(u),{group:d,action:D,nest:y,flags:m,positionals:U}=w;if(C(m)&&!d)return process.stdout.write(`${T}
242
- `),O.OK;if(d==="help"||!d&&E(m)){let q=d==="help"?D||U[0]:void 0;if(q)process.stdout.write(`${s(q)}
243
- `);else Lu();return O.OK}if(!d){if(H(process.stdout))await Ld();else Lu();return O.OK}if(E(m))return process.stdout.write(`${s(d)}
244
- `),O.OK;let $=await mu({apiUrl:V(m,"api-url","apiUrl"),token:V(m,"token"),workspace:V(m,"workspace","w"),json:R(m,"json","j")?!0:void 0,debug:R(m,"debug"),quiet:R(m,"quiet","q"),yes:R(m,"yes","y")});if(C(m))return k({version:T},$),O.OK;try{switch(d){case"auth":await ku($,D,m,U);break;case"workspace":case"workspaces":await Vu($,D,m,U);break;case"account":case"accounts":if(D==="reddit"){await c($,U[0],m,U.slice(1));break}await Qu($,D??"list",m,U);break;case"reddit":await c($,D,m,U);break;case"post":case"posts":await Gu($,D,m,U);break;case"draft":case"drafts":await bu($,D,m,U);break;case"media":await Ku($,D,m,U);break;case"memory":{let q=y?[y,D].filter(Boolean):D?[D]:[];await Tu($,q,m,U);break}case"personalization":case"voice":await Bu($,D,m,U);break;case"skill":case"skills":await _u($,D,m,U);break;case"update":case"upgrade":case"self-update":await ju($,D,m);break;case"version":k({version:T},$);break;default:throw new z(`Unknown command "${d}". Run dun --help`,O.USAGE)}return O.OK}catch(q){return Du(q,$)}}var ed=await vd(process.argv.slice(2));process.exit(ed);
250
+ Or: ${o.manual}`,e);return}if(!o.command||!o.args){m(e.json?{...u,applied:!1,hint:o.manual}:`Update available: ${r} → ${i}
251
+ Cannot auto-update this install (${o.packageManager}).
252
+ Run: ${o.manual}`,e);return}if(!e.json&&!e.quiet)process.stderr.write(`Updating ${T}: ${r} → ${i} via ${o.packageManager}...
253
+ `);let p=await Dt(o.command,o.args);if(p!==0)throw new d(`Update command failed (exit ${p}). Try: ${o.manual}`,a.NETWORK);m(e.json?{...u,applied:!0}:`Updated via ${o.packageManager}. Run dun --version to confirm.`,e)}function _e(){if(U(process.stdout))process.stdout.write(`${le()}
254
+
255
+ `);process.stdout.write(`${H}
256
+ `)}async function _t(){let e=await G(),n=process.env.DUN_TOKEN||e.token,t=process.env.DUN_WORKSPACE_ID||e.defaultWorkspaceId;process.stdout.write(`${pe({token:n,workspaceId:t,config:e})}
257
+ `)}async function Pt(e){let n=oe(e),{group:t,action:s,nest:c,flags:r,positionals:o}=n;if(K(r)&&!t)return process.stdout.write(`${N}
258
+ `),a.OK;if(t==="help"||!t&&B(r)){let l=t==="help"?s||o[0]:void 0;if(l)process.stdout.write(`${z(l)}
259
+ `);else _e();return a.OK}if(!t){if(U(process.stdout))await _t();else _e();return a.OK}if(B(r))return process.stdout.write(`${z(t)}
260
+ `),a.OK;let i=await ie({apiUrl:f(r,"api-url","apiUrl"),token:f(r,"token"),workspace:f(r,"workspace","w"),json:h(r,"json","j")?!0:void 0,debug:h(r,"debug"),quiet:h(r,"quiet","q"),yes:h(r,"yes","y")});if(K(r))return m({version:N},i),a.OK;try{switch(t){case"auth":await fe(i,s,r,o);break;case"workspace":case"workspaces":await ge(i,s,r,o);break;case"account":case"accounts":if(s==="reddit"){await Y(i,o[0],r,o.slice(1));break}await we(i,s??"list",r,o);break;case"reddit":await Y(i,s,r,o);break;case"post":case"posts":await ye(i,s,r,o);break;case"draft":case"drafts":await Ie(i,s,r,o);break;case"media":await Ne(i,s,r,o);break;case"memory":{let l=c?[c,s].filter(Boolean):s?[s]:[];await Ee(i,l,r,o);break}case"personalization":case"voice":await Ce(i,s,r,o);break;case"skill":case"skills":await Le(i,s,r,o);break;case"update":case"upgrade":case"self-update":await De(i,s,r);break;case"version":m({version:N},i);break;default:throw new d(`Unknown command "${t}". Run dun --help`,a.USAGE)}return a.OK}catch(l){return ae(l,i)}}var Mt=await Pt(process.argv.slice(2));process.exit(Mt);
package/package.json CHANGED
@@ -1,18 +1,10 @@
1
1
  {
2
2
  "name": "dunsocial",
3
- "version": "0.10.0",
3
+ "version": "0.12.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",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/thisuxhq/dunpostsapp.git",
10
- "directory": "apps/cli"
11
- },
12
- "bugs": {
13
- "url": "https://github.com/thisuxhq/dunpostsapp/issues"
14
- },
15
- "homepage": "https://dunsocial.com",
7
+ "homepage": "https://www.dunsocial.com/cli",
16
8
  "keywords": [
17
9
  "dunsocial",
18
10
  "cli",
@@ -160,11 +160,16 @@ Also: `dun reddit requirements <subreddit> --json` for live rules (`is_flair_req
160
160
  ```bash
161
161
  dun drafts create --text "WIP" --platforms x,linkedin --json
162
162
  dun memory collections list --json
163
+ dun memory collections create --name "Brand voice" --json
164
+ dun memory collections update <id> --name "Renamed" --json
165
+ dun memory collections delete <id> --yes --json
163
166
  dun memory save --collection <id> --text "…" --json
164
167
  dun memory search --prompt "…" --collections <id> --json
165
168
 
166
169
  # thread.json: [ {"content":"1"},{"content":"2"} ]
167
170
  dun posts schedule-thread --account acc_x --file ./thread.json --in 2h --json
171
+ dun posts publish-thread --account acc_x --file ./thread.json --json
172
+ # or: dun posts publish-thread --account acc_x --text "tweet 1||tweet 2" --json
168
173
  ```
169
174
 
170
175
  ## Personalization (workspace AI voice)