dunsocial 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,27 +1,27 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as mt}from"node:fs";import{mkdir as ft,readFile as gt,writeFile as wt,chmod as ht,unlink as kt}from"node:fs/promises";import{dirname as bt,join as X}from"node:path";import{homedir as yt}from"node:os";var c={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7,PAYMENT:8};class d extends Error{code;details;constructor(e,n=c.USAGE,t){super(e);this.name="CliError",this.code=n,this.details=t}}function he(e){if(e===401)return c.AUTH;if(e===402)return c.PAYMENT;if(e===403)return c.FORBIDDEN;if(e===404)return c.NOT_FOUND;if(e===429)return c.RATE_LIMITED;if(e>=400&&e<500)return c.VALIDATION;return c.NETWORK}var It="https://api.dunsocial.com",ke="0.14.0";function At(){if(!ke.includes("@@"))return ke;try{let e=JSON.parse(mt(X(import.meta.dir,"..","package.json"),"utf8"));if(typeof e.version==="string"&&e.version.length>0)return e.version}catch{}return"0.0.0"}var U=At();function W(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let e=process.env.XDG_CONFIG_HOME,n=e?e:X(yt(),".config");return X(n,"dunsocial","config.json")}function P(){return W()}async function Z(){let e=W();try{let n=await gt(e,"utf8");return JSON.parse(n)}catch(n){if(n.code==="ENOENT")return{};throw new d(`Failed to read config at ${e}: ${n.message}`,c.USAGE)}}async function D(e){let n=W();await ft(bt(n),{recursive:!0});let t={...e,updatedAt:new Date().toISOString()};await wt(n,`${JSON.stringify(t,null,2)}
3
- `,"utf8");try{await ht(n,384)}catch{}}async function be(){let e=W();try{await kt(e)}catch(n){if(n.code!=="ENOENT")throw new d(`Failed to clear config: ${n.message}`,c.USAGE)}}async function ye(e){let n=await Z(),t=(e.apiUrl||process.env.DUN_API_URL||n.apiUrl||It).replace(/\/$/,""),i=e.token||process.env.DUN_TOKEN||n.token,a=e.workspace||process.env.DUN_WORKSPACE_ID||n.defaultWorkspaceId||void 0,r=process.env.DUN_JSON==="0",s=!process.stdout.isTTY,o=r?!1:e.json===!0||process.env.DUN_JSON==="1"||process.env.CI==="true"||process.env.CI==="1"||s;return{apiUrl:t,token:i,workspaceId:a,json:o,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 y(e){if(!e.token)throw new d("Not authenticated. Run `dun auth login` (or `dun auth login --token <token>` / set DUN_TOKEN).",c.AUTH);return e.token}function I(e){if(!e.workspaceId)throw new d("No workspace selected. Run `dun workspace use <id|slug>` or pass --workspace / DUN_WORKSPACE_ID.",c.VALIDATION);return e.workspaceId}function Q(e){let n={},t=[],i=0,a=!0;while(i<e.length){let u=e[i];if(a&&u==="--"){a=!1,i+=1;continue}if(a&&u.startsWith("--")){let p=u.indexOf("=");if(p!==-1){n[u.slice(2,p)]=u.slice(p+1),i+=1;continue}let g=u.slice(2),w=e[i+1];if(w!==void 0&&!w.startsWith("-"))n[g]=w,i+=2;else n[g]=!0,i+=1;continue}if(a&&u.startsWith("-")&&u.length>1&&u!=="-"){let p=u.slice(1);if(p.length>1&&!p.includes("=")){for(let k of p)n[k]=!0;i+=1;continue}let g=p,w=e[i+1];if(w!==void 0&&!w.startsWith("-"))n[g]=w,i+=2;else n[g]=!0,i+=1;continue}t.push(u),i+=1}let r=t[0],s,o,l=1;if(r==="memory"&&(t[1]==="collections"||t[1]==="collection"))o="collections",s=t[2],l=s?3:2;else s=t[1],l=s?2:1;return{group:r,action:s,nest:o,flags:n,positionals:t.slice(l)}}function f(e,...n){for(let t of n){let i=e[t];if(typeof i==="string"&&i.length>0)return i}return}function h(e,...n){for(let t of n){let i=e[t];if(i===!0)return!0;if(typeof i==="string"){let a=i.toLowerCase();if(a==="1"||a==="true"||a==="yes")return!0}}return!1}function C(e,...n){let t=f(e,...n);if(t===void 0)return;let i=Number(t);if(!Number.isFinite(i))throw Error(`Invalid number for --${n[0]}: ${t}`);return i}function E(e,...n){let t=f(e,...n);if(t===void 0)return;return t.split(",").map((i)=>i.trim()).filter(Boolean)}function ee(e){return h(e,"help","h")}function te(e){return h(e,"version","v","V")}function m(e,n){if(n.quiet&&!n.json)return;if(n.json||!process.stdout.isTTY){process.stdout.write(`${JSON.stringify({ok:!0,data:e},null,2)}
2
+ import{readFileSync as ft}from"node:fs";import{mkdir as gt,readFile as wt,writeFile as ht,chmod as kt,unlink as bt}from"node:fs/promises";import{dirname as yt,join as X}from"node:path";import{homedir as It}from"node:os";var c={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7,PAYMENT:8};class d extends Error{code;details;constructor(e,n=c.USAGE,t){super(e);this.name="CliError",this.code=n,this.details=t}}function ke(e){if(e===401)return c.AUTH;if(e===402)return c.PAYMENT;if(e===403)return c.FORBIDDEN;if(e===404)return c.NOT_FOUND;if(e===429)return c.RATE_LIMITED;if(e>=400&&e<500)return c.VALIDATION;return c.NETWORK}var At="https://api.dunsocial.com",be="0.15.0";function St(){if(!be.includes("@@"))return be;try{let e=JSON.parse(ft(X(import.meta.dir,"..","package.json"),"utf8"));if(typeof e.version==="string"&&e.version.length>0)return e.version}catch{}return"0.0.0"}var T=St();function W(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let e=process.env.XDG_CONFIG_HOME,n=e?e:X(It(),".config");return X(n,"dunsocial","config.json")}function D(){return W()}async function Z(){let e=W();try{let n=await wt(e,"utf8");return JSON.parse(n)}catch(n){if(n.code==="ENOENT")return{};throw new d(`Failed to read config at ${e}: ${n.message}`,c.USAGE)}}async function P(e){let n=W();await gt(yt(n),{recursive:!0});let t={...e,updatedAt:new Date().toISOString()};await ht(n,`${JSON.stringify(t,null,2)}
3
+ `,"utf8");try{await kt(n,384)}catch{}}async function ye(){let e=W();try{await bt(e)}catch(n){if(n.code!=="ENOENT")throw new d(`Failed to clear config: ${n.message}`,c.USAGE)}}async function Ie(e){let n=await Z(),t=(e.apiUrl||process.env.DUN_API_URL||n.apiUrl||At).replace(/\/$/,""),i=e.token||process.env.DUN_TOKEN||n.token,a=e.workspace||process.env.DUN_WORKSPACE_ID||n.defaultWorkspaceId||void 0,r=process.env.DUN_JSON==="0",s=!process.stdout.isTTY,o=r?!1:e.json===!0||process.env.DUN_JSON==="1"||process.env.CI==="true"||process.env.CI==="1"||s;return{apiUrl:t,token:i,workspaceId:a,json:o,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 y(e){if(!e.token)throw new d("Not authenticated. Run `dun auth login` (or `dun auth login --token <token>` / set DUN_TOKEN).",c.AUTH);return e.token}function I(e){if(!e.workspaceId)throw new d("No workspace selected. Run `dun workspace use <id|slug>` or pass --workspace / DUN_WORKSPACE_ID.",c.VALIDATION);return e.workspaceId}function Q(e){let n={},t=[],i=0,a=!0;while(i<e.length){let u=e[i];if(a&&u==="--"){a=!1,i+=1;continue}if(a&&u.startsWith("--")){let m=u.indexOf("=");if(m!==-1){n[u.slice(2,m)]=u.slice(m+1),i+=1;continue}let g=u.slice(2),w=e[i+1];if(w!==void 0&&!w.startsWith("-"))n[g]=w,i+=2;else n[g]=!0,i+=1;continue}if(a&&u.startsWith("-")&&u.length>1&&u!=="-"){let m=u.slice(1);if(m.length>1&&!m.includes("=")){for(let k of m)n[k]=!0;i+=1;continue}let g=m,w=e[i+1];if(w!==void 0&&!w.startsWith("-"))n[g]=w,i+=2;else n[g]=!0,i+=1;continue}t.push(u),i+=1}let r=t[0],s,o,l=1;if(r==="memory"&&(t[1]==="collections"||t[1]==="collection"))o="collections",s=t[2],l=s?3:2;else s=t[1],l=s?2:1;return{group:r,action:s,nest:o,flags:n,positionals:t.slice(l)}}function f(e,...n){for(let t of n){let i=e[t];if(typeof i==="string"&&i.length>0)return i}return}function h(e,...n){for(let t of n){let i=e[t];if(i===!0)return!0;if(typeof i==="string"){let a=i.toLowerCase();if(a==="1"||a==="true"||a==="yes")return!0}}return!1}function U(e,...n){let t=f(e,...n);if(t===void 0)return;let i=Number(t);if(!Number.isFinite(i))throw Error(`Invalid number for --${n[0]}: ${t}`);return i}function E(e,...n){let t=f(e,...n);if(t===void 0)return;return t.split(",").map((i)=>i.trim()).filter(Boolean)}function ee(e){return h(e,"help","h")}function te(e){return h(e,"version","v","V")}function p(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
4
  `);return}if(e===void 0||e===null){process.stdout.write(`ok
5
5
  `);return}if(typeof e==="string"){process.stdout.write(`${e}
6
- `);return}process.stdout.write(`${Ct(e,n.emptyMessage)}
7
- `)}function Ae(e,n){let{message:t,code:i,details:a}=Ut(e);if(n.json||!process.stdout.isTTY)process.stdout.write(`${JSON.stringify({ok:!1,error:{code:St(i),message:t,details:a??null}},null,2)}
6
+ `);return}process.stdout.write(`${Et(e,n.emptyMessage)}
7
+ `)}function Se(e,n){let{message:t,code:i,details:a}=Ut(e);if(n.json||!process.stdout.isTTY)process.stdout.write(`${JSON.stringify({ok:!1,error:{code:Ct(i),message:t,details:a??null}},null,2)}
8
8
  `);else{process.stderr.write(`error: ${t}
9
9
  `);let r=Tt(a);if(i===c.PAYMENT&&r)process.stderr.write(`Subscribe: ${r}
10
10
  `);else if(a!==void 0&&process.env.DUN_DEBUG==="1")process.stderr.write(`${JSON.stringify(a,null,2)}
11
- `)}return i}function St(e){switch(e){case c.AUTH:return"auth";case c.FORBIDDEN:return"forbidden";case c.VALIDATION:return"validation";case c.NOT_FOUND:return"not_found";case c.RATE_LIMITED:return"rate_limited";case c.NETWORK:return"network";case c.PAYMENT:return"payment_required";default:return"usage"}}function Tt(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 Ut(e){if(e instanceof d)return{message:e.message,code:e.code,details:e.details};if(e instanceof Error)return{message:e.message,code:c.USAGE};return{message:String(e),code:c.USAGE}}function Ct(e,n){if(Array.isArray(e)){if(e.length===0)return n??"(empty)";if(e.every((t)=>t&&typeof t==="object"))return Ie(e,n);return e.map((t)=>`- ${ne(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 i=typeof t.total==="number"?`total=${t.total} showing=${t.items.length}`:void 0,a=Ie(t.items,n);return i?`${i}
11
+ `)}return i}function Ct(e){switch(e){case c.AUTH:return"auth";case c.FORBIDDEN:return"forbidden";case c.VALIDATION:return"validation";case c.NOT_FOUND:return"not_found";case c.RATE_LIMITED:return"rate_limited";case c.NETWORK:return"network";case c.PAYMENT:return"payment_required";default:return"usage"}}function Tt(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 Ut(e){if(e instanceof d)return{message:e.message,code:e.code,details:e.details};if(e instanceof Error)return{message:e.message,code:c.USAGE};return{message:String(e),code:c.USAGE}}function Et(e,n){if(Array.isArray(e)){if(e.length===0)return n??"(empty)";if(e.every((t)=>t&&typeof t==="object"))return Ae(e,n);return e.map((t)=>`- ${ne(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 i=typeof t.total==="number"?`total=${t.total} showing=${t.items.length}`:void 0,a=Ae(t.items,n);return i?`${i}
13
13
  ${a}`:a}return Object.entries(t).map(([i,a])=>`${i}: ${ne(a)}`).join(`
14
- `)}return String(e)}function Ie(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"],i=new Set;for(let u of e)for(let p of Object.keys(u))i.add(p);let a=[...t.filter((u)=>i.has(u)),...[...i].filter((u)=>!t.includes(u)).slice(0,6)].slice(0,8),r=e.map((u)=>a.map((p)=>Et(ne(u[p]),p==="content"||p==="text"?48:28))),s=a.map((u,p)=>Math.max(u.length,...r.map((g)=>g[p]?.length??0))),o=a.map((u,p)=>u.padEnd(s[p])).join(" "),l=r.map((u)=>u.map((p,g)=>p.padEnd(s[g])).join(" ")).join(`
14
+ `)}return String(e)}function Ae(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"],i=new Set;for(let u of e)for(let m of Object.keys(u))i.add(m);let a=[...t.filter((u)=>i.has(u)),...[...i].filter((u)=>!t.includes(u)).slice(0,6)].slice(0,8),r=e.map((u)=>a.map((m)=>Nt(ne(u[m]),m==="content"||m==="text"?48:28))),s=a.map((u,m)=>Math.max(u.length,...r.map((g)=>g[m]?.length??0))),o=a.map((u,m)=>u.padEnd(s[m])).join(" "),l=r.map((u)=>u.map((m,g)=>m.padEnd(s[g])).join(" ")).join(`
15
15
  `);return`${o}
16
- ${l}`}function ne(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)}
16
+ ${l}`}function ne(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 Nt(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
17
  `);return}process.stderr.write(`[debug] ${n}
18
- `)}function T(e,n){let t=`${e} ${n}`,i="-".repeat(Math.max(12,t.length+4));return`${t}
18
+ `)}function C(e,n){let t=`${e} ${n}`,i="-".repeat(Math.max(12,t.length+4));return`${t}
19
19
  ${i}`}var re=`dun - schedule & publish social posts
20
20
 
21
21
  Usage:
22
22
  dun <command> [flags]
23
23
 
24
- ${T("\uD83D\uDD10","Auth")}
24
+ ${C("\uD83D\uDD10","Auth")}
25
25
  dun auth login # device-code browser flow
26
26
  dun auth login --token <token> # CI / headless
27
27
  dun auth login --no-browser
@@ -29,25 +29,30 @@ ${T("\uD83D\uDD10","Auth")}
29
29
  dun auth whoami
30
30
  dun auth status
31
31
 
32
- ${T("\uD83D\uDCC1","Workspaces")}
32
+ ${C("\uD83D\uDCC1","Workspaces")}
33
33
  dun workspace list
34
34
  dun workspace use <id|slug>
35
35
  dun workspace current
36
36
 
37
- ${T("\uD83D\uDD17","Accounts")}
37
+ ${C("\uD83D\uDD17","Accounts")}
38
38
  dun accounts list [--platform x|reddit|...]
39
39
 
40
- ${T("\uD83D\uDFE0","Reddit")}
40
+ ${C("\uD83D\uDFE0","Reddit")}
41
41
  dun reddit flairs <subreddit> # template id + text + textEditable
42
42
  dun reddit requirements <subreddit> # live post rules (flair required, ...)
43
43
  # alias: dun accounts reddit flairs <subreddit>
44
44
 
45
- ${T("\uD83D\uDCCC","Pinterest")}
45
+ ${C("\uD83D\uDCCC","Pinterest")}
46
46
  dun pinterest boards # live boards for the connected account
47
47
  dun pinterest create-board --name "..." [--private]
48
48
  # alias: dun accounts pinterest boards
49
49
 
50
- ${T("\uD83D\uDCDD","Posts")}
50
+ ${C("\uD83D\uDCAC","Discord")}
51
+ dun discord channels [--account <id>] # text and announcement channels
52
+ # alias: dun accounts discord channels
53
+ # then: --meta '{"discord":{"channelId":"..."}}'
54
+
55
+ ${C("\uD83D\uDCDD","Posts")}
51
56
  dun posts list [--status scheduled|published|failed|draft|cancelled]
52
57
  dun posts get <id>
53
58
  dun posts validate --accounts <id> --text "..." [--media id] [--meta '{...}']
@@ -68,20 +73,20 @@ ${T("\uD83D\uDCDD","Posts")}
68
73
  # Do not mix video + images on LinkedIn.
69
74
  # publish queues async work; without --wait, status scheduled means queued
70
75
 
71
- ${T("\uD83D\uDCC4","Drafts")}
76
+ ${C("\uD83D\uDCC4","Drafts")}
72
77
  dun drafts list
73
78
  dun drafts get <id>
74
79
  dun drafts create --text "..." [--platforms x,linkedin] [--accounts id,id]
75
80
  dun drafts update <id> --text "..."
76
81
  dun drafts delete <id> [--yes]
77
82
 
78
- ${T("\uD83D\uDDBC️","Media")}
83
+ ${C("\uD83D\uDDBC️","Media")}
79
84
  dun media list
80
85
  dun media get <id>
81
86
  dun media upload <file> [--alt "..."] # local preflight: images <=8MB, video <=16GB
82
87
  dun media delete <id> [--yes]
83
88
 
84
- ${T("\uD83E\uDDE0","Memory")}
89
+ ${C("\uD83E\uDDE0","Memory")}
85
90
  dun memory collections list
86
91
  dun memory collections create --name "..." [--color blue] [--private]
87
92
  dun memory collections get <id>
@@ -92,7 +97,7 @@ ${T("\uD83E\uDDE0","Memory")}
92
97
  dun memory search --prompt "..." --collections <id,id> [--top-k 5]
93
98
  dun memory delete <id> --collection <id> [--yes]
94
99
 
95
- ${T("\uD83C\uDF99️","Personalization")}
100
+ ${C("\uD83C\uDF99️","Personalization")}
96
101
  # workspace AI voice
97
102
  dun personalization get
98
103
  dun personalization set --description "..."
@@ -100,14 +105,14 @@ ${T("\uD83C\uDF99️","Personalization")}
100
105
  dun personalization set --clear
101
106
  # requires PAT scope workspace:write for set (owner/admin)
102
107
 
103
- ${T("\uD83E\uDDE9","Agent skills")}
108
+ ${C("\uD83E\uDDE9","Agent skills")}
104
109
  dun skill install <agent|all>
105
110
  dun skill list
106
111
  dun skill path
107
112
  # agents: claude codex cursor pi opencode agents copilot windsurf
108
113
  # goose gemini amp grok continue antigravity
109
114
 
110
- ${T("⬆️","Update")}
115
+ ${C("⬆️","Update")}
111
116
  dun update # upgrade to latest from npm
112
117
  dun update --check # report only
113
118
  dun update --force # reinstall latest even if current
@@ -117,7 +122,7 @@ ${T("⬆️","Update")}
117
122
  # disable: --no-update or DUN_UPDATE=0
118
123
  # major bumps print: dun update --yes
119
124
 
120
- ${T("⚙️","Global flags")}
125
+ ${C("⚙️","Global flags")}
121
126
  --json Machine-readable output (default when non-TTY / CI)
122
127
  --workspace <id> Override workspace for this command
123
128
  --api-url <url> Override API base (default https://api.dunsocial.com)
@@ -129,10 +134,10 @@ ${T("⚙️","Global flags")}
129
134
  --help, -h Show help
130
135
  --version, -v Show version
131
136
 
132
- ${T("\uD83C\uDF31","Env")}
137
+ ${C("\uD83C\uDF31","Env")}
133
138
  DUN_TOKEN DUN_WORKSPACE_ID DUN_API_URL DUN_CONFIG DUN_JSON=1 DUN_DEBUG=1 DUN_UPDATE=0
134
139
 
135
- ${T("\uD83D\uDEAA","Exit codes")}
140
+ ${C("\uD83D\uDEAA","Exit codes")}
136
141
  0 ok | 1 usage | 2 auth | 3 forbidden | 4 validation | 5 not found | 6 rate limit | 7 network | 8 payment
137
142
 
138
143
  Docs: https://dunsocial.com/docs/cli
@@ -150,6 +155,18 @@ Docs: https://dunsocial.com/docs/cli
150
155
  `,workspaces:"Alias of workspace",accounts:`dun accounts list [--platform <name>]
151
156
  dun accounts reddit flairs <subreddit> # alias of dun reddit flairs
152
157
  dun accounts pinterest boards # alias of dun pinterest boards
158
+ dun accounts discord channels # alias of dun discord channels
159
+ `,discord:`dun discord
160
+
161
+ channels [--account <id>] List text and announcement channels for the connected Discord server
162
+
163
+ Output fields: id, name, type (text | announcement)
164
+ Use channel id in
165
+ dun posts publish --meta '{"discord":{"channelId":"..."}}'
166
+
167
+ Example:
168
+ dun discord channels --json
169
+ dun discord channels --account <id> --json
153
170
  `,pinterest:`dun pinterest
154
171
 
155
172
  boards List boards for the connected Pinterest account
@@ -193,6 +210,10 @@ Platform metadata (--meta / --metadata / --meta-json):
193
210
  Reddit (required when publishing to a Reddit account):
194
211
  {"reddit":{"subreddit":"NAME","kind":"self|link|image|video","title":"...","flairId":"..."}}
195
212
  Optional reddit fields: body, url (link), flairText (only if textEditable), posterMediaAssetId, nsfw, spoiler
213
+ Discord (required when publishing to a Discord account):
214
+ {"discord":{"channelId":"SNOWFLAKE"}}
215
+ Optional discord fields: channelName
216
+ Discover channel ids with: dun discord channels --json
196
217
 
197
218
  Reddit happy path:
198
219
  1. dun media upload ./video.mp4 --json # rejects >16GB locally
@@ -252,13 +273,13 @@ after a successful command. Agents, CI, --json, and npx never auto-update.
252
273
  Disable with --no-update or DUN_UPDATE=0.
253
274
 
254
275
  Aliases: dun upgrade, dun self-update
255
- `,upgrade:"Alias of update","self-update":"Alias of update"}[e]??re}var Nt=["DunSocial","---------"].join(`
256
- `),Se="DunSocial";var jn=Te(Nt),Wn=Te(Se);function Te(e){return Math.max(...e.split(`
257
- `).map((n)=>n.length))}function x(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 Ue(){return`DunSocial CLI v${U}`}function Ce(){return`${Se}
258
- CLI v${U}`}var vt="https://dunsocial.com/docs/cli";function xt(e){if(!e.token)return"signed_out";if(!e.workspaceId)return"needs_workspace";return"ready"}function Ee(e){let n=xt(e),t=[`DunSocial CLI v${U}`,""];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 i=e.config.defaultWorkspaceSlug||e.config.defaultWorkspaceName||e.workspaceId||"workspace";t.push(`Ready | workspace: ${i}`,""," Tip: dun accounts list"," dun posts list")}return t.push(""," dun --help all commands",` Docs ${vt}`,""),t.join(`
259
- `)}import{spawn as Lt}from"node:child_process";class Ne{ctx;constructor(e){this.ctx=e}async request(e){let n=e.method??"GET",t=new URL(e.path.startsWith("http")?e.path:`${this.ctx.apiUrl}${e.path}`);if(e.query)for(let[l,u]of Object.entries(e.query)){if(u===void 0||u===null||u==="")continue;t.searchParams.set(l,String(u))}let i={Accept:"application/json","User-Agent":`dunsocial-cli/${U}`,...e.headers??{}};if(e.auth!==!1)i.Authorization=`Bearer ${y(this.ctx)}`;if(e.workspace)i["X-Workspace-Id"]=I(this.ctx);let a;if(e.body!==void 0)i["Content-Type"]="application/json",a=JSON.stringify(e.body);R(this.ctx.debug,`${n} ${t.toString()}`);let r;try{r=await fetch(t,{method:n,headers:i,body:a})}catch(l){throw new d(`Network error: ${l.message}`,c.NETWORK,{cause:String(l)})}let s=await r.text(),o=null;if(s)try{o=JSON.parse(s)}catch{o=null}if(R(this.ctx.debug,`<- ${r.status}`,{ok:r.ok,bodyPreview:s.slice(0,300)}),!r.ok){let l=o?.error||o?.message||(s?s.slice(0,300):`HTTP ${r.status}`);throw new d(l,he(r.status),Rt(r.status,o,s))}if(o&&typeof o==="object"&&"success"in o){if(o.success===!1)throw new d(o.error||o.message||"Request failed",c.VALIDATION,o);return o.data}return o??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 b(e){return new Ne(e)}function Rt(e,n,t){if(e===402&&n&&typeof n==="object"){let i=n;return{billingUrl:typeof i.billingUrl==="string"?i.billingUrl:void 0,subscriptionStatus:typeof i.subscriptionStatus==="string"?i.subscriptionStatus:void 0,workspaceSlug:typeof i.workspaceSlug==="string"?i.workspaceSlug:void 0}}return{status:e,body:n??t}}async function ve(e,n,t,i){switch(n){case"login":return $t(e,t);case"logout":return Dt(e);case"whoami":return _t(e);case"status":return Mt(e);default:throw new d(`Unknown auth command "${n??""}". Try: login, logout, whoami, status`,c.USAGE)}}async function $t(e,n){let t=f(n,"token")||process.env.DUN_TOKEN;if(t)return Ot(e,t);return Pt(e,n)}function q(e){if(e)return"Logged in.";return`Logged in.
260
- Next: dun workspace list && dun workspace use <id|slug>`}async function Ot(e,n){let t={...e,token:n},a=await b(t).get("/api/user/profile");if(await D({...e.config,apiUrl:e.apiUrl,token:n,defaultWorkspaceId:e.config.defaultWorkspaceId,defaultWorkspaceSlug:e.config.defaultWorkspaceSlug,defaultWorkspaceName:e.config.defaultWorkspaceName}),!e.json&&x(process.stdout)){m(q(Boolean(e.config.defaultWorkspaceId)),e);return}m({user:{id:a.id,name:a.name,email:a.email},configPath:P(),method:"token",message:q(Boolean(e.config.defaultWorkspaceId))},e)}async function Pt(e,n){let t=b({...e,token:void 0}),i=await t.post("/api/cli/auth/start",{clientName:"dun-cli"},{auth:!1}),a=!h(n,"no-browser");if(!e.json){if(x(process.stderr))process.stderr.write(`
261
- ${Ce()}
276
+ `,upgrade:"Alias of update","self-update":"Alias of update"}[e]??re}var vt=["DunSocial","---------"].join(`
277
+ `),Ce="DunSocial";var Wn=Te(vt),qn=Te(Ce);function Te(e){return Math.max(...e.split(`
278
+ `).map((n)=>n.length))}function x(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 Ue(){return`DunSocial CLI v${T}`}function Ee(){return`${Ce}
279
+ CLI v${T}`}var xt="https://dunsocial.com/docs/cli";function Rt(e){if(!e.token)return"signed_out";if(!e.workspaceId)return"needs_workspace";return"ready"}function Ne(e){let n=Rt(e),t=[`DunSocial CLI v${T}`,""];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 i=e.config.defaultWorkspaceSlug||e.config.defaultWorkspaceName||e.workspaceId||"workspace";t.push(`Ready | workspace: ${i}`,""," Tip: dun accounts list"," dun posts list")}return t.push(""," dun --help all commands",` Docs ${xt}`,""),t.join(`
280
+ `)}import{spawn as $t}from"node:child_process";class ve{ctx;constructor(e){this.ctx=e}async request(e){let n=e.method??"GET",t=new URL(e.path.startsWith("http")?e.path:`${this.ctx.apiUrl}${e.path}`);if(e.query)for(let[l,u]of Object.entries(e.query)){if(u===void 0||u===null||u==="")continue;t.searchParams.set(l,String(u))}let i={Accept:"application/json","User-Agent":`dunsocial-cli/${T}`,...e.headers??{}};if(e.auth!==!1)i.Authorization=`Bearer ${y(this.ctx)}`;if(e.workspace)i["X-Workspace-Id"]=I(this.ctx);let a;if(e.body!==void 0)i["Content-Type"]="application/json",a=JSON.stringify(e.body);R(this.ctx.debug,`${n} ${t.toString()}`);let r;try{r=await fetch(t,{method:n,headers:i,body:a})}catch(l){throw new d(`Network error: ${l.message}`,c.NETWORK,{cause:String(l)})}let s=await r.text(),o=null;if(s)try{o=JSON.parse(s)}catch{o=null}if(R(this.ctx.debug,`<- ${r.status}`,{ok:r.ok,bodyPreview:s.slice(0,300)}),!r.ok){let l=o?.error||o?.message||(s?s.slice(0,300):`HTTP ${r.status}`);throw new d(l,ke(r.status),Lt(r.status,o,s))}if(o&&typeof o==="object"&&"success"in o){if(o.success===!1)throw new d(o.error||o.message||"Request failed",c.VALIDATION,o);return o.data}return o??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 b(e){return new ve(e)}function Lt(e,n,t){if(e===402&&n&&typeof n==="object"){let i=n;return{billingUrl:typeof i.billingUrl==="string"?i.billingUrl:void 0,subscriptionStatus:typeof i.subscriptionStatus==="string"?i.subscriptionStatus:void 0,workspaceSlug:typeof i.workspaceSlug==="string"?i.workspaceSlug:void 0}}return{status:e,body:n??t}}async function xe(e,n,t,i){switch(n){case"login":return Ot(e,t);case"logout":return _t(e);case"whoami":return jt(e);case"status":return Mt(e);default:throw new d(`Unknown auth command "${n??""}". Try: login, logout, whoami, status`,c.USAGE)}}async function Ot(e,n){let t=f(n,"token")||process.env.DUN_TOKEN;if(t)return Dt(e,t);return Pt(e,n)}function q(e){if(e)return"Logged in.";return`Logged in.
281
+ Next: dun workspace list && dun workspace use <id|slug>`}async function Dt(e,n){let t={...e,token:n},a=await b(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&&x(process.stdout)){p(q(Boolean(e.config.defaultWorkspaceId)),e);return}p({user:{id:a.id,name:a.name,email:a.email},configPath:D(),method:"token",message:q(Boolean(e.config.defaultWorkspaceId))},e)}async function Pt(e,n){let t=b({...e,token:void 0}),i=await t.post("/api/cli/auth/start",{clientName:"dun-cli"},{auth:!1}),a=!h(n,"no-browser");if(!e.json){if(x(process.stderr))process.stderr.write(`
282
+ ${Ee()}
262
283
 
263
284
  `);else process.stderr.write(`
264
285
  DunSocial CLI login
@@ -271,33 +292,34 @@ DunSocial CLI login
271
292
 
272
293
  `),process.stderr.write(`Waiting for approval...
273
294
  `)}else process.stderr.write(JSON.stringify({ok:!0,phase:"waiting",userCode:i.userCode,verificationUrlComplete:i.verificationUrlComplete})+`
274
- `);if(a&&process.stdout.isTTY)try{await Wt(i.verificationUrlComplete)}catch{}let r=Math.max(3,i.interval||5)*1000,s=Date.now()+(i.expiresIn||900)*1000;while(Date.now()<s){await jt(r);let o=await t.post("/api/cli/auth/poll",{deviceCode:i.deviceCode},{auth:!1});if(o.status==="pending")continue;if(o.status==="expired")throw new d("Login expired. Run dun auth login again.",c.AUTH);if(o.status==="denied")throw new d("Login was denied in the browser.",c.AUTH);if(o.status==="approved"){let l=o.token,u={...e,token:l},p=b(u),g=await p.get("/api/user/profile"),w={...e.config,apiUrl:e.apiUrl,token:l,defaultWorkspaceId:o.workspaceId||e.config.defaultWorkspaceId,defaultWorkspaceSlug:e.config.defaultWorkspaceSlug,defaultWorkspaceName:e.config.defaultWorkspaceName};if(o.workspaceId)try{let N=(await p.get("/api/workspaces")).find((pt)=>pt.id===o.workspaceId);if(N)w.defaultWorkspaceId=N.id,w.defaultWorkspaceSlug=N.slug,w.defaultWorkspaceName=N.name}catch{}await D(w);let k=Boolean(w.defaultWorkspaceId);if(!e.json&&x(process.stdout)){m(q(k),e);return}m({user:{id:g.id,name:g.name,email:g.email},workspaceId:w.defaultWorkspaceId??null,configPath:P(),method:"device",message:q(k)},e);return}}throw new d("Login timed out. Run dun auth login again.",c.AUTH)}async function Dt(e){if(await be(),!e.json&&x(process.stdout)){m("Logged out. Config cleared.",e);return}m({message:"Logged out. Config cleared.",configPath:P()},e)}async function _t(e){y(e);let t=await b(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 Mt(e){let n=Boolean(e.token),t=null,i=null;if(n)try{t=await b(e).get("/api/user/profile")}catch(a){i=a instanceof Error?a.message:String(a)}m({authenticated:Boolean(t),tokenPresent:n,apiUrl:e.apiUrl,workspaceId:e.workspaceId??null,workspaceSlug:e.config.defaultWorkspaceSlug??null,workspaceName:e.config.defaultWorkspaceName??null,configPath:P(),user:t?{id:t.id,name:t.name,email:t.email}:null,error:i},e)}function jt(e){return new Promise((n)=>setTimeout(n,e))}async function Wt(e){let n=process.platform,t=n==="darwin"?["open",e]:n==="win32"?["cmd","/c","start","",e]:["xdg-open",e];await new Promise((i,a)=>{let r=Lt(t[0],t.slice(1),{stdio:"ignore",windowsHide:!0});r.on("error",a),r.on("close",()=>i())})}function xe(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 qt(e){return e?.hasAccess===!1}function Vt(e){if(!qt(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}
275
- Subscribe: ${t}`:n}function Bt(){return"Every workspace is unpaid. Open a billing URL above — posts and drafts stay locked until you subscribe."}function L(e,n){if(n.json||!process.stdout.isTTY)return;let t=e.map((i)=>Vt(i)).filter((i)=>Boolean(i));if(t.length===0)return;if(process.stdout.write(`${t.join(`
295
+ `);if(a&&process.stdout.isTTY)try{await qt(i.verificationUrlComplete)}catch{}let r=Math.max(3,i.interval||5)*1000,s=Date.now()+(i.expiresIn||900)*1000;while(Date.now()<s){await Wt(r);let o=await t.post("/api/cli/auth/poll",{deviceCode:i.deviceCode},{auth:!1});if(o.status==="pending")continue;if(o.status==="expired")throw new d("Login expired. Run dun auth login again.",c.AUTH);if(o.status==="denied")throw new d("Login was denied in the browser.",c.AUTH);if(o.status==="approved"){let l=o.token,u={...e,token:l},m=b(u),g=await m.get("/api/user/profile"),w={...e.config,apiUrl:e.apiUrl,token:l,defaultWorkspaceId:o.workspaceId||e.config.defaultWorkspaceId,defaultWorkspaceSlug:e.config.defaultWorkspaceSlug,defaultWorkspaceName:e.config.defaultWorkspaceName};if(o.workspaceId)try{let N=(await m.get("/api/workspaces")).find((mt)=>mt.id===o.workspaceId);if(N)w.defaultWorkspaceId=N.id,w.defaultWorkspaceSlug=N.slug,w.defaultWorkspaceName=N.name}catch{}await P(w);let k=Boolean(w.defaultWorkspaceId);if(!e.json&&x(process.stdout)){p(q(k),e);return}p({user:{id:g.id,name:g.name,email:g.email},workspaceId:w.defaultWorkspaceId??null,configPath:D(),method:"device",message:q(k)},e);return}}throw new d("Login timed out. Run dun auth login again.",c.AUTH)}async function _t(e){if(await ye(),!e.json&&x(process.stdout)){p("Logged out. Config cleared.",e);return}p({message:"Logged out. Config cleared.",configPath:D()},e)}async function jt(e){y(e);let t=await b(e).get("/api/user/profile");p({id:t.id,name:t.name,email:t.email,image:t.image??null,apiUrl:e.apiUrl,workspaceId:e.workspaceId??null},e)}async function Mt(e){let n=Boolean(e.token),t=null,i=null;if(n)try{t=await b(e).get("/api/user/profile")}catch(a){i=a instanceof Error?a.message:String(a)}p({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:i},e)}function Wt(e){return new Promise((n)=>setTimeout(n,e))}async function qt(e){let n=process.platform,t=n==="darwin"?["open",e]:n==="win32"?["cmd","/c","start","",e]:["xdg-open",e];await new Promise((i,a)=>{let r=$t(t[0],t.slice(1),{stdio:"ignore",windowsHide:!0});r.on("error",a),r.on("close",()=>i())})}function Re(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 Vt(e){return e?.hasAccess===!1}function Bt(e){if(!Vt(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}
296
+ Subscribe: ${t}`:n}function Gt(){return"Every workspace is unpaid. Open a billing URL above — posts and drafts stay locked until you subscribe."}function L(e,n){if(n.json||!process.stdout.isTTY)return;let t=e.map((i)=>Bt(i)).filter((i)=>Boolean(i));if(t.length===0)return;if(process.stdout.write(`${t.join(`
276
297
  `)}
277
- `),t.length===e.length&&e.length>0)process.stdout.write(`${Bt()}
278
- `)}async function Re(e,n,t,i){y(e);let a=b(e);switch(n){case"list":case"ls":{let r=await a.get("/api/workspaces");m(r.map(xe),{...e,emptyMessage:"No workspaces yet."}),L(r,e);return}case"use":{let r=i[0];if(!r)throw new d("Usage: dun workspace use <id|slug>",c.USAGE);let o=(await a.get("/api/workspaces")).find((l)=>l.id===r||l.slug===r);if(!o)throw new d(`Workspace not found: ${r}. Run dun workspace list`,c.NOT_FOUND);await D({...e.config,apiUrl:e.apiUrl,token:e.token,defaultWorkspaceId:o.id,defaultWorkspaceSlug:o.slug,defaultWorkspaceName:o.name}),m({id:o.id,name:o.name,slug:o.slug,role:o.role,hasAccess:o.subscription?.hasAccess??null,accessMessage:o.subscription?.accessMessage??null,billingUrl:o.subscription?.billingUrl??null,message:`Default workspace set to ${o.name}`},e),L([o],e);return}case"current":{if(!e.workspaceId&&!e.config.defaultWorkspaceId)throw new d("No workspace selected. Run dun workspace use <id|slug>",c.VALIDATION);let r=e.workspaceId||e.config.defaultWorkspaceId;try{let o=(await a.get("/api/workspaces")).find((l)=>l.id===r||l.slug===r);if(o){m({id:o.id,name:o.name,slug:o.slug,role:o.role,timezone:o.timezone,hasAccess:o.subscription?.hasAccess??null,accessMessage:o.subscription?.accessMessage??null,billingUrl:o.subscription?.billingUrl??null},e),L([o],e);return}}catch{}m({id:r,name:e.config.defaultWorkspaceName??null,slug:e.config.defaultWorkspaceSlug??null},e);return}default:throw new d(`Unknown workspace command "${n??""}". Try: list, use, current`,c.USAGE)}}async function Le(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case void 0:case"list":case"ls":{let r=await a.get("/api/social-accounts",{workspace:!0}),s=f(t,"platform","p")?.toLowerCase(),o=s?r.filter((l)=>{let u=(l.providerName||"").toLowerCase();return({x:["x","twitter"],twitter:["x","twitter"],linkedin:["linkedin"],"linkedin-business":["linkedin business","linkedin-business","linkedin_business"],bluesky:["bluesky"],threads:["threads"],reddit:["reddit"],pinterest:["pinterest"],instagram:["instagram"],youtube:["youtube"],tiktok:["tiktok"]}[s]??[s]).some((w)=>u.includes(w))}):r;m(o.map((l)=>({id:l.id,providerName:l.providerName,username:l.username??null,displayName:l.displayName??null,isConnected:l.isConnected,expiresAt:l.expiresAt??null})),{...e,emptyMessage:s?`No ${s} accounts connected. Connect one in the DunSocial dashboard.`:"No accounts connected. Connect one in the DunSocial dashboard."});try{let l=await a.get(`/api/workspaces/${I(e)}`,{workspace:!0});L([l],e)}catch{}return}default:throw new d(`Unknown accounts command "${n}". Try: list (or: dun accounts reddit flairs <subreddit>)`,c.USAGE)}}import{readFileSync as Kt}from"node:fs";var Gt=/^(\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 oe(e,n=new Date){let t=e.trim();if(!t)throw new d("Empty time value",c.VALIDATION);let i=t.match(Gt);if(i){let s=Number(i[1]),o=i[2].toLowerCase(),l=Ft(o)*s;return new Date(n.getTime()+l)}let a=t.match(/^in\s+(.+)$/i);if(a)return oe(a[1],n);let r=new Date(t);if(Number.isNaN(r.getTime()))throw new d(`Invalid time "${e}". Use ISO-8601 (2026-06-01T09:00:00Z) or relative (2h, 30m, 1d).`,c.VALIDATION);return r}function Ft(e){switch(e){case"s":case"sec":case"secs":case"second":case"seconds":return 1000;case"m":case"min":case"mins":case"minute":case"minutes":return 60000;case"h":case"hr":case"hrs":case"hour":case"hours":return 3600000;case"d":case"day":case"days":return 86400000;case"w":case"week":case"weeks":return 604800000;default:throw new d(`Unknown time unit: ${e}`,c.VALIDATION)}}function V(e){if(e.at&&e.in)throw new d("Pass only one of --at or --in",c.VALIDATION);if(e.at)return oe(e.at).toISOString();if(e.in)return oe(e.in).toISOString();throw new d("Schedule time required. Pass --at <iso> or --in <duration> (e.g. 2h).",c.VALIDATION)}function Oe(e){if(!e)return[];return e.split(",").map((n)=>n.trim()).filter(Boolean)}function se(e,n){let t=f(e,"text","content","c","t");if(t)return t;let i=n?.reddit;if(i&&typeof i==="object"&&!Array.isArray(i)&&typeof i.title==="string"&&i.title.trim())return i.title.trim();throw new d("Missing --text",c.VALIDATION)}function B(e){let n=E(e,"accounts","account","a")??Oe(f(e,"accounts","account","a"));if(!n||n.length===0)throw new d("Missing --accounts <id,id>",c.VALIDATION);return n}function G(e){return E(e,"media","media-urls")??Oe(f(e,"media","media-urls"))}function Ht(e,n){if(e?.reddit)return!0;return n.some((t)=>t.toLowerCase().includes("reddit"))}function zt(e){let n=e.existing.metadata&&typeof e.existing.metadata==="object"&&!Array.isArray(e.existing.metadata)?e.existing.metadata:{};return{scheduledAt:e.scheduledAt,content:e.text??e.existing.content,mediaUrls:e.media&&e.media.length>0?e.media:e.existing.mediaUrls,metadata:e.meta?{...n,...e.meta}:n}}function O(e){let n=f(e,"meta","metadata","meta-json");if(!n)return;try{let t=JSON.parse(n);if(!t||typeof t!=="object"||Array.isArray(t))throw Error("metadata must be a JSON object");return t}catch(t){throw new d(`Invalid --meta JSON: ${t.message}`,c.VALIDATION)}}var Jt=new Set(["published","failed","cancelled"]);async function Yt(e){await new Promise((n)=>setTimeout(n,e))}async function Xt(e,n,t,i){let a=Date.now()+i*1000,r=new Map,s=1500;while(r.size<t.length){if(Date.now()>a){let u=t.filter((p)=>!r.has(p.id)).map((p)=>p.id);throw new d(`Timed out waiting for publish after ${i}s. Still pending: ${u.join(", ")}. Poll with: dun posts get <id> --json`,c.NETWORK,{pending:u})}for(let u of t){if(r.has(u.id))continue;let p=await e.get(`/api/posts/${u.id}`,{workspace:!0});if(R(n.debug,`wait status ${u.id}=${p.status}`),Jt.has(p.status))r.set(u.id,p)}if(r.size>=t.length)break;await Yt(s),s=Math.min(s*1.4,8000)}let o=t.map((u)=>r.get(u.id)),l=o.filter((u)=>u.status==="failed");if(l.length>0){let u=l.map((p)=>`${p.id}: ${p.error||"publish failed"}`).join("; ");throw new d(`Publish failed — ${u}`,c.VALIDATION,{posts:o})}return o}async function $e(e,n,t){if(t.meta?.reddit){await ae(e,{accounts:n,text:t.text,media:t.media,meta:t.meta});return}let i=await e.get("/api/social-accounts",{workspace:!0}),a=n.map((r)=>i.find((o)=>o.id===r)?.providerName||"");if(!Ht(t.meta,a))return;await ae(e,{accounts:n,text:t.text,media:t.media,meta:t.meta})}async function ae(e,n){let t=[],i=n.meta?.reddit??null;if(!i||typeof i!=="object")throw new d("Reddit accounts require --meta with a reddit object, e.g. "+`--meta '{"reddit":{"subreddit":"…","kind":"self","title":"…","flairId":"…"}}'`,c.VALIDATION);let a=(i.subreddit??"").trim().replace(/^\/?r\//i,""),r=(i.title??"").trim(),s=(i.kind??"").trim(),o=(i.flairId??"").trim(),l=(i.flairText??"").trim();if(!a)throw new d("metadata.reddit.subreddit is required",c.VALIDATION);if(!r)throw new d("metadata.reddit.title is required",c.VALIDATION);if(r.length>300)throw new d(`Reddit title exceeds 300 characters: ${r.length}/300`,c.VALIDATION);if(!["self","link","image","video"].includes(s))throw new d("metadata.reddit.kind must be one of: self, link, image, video",c.VALIDATION);if(t.push(`kind=${s}`),t.push(`titleLength=${r.length}`),l&&!o)throw new d(`Can't set flairText without flairId for r/${a}. Run: dun reddit flairs ${a} --json`,c.VALIDATION);if(s==="link"){if(!i.url?.trim())throw new d("Reddit link posts require metadata.reddit.url",c.VALIDATION);try{new URL(i.url)}catch{throw new d("metadata.reddit.url is invalid",c.VALIDATION)}if(n.media.length>0)throw new d("Reddit link posts do not accept --media",c.VALIDATION)}else if(s==="image"||s==="video"){if(n.media.length!==1)throw new d(`Reddit ${s} posts require exactly one --media asset id`,c.VALIDATION)}else if(n.media.length>0)throw new d("Reddit self posts do not accept --media. Use kind image or video.",c.VALIDATION);let u=await e.get("/api/social-accounts/reddit/post-requirements",{workspace:!0,query:{subreddit:a}});if(t.push("fetched post-requirements"),u?.is_flair_required&&!o)throw new d(`r/${a} requires post flair. Discover ids with: dun reddit flairs ${a} --json`,c.VALIDATION);let p=u?.title_text_max_length??300;if(r.length>p)throw new d(`Reddit title too long for r/${a}: ${r.length}/${p}`,c.VALIDATION);if(u?.title_text_min_length&&r.length<u.title_text_min_length)throw new d(`Reddit title must be at least ${u.title_text_min_length} characters`,c.VALIDATION);if(o){let g=await e.get("/api/social-accounts/reddit/flair-options",{workspace:!0,query:{subreddit:a}});t.push(`fetched ${g.choices?.length??0} flairs`);let w=(g.choices??[]).find((k)=>k.id===o);if(!w)throw new d(`Unknown flairId "${o}" for r/${a}. Run: dun reddit flairs ${a} --json`,c.VALIDATION);if(w.textEditable===!1&&l&&l!==w.text)throw new d(`Customization not allowed for this flair template. Use flairId "${w.id}" alone`+(w.text?` (template text: "${w.text}")`:""),c.VALIDATION);t.push(`flairId=${w.id}`)}return{ok:!0,checks:t,reddit:{subreddit:a,kind:s,title:r,flairId:o||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 Pe(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"list":case"ls":{let r=await a.get("/api/posts",{workspace:!0,query:{status:f(t,"status","s"),socialAccountId:f(t,"account","social-account"),groupId:f(t,"group"),limit:C(t,"limit")??50,offset:C(t,"offset")??0}});m(r,{...e,emptyMessage:"No posts yet. Schedule one with dun posts schedule."});return}case"get":{let r=i[0];if(!r)throw new d("Usage: dun posts get <id>",c.USAGE);let s=await a.get(`/api/posts/${r}`,{workspace:!0});m(s,e);return}case"validate":{let r=B(t),s=G(t),o=O(t)??{},l=se(t,o),u=await a.post("/api/posts/validate",{content:l,socialAccountIds:r,mediaUrls:s,metadata:o},{workspace:!0});if(!u.valid){let w=u.issues.map((k)=>k.next?`${k.message} ${k.next}`:k.message).join("; ");throw new d(w||"Post validation failed",c.VALIDATION,u)}let p=Boolean(o.reddit),g=p;if(!p){let w=await a.get("/api/social-accounts",{workspace:!0});g=r.some((k)=>(w.find((N)=>N.id===k)?.providerName||"").toLowerCase().includes("reddit"))}if(g){let w=await ae(a,{accounts:r,text:l,media:s,meta:o});m({ok:!0,...u,reddit:w},e);return}m({ok:!0,...u},e);return}case"schedule":{let r=O(t)??{},s=se(t,r),o=B(t),l=G(t);await $e(a,o,{text:s,media:l,meta:r});let u=V({at:f(t,"at"),in:f(t,"in")}),p=await a.post("/api/posts/schedule",{content:s,socialAccountIds:o,scheduledAt:u,mediaUrls:l,metadata:r},{workspace:!0});m(p,e);return}case"publish":case"publish-now":{let r=O(t)??{},s=se(t,r),o=B(t),l=G(t);await $e(a,o,{text:s,media:l,meta:r});let u=await a.post("/api/posts/publish-now",{content:s,socialAccountIds:o,mediaUrls:l,metadata:r,naturalPosting:h(t,"natural","natural-posting")},{workspace:!0});if(!h(t,"wait")){m(u,e);return}let g=Array.isArray(u)?u:[];if(g.length===0||!g.every((v)=>typeof v?.id==="string"))throw new d("publish --wait expected an array of created posts with ids",c.NETWORK,{data:u});let w=C(t,"wait-timeout","timeout")??120,k=await Xt(a,e,g,w);m({queued:g,posts:k,status:k.every((v)=>v.status==="published")?"published":k[0]?.status},e);return}case"reschedule":{let r=i[0];if(!r)throw new d("Usage: dun posts reschedule <id> --at|--in",c.USAGE);let s=V({at:f(t,"at"),in:f(t,"in")}),o=await a.get(`/api/posts/${r}`,{workspace:!0}),l=zt({scheduledAt:s,text:f(t,"text","content","c","t"),media:G(t),meta:O(t),existing:o}),u=await a.post(`/api/posts/${r}/reschedule`,l,{workspace:!0});m(u,e);return}case"cancel":{let r=i[0];if(!r)throw new d("Usage: dun posts cancel <id>",c.USAGE);let s=await a.post(`/api/posts/${r}/cancel`,void 0,{workspace:!0});m(s??{id:r,status:"cancelled"},e);return}case"delete":case"rm":{let r=i[0];if(!r)throw new d("Usage: dun posts delete <id> [--yes]",c.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes (or CI/yes mode)",c.VALIDATION);let s=await a.delete(`/api/posts/${r}`,void 0,{workspace:!0});m(s??{id:r,deleted:!0},e);return}case"x-cap":case"xcap":{let r=await a.get("/api/posts/x-cap-usage",{workspace:!0});m(r,e);return}case"schedule-thread":case"publish-thread":case"publish-thread-now":{let r=f(t,"file","f"),s=f(t,"account","accounts","a")??B(t)[0];if(!s)throw new d("Missing --account <xAccountId>",c.VALIDATION);let o;if(r){let u;try{u=Kt(r,"utf8")}catch(g){throw new d(`Could not read file: ${g.message}`,c.VALIDATION)}let p=JSON.parse(u);if(Array.isArray(p))o=p;else if(p&&typeof p==="object"&&Array.isArray(p.tweets))o=p.tweets;else throw new d("Thread file must be an array of tweets or { tweets: [] }",c.VALIDATION)}else{let u=f(t,"text","content");if(!u)throw new d('Provide --file thread.json (array of {content}) or --text "tweet1||tweet2"',c.VALIDATION);o=u.split("||").map((p)=>({content:p.trim()})).filter((p)=>p.content)}if(o.length<2)throw new d("Threads require at least 2 tweets",c.VALIDATION);if(n==="schedule-thread"){let u=V({at:f(t,"at"),in:f(t,"in")}),p=await a.post("/api/posts/schedule-thread",{socialAccountId:s,tweets:o,scheduledAt:u,metadata:O(t)??{}},{workspace:!0});m(p,e);return}let l=await a.post("/api/posts/publish-thread-now",{socialAccountId:s,tweets:o,metadata:O(t)??{}},{workspace:!0});m(l,e);return}default:throw new d(`Unknown posts command "${n??""}". Try: list, get, validate, schedule, publish, reschedule, cancel, delete, x-cap, schedule-thread, publish-thread`,c.USAGE)}}async function De(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"list":case"ls":{let r=await a.get("/api/drafts",{workspace:!0,query:{limit:C(t,"limit")??50,offset:C(t,"offset")??0}});m(r,{...e,emptyMessage:'No drafts yet. Create one with dun drafts create --text "..."'});return}case"get":{let r=i[0];if(!r)throw new d("Usage: dun drafts get <id>",c.USAGE);let s=await a.get(`/api/drafts/${r}`,{workspace:!0});m(s,e);return}case"create":{let r=f(t,"text","content","c","t");if(!r)throw new d("Missing --text",c.VALIDATION);let s=await a.post("/api/drafts",{content:r,name:f(t,"name","n"),socialAccountIds:E(t,"accounts","account","a")??[],selectedPlatforms:E(t,"platforms","platform","p")??[],mediaUrls:E(t,"media")??[]},{workspace:!0});m(s,e);return}case"update":{let r=i[0];if(!r)throw new d("Usage: dun drafts update <id> --text ...",c.USAGE);let s={id:r},o=f(t,"text","content","c","t");if(o)s.content=o;let l=f(t,"name","n");if(l!==void 0)s.name=l;let u=E(t,"accounts","account","a");if(u)s.socialAccountIds=u;let p=E(t,"platforms","platform","p");if(p)s.selectedPlatforms=p;let g=E(t,"media");if(g)s.mediaUrls=g;let w=await a.patch(`/api/drafts/${r}`,s,{workspace:!0});m(w,e);return}case"delete":case"rm":{let r=i[0];if(!r)throw new d("Usage: dun drafts delete <id> [--yes]",c.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",c.VALIDATION);let s=await a.delete(`/api/drafts/${r}`,void 0,{workspace:!0});m(s??{id:r,deleted:!0},e);return}default:throw new d(`Unknown drafts command "${n??""}". Try: list, get, create, update, delete`,c.USAGE)}}import{readFileSync as Zt,existsSync as Qt,statSync as en}from"node:fs";import{basename as tn}from"node:path";var _e=["image/jpeg","image/png","image/gif","image/webp"],Me=["video/mp4","video/quicktime","video/webm"];function ce(e){if(e<1024)return`${e}B`;if(e<1048576)return`${(e/1024).toFixed(1)}KB`;if(e<1073741824)return`${(e/1048576).toFixed(1)}MB`;let n=e/1073741824;return Number.isInteger(n)?`${n}GB`:`${n.toFixed(1)}GB`}function je(e){let{mimeType:n,fileSize:t}=e,i=_e.includes(n),a=Me.includes(n);if(!i&&!a)return{ok:!1,message:`Unsupported MIME type "${n}". Allowed images: ${_e.join(", ")}; videos: ${Me.join(", ")}`};if(a&&t>17179869184)return{ok:!1,message:`Video files must be less than ${ce(17179869184)} (got ${ce(t)}). Compress first, e.g.:
279
- ffmpeg -i input.mp4 -vf scale=-2:1080 -c:v libx264 -crf 23 -c:a aac output.mp4`};if(i&&t>8388608)return{ok:!1,message:`Image files must be less than 8MB (got ${ce(t)}).`};return{ok:!0}}function nn(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 We(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"list":case"ls":{let r=await a.get("/api/media",{workspace:!0,query:{limit:C(t,"limit")??50,offset:C(t,"offset")??0}});m(r,{...e,emptyMessage:"No media yet. Upload with dun media upload <file>."});return}case"get":{let r=i[0];if(!r)throw new d("Usage: dun media get <id>",c.USAGE);let s=await a.get(`/api/media/${r}`,{workspace:!0});m(s,e);return}case"upload":{let r=i[0]||f(t,"file","f");if(!r)throw new d('Usage: dun media upload <file> [--alt "..."]',c.USAGE);if(!Qt(r))throw new d(`File not found: ${r}`,c.VALIDATION);let s=tn(r),o=en(r).size,l=f(t,"mime","type","content-type")||nn(s),u=f(t,"alt","alt-text"),p=je({mimeType:l,fileSize:o});if(!p.ok)throw new d(p.message,c.VALIDATION);let g=await a.post("/api/media/upload-url",{filename:s,mimeType:l,fileSize:o},{workspace:!0});R(e.debug,`PUT ${g.uploadUrl}`,{storageKey:g.storageKey,fileSize:o});let w=Zt(r),k;try{k=await fetch(g.uploadUrl,{method:"PUT",headers:{"Content-Type":l,"Content-Length":String(o)},body:w})}catch(N){throw new d(`Upload PUT failed: ${N.message}`,c.NETWORK)}if(!k.ok){let N=await k.text().catch(()=>"");throw new d(`Upload PUT failed with HTTP ${k.status}${N?`: ${N.slice(0,200)}`:""}`,c.NETWORK)}let v=await a.post("/api/media/complete",{storageKey:g.storageKey,originalFilename:s,mimeType:l,fileSize:o,altText:u},{workspace:!0});m(v,e);return}case"delete":case"rm":{let r=i[0];if(!r)throw new d("Usage: dun media delete <id> [--yes]",c.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",c.VALIDATION);let s=await a.delete(`/api/media/${r}`,void 0,{workspace:!0});m(s??{id:r,deleted:!0},e);return}default:throw new d(`Unknown media command "${n??""}". Try: list, get, upload, delete`,c.USAGE)}}async function qe(e,n,t,i){y(e),I(e);let a=b(e),r=n[0];if(r==="collections"||r==="collection"){let s=n[1]||"list";switch(s){case"list":case"ls":{let o=await a.get("/api/memory/collections",{workspace:!0});m(o,{...e,emptyMessage:'No memory collections yet. Create one with dun memory collections create --name "..."'});return}case"create":{let o=f(t,"name","n");if(!o)throw new d("Missing --name",c.VALIDATION);let l=await a.post("/api/memory/collections",{name:o,color:f(t,"color")??"blue",isPrivate:h(t,"private")},{workspace:!0});m(l,e);return}case"get":{let o=n[2]||i[0];if(!o)throw new d("Usage: dun memory collections get <id>",c.USAGE);let l=await a.get(`/api/memory/collections/${o}`,{workspace:!0});m(l,e);return}case"update":{let o=n[2]||i[0];if(!o)throw new d("Usage: dun memory collections update <id> [--name] [--color] [--private|--public]",c.USAGE);let l=f(t,"name","n"),u=f(t,"color"),p=h(t,"private"),g=h(t,"public");if(p&&g)throw new d("Use either --private or --public, not both",c.VALIDATION);let w={};if(l)w.name=l;if(u)w.color=u;if(p)w.isPrivate=!0;if(g)w.isPrivate=!1;if(Object.keys(w).length===0)throw new d("Provide at least one of --name, --color, --private, or --public",c.VALIDATION);let k=await a.patch(`/api/memory/collections/${o}`,w,{workspace:!0});m(k,e);return}case"delete":case"rm":{let o=n[2]||i[0];if(!o)throw new d("Usage: dun memory collections delete <id> [--yes]",c.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",c.VALIDATION);let l=await a.delete(`/api/memory/collections/${o}`,void 0,{workspace:!0});m(l??{id:o,deleted:!0},e);return}default:throw new d(`Unknown memory collections command "${s}". Try: list, create, get, update, delete`,c.USAGE)}}switch(r){case"list":case"ls":{let s=f(t,"collection","collection-id","c");if(!s)throw new d("Missing --collection <id>",c.VALIDATION);let o=await a.get("/api/memory",{workspace:!0,query:{collectionId:s,limit:C(t,"limit")??100}});m(o,{...e,emptyMessage:"No memories in this collection."});return}case"save":case"add":{let s=f(t,"collection","collection-id","c"),o=f(t,"text","t");if(!s)throw new d("Missing --collection <id>",c.VALIDATION);if(!o)throw new d("Missing --text",c.VALIDATION);let l=await a.post("/api/memory",{collectionId:s,text:o},{workspace:!0});m(l,e);return}case"search":{let s=f(t,"prompt","q","query","text"),o=E(t,"collections","collection","collection-ids","c")??[];if(!s)throw new d("Missing --prompt",c.VALIDATION);if(o.length===0)throw new d("Missing --collections <id,id>",c.VALIDATION);let l=await a.post("/api/memory/search",{prompt:s,collectionIds:o,topK:C(t,"top-k","topk","k")??5},{workspace:!0});m(l,e);return}case"delete":case"rm":{let s=n[1]||i[0],o=f(t,"collection","collection-id","c");if(!s)throw new d("Usage: dun memory delete <id> --collection <id>",c.USAGE);if(!o)throw new d("Missing --collection <id>",c.VALIDATION);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",c.VALIDATION);let l=await a.delete(`/api/memory/${s}`,{collectionId:o,memoryId:s},{workspace:!0});m(l??{id:s,deleted:!0},e);return}default:throw new d(`Unknown memory command "${r??""}". Try: collections, list, save, search, delete`,c.USAGE)}}import{readFileSync as rn}from"node:fs";async function Ve(e,n,t,i){y(e);let a=I(e),r=b(e),s=`/api/workspaces/${a}/ai-voice-settings`;switch(n){case"get":case"show":case void 0:{let o=await r.get(s,{workspace:!0});m(o??{personalization:null,hint:'No personalization set. Use dun personalization set --description "..."'},e);return}case"set":case"update":{let o=h(t,"clear"),l=f(t,"description","d"),u=f(t,"file","f");if([o,Boolean(l),Boolean(u)].filter(Boolean).length!==1)throw new d("Use exactly one of --description, --file, or --clear",c.VALIDATION);let g;if(o)g=null;else if(u){let k;try{k=rn(u,"utf8").trim()}catch(v){throw new d(`Could not read file: ${v.message}`,c.VALIDATION)}if(!k)throw new d("File is empty",c.VALIDATION);g={description:k}}else{let k=l.trim();if(!k)throw new d("--description cannot be empty",c.VALIDATION);g={description:k}}let w=await r.patch(s,g,{workspace:!0});m(w,e);return}default:throw new d(`Unknown personalization command "${n}". Try: get, set`,c.USAGE)}}function Be(e){return e.trim().replace(/^\/?r\//i,"").replace(/^@/,"")}async function de(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"flairs":case"flair":case"flair-options":{let r=i[0]||f(t,"subreddit","sub","r");if(!r)throw new d(`Usage: dun reddit flairs <subreddit>
280
- Example: dun reddit flairs tamilyapping --json`,c.USAGE);let s=Be(r),l=((await a.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:l},{...e,emptyMessage:`No flairs returned for r/${s}. The subreddit may not use post flair.`});return}case"requirements":case"post-requirements":{let r=i[0]||f(t,"subreddit","sub","r");if(!r)throw new d("Usage: dun reddit requirements <subreddit>",c.USAGE);let s=Be(r),o=await a.get("/api/social-accounts/reddit/post-requirements",{workspace:!0,query:{subreddit:s}});m({subreddit:s,requirements:o},e);return}default:throw new d(`Unknown reddit command "${n??""}". Try: flairs, requirements`,c.USAGE)}}async function le(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"boards":case"list":case"list-boards":{let r=await a.get("/api/social-accounts/pinterest/boards",{workspace:!0});m({boards:r.boards??[]},{...e,emptyMessage:'No Pinterest boards on this account. Create one with dun pinterest create-board --name "..."'});return}case"create-board":case"board-create":case"create":{let r=f(t,"name","n")||i[0]||"";if(!r.trim())throw new d(`Usage: dun pinterest create-board --name "Brand assets" [--private]
281
- Example: dun pinterest create-board --name "Landing page" --json`,c.USAGE);let s=await a.post("/api/social-accounts/pinterest/boards",{name:r.trim(),privacy:h(t,"private")?"SECRET":"PUBLIC"},{workspace:!0});m(s,e);return}default:throw new d(`Unknown pinterest command "${n??""}". Try: boards, create-board`,c.USAGE)}}import{existsSync as Ke}from"node:fs";import{mkdir as He,writeFile as ze,readFile as me}from"node:fs/promises";import{dirname as fe,join as A}from"node:path";import{homedir as F}from"node:os";import{fileURLToPath as on}from"node:url";var _=[{id:"claude",label:"Claude Code",aliases:["claude-code"],dest:(e)=>A(e,".claude","skills","dunsocial","SKILL.md")},{id:"codex",label:"Codex",dest:(e)=>A(e,".codex","skills","dunsocial","SKILL.md"),afterInstall:async(e)=>({agentsPath:await an(e)})},{id:"cursor",label:"Cursor",dest:(e)=>A(e,".cursor","skills","dunsocial","SKILL.md")},{id:"pi",label:"Pi",dest:(e)=>A(e,".pi","agent","skills","dunsocial","SKILL.md")},{id:"opencode",label:"OpenCode",dest:(e)=>A(e,".config","opencode","skills","dunsocial","SKILL.md")},{id:"agents",label:"Universal (.agents)",aliases:["universal"],dest:(e)=>A(e,".agents","skills","dunsocial","SKILL.md")},{id:"copilot",label:"GitHub Copilot",aliases:["github-copilot","github"],dest:(e)=>A(e,".copilot","skills","dunsocial","SKILL.md")},{id:"windsurf",label:"Windsurf",dest:(e)=>A(e,".codeium","windsurf","skills","dunsocial","SKILL.md")},{id:"goose",label:"Goose",dest:(e)=>A(e,".config","goose","skills","dunsocial","SKILL.md")},{id:"gemini",label:"Gemini CLI",aliases:["gemini-cli"],dest:(e)=>A(e,".gemini","skills","dunsocial","SKILL.md")},{id:"amp",label:"Amp",dest:(e)=>A(e,".config","agents","skills","dunsocial","SKILL.md")},{id:"grok",label:"Grok Build",dest:(e)=>A(e,".grok","skills","dunsocial","SKILL.md")},{id:"continue",label:"Continue",dest:(e)=>A(e,".continue","skills","dunsocial","SKILL.md")},{id:"antigravity",label:"Antigravity",dest:(e)=>A(e,".gemini","antigravity","skills","dunsocial","SKILL.md")}],ue=new Map;for(let e of _){ue.set(e.id,e);for(let n of e.aliases??[])ue.set(n,e)}var Ge=`Usage: dun skill install <${_.map((e)=>e.id).join("|")}|all>`;function pe(){let e=fe(on(import.meta.url)),n=A("skills","dunsocial","SKILL.md"),t=[A(e,"..",n),A(e,"..","..",n)];return t.find(Ke)??t[0]}function sn(){let e=F();return _.map((n)=>({id:n.id,label:n.label,path:n.dest(e),aliases:n.aliases??[]}))}async function Je(){try{return await me(pe(),"utf8")}catch{throw new d(`Bundled skill not found at ${pe()}. Reinstall dunsocial.`,c.USAGE)}}async function Ye(e,n){await He(fe(e),{recursive:!0}),await ze(e,n,"utf8")}async function an(e){let n=A(F(),".codex","AGENTS.md");await He(fe(n),{recursive:!0});let t="";try{t=await me(n,"utf8")}catch{t=""}let i=["<!-- 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(`
298
+ `),t.length===e.length&&e.length>0)process.stdout.write(`${Gt()}
299
+ `)}async function Le(e,n,t,i){y(e);let a=b(e);switch(n){case"list":case"ls":{let r=await a.get("/api/workspaces");p(r.map(Re),{...e,emptyMessage:"No workspaces yet."}),L(r,e);return}case"use":{let r=i[0];if(!r)throw new d("Usage: dun workspace use <id|slug>",c.USAGE);let o=(await a.get("/api/workspaces")).find((l)=>l.id===r||l.slug===r);if(!o)throw new d(`Workspace not found: ${r}. Run dun workspace list`,c.NOT_FOUND);await P({...e.config,apiUrl:e.apiUrl,token:e.token,defaultWorkspaceId:o.id,defaultWorkspaceSlug:o.slug,defaultWorkspaceName:o.name}),p({id:o.id,name:o.name,slug:o.slug,role:o.role,hasAccess:o.subscription?.hasAccess??null,accessMessage:o.subscription?.accessMessage??null,billingUrl:o.subscription?.billingUrl??null,message:`Default workspace set to ${o.name}`},e),L([o],e);return}case"current":{if(!e.workspaceId&&!e.config.defaultWorkspaceId)throw new d("No workspace selected. Run dun workspace use <id|slug>",c.VALIDATION);let r=e.workspaceId||e.config.defaultWorkspaceId;try{let o=(await a.get("/api/workspaces")).find((l)=>l.id===r||l.slug===r);if(o){p({id:o.id,name:o.name,slug:o.slug,role:o.role,timezone:o.timezone,hasAccess:o.subscription?.hasAccess??null,accessMessage:o.subscription?.accessMessage??null,billingUrl:o.subscription?.billingUrl??null},e),L([o],e);return}}catch{}p({id:r,name:e.config.defaultWorkspaceName??null,slug:e.config.defaultWorkspaceSlug??null},e);return}default:throw new d(`Unknown workspace command "${n??""}". Try: list, use, current`,c.USAGE)}}async function $e(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case void 0:case"list":case"ls":{let r=await a.get("/api/social-accounts",{workspace:!0}),s=f(t,"platform","p")?.toLowerCase(),o=s?r.filter((l)=>{let u=(l.providerName||"").toLowerCase();return({x:["x","twitter"],twitter:["x","twitter"],linkedin:["linkedin"],"linkedin-business":["linkedin business","linkedin-business","linkedin_business"],bluesky:["bluesky"],threads:["threads"],reddit:["reddit"],pinterest:["pinterest"],instagram:["instagram"],youtube:["youtube"],tiktok:["tiktok"],discord:["discord"]}[s]??[s]).some((w)=>u.includes(w))}):r;p(o.map((l)=>({id:l.id,providerName:l.providerName,username:l.username??null,displayName:l.displayName??null,isConnected:l.isConnected,expiresAt:l.expiresAt??null})),{...e,emptyMessage:s?`No ${s} accounts connected. Connect one in the DunSocial dashboard.`:"No accounts connected. Connect one in the DunSocial dashboard."});try{let l=await a.get(`/api/workspaces/${I(e)}`,{workspace:!0});L([l],e)}catch{}return}default:throw new d(`Unknown accounts command "${n}". Try: list (or: dun accounts reddit flairs <subreddit>)`,c.USAGE)}}import{readFileSync as Ht}from"node:fs";var Ft=/^(\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 oe(e,n=new Date){let t=e.trim();if(!t)throw new d("Empty time value",c.VALIDATION);let i=t.match(Ft);if(i){let s=Number(i[1]),o=i[2].toLowerCase(),l=Kt(o)*s;return new Date(n.getTime()+l)}let a=t.match(/^in\s+(.+)$/i);if(a)return oe(a[1],n);let r=new Date(t);if(Number.isNaN(r.getTime()))throw new d(`Invalid time "${e}". Use ISO-8601 (2026-06-01T09:00:00Z) or relative (2h, 30m, 1d).`,c.VALIDATION);return r}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 d(`Unknown time unit: ${e}`,c.VALIDATION)}}function V(e){if(e.at&&e.in)throw new d("Pass only one of --at or --in",c.VALIDATION);if(e.at)return oe(e.at).toISOString();if(e.in)return oe(e.in).toISOString();throw new d("Schedule time required. Pass --at <iso> or --in <duration> (e.g. 2h).",c.VALIDATION)}function De(e){if(!e)return[];return e.split(",").map((n)=>n.trim()).filter(Boolean)}function se(e,n){let t=f(e,"text","content","c","t");if(t)return t;let i=n?.reddit;if(i&&typeof i==="object"&&!Array.isArray(i)&&typeof i.title==="string"&&i.title.trim())return i.title.trim();throw new d("Missing --text",c.VALIDATION)}function B(e){let n=E(e,"accounts","account","a")??De(f(e,"accounts","account","a"));if(!n||n.length===0)throw new d("Missing --accounts <id,id>",c.VALIDATION);return n}function G(e){return E(e,"media","media-urls")??De(f(e,"media","media-urls"))}function zt(e,n){if(e?.reddit)return!0;return n.some((t)=>t.toLowerCase().includes("reddit"))}function Jt(e){let n=e.existing.metadata&&typeof e.existing.metadata==="object"&&!Array.isArray(e.existing.metadata)?e.existing.metadata:{};return{scheduledAt:e.scheduledAt,content:e.text??e.existing.content,mediaUrls:e.media&&e.media.length>0?e.media:e.existing.mediaUrls,metadata:e.meta?{...n,...e.meta}:n}}function O(e){let n=f(e,"meta","metadata","meta-json");if(!n)return;try{let t=JSON.parse(n);if(!t||typeof t!=="object"||Array.isArray(t))throw Error("metadata must be a JSON object");return t}catch(t){throw new d(`Invalid --meta JSON: ${t.message}`,c.VALIDATION)}}var Yt=new Set(["published","failed","cancelled"]);async function Xt(e){await new Promise((n)=>setTimeout(n,e))}async function Zt(e,n,t,i){let a=Date.now()+i*1000,r=new Map,s=1500;while(r.size<t.length){if(Date.now()>a){let u=t.filter((m)=>!r.has(m.id)).map((m)=>m.id);throw new d(`Timed out waiting for publish after ${i}s. Still pending: ${u.join(", ")}. Poll with: dun posts get <id> --json`,c.NETWORK,{pending:u})}for(let u of t){if(r.has(u.id))continue;let m=await e.get(`/api/posts/${u.id}`,{workspace:!0});if(R(n.debug,`wait status ${u.id}=${m.status}`),Yt.has(m.status))r.set(u.id,m)}if(r.size>=t.length)break;await Xt(s),s=Math.min(s*1.4,8000)}let o=t.map((u)=>r.get(u.id)),l=o.filter((u)=>u.status==="failed");if(l.length>0){let u=l.map((m)=>`${m.id}: ${m.error||"publish failed"}`).join("; ");throw new d(`Publish failed — ${u}`,c.VALIDATION,{posts:o})}return o}async function Oe(e,n,t){if(t.meta?.reddit){await ae(e,{accounts:n,text:t.text,media:t.media,meta:t.meta});return}let i=await e.get("/api/social-accounts",{workspace:!0}),a=n.map((r)=>i.find((o)=>o.id===r)?.providerName||"");if(!zt(t.meta,a))return;await ae(e,{accounts:n,text:t.text,media:t.media,meta:t.meta})}async function ae(e,n){let t=[],i=n.meta?.reddit??null;if(!i||typeof i!=="object")throw new d("Reddit accounts require --meta with a reddit object, e.g. "+`--meta '{"reddit":{"subreddit":"…","kind":"self","title":"…","flairId":"…"}}'`,c.VALIDATION);let a=(i.subreddit??"").trim().replace(/^\/?r\//i,""),r=(i.title??"").trim(),s=(i.kind??"").trim(),o=(i.flairId??"").trim(),l=(i.flairText??"").trim();if(!a)throw new d("metadata.reddit.subreddit is required",c.VALIDATION);if(!r)throw new d("metadata.reddit.title is required",c.VALIDATION);if(r.length>300)throw new d(`Reddit title exceeds 300 characters: ${r.length}/300`,c.VALIDATION);if(!["self","link","image","video"].includes(s))throw new d("metadata.reddit.kind must be one of: self, link, image, video",c.VALIDATION);if(t.push(`kind=${s}`),t.push(`titleLength=${r.length}`),l&&!o)throw new d(`Can't set flairText without flairId for r/${a}. Run: dun reddit flairs ${a} --json`,c.VALIDATION);if(s==="link"){if(!i.url?.trim())throw new d("Reddit link posts require metadata.reddit.url",c.VALIDATION);try{new URL(i.url)}catch{throw new d("metadata.reddit.url is invalid",c.VALIDATION)}if(n.media.length>0)throw new d("Reddit link posts do not accept --media",c.VALIDATION)}else if(s==="image"||s==="video"){if(n.media.length!==1)throw new d(`Reddit ${s} posts require exactly one --media asset id`,c.VALIDATION)}else if(n.media.length>0)throw new d("Reddit self posts do not accept --media. Use kind image or video.",c.VALIDATION);let u=await e.get("/api/social-accounts/reddit/post-requirements",{workspace:!0,query:{subreddit:a}});if(t.push("fetched post-requirements"),u?.is_flair_required&&!o)throw new d(`r/${a} requires post flair. Discover ids with: dun reddit flairs ${a} --json`,c.VALIDATION);let m=u?.title_text_max_length??300;if(r.length>m)throw new d(`Reddit title too long for r/${a}: ${r.length}/${m}`,c.VALIDATION);if(u?.title_text_min_length&&r.length<u.title_text_min_length)throw new d(`Reddit title must be at least ${u.title_text_min_length} characters`,c.VALIDATION);if(o){let g=await e.get("/api/social-accounts/reddit/flair-options",{workspace:!0,query:{subreddit:a}});t.push(`fetched ${g.choices?.length??0} flairs`);let w=(g.choices??[]).find((k)=>k.id===o);if(!w)throw new d(`Unknown flairId "${o}" for r/${a}. Run: dun reddit flairs ${a} --json`,c.VALIDATION);if(w.textEditable===!1&&l&&l!==w.text)throw new d(`Customization not allowed for this flair template. Use flairId "${w.id}" alone`+(w.text?` (template text: "${w.text}")`:""),c.VALIDATION);t.push(`flairId=${w.id}`)}return{ok:!0,checks:t,reddit:{subreddit:a,kind:s,title:r,flairId:o||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:m}}}}async function Pe(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"list":case"ls":{let r=await a.get("/api/posts",{workspace:!0,query:{status:f(t,"status","s"),socialAccountId:f(t,"account","social-account"),groupId:f(t,"group"),limit:U(t,"limit")??50,offset:U(t,"offset")??0}});p(r,{...e,emptyMessage:"No posts yet. Schedule one with dun posts schedule."});return}case"get":{let r=i[0];if(!r)throw new d("Usage: dun posts get <id>",c.USAGE);let s=await a.get(`/api/posts/${r}`,{workspace:!0});p(s,e);return}case"validate":{let r=B(t),s=G(t),o=O(t)??{},l=se(t,o),u=await a.post("/api/posts/validate",{content:l,socialAccountIds:r,mediaUrls:s,metadata:o},{workspace:!0});if(!u.valid){let w=u.issues.map((k)=>k.next?`${k.message} ${k.next}`:k.message).join("; ");throw new d(w||"Post validation failed",c.VALIDATION,u)}let m=Boolean(o.reddit),g=m;if(!m){let w=await a.get("/api/social-accounts",{workspace:!0});g=r.some((k)=>(w.find((N)=>N.id===k)?.providerName||"").toLowerCase().includes("reddit"))}if(g){let w=await ae(a,{accounts:r,text:l,media:s,meta:o});p({ok:!0,...u,reddit:w},e);return}p({ok:!0,...u},e);return}case"schedule":{let r=O(t)??{},s=se(t,r),o=B(t),l=G(t);await Oe(a,o,{text:s,media:l,meta:r});let u=V({at:f(t,"at"),in:f(t,"in")}),m=await a.post("/api/posts/schedule",{content:s,socialAccountIds:o,scheduledAt:u,mediaUrls:l,metadata:r},{workspace:!0});p(m,e);return}case"publish":case"publish-now":{let r=O(t)??{},s=se(t,r),o=B(t),l=G(t);await Oe(a,o,{text:s,media:l,meta:r});let u=await a.post("/api/posts/publish-now",{content:s,socialAccountIds:o,mediaUrls:l,metadata:r,naturalPosting:h(t,"natural","natural-posting")},{workspace:!0});if(!h(t,"wait")){p(u,e);return}let g=Array.isArray(u)?u:[];if(g.length===0||!g.every((v)=>typeof v?.id==="string"))throw new d("publish --wait expected an array of created posts with ids",c.NETWORK,{data:u});let w=U(t,"wait-timeout","timeout")??120,k=await Zt(a,e,g,w);p({queued:g,posts:k,status:k.every((v)=>v.status==="published")?"published":k[0]?.status},e);return}case"reschedule":{let r=i[0];if(!r)throw new d("Usage: dun posts reschedule <id> --at|--in",c.USAGE);let s=V({at:f(t,"at"),in:f(t,"in")}),o=await a.get(`/api/posts/${r}`,{workspace:!0}),l=Jt({scheduledAt:s,text:f(t,"text","content","c","t"),media:G(t),meta:O(t),existing:o}),u=await a.post(`/api/posts/${r}/reschedule`,l,{workspace:!0});p(u,e);return}case"cancel":{let r=i[0];if(!r)throw new d("Usage: dun posts cancel <id>",c.USAGE);let s=await a.post(`/api/posts/${r}/cancel`,void 0,{workspace:!0});p(s??{id:r,status:"cancelled"},e);return}case"delete":case"rm":{let r=i[0];if(!r)throw new d("Usage: dun posts delete <id> [--yes]",c.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes (or CI/yes mode)",c.VALIDATION);let s=await a.delete(`/api/posts/${r}`,void 0,{workspace:!0});p(s??{id:r,deleted:!0},e);return}case"x-cap":case"xcap":{let r=await a.get("/api/posts/x-cap-usage",{workspace:!0});p(r,e);return}case"schedule-thread":case"publish-thread":case"publish-thread-now":{let r=f(t,"file","f"),s=f(t,"account","accounts","a")??B(t)[0];if(!s)throw new d("Missing --account <xAccountId>",c.VALIDATION);let o;if(r){let u;try{u=Ht(r,"utf8")}catch(g){throw new d(`Could not read file: ${g.message}`,c.VALIDATION)}let m=JSON.parse(u);if(Array.isArray(m))o=m;else if(m&&typeof m==="object"&&Array.isArray(m.tweets))o=m.tweets;else throw new d("Thread file must be an array of tweets or { tweets: [] }",c.VALIDATION)}else{let u=f(t,"text","content");if(!u)throw new d('Provide --file thread.json (array of {content}) or --text "tweet1||tweet2"',c.VALIDATION);o=u.split("||").map((m)=>({content:m.trim()})).filter((m)=>m.content)}if(o.length<2)throw new d("Threads require at least 2 tweets",c.VALIDATION);if(n==="schedule-thread"){let u=V({at:f(t,"at"),in:f(t,"in")}),m=await a.post("/api/posts/schedule-thread",{socialAccountId:s,tweets:o,scheduledAt:u,metadata:O(t)??{}},{workspace:!0});p(m,e);return}let l=await a.post("/api/posts/publish-thread-now",{socialAccountId:s,tweets:o,metadata:O(t)??{}},{workspace:!0});p(l,e);return}default:throw new d(`Unknown posts command "${n??""}". Try: list, get, validate, schedule, publish, reschedule, cancel, delete, x-cap, schedule-thread, publish-thread`,c.USAGE)}}async function _e(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"list":case"ls":{let r=await a.get("/api/drafts",{workspace:!0,query:{limit:U(t,"limit")??50,offset:U(t,"offset")??0}});p(r,{...e,emptyMessage:'No drafts yet. Create one with dun drafts create --text "..."'});return}case"get":{let r=i[0];if(!r)throw new d("Usage: dun drafts get <id>",c.USAGE);let s=await a.get(`/api/drafts/${r}`,{workspace:!0});p(s,e);return}case"create":{let r=f(t,"text","content","c","t");if(!r)throw new d("Missing --text",c.VALIDATION);let s=await a.post("/api/drafts",{content:r,name:f(t,"name","n"),socialAccountIds:E(t,"accounts","account","a")??[],selectedPlatforms:E(t,"platforms","platform","p")??[],mediaUrls:E(t,"media")??[]},{workspace:!0});p(s,e);return}case"update":{let r=i[0];if(!r)throw new d("Usage: dun drafts update <id> --text ...",c.USAGE);let s={id:r},o=f(t,"text","content","c","t");if(o)s.content=o;let l=f(t,"name","n");if(l!==void 0)s.name=l;let u=E(t,"accounts","account","a");if(u)s.socialAccountIds=u;let m=E(t,"platforms","platform","p");if(m)s.selectedPlatforms=m;let g=E(t,"media");if(g)s.mediaUrls=g;let w=await a.patch(`/api/drafts/${r}`,s,{workspace:!0});p(w,e);return}case"delete":case"rm":{let r=i[0];if(!r)throw new d("Usage: dun drafts delete <id> [--yes]",c.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",c.VALIDATION);let s=await a.delete(`/api/drafts/${r}`,void 0,{workspace:!0});p(s??{id:r,deleted:!0},e);return}default:throw new d(`Unknown drafts command "${n??""}". Try: list, get, create, update, delete`,c.USAGE)}}import{readFileSync as Qt,existsSync as en,statSync as tn}from"node:fs";import{basename as nn}from"node:path";var je=["image/jpeg","image/png","image/gif","image/webp"],Me=["video/mp4","video/quicktime","video/webm"];function ce(e){if(e<1024)return`${e}B`;if(e<1048576)return`${(e/1024).toFixed(1)}KB`;if(e<1073741824)return`${(e/1048576).toFixed(1)}MB`;let n=e/1073741824;return Number.isInteger(n)?`${n}GB`:`${n.toFixed(1)}GB`}function We(e){let{mimeType:n,fileSize:t}=e,i=je.includes(n),a=Me.includes(n);if(!i&&!a)return{ok:!1,message:`Unsupported MIME type "${n}". Allowed images: ${je.join(", ")}; videos: ${Me.join(", ")}`};if(a&&t>17179869184)return{ok:!1,message:`Video files must be less than ${ce(17179869184)} (got ${ce(t)}). Compress first, e.g.:
300
+ ffmpeg -i input.mp4 -vf scale=-2:1080 -c:v libx264 -crf 23 -c:a aac output.mp4`};if(i&&t>8388608)return{ok:!1,message:`Image files must be less than 8MB (got ${ce(t)}).`};return{ok:!0}}function rn(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 qe(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"list":case"ls":{let r=await a.get("/api/media",{workspace:!0,query:{limit:U(t,"limit")??50,offset:U(t,"offset")??0}});p(r,{...e,emptyMessage:"No media yet. Upload with dun media upload <file>."});return}case"get":{let r=i[0];if(!r)throw new d("Usage: dun media get <id>",c.USAGE);let s=await a.get(`/api/media/${r}`,{workspace:!0});p(s,e);return}case"upload":{let r=i[0]||f(t,"file","f");if(!r)throw new d('Usage: dun media upload <file> [--alt "..."]',c.USAGE);if(!en(r))throw new d(`File not found: ${r}`,c.VALIDATION);let s=nn(r),o=tn(r).size,l=f(t,"mime","type","content-type")||rn(s),u=f(t,"alt","alt-text"),m=We({mimeType:l,fileSize:o});if(!m.ok)throw new d(m.message,c.VALIDATION);let g=await a.post("/api/media/upload-url",{filename:s,mimeType:l,fileSize:o},{workspace:!0});R(e.debug,`PUT ${g.uploadUrl}`,{storageKey:g.storageKey,fileSize:o});let w=Qt(r),k;try{k=await fetch(g.uploadUrl,{method:"PUT",headers:{"Content-Type":l,"Content-Length":String(o)},body:w})}catch(N){throw new d(`Upload PUT failed: ${N.message}`,c.NETWORK)}if(!k.ok){let N=await k.text().catch(()=>"");throw new d(`Upload PUT failed with HTTP ${k.status}${N?`: ${N.slice(0,200)}`:""}`,c.NETWORK)}let v=await a.post("/api/media/complete",{storageKey:g.storageKey,originalFilename:s,mimeType:l,fileSize:o,altText:u},{workspace:!0});p(v,e);return}case"delete":case"rm":{let r=i[0];if(!r)throw new d("Usage: dun media delete <id> [--yes]",c.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",c.VALIDATION);let s=await a.delete(`/api/media/${r}`,void 0,{workspace:!0});p(s??{id:r,deleted:!0},e);return}default:throw new d(`Unknown media command "${n??""}". Try: list, get, upload, delete`,c.USAGE)}}async function Ve(e,n,t,i){y(e),I(e);let a=b(e),r=n[0];if(r==="collections"||r==="collection"){let s=n[1]||"list";switch(s){case"list":case"ls":{let o=await a.get("/api/memory/collections",{workspace:!0});p(o,{...e,emptyMessage:'No memory collections yet. Create one with dun memory collections create --name "..."'});return}case"create":{let o=f(t,"name","n");if(!o)throw new d("Missing --name",c.VALIDATION);let l=await a.post("/api/memory/collections",{name:o,color:f(t,"color")??"blue",isPrivate:h(t,"private")},{workspace:!0});p(l,e);return}case"get":{let o=n[2]||i[0];if(!o)throw new d("Usage: dun memory collections get <id>",c.USAGE);let l=await a.get(`/api/memory/collections/${o}`,{workspace:!0});p(l,e);return}case"update":{let o=n[2]||i[0];if(!o)throw new d("Usage: dun memory collections update <id> [--name] [--color] [--private|--public]",c.USAGE);let l=f(t,"name","n"),u=f(t,"color"),m=h(t,"private"),g=h(t,"public");if(m&&g)throw new d("Use either --private or --public, not both",c.VALIDATION);let w={};if(l)w.name=l;if(u)w.color=u;if(m)w.isPrivate=!0;if(g)w.isPrivate=!1;if(Object.keys(w).length===0)throw new d("Provide at least one of --name, --color, --private, or --public",c.VALIDATION);let k=await a.patch(`/api/memory/collections/${o}`,w,{workspace:!0});p(k,e);return}case"delete":case"rm":{let o=n[2]||i[0];if(!o)throw new d("Usage: dun memory collections delete <id> [--yes]",c.USAGE);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",c.VALIDATION);let l=await a.delete(`/api/memory/collections/${o}`,void 0,{workspace:!0});p(l??{id:o,deleted:!0},e);return}default:throw new d(`Unknown memory collections command "${s}". Try: list, create, get, update, delete`,c.USAGE)}}switch(r){case"list":case"ls":{let s=f(t,"collection","collection-id","c");if(!s)throw new d("Missing --collection <id>",c.VALIDATION);let o=await a.get("/api/memory",{workspace:!0,query:{collectionId:s,limit:U(t,"limit")??100}});p(o,{...e,emptyMessage:"No memories in this collection."});return}case"save":case"add":{let s=f(t,"collection","collection-id","c"),o=f(t,"text","t");if(!s)throw new d("Missing --collection <id>",c.VALIDATION);if(!o)throw new d("Missing --text",c.VALIDATION);let l=await a.post("/api/memory",{collectionId:s,text:o},{workspace:!0});p(l,e);return}case"search":{let s=f(t,"prompt","q","query","text"),o=E(t,"collections","collection","collection-ids","c")??[];if(!s)throw new d("Missing --prompt",c.VALIDATION);if(o.length===0)throw new d("Missing --collections <id,id>",c.VALIDATION);let l=await a.post("/api/memory/search",{prompt:s,collectionIds:o,topK:U(t,"top-k","topk","k")??5},{workspace:!0});p(l,e);return}case"delete":case"rm":{let s=n[1]||i[0],o=f(t,"collection","collection-id","c");if(!s)throw new d("Usage: dun memory delete <id> --collection <id>",c.USAGE);if(!o)throw new d("Missing --collection <id>",c.VALIDATION);if(!e.yes&&!h(t,"yes","y"))throw new d("Refusing to delete without --yes",c.VALIDATION);let l=await a.delete(`/api/memory/${s}`,{collectionId:o,memoryId:s},{workspace:!0});p(l??{id:s,deleted:!0},e);return}default:throw new d(`Unknown memory command "${r??""}". Try: collections, list, save, search, delete`,c.USAGE)}}import{readFileSync as on}from"node:fs";async function Be(e,n,t,i){y(e);let a=I(e),r=b(e),s=`/api/workspaces/${a}/ai-voice-settings`;switch(n){case"get":case"show":case void 0:{let o=await r.get(s,{workspace:!0});p(o??{personalization:null,hint:'No personalization set. Use dun personalization set --description "..."'},e);return}case"set":case"update":{let o=h(t,"clear"),l=f(t,"description","d"),u=f(t,"file","f");if([o,Boolean(l),Boolean(u)].filter(Boolean).length!==1)throw new d("Use exactly one of --description, --file, or --clear",c.VALIDATION);let g;if(o)g=null;else if(u){let k;try{k=on(u,"utf8").trim()}catch(v){throw new d(`Could not read file: ${v.message}`,c.VALIDATION)}if(!k)throw new d("File is empty",c.VALIDATION);g={description:k}}else{let k=l.trim();if(!k)throw new d("--description cannot be empty",c.VALIDATION);g={description:k}}let w=await r.patch(s,g,{workspace:!0});p(w,e);return}default:throw new d(`Unknown personalization command "${n}". Try: get, set`,c.USAGE)}}function Ge(e){return e.trim().replace(/^\/?r\//i,"").replace(/^@/,"")}async function de(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"flairs":case"flair":case"flair-options":{let r=i[0]||f(t,"subreddit","sub","r");if(!r)throw new d(`Usage: dun reddit flairs <subreddit>
301
+ Example: dun reddit flairs tamilyapping --json`,c.USAGE);let s=Ge(r),l=((await a.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}));p({subreddit:s,choices:l},{...e,emptyMessage:`No flairs returned for r/${s}. The subreddit may not use post flair.`});return}case"requirements":case"post-requirements":{let r=i[0]||f(t,"subreddit","sub","r");if(!r)throw new d("Usage: dun reddit requirements <subreddit>",c.USAGE);let s=Ge(r),o=await a.get("/api/social-accounts/reddit/post-requirements",{workspace:!0,query:{subreddit:s}});p({subreddit:s,requirements:o},e);return}default:throw new d(`Unknown reddit command "${n??""}". Try: flairs, requirements`,c.USAGE)}}async function le(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"boards":case"list":case"list-boards":{let r=await a.get("/api/social-accounts/pinterest/boards",{workspace:!0});p({boards:r.boards??[]},{...e,emptyMessage:'No Pinterest boards on this account. Create one with dun pinterest create-board --name "..."'});return}case"create-board":case"board-create":case"create":{let r=f(t,"name","n")||i[0]||"";if(!r.trim())throw new d(`Usage: dun pinterest create-board --name "Brand assets" [--private]
302
+ Example: dun pinterest create-board --name "Landing page" --json`,c.USAGE);let s=await a.post("/api/social-accounts/pinterest/boards",{name:r.trim(),privacy:h(t,"private")?"SECRET":"PUBLIC"},{workspace:!0});p(s,e);return}default:throw new d(`Unknown pinterest command "${n??""}". Try: boards, create-board`,c.USAGE)}}async function ue(e,n,t,i){y(e),I(e);let a=b(e);switch(n){case"channels":case"list":case"list-channels":{let r=f(t,"account","a"),s=r?{socialAccountId:r}:void 0,o=await a.get("/api/social-accounts/discord/channels",{workspace:!0,query:s});p({channels:o.channels??[],guildId:o.guildId,source:o.source},{...e,emptyMessage:"No text or announcement channels. Reconnect Discord and pick a server the bot can post in."});return}default:throw new d(`Unknown discord command "${n??""}". Try: channels
303
+ Example: dun discord channels --account <id>`,c.USAGE)}}import{existsSync as He}from"node:fs";import{mkdir as ze,writeFile as Je,readFile as fe}from"node:fs/promises";import{dirname as ge,join as A}from"node:path";import{homedir as F}from"node:os";import{fileURLToPath as sn}from"node:url";var _=[{id:"claude",label:"Claude Code",aliases:["claude-code"],dest:(e)=>A(e,".claude","skills","dunsocial","SKILL.md")},{id:"codex",label:"Codex",dest:(e)=>A(e,".codex","skills","dunsocial","SKILL.md"),afterInstall:async(e)=>({agentsPath:await cn(e)})},{id:"cursor",label:"Cursor",dest:(e)=>A(e,".cursor","skills","dunsocial","SKILL.md")},{id:"pi",label:"Pi",dest:(e)=>A(e,".pi","agent","skills","dunsocial","SKILL.md")},{id:"opencode",label:"OpenCode",dest:(e)=>A(e,".config","opencode","skills","dunsocial","SKILL.md")},{id:"agents",label:"Universal (.agents)",aliases:["universal"],dest:(e)=>A(e,".agents","skills","dunsocial","SKILL.md")},{id:"copilot",label:"GitHub Copilot",aliases:["github-copilot","github"],dest:(e)=>A(e,".copilot","skills","dunsocial","SKILL.md")},{id:"windsurf",label:"Windsurf",dest:(e)=>A(e,".codeium","windsurf","skills","dunsocial","SKILL.md")},{id:"goose",label:"Goose",dest:(e)=>A(e,".config","goose","skills","dunsocial","SKILL.md")},{id:"gemini",label:"Gemini CLI",aliases:["gemini-cli"],dest:(e)=>A(e,".gemini","skills","dunsocial","SKILL.md")},{id:"amp",label:"Amp",dest:(e)=>A(e,".config","agents","skills","dunsocial","SKILL.md")},{id:"grok",label:"Grok Build",dest:(e)=>A(e,".grok","skills","dunsocial","SKILL.md")},{id:"continue",label:"Continue",dest:(e)=>A(e,".continue","skills","dunsocial","SKILL.md")},{id:"antigravity",label:"Antigravity",dest:(e)=>A(e,".gemini","antigravity","skills","dunsocial","SKILL.md")}],pe=new Map;for(let e of _){pe.set(e.id,e);for(let n of e.aliases??[])pe.set(n,e)}var Fe=`Usage: dun skill install <${_.map((e)=>e.id).join("|")}|all>`;function me(){let e=ge(sn(import.meta.url)),n=A("skills","dunsocial","SKILL.md"),t=[A(e,"..",n),A(e,"..","..",n)];return t.find(He)??t[0]}function an(){let e=F();return _.map((n)=>({id:n.id,label:n.label,path:n.dest(e),aliases:n.aliases??[]}))}async function Ye(){try{return await fe(me(),"utf8")}catch{throw new d(`Bundled skill not found at ${me()}. Reinstall dunsocial.`,c.USAGE)}}async function Xe(e,n){await ze(ge(e),{recursive:!0}),await Je(e,n,"utf8")}async function cn(e){let n=A(F(),".codex","AGENTS.md");await ze(ge(n),{recursive:!0});let t="";try{t=await fe(n,"utf8")}catch{t=""}let i=["<!-- 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(`
282
304
  `),a="<!-- BEGIN DUNSOCIAL CLI SKILL -->",r="<!-- END DUNSOCIAL CLI SKILL -->",s,o=t.indexOf(a),l=t.indexOf(r);if(o!==-1&&l!==-1&&l>o)s=t.slice(0,o)+i+t.slice(l+r.length);else if(t.trim())s=`${t.trimEnd()}
283
305
 
284
306
  ${i}
285
307
  `;else s=`${i}
286
- `;return await ze(n,s,"utf8"),n}async function K(e){let n=e?.home??F(),t=e?.markdown;if(t===void 0)try{t=await Je()}catch{return[]}let i=[];for(let a of _){let r=a.dest(n);if(!Ke(r))continue;let s="";try{s=await me(r,"utf8")}catch{continue}if(s===t){i.push({target:a.id,path:r,updated:!1});continue}if(await Ye(r,t),a.afterInstall)try{await a.afterInstall(r)}catch{}i.push({target:a.id,path:r,updated:!0})}return i}async function Fe(e,n){let t=e.dest(F());await Ye(t,n);let i=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.`,...i}}async function Xe(e,n,t,i){switch(n){case"path":{m({path:pe()},e);return}case"list":{m({targets:sn(),hint:"Install with: dun skill install <target|all>"},e);return}case"install":{let a=(i[0]||"").toLowerCase();if(!a)throw new d(Ge,c.USAGE);let r=await Je();if(a==="all"){let l=[];for(let u of _){let p=await Fe(u,r);l.push({target:p.target,path:p.path})}m({target:"all",count:l.length,installed:l,message:`Installed DunSocial skill for ${l.length} agents.`},e);return}let s=ue.get(a);if(!s)throw new d(`Unknown agent "${a}". ${Ge}
287
- Try: dun skill list`,c.USAGE);let o=await Fe(s,r);m(o,e);return}default:throw new d(`Unknown skill command "${n??""}". Try: install, list, path`,c.USAGE)}}import{spawn as cn}from"node:child_process";import{realpathSync as dn}from"node:fs";var S="dunsocial",ln=`https://registry.npmjs.org/${S}/latest`,un=400;function M(e=process.argv[1]??""){let n=e.replaceAll("\\","/");try{n=dn(e).replaceAll("\\","/")}catch{}if(n.includes("/.npm/_npx")||n.includes("/npm/_npx")||n.includes("/_npx/"))return{packageManager:"npx",manual:`npm install -g ${S}@latest`};if(n.includes("/.pnpm/")||n.includes("/pnpm/"))return{packageManager:"pnpm",command:"pnpm",args:["add","-g",`${S}@latest`],manual:`pnpm add -g ${S}@latest`};if(n.includes("/.yarn/")||n.includes("/yarn/global"))return{packageManager:"yarn",command:"yarn",args:["global","add",`${S}@latest`],manual:`yarn global add ${S}@latest`};if(n.includes("/.bun/")||n.includes("/bun/install/global"))return{packageManager:"bun",command:"bun",args:["add","-g",`${S}@latest`],manual:`bun add -g ${S}@latest`};if(n.includes(`/node_modules/${S}/`))return{packageManager:"npm",command:"npm",args:["install","-g",`${S}@latest`],manual:`npm install -g ${S}@latest`};return{packageManager:"unknown",manual:`npm install -g ${S}@latest`}}function j(e,n){let t=e.replace(/^v/,"").split(".").map((r)=>Number.parseInt(r,10)||0),i=n.replace(/^v/,"").split(".").map((r)=>Number.parseInt(r,10)||0),a=Math.max(t.length,i.length);for(let r=0;r<a;r++){let s=t[r]??0,o=i[r]??0;if(s>o)return 1;if(s<o)return-1}return 0}function Ze(e){return Number.parseInt(e.replace(/^v/,"").split(".")[0]??"0",10)||0}function Qe(e,n){return Ze(n)>Ze(e)}async function H(e=fetch,n){let t=await e(ln,{headers:{Accept:"application/json"},...n});if(!t.ok)throw new d(`Could not check npm for updates (HTTP ${t.status}). Try: npm install -g ${S}@latest`,c.NETWORK);let i=await t.json();if(!i.version)throw new d("npm registry response missing version",c.NETWORK);return i.version}async function ge(e=un,n=fetch){let t=new AbortController,i=setTimeout(()=>t.abort(),e);try{return await H(n,{signal:t.signal})}catch{return}finally{clearTimeout(i)}}function z(e,n,t){let i=t?.stdio??"inherit";return new Promise((a,r)=>{let s=cn(e,n,{stdio:i,env:process.env,windowsHide:!0,shell:process.platform==="win32"});s.on("error",(o)=>{r(new d(`Failed to run ${e}: ${o.message}. Try manually: ${e} ${n.join(" ")}`,c.NETWORK))}),s.on("close",(o)=>a(o??1))})}async function et(e,n,t){let i=h(t,"check"),a=h(t,"force"),r=U,s=M(),o;try{o=await H()}catch(g){if(g instanceof d)throw g;throw new d(`Could not check npm for updates: ${g.message}`,c.NETWORK)}let l=j(o,r)>0,u={current:r,latest:o,upToDate:!l,packageManager:s.packageManager,command:s.manual};if(!l&&!a){m(e.json?u:`Already on the latest version (${r}).`,e);return}if(i){m(e.json?{...u,updateAvailable:l}:`Update available: ${r} → ${o}
308
+ `;return await Je(n,s,"utf8"),n}async function K(e){let n=e?.home??F(),t=e?.markdown;if(t===void 0)try{t=await Ye()}catch{return[]}let i=[];for(let a of _){let r=a.dest(n);if(!He(r))continue;let s="";try{s=await fe(r,"utf8")}catch{continue}if(s===t){i.push({target:a.id,path:r,updated:!1});continue}if(await Xe(r,t),a.afterInstall)try{await a.afterInstall(r)}catch{}i.push({target:a.id,path:r,updated:!0})}return i}async function Ke(e,n){let t=e.dest(F());await Xe(t,n);let i=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.`,...i}}async function Ze(e,n,t,i){switch(n){case"path":{p({path:me()},e);return}case"list":{p({targets:an(),hint:"Install with: dun skill install <target|all>"},e);return}case"install":{let a=(i[0]||"").toLowerCase();if(!a)throw new d(Fe,c.USAGE);let r=await Ye();if(a==="all"){let l=[];for(let u of _){let m=await Ke(u,r);l.push({target:m.target,path:m.path})}p({target:"all",count:l.length,installed:l,message:`Installed DunSocial skill for ${l.length} agents.`},e);return}let s=pe.get(a);if(!s)throw new d(`Unknown agent "${a}". ${Fe}
309
+ Try: dun skill list`,c.USAGE);let o=await Ke(s,r);p(o,e);return}default:throw new d(`Unknown skill command "${n??""}". Try: install, list, path`,c.USAGE)}}import{spawn as dn}from"node:child_process";import{realpathSync as ln}from"node:fs";var S="dunsocial",un=`https://registry.npmjs.org/${S}/latest`,pn=400;function j(e=process.argv[1]??""){let n=e.replaceAll("\\","/");try{n=ln(e).replaceAll("\\","/")}catch{}if(n.includes("/.npm/_npx")||n.includes("/npm/_npx")||n.includes("/_npx/"))return{packageManager:"npx",manual:`npm install -g ${S}@latest`};if(n.includes("/.pnpm/")||n.includes("/pnpm/"))return{packageManager:"pnpm",command:"pnpm",args:["add","-g",`${S}@latest`],manual:`pnpm add -g ${S}@latest`};if(n.includes("/.yarn/")||n.includes("/yarn/global"))return{packageManager:"yarn",command:"yarn",args:["global","add",`${S}@latest`],manual:`yarn global add ${S}@latest`};if(n.includes("/.bun/")||n.includes("/bun/install/global"))return{packageManager:"bun",command:"bun",args:["add","-g",`${S}@latest`],manual:`bun add -g ${S}@latest`};if(n.includes(`/node_modules/${S}/`))return{packageManager:"npm",command:"npm",args:["install","-g",`${S}@latest`],manual:`npm install -g ${S}@latest`};return{packageManager:"unknown",manual:`npm install -g ${S}@latest`}}function M(e,n){let t=e.replace(/^v/,"").split(".").map((r)=>Number.parseInt(r,10)||0),i=n.replace(/^v/,"").split(".").map((r)=>Number.parseInt(r,10)||0),a=Math.max(t.length,i.length);for(let r=0;r<a;r++){let s=t[r]??0,o=i[r]??0;if(s>o)return 1;if(s<o)return-1}return 0}function Qe(e){return Number.parseInt(e.replace(/^v/,"").split(".")[0]??"0",10)||0}function et(e,n){return Qe(n)>Qe(e)}async function H(e=fetch,n){let t=await e(un,{headers:{Accept:"application/json"},...n});if(!t.ok)throw new d(`Could not check npm for updates (HTTP ${t.status}). Try: npm install -g ${S}@latest`,c.NETWORK);let i=await t.json();if(!i.version)throw new d("npm registry response missing version",c.NETWORK);return i.version}async function we(e=pn,n=fetch){let t=new AbortController,i=setTimeout(()=>t.abort(),e);try{return await H(n,{signal:t.signal})}catch{return}finally{clearTimeout(i)}}function z(e,n,t){let i=t?.stdio??"inherit";return new Promise((a,r)=>{let s=dn(e,n,{stdio:i,env:process.env,windowsHide:!0,shell:process.platform==="win32"});s.on("error",(o)=>{r(new d(`Failed to run ${e}: ${o.message}. Try manually: ${e} ${n.join(" ")}`,c.NETWORK))}),s.on("close",(o)=>a(o??1))})}async function tt(e,n,t){let i=h(t,"check"),a=h(t,"force"),r=T,s=j(),o;try{o=await H()}catch(g){if(g instanceof d)throw g;throw new d(`Could not check npm for updates: ${g.message}`,c.NETWORK)}let l=M(o,r)>0,u={current:r,latest:o,upToDate:!l,packageManager:s.packageManager,command:s.manual};if(!l&&!a){p(e.json?u:`Already on the latest version (${r}).`,e);return}if(i){p(e.json?{...u,updateAvailable:l}:`Update available: ${r} → ${o}
288
310
  Run: dun update
289
- Or: ${s.manual}`,e);return}if(!s.command||!s.args){m(e.json?{...u,applied:!1,hint:s.manual}:`Update available: ${r} → ${o}
311
+ Or: ${s.manual}`,e);return}if(!s.command||!s.args){p(e.json?{...u,applied:!1,hint:s.manual}:`Update available: ${r} → ${o}
290
312
  Cannot auto-update this install (${s.packageManager}).
291
313
  Run: ${s.manual}`,e);return}if(!e.json&&!e.quiet)process.stderr.write(`Updating ${S}: ${r} → ${o} via ${s.packageManager}...
292
- `);let p=await z(s.command,s.args);if(p!==0)throw new d(`Update command failed (exit ${p}). Try: ${s.manual}`,c.NETWORK);try{await K()}catch{}m(e.json?{...u,applied:!0}:`Updated via ${s.packageManager}. Run dun --version to confirm.`,e)}import{mkdir as pn,readFile as mn,writeFile as fn}from"node:fs/promises";import{dirname as gn,join as tt}from"node:path";import{homedir as wn}from"node:os";var nt=86400000,hn=new Set(["update","upgrade","self-update"]);function Y(e=process.env){if(e.DUN_UPDATE_CACHE)return e.DUN_UPDATE_CACHE;let n=e.XDG_CACHE_HOME,t=n?n:tt(wn(),".cache");return tt(t,"dunsocial","update.json")}function kn(e){let n=(e??"").trim().toLowerCase();return n==="0"||n==="never"||n==="off"||n==="false"||n==="no"}function bn(e=process.env){return e.CI==="true"||e.CI==="1"||Boolean(e.GITHUB_ACTIONS)||Boolean(e.TF_BUILD)||Boolean(e.GITLAB_CI)||Boolean(e.CIRCLECI)||Boolean(e.BUILDKITE)}function yn(e){return Boolean(e&&hn.has(e))}function In(e){return Boolean(e.command&&e.args)}function rt(e){let n=e.env??process.env;if(h(e.flags,"no-update"))return!1;if(h(e.flags,"json","j"))return!1;if(yn(e.group))return!1;if(!e.stdoutIsTty)return!1;if(n.DUN_JSON==="1")return!1;if(bn(n))return!1;if(kn(n.DUN_UPDATE))return!1;return!0}function it(e,n=Date.now(),t=nt){if(!e.lastCheckedAt||!e.latest)return!1;let i=Date.parse(e.lastCheckedAt);if(!Number.isFinite(i))return!1;return n-i<t}function An(e,n,t=Date.now(),i=nt){if(e.lastFailedVersion!==n||!e.lastFailedAt)return!0;let a=Date.parse(e.lastFailedAt);if(!Number.isFinite(a))return!0;return t-a>=i}async function ot(e=Y()){try{let n=await mn(e,"utf8");return JSON.parse(n)}catch{return{}}}async function J(e,n=Y()){return await pn(gn(n),{recursive:!0}),await fn(n,`${JSON.stringify(e,null,2)}
293
- `,"utf8"),e}async function st(e){let n=e?.cachePath??Y(),t=e?.now??Date.now(),i=await ot(n);if(it(i,t))return i;let a=await(e?.fetchLatest??ge)();if(!a)return i;return J({...i,lastCheckedAt:new Date(t).toISOString(),latest:a},n)}async function Sn(e){let n=e.write??((o)=>process.stderr.write(o)),t=e.cachePath??Y(),i=e.now??Date.now(),a=e.install??M();if(!In(a))return{applied:!1,reason:"no-install-command"};let r=await e.checkPromise??await ot(t),s=r.latest;if(!s||!it(r,i)){let o=await(e.fetchLatest??ge)();if(o)r=await J({...r,lastCheckedAt:new Date(i).toISOString(),latest:o},t),s=o}if(!s)return{applied:!1,reason:"no-latest"};if(j(s,e.current)<=0)return{applied:!1,reason:"up-to-date",latest:s};if(Qe(e.current,s)){if(!e.quiet)n(`Major update available: ${e.current} -> ${s}. Run: dun update --yes
294
- `);return{applied:!1,reason:"major",latest:s}}if(!An(r,s,i))return{applied:!1,reason:"failed-backoff",latest:s};try{let o=await(e.spawn??z)(a.command,a.args,{stdio:"ignore"});if(o!==0)throw Error(`exit ${o}`);if(await J({...r,latest:s,lastAppliedAt:new Date(i).toISOString(),lastFailedAt:void 0,lastFailedVersion:void 0},t),!e.quiet)n(`Updated ${S} ${e.current} → ${s}
314
+ `);let m=await z(s.command,s.args);if(m!==0)throw new d(`Update command failed (exit ${m}). Try: ${s.manual}`,c.NETWORK);try{await K()}catch{}p(e.json?{...u,applied:!0}:`Updated via ${s.packageManager}. Run dun --version to confirm.`,e)}import{mkdir as mn,readFile as fn,writeFile as gn}from"node:fs/promises";import{dirname as wn,join as nt}from"node:path";import{homedir as hn}from"node:os";var rt=86400000,kn=new Set(["update","upgrade","self-update"]);function Y(e=process.env){if(e.DUN_UPDATE_CACHE)return e.DUN_UPDATE_CACHE;let n=e.XDG_CACHE_HOME,t=n?n:nt(hn(),".cache");return nt(t,"dunsocial","update.json")}function bn(e){let n=(e??"").trim().toLowerCase();return n==="0"||n==="never"||n==="off"||n==="false"||n==="no"}function yn(e=process.env){return e.CI==="true"||e.CI==="1"||Boolean(e.GITHUB_ACTIONS)||Boolean(e.TF_BUILD)||Boolean(e.GITLAB_CI)||Boolean(e.CIRCLECI)||Boolean(e.BUILDKITE)}function In(e){return Boolean(e&&kn.has(e))}function An(e){return Boolean(e.command&&e.args)}function it(e){let n=e.env??process.env;if(h(e.flags,"no-update"))return!1;if(h(e.flags,"json","j"))return!1;if(In(e.group))return!1;if(!e.stdoutIsTty)return!1;if(n.DUN_JSON==="1")return!1;if(yn(n))return!1;if(bn(n.DUN_UPDATE))return!1;return!0}function ot(e,n=Date.now(),t=rt){if(!e.lastCheckedAt||!e.latest)return!1;let i=Date.parse(e.lastCheckedAt);if(!Number.isFinite(i))return!1;return n-i<t}function Sn(e,n,t=Date.now(),i=rt){if(e.lastFailedVersion!==n||!e.lastFailedAt)return!0;let a=Date.parse(e.lastFailedAt);if(!Number.isFinite(a))return!0;return t-a>=i}async function st(e=Y()){try{let n=await fn(e,"utf8");return JSON.parse(n)}catch{return{}}}async function J(e,n=Y()){return await mn(wn(n),{recursive:!0}),await gn(n,`${JSON.stringify(e,null,2)}
315
+ `,"utf8"),e}async function at(e){let n=e?.cachePath??Y(),t=e?.now??Date.now(),i=await st(n);if(ot(i,t))return i;let a=await(e?.fetchLatest??we)();if(!a)return i;return J({...i,lastCheckedAt:new Date(t).toISOString(),latest:a},n)}async function Cn(e){let n=e.write??((o)=>process.stderr.write(o)),t=e.cachePath??Y(),i=e.now??Date.now(),a=e.install??j();if(!An(a))return{applied:!1,reason:"no-install-command"};let r=await e.checkPromise??await st(t),s=r.latest;if(!s||!ot(r,i)){let o=await(e.fetchLatest??we)();if(o)r=await J({...r,lastCheckedAt:new Date(i).toISOString(),latest:o},t),s=o}if(!s)return{applied:!1,reason:"no-latest"};if(M(s,e.current)<=0)return{applied:!1,reason:"up-to-date",latest:s};if(et(e.current,s)){if(!e.quiet)n(`Major update available: ${e.current} -> ${s}. Run: dun update --yes
316
+ `);return{applied:!1,reason:"major",latest:s}}if(!Sn(r,s,i))return{applied:!1,reason:"failed-backoff",latest:s};try{let o=await(e.spawn??z)(a.command,a.args,{stdio:"ignore"});if(o!==0)throw Error(`exit ${o}`);if(await J({...r,latest:s,lastAppliedAt:new Date(i).toISOString(),lastFailedAt:void 0,lastFailedVersion:void 0},t),!e.quiet)n(`Updated ${S} ${e.current} → ${s}
295
317
  `);return{applied:!0,reason:"applied",latest:s}}catch{if(await J({...r,latest:s,lastFailedAt:new Date(i).toISOString(),lastFailedVersion:s},t),!e.quiet)n(`Auto-update failed. Try: ${a.manual}
296
- `);return{applied:!1,reason:"apply-failed",latest:s}}}async function at(e){try{await e.refreshSkills();let t=await(e.apply??Sn)({current:e.current,checkPromise:e.checkPromise,quiet:e.quiet,install:e.install,cachePath:e.cachePath,now:e.now,fetchLatest:e.fetchLatest,spawn:e.spawn,write:e.write});if(t.applied)await e.refreshSkills();return t}catch{return}}function ct(){if(x(process.stdout))process.stdout.write(`${Ue()}
318
+ `);return{applied:!1,reason:"apply-failed",latest:s}}}async function ct(e){try{await e.refreshSkills();let t=await(e.apply??Cn)({current:e.current,checkPromise:e.checkPromise,quiet:e.quiet,install:e.install,cachePath:e.cachePath,now:e.now,fetchLatest:e.fetchLatest,spawn:e.spawn,write:e.write});if(t.applied)await e.refreshSkills();return t}catch{return}}function dt(){if(x(process.stdout))process.stdout.write(`${Ue()}
297
319
 
298
320
  `);process.stdout.write(`${re}
299
- `)}async function Tn(){let e=await Z(),n=process.env.DUN_TOKEN||e.token,t=process.env.DUN_WORKSPACE_ID||e.defaultWorkspaceId;process.stdout.write(`${Ee({token:n,workspaceId:t,config:e})}
300
- `)}async function Un(e){let n=Q(e),{group:t,action:i,nest:a,flags:r,positionals:s}=n;if(te(r)&&!t)return process.stdout.write(`${U}
321
+ `)}async function Tn(){let e=await Z(),n=process.env.DUN_TOKEN||e.token,t=process.env.DUN_WORKSPACE_ID||e.defaultWorkspaceId;process.stdout.write(`${Ne({token:n,workspaceId:t,config:e})}
322
+ `)}async function Un(e){let n=Q(e),{group:t,action:i,nest:a,flags:r,positionals:s}=n;if(te(r)&&!t)return process.stdout.write(`${T}
301
323
  `),c.OK;if(t==="help"||!t&&ee(r)){let l=t==="help"?i||s[0]:void 0;if(l)process.stdout.write(`${ie(l)}
302
- `);else ct();return c.OK}if(!t){if(x(process.stdout))await Tn();else ct();return c.OK}if(ee(r))return process.stdout.write(`${ie(t)}
303
- `),c.OK;let o=await ye({apiUrl:f(r,"api-url","apiUrl"),token:f(r,"token"),workspace:f(r,"workspace","w"),json:h(r,"json","j")?!0:void 0,debug:h(r,"debug"),quiet:h(r,"quiet","q"),yes:h(r,"yes","y")});if(te(r))return m({version:U},o),c.OK;try{switch(t){case"auth":await ve(o,i,r,s);break;case"workspace":case"workspaces":await Re(o,i,r,s);break;case"account":case"accounts":if(i==="reddit"){await de(o,s[0],r,s.slice(1));break}if(i==="pinterest"){await le(o,s[0],r,s.slice(1));break}await Le(o,i??"list",r,s);break;case"reddit":await de(o,i,r,s);break;case"pinterest":await le(o,i,r,s);break;case"post":case"posts":await Pe(o,i,r,s);break;case"draft":case"drafts":await De(o,i,r,s);break;case"media":await We(o,i,r,s);break;case"memory":{let l=a?[a,i].filter(Boolean):i?[i]:[];await qe(o,l,r,s);break}case"personalization":case"voice":await Ve(o,i,r,s);break;case"skill":case"skills":await Xe(o,i,r,s);break;case"update":case"upgrade":case"self-update":await et(o,i,r);break;case"version":m({version:U},o);break;default:throw new d(`Unknown command "${t}". Run dun --help`,c.USAGE)}return c.OK}catch(l){return Ae(l,o)}}var dt=process.argv.slice(2),we=Q(dt),lt=rt({flags:we.flags,group:we.group,stdoutIsTty:Boolean(process.stdout.isTTY),env:process.env}),Cn=lt?st():Promise.resolve(void 0),ut=await Un(dt);if(ut===c.OK&&lt)await at({current:U,checkPromise:Cn,quiet:h(we.flags,"quiet","q"),refreshSkills:K});process.exit(ut);
324
+ `);else dt();return c.OK}if(!t){if(x(process.stdout))await Tn();else dt();return c.OK}if(ee(r))return process.stdout.write(`${ie(t)}
325
+ `),c.OK;let o=await Ie({apiUrl:f(r,"api-url","apiUrl"),token:f(r,"token"),workspace:f(r,"workspace","w"),json:h(r,"json","j")?!0:void 0,debug:h(r,"debug"),quiet:h(r,"quiet","q"),yes:h(r,"yes","y")});if(te(r))return p({version:T},o),c.OK;try{switch(t){case"auth":await xe(o,i,r,s);break;case"workspace":case"workspaces":await Le(o,i,r,s);break;case"account":case"accounts":if(i==="reddit"){await de(o,s[0],r,s.slice(1));break}if(i==="pinterest"){await le(o,s[0],r,s.slice(1));break}if(i==="discord"){await ue(o,s[0],r,s.slice(1));break}await $e(o,i??"list",r,s);break;case"reddit":await de(o,i,r,s);break;case"pinterest":await le(o,i,r,s);break;case"discord":await ue(o,i,r,s);break;case"post":case"posts":await Pe(o,i,r,s);break;case"draft":case"drafts":await _e(o,i,r,s);break;case"media":await qe(o,i,r,s);break;case"memory":{let l=a?[a,i].filter(Boolean):i?[i]:[];await Ve(o,l,r,s);break}case"personalization":case"voice":await Be(o,i,r,s);break;case"skill":case"skills":await Ze(o,i,r,s);break;case"update":case"upgrade":case"self-update":await tt(o,i,r);break;case"version":p({version:T},o);break;default:throw new d(`Unknown command "${t}". Run dun --help`,c.USAGE)}return c.OK}catch(l){return Se(l,o)}}var lt=process.argv.slice(2),he=Q(lt),ut=it({flags:he.flags,group:he.group,stdoutIsTty:Boolean(process.stdout.isTTY),env:process.env}),En=ut?at():Promise.resolve(void 0),pt=await Un(lt);if(pt===c.OK&&ut)await ct({current:T,checkPromise:En,quiet:h(he.flags,"quiet","q"),refreshSkills:K});process.exit(pt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dunsocial",
3
- "version": "0.14.0",
3
+ "version": "0.15.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",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: dunsocial
3
- description: Schedule, publish, and manage DunSocial posts, drafts, media, memory, and workspace personalization (AI voice) from the shell via the dun CLI (npm package dunsocial). Use when the user wants to post or schedule to X/LinkedIn/Bluesky/Threads/Reddit/Pinterest/Instagram/YouTube through DunSocial, manage drafts or media uploads, search workspace memory, get/set AI voice personalization, or wire social publishing into CI/agent workflows.
3
+ description: Schedule, publish, and manage DunSocial posts, drafts, media, memory, and workspace personalization (AI voice) from the shell via the dun CLI (npm package dunsocial). Use when the user wants to post or schedule to X/LinkedIn/Bluesky/Threads/Reddit/Pinterest/Instagram/YouTube/Discord through DunSocial, manage drafts or media uploads, search workspace memory, get/set AI voice personalization, or wire social publishing into CI/agent workflows.
4
4
  ---
5
5
 
6
6
  # DunSocial CLI skill
@@ -170,6 +170,31 @@ dun posts publish \
170
170
 
171
171
  Also: `dun reddit requirements <subreddit> --json` for live rules (`is_flair_required`, title limits).
172
172
 
173
+ ## Discord (channel required)
174
+
175
+ Discord posts need `metadata.discord.channelId` (a snowflake from the connected server). One Discord server per workspace.
176
+
177
+ ```bash
178
+ dun accounts list --platform discord --json
179
+ dun discord channels --json
180
+ # -> { id, name, type }
181
+
182
+ dun posts validate \
183
+ --accounts <discordAccountId> \
184
+ --text "Shipped from the CLI" \
185
+ --meta '{"discord":{"channelId":"<id>"}}' \
186
+ --json
187
+
188
+ dun posts publish \
189
+ --accounts <discordAccountId> \
190
+ --text "Shipped from the CLI" \
191
+ --meta '{"discord":{"channelId":"<id>"}}' \
192
+ --wait \
193
+ --json
194
+ ```
195
+
196
+ Text up to 2000 characters, up to 10 attachments. Mentions like `@everyone` are stripped on publish.
197
+
173
198
  ## Drafts / memory / threads
174
199
 
175
200
  ```bash
@@ -233,6 +258,7 @@ Failure JSON:
233
258
  - `--accounts` = DunSocial social account **ids**, not handles
234
259
  - Upload local files with `media upload` before attaching
235
260
  - Reddit (and some other platforms) need `--meta '{"reddit":{…}}'`
261
+ - Discord needs `--meta '{"discord":{"channelId":"…"}}'`. Discover ids with `dun discord channels --json`.
236
262
  - `posts get` / list never include OAuth secrets — safe to log
237
263
  - MCP OAuth and CLI tokens are different systems
238
264
 
@@ -242,4 +268,5 @@ Failure JSON:
242
268
  dun --help
243
269
  dun posts --help
244
270
  dun reddit --help
271
+ dun discord --help
245
272
  ```