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