@barbapapazes/content-creation 0.20.3 → 0.20.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -53
- package/dist/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/x-BNh7Evu6.mjs +13 -0
- package/package.json +2 -2
- package/dist/x-CAcjVnjQ.mjs +0 -12
package/README.md
CHANGED
|
@@ -17,59 +17,6 @@ content-creation --help
|
|
|
17
17
|
content-creation <command> --help
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
## Commands
|
|
21
|
-
|
|
22
|
-
- `content new` — create a dated content directory
|
|
23
|
-
- `content upcoming` — list upcoming publications
|
|
24
|
-
- `content rebuild-indexes` — regenerate content indexes
|
|
25
|
-
- `series new` — create a new series directory
|
|
26
|
-
- `series video process` — process a series video into subtitles and optional thumbnails
|
|
27
|
-
- `series article new` — insert a new article in a series
|
|
28
|
-
- `series article ready` — mark a series article as ready and refresh reading time
|
|
29
|
-
- `series article estimate-time` — compute reading time for a series article
|
|
30
|
-
- `linkedin ready` — link images, mark the post ready, and mark the script ready when present
|
|
31
|
-
- `linkedin publish-calendar` — publish the LinkedIn calendar to Cloudflare R2
|
|
32
|
-
- `x schedule-reminder` — schedule an X reminder through the automation endpoint
|
|
33
|
-
- `resource new` — create a new resource directory
|
|
34
|
-
|
|
35
|
-
## Configuration
|
|
36
|
-
|
|
37
|
-
The package reads configuration from:
|
|
38
|
-
|
|
39
|
-
- `content-creation.config.{ts,js,mjs,json}` in your project
|
|
40
|
-
- `~/.content-creationrc`
|
|
41
|
-
- environment variables loaded from `.env`
|
|
42
|
-
|
|
43
|
-
Environment variables used by advanced workflows:
|
|
44
|
-
|
|
45
|
-
- `CALENDAR_PUBLIC_URL`
|
|
46
|
-
- `CALENDAR_TOKEN`
|
|
47
|
-
- `AUTOMATION_ENDPOINT`
|
|
48
|
-
- `CF_ACCESS_CLIENT_ID`
|
|
49
|
-
- `CF_ACCESS_CLIENT_SECRET`
|
|
50
|
-
- `OPENAI_API_KEY`
|
|
51
|
-
- `OPENAI_TRANSCRIPTION_LANGUAGE`
|
|
52
|
-
- `OPENAI_TRANSCRIPTION_MODEL`
|
|
53
|
-
|
|
54
|
-
Example configuration for the `linkedin ready` command only:
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
import { defineConfig } from '@barbapapazes/content-creation'
|
|
58
|
-
|
|
59
|
-
export default defineConfig({
|
|
60
|
-
linkedin: {
|
|
61
|
-
ready: {
|
|
62
|
-
copilot: {
|
|
63
|
-
model: 'gpt-5',
|
|
64
|
-
reasoningEffort: 'medium',
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
})
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
For series video processing, the command scans the selected series directory recursively for `.mp4` files, generates an `.srt` transcription next to the chosen video, and can optionally render thumbnails into a `thumbnails/` subdirectory.
|
|
72
|
-
|
|
73
20
|
## License
|
|
74
21
|
|
|
75
22
|
MIT
|
package/dist/cli.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{C as e,S as t,a as n,g as r,i,o as a,r as o,s,t as c,v as l,w as u,y as d}from"./x-
|
|
2
|
+
import{C as e,S as t,a as n,g as r,i,o as a,r as o,s,t as c,v as l,w as u,y as d}from"./x-BNh7Evu6.mjs";import f from"node:process";import{log as p}from"@clack/prompts";import{Command as m}from"commander";var h=`@barbapapazes/content-creation`,g=`0.20.4`,_=`CLI tool for content creation and management`;const v=new m;v.name(h).description(_).version(g).showHelpAfterError();const y=v.command(`content`);y.description(`Manage dated content`),y.command(`new`).description(`Create a dated content directory`).action(()=>u(f.cwd())),y.command(`upcoming`).description(`List upcoming publications`).action(()=>e(f.cwd())),y.command(`rebuild-indexes`).description(`Rebuild all content index files`).action(()=>t(f.cwd()));const b=v.command(`series`);b.description(`Manage article series`),b.command(`new`).description(`Create a new series`).action(()=>a(f.cwd())),b.command(`video`).description(`Manage series videos`).command(`process`).description(`Process a video from a series directory`).action(()=>i(f.cwd()));const x=b.command(`article`).description(`Manage series articles`);x.command(`new`).description(`Create a new article in a series`).action(()=>n(f.cwd())),x.command(`ready`).description(`Mark a series article as ready`).action(()=>o(f.cwd())),x.command(`estimate-time`).description(`Estimate the reading time of a series article`).action(()=>s(f.cwd()));const S=v.command(`linkedin`);S.description(`Manage LinkedIn publications`),S.command(`ready`).description(`Proofread a LinkedIn publication, create a checkpoint commit, and mark it as ready`).action(()=>l(f.cwd())),S.command(`publish-calendar`).description(`Publish the LinkedIn calendar`).action(()=>d(f.cwd()));const C=v.command(`x`);C.description(`Manage X publications`),C.command(`schedule-reminder`).description(`Schedule an X publication reminder`).action(()=>c(f.cwd()));const w=v.command(`resource`);w.description(`Manage resources`),w.command(`new`).description(`Create a new resource`).action(()=>r(f.cwd())),v.parseAsync().catch(e=>{p.error(e instanceof Error?e.message:String(e)),f.exit(1)});export{};
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{A as e,C as t,D as n,E as r,F as i,I as a,L as o,M as s,N as c,O as l,P as u,S as d,T as f,_ as p,a as m,b as h,c as g,d as _,f as v,g as y,h as b,i as x,j as S,k as C,l as w,m as T,n as E,o as D,p as O,r as k,s as A,t as j,u as M,v as N,w as P,x as F,y as I}from"./x-
|
|
1
|
+
import{A as e,C as t,D as n,E as r,F as i,I as a,L as o,M as s,N as c,O as l,P as u,S as d,T as f,_ as p,a as m,b as h,c as g,d as _,f as v,g as y,h as b,i as x,j as S,k as C,l as w,m as T,n as E,o as D,p as O,r as k,s as A,t as j,u as M,v as N,w as P,x as F,y as I}from"./x-BNh7Evu6.mjs";function L(e){return e}export{n as capitalize,P as contentNewWorkflow,t as contentUpcomingWorkflow,a as createContentDirectory,p as createResourceDirectory,_ as createSeriesArticle,M as createSeriesDirectory,v as createSeriesIndex,L as defineConfig,l as escapeRegExp,e as estimateReadingMinutesFromMarkdown,w as estimateSeriesArticleTime,O as getSeriesArticles,T as getSeriesDirectories,b as getSeriesRootPath,C as humanizeSlug,I as linkedinPublishCalendarWorkflow,N as linkedinReadyWorkflow,i as listUpcomingContent,o as loadContentCreationConfig,r as markEntryAsReady,g as markSeriesArticleReady,S as promptForRequiredText,F as publishLinkedInCalendar,s as readMarkdownDocument,h as readyLinkedInPublication,u as rebuildContentIndexes,d as rebuildContentIndexesWorkflow,y as resourceNewWorkflow,E as scheduleReminder,A as seriesArticleEstimateTimeWorkflow,m as seriesArticleNewWorkflow,k as seriesArticleReadyWorkflow,D as seriesNewWorkflow,x as seriesVideoProcessWorkflow,f as slugify,c as writeMarkdownDocument,j as xScheduleReminderWorkflow};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import e from"node:process";import{intro as t,isCancel as n,log as r,multiselect as i,outro as a,select as o,text as s}from"@clack/prompts";import{existsSync as c,mkdirSync as l,mkdtempSync as u,readFileSync as d,readdirSync as f,renameSync as p,rmSync as ee,statSync as m,writeFileSync as h}from"node:fs";import{basename as te,dirname as ne,extname as g,isAbsolute as re,join as _,relative as v,resolve as y}from"node:path";import{addDays as b,format as x,parseISO as ie,setHours as ae,setMinutes as oe,setSeconds as se}from"date-fns";import S from"gray-matter";import{loadConfig as ce}from"c12";import{execFileSync as le}from"node:child_process";import{createHash as ue}from"node:crypto";import{fileURLToPath as de}from"node:url";import{tmpdir as fe}from"node:os";import{xSync as pe}from"tinyexec";import{CopilotClient as me,approveAll as he}from"@github/copilot-sdk";import{cleanupFile as ge,extractAudio as _e,generateThumbnail as ve,generateTranscription as ye}from"@barbapapazes/video-toolkit";const C={thematic:[],templatesDir:void 0,linkedin:{ready:{copilot:{}}},templates:{},videoProcessing:{openaiApiKey:``,language:`fr`,model:`whisper-1`},scheduling:{},calendar:{},reading:{wordsPerMinute:100}};function be(t,n){return t?re(t)?t:y(n?ne(n):e.cwd(),t):n?ne(n):e.cwd()}function w(e,t,n){if(!t)return;let i=y(e,t);if(!c(i)){r.warn(`Configured ${n} template was not found: ${i}`);return}return i}function xe(e,t){return{footerPath:w(t,e?.footerPath,`LinkedIn footer`)}}function Se(e,t){return{templatePath:w(t,e?.templatePath,`video description`)}}function Ce(e){let t=e?.model?.trim();return{model:t&&t.length>0?t:void 0,reasoningEffort:e?.reasoningEffort}}function we(e){return{ready:{copilot:Ce(e?.ready?.copilot)}}}async function T(){let{config:t,configFile:n}=await ce({name:`content-creation`,defaults:C,globalRc:!0,dotenv:!0}),r=be(t.templatesDir,n),i={linkedin:xe(t.templates?.linkedin,r),youtube:Se(t.templates?.youtube,r),instagram:Se(t.templates?.instagram,r)},a={automationEndpoint:t.scheduling?.automationEndpoint??e.env.AUTOMATION_ENDPOINT,cfAccessClientId:t.scheduling?.cfAccessClientId??e.env.CF_ACCESS_CLIENT_ID,cfAccessClientSecret:t.scheduling?.cfAccessClientSecret??e.env.CF_ACCESS_CLIENT_SECRET},o={openaiApiKey:t.videoProcessing?.openaiApiKey??e.env.OPENAI_API_KEY??``,language:t.videoProcessing?.language??e.env.OPENAI_TRANSCRIPTION_LANGUAGE??C.videoProcessing?.language??`fr`,model:t.videoProcessing?.model??e.env.OPENAI_TRANSCRIPTION_MODEL??C.videoProcessing?.model??`whisper-1`},s={publicUrl:t.calendar?.publicUrl??e.env.CALENDAR_PUBLIC_URL,token:t.calendar?.token??e.env.CALENDAR_TOKEN},c=t.reading?.wordsPerMinute??C.reading?.wordsPerMinute??100;if(!Number.isFinite(c)||c<=0)throw RangeError(`Reading wordsPerMinute must be a positive number`);return{thematic:t.thematic??[],templatesDir:t.templatesDir??``,linkedin:we(t.linkedin),templates:i,videoProcessing:o,scheduling:a,calendar:s,reading:{wordsPerMinute:c}}}function Te(e){return e?c(e)?d(e,`utf-8`):(r.warn(`Template file not found: ${e}`),``):``}const E=[`linkedin`,`x`,`youtube`,`instagram`],D={linkedin:`LinkedIn`,x:`X`,youtube:`YouTube`,instagram:`Instagram`},O={linkedin:{mainFile:`linkedin.md`,additionalFiles:[`linkedin-script.md`]},x:{mainFile:`x.md`},youtube:{mainFile:`youtube-script.md`,additionalFiles:[`youtube-description.md`]},instagram:{mainFile:`instagram-script.md`,additionalFiles:[`instagram-description.md`]}},Ee={linkedin:{fileName:`linkedin-posts.md`,heading:`LinkedIn Posts`},x:{fileName:`x-posts.md`,heading:`X Posts`},youtube:{fileName:`youtube-videos.md`,heading:`YouTube Videos`},instagram:{fileName:`instagram-posts.md`,heading:`Instagram Posts`}},De=`__load-more__`,Oe=[`.jpg`,`.jpeg`,`.png`],k={hours:11,minutes:0,seconds:0};function ke(e,t,n,i,a){let o=y(_(a,x(e,`yyyy`),x(e,`MM`),x(e,`dd`)));c(o)||(l(o,{recursive:!0}),r.success(`Created directory: ${o}`));let s=Ae(n);for(let e of t)je(e,o,s,i);return o}function Ae(e){return e.hasVideo?{...e,hasImages:!1}:{...e,hasImages:!0}}function je(e,t,n,i){let a=O[e],o=_(t,a.mainFile),s=`additionalFiles`in a?a.additionalFiles:void 0;if(c(o)?r.info(`${a.mainFile} already exists, skipping`):Me(e,o,n,i),s)for(let a of s){let o=_(t,a);Pe(a,e,n)&&(c(o)?r.info(`${a} already exists, skipping`):Ne(e,o,a,i))}}function Me(e,t,n,i){let a={title:n.title};e===`linkedin`&&(n.theme&&(a.theme=n.theme),n.hasVideo&&(a.video=!0),n.hasImages&&(a.images=[null]));let o=``;e===`linkedin`&&(o=Te(i.templates.linkedin?.footerPath)),h(t,S.stringify(o,a),`utf-8`),r.success(`Created ${t}`)}function Ne(e,t,n,i){let a=``;n.endsWith(`-description.md`)&&(a=Te(i.templates[e]?.templatePath)),h(t,a,`utf-8`),r.success(`Created ${t}`)}function Pe(e,t,n){return t===`linkedin`&&e===`linkedin-script.md`?n.hasVideo||!1:!0}function Fe(e,t,n){let r=n===`date-asc`?e.date.getTime()-t.date.getTime():t.date.getTime()-e.date.getTime();if(r!==0)return r;let i=E.indexOf(e.contentType)-E.indexOf(t.contentType);return i===0?e.relativePath.localeCompare(t.relativePath):i}function A(e,t={}){let n=[],i=t.contentTypes?.length?t.contentTypes:[...E],a=t.fromDate,o=t.sort||`date-desc`;try{let t=f(e).filter(t=>m(_(e,t)).isDirectory()&&/^\d{4}$/.test(t));for(let o of t){let t=_(e,o),s=f(t).filter(e=>m(_(t,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of s){let s=_(t,e),l=f(s).filter(e=>m(_(s,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of l){let l=_(s,t),u=`${o}-${e}-${t}`,f=new Date(Number(o),Number(e)-1,Number(t));if(!(a&&f.getTime()<a.getTime()))for(let a of i){let i=O[a].mainFile,s=y(l,i);if(c(s))try{let{data:r}=S(d(s,`utf-8`)),c=r,p=c.title||`Untitled (${u})`;n.push({contentType:a,path:s,folderPath:l,date:f,title:p,relativePath:`${o}/${e}/${t}/${i}`,frontmatter:c})}catch(e){r.warn(`Error reading ${s}: ${e}`)}}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>Fe(e,t,o))}function Ie(t){let n=new Date;n.setHours(0,0,0,0);let i=A(t,{fromDate:n,sort:`date-asc`});if(i.length===0){r.info(`No upcoming publications found from today onward.`);return}r.info(`Found ${i.length} upcoming publication${i.length===1?``:`s`}:`),e.stdout.write(`
|
|
2
|
+
`);for(let t of i){let n=x(t.date,`EEEE, MMMM d, yyyy`),r=x(t.date,`yyyy-MM-dd`);e.stdout.write(`${n} (${r}) · ${D[t.contentType]} · ${t.title}\n`),e.stdout.write(`${t.path}\n\n`)}}function Le(e,t){let n=A(e,{contentTypes:[t],sort:`date-desc`});if(n.length===0){r.info(`No ${t} posts found`);return}let{fileName:i,heading:a}=Ee[t],o=[`# ${a}`,``,`_Generated on ${new Date().toLocaleDateString()}_`,``,`Total posts: ${n.length}`,``];for(let e of n){let t=e.date.toLocaleDateString(`en-US`,{year:`numeric`,month:`long`,day:`numeric`});o.push(`- [${e.title}](${e.relativePath}) - _${t}_`)}h(_(e,i),`${o.join(`
|
|
3
|
+
`)}\n`,`utf-8`),r.success(`Created ${i} with ${n.length} posts`)}function j(e){for(let t of E)Le(e,t)}function Re(t=`Operation cancelled.`){r.error(t),e.exit(0)}function M(t){r.error(t),e.exit(1)}function ze(e){return e instanceof Error?e.message:String(e)}function N(e,t){return n(e)&&Re(t),e}async function P(e){let t=A(e.basePath,{contentTypes:e.contentTypes,sort:e.sort});t.length===0&&M(e.emptyMessage);let n=20;for(;;){let r=N(await o({message:e.message,options:Be(t,n)}));if(r===`__load-more__`){n+=20;continue}typeof r!=`string`&&M(`Unable to resolve selected publication: ${String(r)}`);let i=t.find(e=>e.path===r);return i||M(`Unable to resolve selected publication: ${r}`),i}}function Be(e,t){let n=e.slice(0,t).map(e=>Ve(e));if(t>=e.length)return n;let r=Math.min(t+20,e.length);return[...n,{label:`Show ${r-t} more publications (${t+1}-${r} of ${e.length})`,value:De,hint:`Extend the list to ${e[r-1]?.relativePath}`}]}function Ve(e){return{label:`${x(e.date,`yyyy-MM-dd`)} · ${D[e.contentType]} · ${e.title}`,value:e.path,hint:e.relativePath}}async function He(){let e=N(await i({message:`Which content types do you want to create?`,options:[{label:`LinkedIn`,value:`linkedin`,hint:`Create LinkedIn post and optional script`},{label:`X (Twitter)`,value:`x`,hint:`Create X post`},{label:`YouTube`,value:`youtube`,hint:`Create YouTube script and description`},{label:`Instagram`,value:`instagram`,hint:`Create Instagram script and description`}],required:!0}));return(!Array.isArray(e)||e.some(e=>!E.includes(e)))&&M(`Unable to resolve selected content types.`),e}async function Ue(){let e=new Date,t=7;for(;;){let n=N(await o({message:`When do you want to create content?`,options:We(e,t)}));if(n===`__load-more__`){t+=7;continue}return typeof n!=`string`&&M(`Unable to resolve selected date: ${String(n)}`),ie(n)}}function We(e,t){let n=[];for(let r=0;r<t;r++){let t=b(e,r),i=x(t,`yyyy-MM-dd`),a=x(t,`EEEE`),o=i;o=r===0?`${a.padEnd(10)} ${i} - Today`:r===1?`${a.padEnd(10)} ${i} - Tomorrow`:`${a.padEnd(10)} ${i} - ${r} days ahead`,n.push({label:o,value:i})}return n.push({label:`Show 7 more dates (${t+1}-${t+7} days ahead)`,value:De,hint:`Extend the list up to ${x(b(e,t+7-1),`yyyy-MM-dd`)}`}),n}async function Ge(e){if(e.length===0)return;let t=N(await o({message:`What is the content theme? (optional)`,options:[{label:`No theme`,value:``,hint:`Leave theme empty`},...e.map(e=>({label:e,value:e,hint:`Use theme: ${e}`}))]}));typeof t!=`string`&&M(`Unable to resolve selected theme: ${String(t)}`);let n=t;return n.length>0?n:void 0}function F(e){let{content:t,data:n}=S(d(e,`utf-8`));return{content:t,frontmatter:n}}function I(e,t){h(e,S.stringify(t.content,t.frontmatter),`utf-8`)}async function L(e){return N(await s({message:e.message,placeholder:e.placeholder,validate:t=>{if(!t||t.trim().length===0)return e.requiredMessage||`Value is required`}})).trim()}const Ke=/<!--([\s\S]*?)-->/g;function qe(e,t){let n=e.replace(Ke,``).replace(/\s+/g,``);if(n.length===0)return 0;let r=n.length/5;return Math.ceil(r/t)}function Je(e){return e.charAt(0).toUpperCase()+e.slice(1)}function R(e){return e.split(`-`).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}function z(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function B(e,t){try{let{content:n,frontmatter:i}=F(e.path);if(i.ready===!0){r.warn(`This ${t} is already marked as ready.`);return}let a={...i,ready:!0};I(e.path,{content:n,frontmatter:a}),r.success(`${Je(t)} marked as ready: ${e.relativePath}`)}catch(e){M(`Failed to mark ${t} as ready: ${e instanceof Error?e.message:String(e)}`)}}function V(e){return e.toLowerCase().trim().normalize(`NFD`).replace(/[\u0300-\u036F]/g,``).replace(/[^a-z0-9\s-]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+|-+$/g,``)||`untitled`}async function Ye(){return L({message:`What is the title of your content?`,placeholder:`Enter content title`,requiredMessage:`Title is required`})}async function Xe(){let e=N(await o({message:`Is a LinkedIn video planned?`,options:[{label:`Yes`,value:!0,hint:`Add video: true to frontmatter`},{label:`No`,value:!1,hint:`No video metadata`}]}));return typeof e!=`boolean`&&M(`Unable to resolve LinkedIn video choice: ${String(e)}`),e}async function Ze(e){t(`Content Creation - Create dated content directory`);let n=await T(),r=await Ye(),i=await He(),o={title:r};i.includes(`linkedin`)&&(o.hasVideo=await Xe(),o.hasImages=!o.hasVideo,n.thematic.length>0&&(o.theme=await Ge(n.thematic)));let s=await Ue();ke(s,i,o,n,e);let c=i.join(`, `);a(`✓ Content directory created: ${x(s,`yyyy/MM/dd`)} (${c})`)}function Qe(e){t(`Content Creation - List Upcoming Publications`),Ie(e),a(`✓ Upcoming publications listed`)}function $e(e){t(`Content Creation - Rebuild Content Indexes`),j(e),a(`✓ Content indexes rebuilt successfully`)}const H=de(new URL(`../../../../`,import.meta.url)),U=`content-creation.ics`;function W(e){return e.replace(/\\/g,`\\\\`).replace(/\r\n|\r|\n/g,`\\n`).replace(/;/g,`\\;`).replace(/,/g,`\\,`)}function et(e){if(e.length<=75)return e;let t=[],n=e;for(;n.length>75;)t.push(n.slice(0,75)),n=` ${n.slice(75)}`;return t.push(n),t.join(`\r
|
|
4
|
+
`)}function tt(e){return e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}function nt(e){return x(e,`yyyyMMdd`)}function rt(e){return e.split(`/`).map(e=>encodeURIComponent(e)).join(`/`)}function it(e){return`https://github.com/barbapapazes/content-creation/blob/main/${rt(e)}`}function at(e){let t=rt(e);return`vscode://file${t.startsWith(`/`)?``:`/`}${t}`}function ot(e){let t=[`GitHub: ${it(e.relativePath)}`,`VS Code: ${at(e.path)}`,`Status: ${e.ready?`ready`:`not ready`}`];return e.theme&&t.push(`Theme: ${e.theme}`),typeof e.video==`boolean`&&t.push(`Video planned: ${e.video?`yes`:`no`}`),e.imageCount>0&&t.push(`Images planned: ${e.imageCount}`),t.join(`
|
|
5
|
+
`)}function st(e){return`${e.ready?`Ready`:`Not ready`} · ${e.title} · ${D.linkedin}`}function ct(e){return`linkedin-${ue(`sha1`).update(e).digest(`hex`)}@barbapapazes`}function lt(e){let t=[`BEGIN:VCALENDAR`,`VERSION:2.0`,`PRODID:-//Barbapapazes//Content Creation LinkedIn Calendar//EN`,`CALSCALE:GREGORIAN`,`METHOD:PUBLISH`,`X-WR-CALNAME:${W(`content-creation.ics`)}`];for(let n of e){let e=tt(new Date(Date.UTC(n.date.getFullYear(),n.date.getMonth(),n.date.getDate()))),r=b(n.date,1);t.push(`BEGIN:VEVENT`,`UID:${ct(n.relativePath)}`,`DTSTAMP:${e}`,`SUMMARY:${W(st(n))}`,`DTSTART;VALUE=DATE:${nt(n.date)}`,`DTEND;VALUE=DATE:${nt(r)}`,`TRANSP:TRANSPARENT`,`DESCRIPTION:${W(ot(n))}`,`END:VEVENT`)}return t.push(`END:VCALENDAR`),`${t.map(et).join(`\r
|
|
6
|
+
`)}\r\n`}function ut(){let t=_(H,`node_modules`,`.bin`,e.platform===`win32`?`wrangler.cmd`:`wrangler`);return c(t)?t:`wrangler`}function dt(t){(!t.calendar.publicUrl||!t.calendar.token)&&(r.error(`Calendar publishing configuration is missing. Please set CALENDAR_PUBLIC_URL and CALENDAR_TOKEN in your .env file or config.`),e.exit(1));let n=t.calendar.publicUrl.endsWith(`/`)?t.calendar.publicUrl:`${t.calendar.publicUrl}/`,i=new URL(U,n);return i.searchParams.set(`token`,t.calendar.token),i.toString()}function ft(e){return A(e,{contentTypes:[`linkedin`],sort:`date-asc`}).map(e=>{let t=e.frontmatter;return{...e,title:t.title||e.title,ready:t.ready===!0,theme:t.theme,video:t.video,imageCount:t.images?.filter(Boolean).length??0}})}function pt(t){let n=ut();try{le(n,[`r2`,`object`,`put`,`content-creation/${U}`,`--pipe`,`--content-type`,`text/calendar; charset=utf-8`,`--remote`],{cwd:H,stdio:[`pipe`,`inherit`,`inherit`],env:e.env,input:t})}catch(t){r.error(`Failed to upload LinkedIn calendar: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}async function mt(e,t){let n=ft(e);if(n.length===0)return r.info(`No LinkedIn publications found. Skipping calendar upload.`),null;let i=dt(t);return pt(lt(n)),r.success(`✓ LinkedIn calendar uploaded to R2 as ${U}`),r.info(`Subscription URL: ${i}`),i}function ht(e){let t=[];try{let n=f(e);for(let r of n){if(!m(_(e,r)).isFile())continue;let n=r.toLowerCase().substring(r.lastIndexOf(`.`));Oe.includes(n)&&t.push(r)}}catch(e){M(`Error reading directory: ${e instanceof Error?e.message:String(e)}`)}return t.sort()}function gt(e,t){try{let{content:n,frontmatter:i}=F(e.path);I(e.path,{content:n,frontmatter:{...i,images:t.length>0?t:void 0}}),r.success(`LinkedIn publication updated: ${e.relativePath} (${t.length} image(s))`)}catch(e){M(`Failed to update LinkedIn publication: ${e instanceof Error?e.message:String(e)}`)}}function _t(e){let t=ht(e.folderPath);t.length===0?r.info(`No images found for LinkedIn publication: ${e.relativePath}`):r.info(`Found ${t.length} image(s) for ${e.relativePath}: ${t.join(`, `)}`),gt(e,t),B({path:e.path,relativePath:e.relativePath},`linkedin publication`);let n=_(e.folderPath,`linkedin-script.md`),i=e.relativePath.slice(0,e.relativePath.lastIndexOf(`/`)),a=c(n);return a&&B({path:n,relativePath:`${i}/linkedin-script.md`},`linkedin script`),{imageCount:t.length,scriptMarked:a}}async function vt(e){if(t(`Content Creation - Publish LinkedIn Calendar`),await mt(e,await T())){a(`✓ LinkedIn calendar published successfully`);return}a(`✓ No LinkedIn publications found, calendar was not updated`)}function yt(e){let t=O.linkedin,n=e.relativePath.slice(0,e.relativePath.lastIndexOf(`/`)),r=[{kind:`publication`,path:e.path,relativePath:e.relativePath,isMain:!0}];for(let i of t.additionalFiles??[]){let t=_(e.folderPath,i);c(t)&&r.push({kind:i===`linkedin-script.md`?`script`:`publication`,path:t,relativePath:`${n}/${i}`,isMain:!1})}return r}async function bt(e){return P({basePath:e,contentTypes:[`linkedin`],message:`Which LinkedIn publication do you want to polish and mark as ready?`,emptyMessage:`No LinkedIn publications found in dated folders`,sort:`date-desc`})}function G(t,n,r={}){return pe(`git`,n,{throwOnError:!0,nodeOptions:{cwd:t,env:{...e.env,...r}}}).stdout.trim()}function xt(e,t,n){let r=u(_(fe(),`content-creation-linkedin-ready-`)),i={GIT_INDEX_FILE:_(r,`index`)};try{G(e,[`rev-parse`,`--is-inside-work-tree`]),G(e,[`read-tree`,`HEAD`],i);let r=n.map(t=>v(e,t.path));if(r.length===0)return{created:!1,message:`No related LinkedIn files found to checkpoint.`};G(e,[`add`,`--`,...r],i);try{return G(e,[`diff`,`--cached`,`--quiet`,`--exit-code`],i),{created:!1,message:`No changes detected in the LinkedIn publication files. Skipping checkpoint commit.`}}catch{let n=`chore(linkedin): checkpoint ${t} before ready polish`;return G(e,[`commit`,`--no-gpg-sign`,`-m`,n],i),{created:!0,commitHash:G(e,[`rev-parse`,`HEAD`]),message:n}}}catch(e){throw Error(`Failed to create LinkedIn checkpoint commit: ${ze(e)}`)}finally{ee(r,{recursive:!0,force:!0})}}const St=`linkedin-ready-editor`;function Ct(t){let n=!1,i=new Set,a=new Map,o=new Set;function s(t){t&&(e.stdout.write(t),n=!t.endsWith(`
|
|
7
|
+
`))}function c(){n&&=(e.stdout.write(`
|
|
8
|
+
`),!1)}function l(){i.clear(),a.clear(),o.clear()}let u=t.on(`assistant.message_delta`,e=>{i.add(e.data.messageId),s(e.data.deltaContent)}),d=t.on(`assistant.message`,e=>{i.has(e.data.messageId)||s(e.data.content)}),f=t.on(`tool.execution_start`,e=>{a.set(e.data.toolCallId,e.data.toolName),c(),r.info(`Copilot tool started: ${e.data.toolName}`)}),p=t.on(`tool.execution_progress`,e=>{c(),r.info(`Copilot: ${e.data.progressMessage}`)}),ee=t.on(`tool.execution_partial_result`,e=>{o.add(e.data.toolCallId),s(e.data.partialOutput)}),m=t.on(`tool.execution_complete`,e=>{let t=a.get(e.data.toolCallId)??e.data.toolCallId;if(c(),!e.data.success){r.warn(`Copilot tool failed: ${t}${e.data.error?.message?` — ${e.data.error.message}`:``}`);return}o.has(e.data.toolCallId)||r.info(`Copilot tool completed: ${t}`)}),h=t.on(`session.idle`,()=>{c(),l()});return()=>{c(),u(),d(),f(),p(),ee(),m(),h()}}function wt(e,t,n){let r=y(e,t);return c(r)||M(`Missing ${n}: ${r}`),r}function Tt(){return[`You are a senior LinkedIn copywriter and editor.`,`Directly edit Markdown files in the workspace when needed.`,`Focus on copy quality, readability, and LinkedIn-ready phrasing.`,`Do not explain your changes.`].join(`
|
|
9
|
+
`)}function Et(e,t,n){let r=v(e,t.folderPath)||`.`,i=n.map(e=>`- ${e.relativePath}${e.isMain?` (main publication)`:e.kind===`script`?` (LinkedIn script)`:``}`);return[`Open the LinkedIn publication folder at ${r}.`,`Review and edit only the LinkedIn-related Markdown files for this publication:`,...i,`Act as a copywriter preparing this publication for LinkedIn.`,`Improve grammar, spelling, punctuation, spacing, clarity, flow, rhythm, scannability, and light formatting.`,`Keep the original meaning, facts, structure, and voice. Do not invent new claims or add new sections unless a tiny formatting adjustment is needed for readability.`,`Treat ${t.relativePath} as the main publication file. You may improve its frontmatter title, but keep all other frontmatter keys intact.`,`If linkedin-script.md exists, polish it for natural spoken delivery while preserving intent and tone.`,`Do not modify unrelated files outside this publication folder.`].filter(Boolean).join(`
|
|
10
|
+
`)}async function Dt(e,t,n,i){let a=wt(e,`.github/skills`,`Copilot skills directory`),o=new me({logLevel:`all`,telemetry:{exporterType:`file`,filePath:y(e,`copilot-telemetry.log`),captureContent:!0}});await o.start();let s,c;try{s=await o.createSession({...i.model?{model:i.model}:{},...i.reasoningEffort?{reasoningEffort:i.reasoningEffort}:{},streaming:!0,workingDirectory:e,skillDirectories:[a],customAgents:[{name:St,displayName:`LinkedIn Ready Editor`,description:`Proofreads and polishes LinkedIn publication files before they are marked as ready.`,prompt:Tt()}],agent:St,onPermissionRequest:he}),c=Ct(s),n.find(e=>e.isMain)||M(`Unable to find the main LinkedIn publication file to polish.`),r.info(`Streaming Copilot output for publication folder: ${v(e,t.folderPath)||`.`}`),await s.sendAndWait({prompt:Et(e,t,n)})}catch(e){M(`Failed to polish the LinkedIn publication with Copilot: ${e instanceof Error?e.message:String(e)}`)}finally{c?.(),await s?.disconnect(),await o.stop()}}async function Ot(e){t(`Content Creation - Polish and Mark LinkedIn Publication Ready`);let n=await T(),i=await bt(e),o=yt(i),s=v(e,i.folderPath);r.info(`Preparing ${i.relativePath} with ${o.length} Markdown file(s).`);let c=xt(e,s,o);c.created?r.success(`Checkpoint commit created: ${c.commitHash}`):r.warn(c.message),await Dt(e,i,o,n.linkedin.ready.copilot);let l=_t(i),u=l.scriptMarked?`script marked ready`:`no script found`,d=c.created?`checkpoint ${c.commitHash} created`:`no checkpoint commit created`;a(`✓ LinkedIn publication ready: ${i.relativePath} (${o.length} file(s) reviewed, ${l.imageCount} image(s), ${u}, ${d}; final changes left uncommitted)`)}function kt(e,t,n){let i=V(e),a=_(y(_(n,`resources`)),i),o=`${t}.md`,s=_(a,o);if(c(s))return r.info(`${o} already exists at: ${s}`),a;l(a,{recursive:!0}),r.success(`Created directory: ${a}`);let u={title:e,url:``,date:``};return h(s,S.stringify(``,u),`utf-8`),r.success(`Created ${o} at: ${s}`),a}async function At(){let e=N(await o({message:`What type of resource is this?`,options:[{label:`Article`,value:`article`,hint:`Create article.md`},{label:`Video`,value:`video`,hint:`Create video.md`},{label:`Audio`,value:`audio`,hint:`Create audio.md`},{label:`Tweet`,value:`tweet`,hint:`Create tweet.md`}]}));return[`article`,`video`,`audio`,`tweet`].includes(String(e))||M(`Unable to resolve resource type: ${String(e)}`),e}async function jt(){return L({message:`What is the title of your content?`,placeholder:`Enter content title`,requiredMessage:`Title is required`})}async function Mt(e){t(`Content Creation - Create Resource`);let n=await jt(),r=await At();kt(n,r,e),a(`✓ Resource created: resources/${V(n)}/${r}.md`)}const Nt=`## Index`,K=`<!-- content-creation:series-index:start -->`,q=`<!-- content-creation:series-index:end -->`,Pt=/^(\d+)\.([^.]+(?:\.[^.]+)*)\.md$/;function J(e){return y(_(e,`series`))}function Ft(e){let t=J(e);return c(t)?f(t).filter(e=>zt(_(t,e))).map(e=>Bt(t,e)).sort((e,t)=>e.title.localeCompare(t.title,`fr`)):[]}function Y(e){return f(e).map(Wt).filter(e=>e!==null).map(t=>Vt(e,t)).sort((e,t)=>e.index-t.index)}function X(e){let t=Y(e),n=_(e,`README.md`);h(n,It(c(n)?d(n,`utf-8`):``,t,e),`utf-8`),r.success(`Generated series index: ${n}`)}function It(e,t,n){let r=Lt(t);if(e.includes(K)&&e.includes(q))return e.replace(Rt(),r);let i=e.trimEnd();return i.length>0?`${i}\n\n${r}\n`:`# ${R(n.split(`/`).pop()||`series`)}\n\n${r}\n`}function Lt(e){let t=[Nt,``,K];if(e.length===0)t.push(``,`_No articles yet._`);else{t.push(``);for(let n of e)t.push(`${n.index}. [${n.title}](./${n.fileName})`)}return t.push(``,q),t.join(`
|
|
11
|
+
`)}function Rt(){return RegExp(`${z(Nt)}\n\n${z(K)}[\\s\\S]*?${z(q)}`)}function zt(e){return m(e).isDirectory()}function Bt(e,t){let n=_(e,t);return{name:t,path:n,title:Ht(n,t)}}function Vt(e,t){let n=_(e,t.fileName);return{...t,path:n,title:Ut(n,t.slug)}}function Ht(e,t){let n=_(e,`README.md`);if(c(n)){let e=d(n,`utf-8`).split(`
|
|
12
|
+
`).map(e=>e.trim()).find(e=>e.startsWith(`# `));if(e)return e.slice(2).trim()}return R(t)}function Ut(e,t){try{let{data:t}=S(d(e,`utf-8`)),n=typeof t.title==`string`?t.title.trim():``;if(n.length>0)return n}catch(t){r.warn(`Unable to read article title from ${e}: ${t}`)}return R(t)}function Wt(e){let t=e.match(Pt);return t?{index:Number(t[1]),slug:t[2],fileName:e}:null}function Gt(e,t,n){let i=Y(e),a=qt(n,i.length),o=_(e,`${a}.${V(t)}.md`);if(c(o))throw Error(`Article already exists at ${o}`);return Kt(e,i,a),h(o,S.stringify(``,{title:t}),`utf-8`),r.success(`Created article: ${o}`),X(e),o}function Kt(e,t,n){let i=t.filter(e=>e.index>=n).sort((e,t)=>t.index-e.index);for(let t of i){let n=`${t.index+1}.${t.slug}.md`,i=_(e,n);p(t.path,i),r.info(`Renamed ${t.fileName} → ${n}`)}}function qt(e,t){if(!Number.isInteger(e))throw TypeError(`Article index must be an integer`);if(e<1||e>t+1)throw RangeError(`Article index must be between 1 and ${t+1}`);return e}function Jt(e,t){let n=t.trim();if(n.length===0)throw Error(`Series title is required`);let i=V(n),a=J(e),o=_(a,i);if(l(a,{recursive:!0}),c(o))throw Error(`Series already exists: series/${i}`);return l(o),h(_(o,`README.md`),`# ${n}\n`,`utf-8`),X(o),r.success(`Created series: ${o}`),{name:i,path:o,title:n}}function Z(e,t){let{content:n,frontmatter:i}=F(e),a=qe(n,t);return I(e,{content:n,frontmatter:{...i,time:a}}),r.success(`Estimated time set to ${a} min: ${e}`),a}function Yt(e){B({path:e.path,relativePath:e.fileName},`series article`)}async function Q(e){let t=Y(e);if(t.length===0&&M(`No series articles found`),t.length===1)return t[0];let n=N(await o({message:`Which series article do you want to update?`,options:t.map(e=>({label:`#${e.index} · ${e.title}`,value:e.path,hint:e.fileName}))}));typeof n!=`string`&&M(`Unable to resolve selected series article: ${String(n)}`);let r=t.find(e=>e.path===n);return r||M(`Unable to resolve selected series article: ${n}`),r}async function Xt(){return L({message:`What is the title of your article?`,placeholder:`Enter article title`,requiredMessage:`Title is required`})}async function $(e){let t=Ft(e);if(t.length===0&&M(`No series directories found`),t.length===1)return t[0];let n=N(await o({message:`Which series do you want to work on?`,options:t.map(e=>({label:e.title,value:e.path,hint:`series/${e.name}`}))}));typeof n!=`string`&&M(`Unable to resolve selected series: ${String(n)}`);let r=t.find(e=>e.path===n);return r||M(`Unable to resolve selected series: ${n}`),r}async function Zt(e){let t=Y(e),n=t.map(e=>({label:`Insert at #${e.index} · before ${e.title}`,value:String(e.index),hint:e.fileName}));n.push({label:`Append as #${t.length+1}`,value:String(t.length+1),hint:`Add the new article at the end of the series`});let r=N(await o({message:`Where should the new article be inserted?`,options:n}));return typeof r!=`string`&&M(`Unable to resolve selected insert position: ${String(r)}`),Number(r)}async function Qt(){return L({message:`What is the title of your series?`,placeholder:`Enter series title`,requiredMessage:`Title is required`})}function $t(e){return en(e,e).sort((e,t)=>e.relativePath.localeCompare(t.relativePath))}function en(e,t){let n=f(t,{withFileTypes:!0}),r=[];for(let i of n){let n=_(t,i.name);if(i.isDirectory()){r.push(...en(e,n));continue}!i.isFile()||g(i.name).toLowerCase()!==`.mp4`||r.push({name:i.name,path:n,relativePath:v(e,n)})}return r}async function tn(e){let t=$t(e);if(t.length===0&&M(`No .mp4 files found in series directory: ${e}`),t.length===1)return t[0];let n=N(await o({message:`Which series video do you want to process?`,options:t.map(e=>({label:e.relativePath,value:e.path,hint:e.name}))}));typeof n!=`string`&&M(`Unable to resolve selected series video: ${String(n)}`);let r=t.find(e=>e.path===n);return r||M(`Unable to resolve selected series video: ${n}`),r}async function nn(){let e=await s({message:`Enter text for the thumbnail (or press Enter to skip):`,placeholder:`Optional thumbnail text`});if(typeof e!=`symbol`)return e.trim()||void 0}async function rn(e){let t=an(e);if(t.length===0){r.error(`No templates found in ${e||`the configured templates directory`}`),r.info(`Please add SVG templates before generating thumbnails.`);return}let n=await o({message:`Choose a template:`,options:t.map(e=>({label:e,value:e}))});if(typeof n!=`symbol`)return n}function an(e){if(!e||!c(e))return[];try{return f(e,{withFileTypes:!0}).filter(e=>e.isFile()&&g(e.name).toLowerCase()===`.svg`).map(e=>e.name.replace(/\.svg$/i,``)).sort((e,t)=>e.localeCompare(t))}catch{return[]}}async function on(e){t(`Content Creation - Estimate Series Article Time`);let n=await T(),r=await $(e),i=await Q(r.path),o=Z(i.path,n.reading.wordsPerMinute);a(`✓ Estimated time updated for ${r.name}/${i.fileName}: ${o} min`)}async function sn(e){t(`Content Creation - Create Series`),a(`✓ Series created: series/${Jt(e,await Qt()).name}`)}async function cn(e){t(`Content Creation - Create Series Article`);let n=await $(e),r=await Xt(),i=await Zt(n.path),o=Gt(n.path,r,i);a(`✓ Series article created: ${n.name}/${te(o)}`)}async function ln(e){let n=await T();n.videoProcessing.openaiApiKey||M(`OpenAI API key is required. Set OPENAI_API_KEY in .env or configure videoProcessing.openaiApiKey in content-creation.config.{ts,js,mjs,json}.`);let i=await $(e),o=await tn(i.path);t(`Content Creation - Process Series Video: ${te(o.path)}`);let{videoPath:s,audioPath:c}=await _e(o.path),{srtPath:l}=await ye(s,c,{openaiApiKey:n.videoProcessing.openaiApiKey,language:n.videoProcessing.language,model:n.videoProcessing.model,templatesDir:n.templatesDir});ge(c,`audio file`);let u=await nn();if(!u){a(`✓ Transcription saved to: ${l}`);return}let d=await rn(n.templatesDir);if(!d){r.warn(`No template selected. Skipping thumbnail generation.`),a(`✓ Transcription saved to: ${l}`);return}let{thumbnailPaths:f}=await ve(s,u,d,n.templatesDir);a(`✓ Series video processed: series/${i.name}/${o.relativePath}\n✓ Transcription saved to: ${l}\n✓ Thumbnails saved:\n - ${f.join(`
|
|
13
|
+
- `)}`)}async function un(e){t(`Content Creation - Mark Series Article Ready`);let n=await T(),r=await $(e),i=await Q(r.path);Yt(i);let o=Z(i.path,n.reading.wordsPerMinute);a(`✓ Series article updated for ${r.name}/${i.fileName}: ready + ${o} min`)}async function dn(e,t){let n=e.path,{content:i,frontmatter:a}=F(n);if(a.scheduled===!0)return r.warn(`This X publication is already scheduled. Skipping.`),!1;(!t.scheduling.automationEndpoint||!t.scheduling.cfAccessClientId||!t.scheduling.cfAccessClientSecret)&&M(`Scheduling configuration is missing. Please set AUTOMATION_ENDPOINT, CF_ACCESS_CLIENT_ID, and CF_ACCESS_CLIENT_SECRET in your .env file or config.`);let o=new Date(e.date);o=ae(o,k.hours),o=oe(o,k.minutes),o=se(o,k.seconds);let s={content:i.trim(),scheduleAt:o.getTime()};try{let e=await fetch(t.scheduling.automationEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`,"CF-Access-Client-Id":t.scheduling.cfAccessClientId,"CF-Access-Client-Secret":t.scheduling.cfAccessClientSecret},body:JSON.stringify(s)});if(!e.ok){let t=await e.text();M(`Failed to schedule reminder: ${e.status} ${e.statusText}\n${t}`)}let o=await e.json();I(n,{content:i,frontmatter:{...a,scheduled:!0}});let c=new Date(o.scheduledAt);return r.success(`✓ X publication reminder scheduled`),r.info(`Reminder scheduled for: ${x(c,`yyyy-MM-dd HH:mm:ss`)} UTC`),!0}catch(e){M(`Failed to schedule X publication reminder: ${e instanceof Error?e.message:String(e)}`)}}async function fn(e){return P({basePath:e,contentTypes:[`x`],message:`Which X publication do you want to schedule a reminder for?`,emptyMessage:`No X publications found in dated folders`,sort:`date-desc`})}async function pn(e){t(`Content Creation - Schedule X Publication Reminder`);let n=await T();a(await dn(await fn(e),n)?`✓ X publication reminder scheduled`:`✓ X publication was already scheduled`)}export{qe as A,Qe as C,Je as D,B as E,Ie as F,ke as I,T as L,F as M,I as N,z as O,j as P,$e as S,V as T,kt as _,cn as a,_t as b,Yt as c,Gt as d,X as f,Mt as g,J as h,ln as i,L as j,R as k,Z as l,Ft as m,dn as n,sn as o,Y as p,un as r,on as s,pn as t,Jt as u,Ot as v,Ze as w,mt as x,vt as y};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barbapapazes/content-creation",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.20.
|
|
4
|
+
"version": "0.20.4",
|
|
5
5
|
"description": "CLI tool for content creation and management",
|
|
6
6
|
"author": "Estéban Soubiran <esteban@soubiran.dev>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"gray-matter": "^4.0.3",
|
|
40
40
|
"tinyexec": "1.1.2",
|
|
41
41
|
"wrangler": "^4.61.1",
|
|
42
|
-
"@barbapapazes/video-toolkit": "0.20.
|
|
42
|
+
"@barbapapazes/video-toolkit": "0.20.4"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@tsconfig/node24": "^24.0.4",
|
package/dist/x-CAcjVnjQ.mjs
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import e from"node:process";import{intro as t,isCancel as n,log as r,multiselect as i,outro as a,select as o,text as s}from"@clack/prompts";import{existsSync as c,mkdirSync as l,mkdtempSync as u,readFileSync as d,readdirSync as f,renameSync as ee,rmSync as te,statSync as p,writeFileSync as m}from"node:fs";import{basename as h,dirname as ne,extname as re,isAbsolute as ie,join as g,relative as _,resolve as v}from"node:path";import{addDays as y,format as b,parseISO as ae,setHours as oe,setMinutes as se,setSeconds as ce}from"date-fns";import x from"gray-matter";import{loadConfig as le}from"c12";import{execFileSync as ue}from"node:child_process";import{createHash as de}from"node:crypto";import{fileURLToPath as fe}from"node:url";import{tmpdir as pe}from"node:os";import{xSync as me}from"tinyexec";import{CopilotClient as he,approveAll as ge}from"@github/copilot-sdk";import{cleanupFile as _e,extractAudio as ve,generateThumbnail as ye,generateTranscription as be}from"@barbapapazes/video-toolkit";const S={thematic:[],templatesDir:void 0,linkedin:{ready:{copilot:{}}},templates:{},videoProcessing:{openaiApiKey:``,language:`fr`,model:`whisper-1`},scheduling:{},calendar:{},reading:{wordsPerMinute:100}};function xe(t,n){return t?ie(t)?t:v(n?ne(n):e.cwd(),t):n?ne(n):e.cwd()}function Se(e,t,n){if(!t)return;let i=v(e,t);if(!c(i)){r.warn(`Configured ${n} template was not found: ${i}`);return}return i}function Ce(e,t){return{footerPath:Se(t,e?.footerPath,`LinkedIn footer`)}}function we(e,t){return{templatePath:Se(t,e?.templatePath,`video description`)}}function Te(e){let t=e?.model?.trim();return{model:t&&t.length>0?t:void 0,reasoningEffort:e?.reasoningEffort}}function Ee(e){return{ready:{copilot:Te(e?.ready?.copilot)}}}async function C(){let{config:t,configFile:n}=await le({name:`content-creation`,defaults:S,globalRc:!0,dotenv:!0}),r=xe(t.templatesDir,n),i={linkedin:Ce(t.templates?.linkedin,r),youtube:we(t.templates?.youtube,r),instagram:we(t.templates?.instagram,r)},a={automationEndpoint:t.scheduling?.automationEndpoint??e.env.AUTOMATION_ENDPOINT,cfAccessClientId:t.scheduling?.cfAccessClientId??e.env.CF_ACCESS_CLIENT_ID,cfAccessClientSecret:t.scheduling?.cfAccessClientSecret??e.env.CF_ACCESS_CLIENT_SECRET},o={openaiApiKey:t.videoProcessing?.openaiApiKey??e.env.OPENAI_API_KEY??``,language:t.videoProcessing?.language??e.env.OPENAI_TRANSCRIPTION_LANGUAGE??S.videoProcessing?.language??`fr`,model:t.videoProcessing?.model??e.env.OPENAI_TRANSCRIPTION_MODEL??S.videoProcessing?.model??`whisper-1`},s={publicUrl:t.calendar?.publicUrl??e.env.CALENDAR_PUBLIC_URL,token:t.calendar?.token??e.env.CALENDAR_TOKEN},c=t.reading?.wordsPerMinute??S.reading?.wordsPerMinute??100;if(!Number.isFinite(c)||c<=0)throw RangeError(`Reading wordsPerMinute must be a positive number`);return{thematic:t.thematic??[],templatesDir:t.templatesDir??``,linkedin:Ee(t.linkedin),templates:i,videoProcessing:o,scheduling:a,calendar:s,reading:{wordsPerMinute:c}}}function w(e){return e?c(e)?d(e,`utf-8`):(r.warn(`Template file not found: ${e}`),``):``}const T=[`linkedin`,`x`,`youtube`,`instagram`],E={linkedin:`LinkedIn`,x:`X`,youtube:`YouTube`,instagram:`Instagram`},D={linkedin:{mainFile:`linkedin.md`,additionalFiles:[`linkedin-script.md`]},x:{mainFile:`x.md`},youtube:{mainFile:`youtube-script.md`,additionalFiles:[`youtube-description.md`]},instagram:{mainFile:`instagram-script.md`,additionalFiles:[`instagram-description.md`]}},De={linkedin:{fileName:`linkedin-posts.md`,heading:`LinkedIn Posts`},x:{fileName:`x-posts.md`,heading:`X Posts`},youtube:{fileName:`youtube-videos.md`,heading:`YouTube Videos`},instagram:{fileName:`instagram-posts.md`,heading:`Instagram Posts`}},Oe=`__load-more__`,ke=[`.jpg`,`.jpeg`,`.png`],O={hours:11,minutes:0,seconds:0};function Ae(e,t,n,i,a){let o=v(g(a,b(e,`yyyy`),b(e,`MM`),b(e,`dd`)));c(o)||(l(o,{recursive:!0}),r.success(`Created directory: ${o}`));let s=je(n);for(let e of t)Me(e,o,s,i);return o}function je(e){return e.hasVideo?{...e,hasImages:!1}:{...e,hasImages:!0}}function Me(e,t,n,i){let a=D[e],o=g(t,a.mainFile),s=`additionalFiles`in a?a.additionalFiles:void 0;if(c(o)?r.info(`${a.mainFile} already exists, skipping`):Ne(e,o,n,i),s)for(let a of s){let o=g(t,a);Fe(a,e,n)&&(c(o)?r.info(`${a} already exists, skipping`):Pe(e,o,a,i))}}function Ne(e,t,n,i){let a={title:n.title};e===`linkedin`&&(n.theme&&(a.theme=n.theme),n.hasVideo&&(a.video=!0),n.hasImages&&(a.images=[null]));let o=``;e===`linkedin`&&(o=w(i.templates.linkedin?.footerPath)),m(t,x.stringify(o,a),`utf-8`),r.success(`Created ${t}`)}function Pe(e,t,n,i){let a=``;n.endsWith(`-description.md`)&&(a=w(i.templates[e]?.templatePath)),m(t,a,`utf-8`),r.success(`Created ${t}`)}function Fe(e,t,n){return t===`linkedin`&&e===`linkedin-script.md`?n.hasVideo||!1:!0}function Ie(e,t,n){let r=n===`date-asc`?e.date.getTime()-t.date.getTime():t.date.getTime()-e.date.getTime();if(r!==0)return r;let i=T.indexOf(e.contentType)-T.indexOf(t.contentType);return i===0?e.relativePath.localeCompare(t.relativePath):i}function k(e,t={}){let n=[],i=t.contentTypes?.length?t.contentTypes:[...T],a=t.fromDate,o=t.sort||`date-desc`;try{let t=f(e).filter(t=>p(g(e,t)).isDirectory()&&/^\d{4}$/.test(t));for(let o of t){let t=g(e,o),s=f(t).filter(e=>p(g(t,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of s){let s=g(t,e),l=f(s).filter(e=>p(g(s,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of l){let l=g(s,t),u=`${o}-${e}-${t}`,f=new Date(Number(o),Number(e)-1,Number(t));if(!(a&&f.getTime()<a.getTime()))for(let a of i){let i=D[a].mainFile,s=v(l,i);if(c(s))try{let{data:r}=x(d(s,`utf-8`)),c=r,ee=c.title||`Untitled (${u})`;n.push({contentType:a,path:s,folderPath:l,date:f,title:ee,relativePath:`${o}/${e}/${t}/${i}`,frontmatter:c})}catch(e){r.warn(`Error reading ${s}: ${e}`)}}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>Ie(e,t,o))}function A(t){let n=new Date;n.setHours(0,0,0,0);let i=k(t,{fromDate:n,sort:`date-asc`});if(i.length===0){r.info(`No upcoming publications found from today onward.`);return}r.info(`Found ${i.length} upcoming publication${i.length===1?``:`s`}:`),e.stdout.write(`
|
|
2
|
-
`);for(let t of i){let n=b(t.date,`EEEE, MMMM d, yyyy`),r=b(t.date,`yyyy-MM-dd`);e.stdout.write(`${n} (${r}) · ${E[t.contentType]} · ${t.title}\n`),e.stdout.write(`${t.path}\n\n`)}}function Le(e,t){let n=k(e,{contentTypes:[t],sort:`date-desc`});if(n.length===0){r.info(`No ${t} posts found`);return}let{fileName:i,heading:a}=De[t],o=[`# ${a}`,``,`_Generated on ${new Date().toLocaleDateString()}_`,``,`Total posts: ${n.length}`,``];for(let e of n){let t=e.date.toLocaleDateString(`en-US`,{year:`numeric`,month:`long`,day:`numeric`});o.push(`- [${e.title}](${e.relativePath}) - _${t}_`)}m(g(e,i),`${o.join(`
|
|
3
|
-
`)}\n`,`utf-8`),r.success(`Created ${i} with ${n.length} posts`)}function j(e){for(let t of T)Le(e,t)}function Re(t=`Operation cancelled.`){r.error(t),e.exit(0)}function M(t){r.error(t),e.exit(1)}function ze(e){return e instanceof Error?e.message:String(e)}function N(e,t){return n(e)&&Re(t),e}async function Be(e){let t=k(e.basePath,{contentTypes:e.contentTypes,sort:e.sort});t.length===0&&M(e.emptyMessage);let n=20;for(;;){let r=N(await o({message:e.message,options:Ve(t,n)}));if(r===`__load-more__`){n+=20;continue}typeof r!=`string`&&M(`Unable to resolve selected publication: ${String(r)}`);let i=t.find(e=>e.path===r);return i||M(`Unable to resolve selected publication: ${r}`),i}}function Ve(e,t){let n=e.slice(0,t).map(e=>He(e));if(t>=e.length)return n;let r=Math.min(t+20,e.length);return[...n,{label:`Show ${r-t} more publications (${t+1}-${r} of ${e.length})`,value:Oe,hint:`Extend the list to ${e[r-1]?.relativePath}`}]}function He(e){return{label:`${b(e.date,`yyyy-MM-dd`)} · ${E[e.contentType]} · ${e.title}`,value:e.path,hint:e.relativePath}}async function Ue(){let e=N(await i({message:`Which content types do you want to create?`,options:[{label:`LinkedIn`,value:`linkedin`,hint:`Create LinkedIn post and optional script`},{label:`X (Twitter)`,value:`x`,hint:`Create X post`},{label:`YouTube`,value:`youtube`,hint:`Create YouTube script and description`},{label:`Instagram`,value:`instagram`,hint:`Create Instagram script and description`}],required:!0}));return(!Array.isArray(e)||e.some(e=>!T.includes(e)))&&M(`Unable to resolve selected content types.`),e}async function We(){let e=new Date,t=7;for(;;){let n=N(await o({message:`When do you want to create content?`,options:Ge(e,t)}));if(n===`__load-more__`){t+=7;continue}return typeof n!=`string`&&M(`Unable to resolve selected date: ${String(n)}`),ae(n)}}function Ge(e,t){let n=[];for(let r=0;r<t;r++){let t=y(e,r),i=b(t,`yyyy-MM-dd`),a=b(t,`EEEE`),o=i;o=r===0?`${a.padEnd(10)} ${i} - Today`:r===1?`${a.padEnd(10)} ${i} - Tomorrow`:`${a.padEnd(10)} ${i} - ${r} days ahead`,n.push({label:o,value:i})}return n.push({label:`Show 7 more dates (${t+1}-${t+7} days ahead)`,value:Oe,hint:`Extend the list up to ${b(y(e,t+7-1),`yyyy-MM-dd`)}`}),n}async function Ke(e){if(e.length===0)return;let t=N(await o({message:`What is the content theme? (optional)`,options:[{label:`No theme`,value:``,hint:`Leave theme empty`},...e.map(e=>({label:e,value:e,hint:`Use theme: ${e}`}))]}));typeof t!=`string`&&M(`Unable to resolve selected theme: ${String(t)}`);let n=t;return n.length>0?n:void 0}function P(e){let{content:t,data:n}=x(d(e,`utf-8`));return{content:t,frontmatter:n}}function F(e,t){m(e,x.stringify(t.content,t.frontmatter),`utf-8`)}async function I(e){return N(await s({message:e.message,placeholder:e.placeholder,validate:t=>{if(!t||t.trim().length===0)return e.requiredMessage||`Value is required`}})).trim()}const qe=/<!--([\s\S]*?)-->/g;function Je(e,t){let n=e.replace(qe,``).replace(/\s+/g,``);if(n.length===0)return 0;let r=n.length/5;return Math.ceil(r/t)}function Ye(e){return e.charAt(0).toUpperCase()+e.slice(1)}function L(e){return e.split(`-`).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}function R(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function z(e,t){try{let{content:n,frontmatter:i}=P(e.path);if(i.ready===!0){r.warn(`This ${t} is already marked as ready.`);return}let a={...i,ready:!0};F(e.path,{content:n,frontmatter:a}),r.success(`${Ye(t)} marked as ready: ${e.relativePath}`)}catch(e){M(`Failed to mark ${t} as ready: ${e instanceof Error?e.message:String(e)}`)}}function B(e){return e.toLowerCase().trim().normalize(`NFD`).replace(/[\u0300-\u036F]/g,``).replace(/[^a-z0-9\s-]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+|-+$/g,``)||`untitled`}async function Xe(){return I({message:`What is the title of your content?`,placeholder:`Enter content title`,requiredMessage:`Title is required`})}async function Ze(){let e=N(await o({message:`Is a LinkedIn video planned?`,options:[{label:`Yes`,value:!0,hint:`Add video: true to frontmatter`},{label:`No`,value:!1,hint:`No video metadata`}]}));return typeof e!=`boolean`&&M(`Unable to resolve LinkedIn video choice: ${String(e)}`),e}async function Qe(e){t(`Content Creation - Create dated content directory`);let n=await C(),r=await Xe(),i=await Ue(),o={title:r};i.includes(`linkedin`)&&(o.hasVideo=await Ze(),o.hasImages=!o.hasVideo,n.thematic.length>0&&(o.theme=await Ke(n.thematic)));let s=await We();Ae(s,i,o,n,e);let c=i.join(`, `);a(`✓ Content directory created: ${b(s,`yyyy/MM/dd`)} (${c})`)}function $e(e){t(`Content Creation - List Upcoming Publications`),A(e),a(`✓ Upcoming publications listed`)}function et(e){t(`Content Creation - Rebuild Content Indexes`),j(e),a(`✓ Content indexes rebuilt successfully`)}const V=fe(new URL(`../../../../`,import.meta.url)),H=`content-creation.ics`;function U(e){return e.replace(/\\/g,`\\\\`).replace(/\r\n|\r|\n/g,`\\n`).replace(/;/g,`\\;`).replace(/,/g,`\\,`)}function tt(e){if(e.length<=75)return e;let t=[],n=e;for(;n.length>75;)t.push(n.slice(0,75)),n=` ${n.slice(75)}`;return t.push(n),t.join(`\r
|
|
4
|
-
`)}function nt(e){return e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}function W(e){return b(e,`yyyyMMdd`)}function rt(e){return e.split(`/`).map(e=>encodeURIComponent(e)).join(`/`)}function it(e){return`https://github.com/barbapapazes/content-creation/blob/main/${rt(e)}`}function at(e){let t=rt(e);return`vscode://file${t.startsWith(`/`)?``:`/`}${t}`}function ot(e){let t=[`GitHub: ${it(e.relativePath)}`,`VS Code: ${at(e.path)}`,`Status: ${e.ready?`ready`:`not ready`}`];return e.theme&&t.push(`Theme: ${e.theme}`),typeof e.video==`boolean`&&t.push(`Video planned: ${e.video?`yes`:`no`}`),e.imageCount>0&&t.push(`Images planned: ${e.imageCount}`),t.join(`
|
|
5
|
-
`)}function st(e){return`${e.ready?`Ready`:`Not ready`} · ${e.title} · ${E.linkedin}`}function ct(e){return`linkedin-${de(`sha1`).update(e).digest(`hex`)}@barbapapazes`}function lt(e){let t=[`BEGIN:VCALENDAR`,`VERSION:2.0`,`PRODID:-//Barbapapazes//Content Creation LinkedIn Calendar//EN`,`CALSCALE:GREGORIAN`,`METHOD:PUBLISH`,`X-WR-CALNAME:${U(`content-creation.ics`)}`];for(let n of e){let e=nt(new Date(Date.UTC(n.date.getFullYear(),n.date.getMonth(),n.date.getDate()))),r=y(n.date,1);t.push(`BEGIN:VEVENT`,`UID:${ct(n.relativePath)}`,`DTSTAMP:${e}`,`SUMMARY:${U(st(n))}`,`DTSTART;VALUE=DATE:${W(n.date)}`,`DTEND;VALUE=DATE:${W(r)}`,`TRANSP:TRANSPARENT`,`DESCRIPTION:${U(ot(n))}`,`END:VEVENT`)}return t.push(`END:VCALENDAR`),`${t.map(tt).join(`\r
|
|
6
|
-
`)}\r\n`}function ut(){let t=g(V,`node_modules`,`.bin`,e.platform===`win32`?`wrangler.cmd`:`wrangler`);return c(t)?t:`wrangler`}function dt(t){(!t.calendar.publicUrl||!t.calendar.token)&&(r.error(`Calendar publishing configuration is missing. Please set CALENDAR_PUBLIC_URL and CALENDAR_TOKEN in your .env file or config.`),e.exit(1));let n=t.calendar.publicUrl.endsWith(`/`)?t.calendar.publicUrl:`${t.calendar.publicUrl}/`,i=new URL(H,n);return i.searchParams.set(`token`,t.calendar.token),i.toString()}function ft(e){return k(e,{contentTypes:[`linkedin`],sort:`date-asc`}).map(e=>{let t=e.frontmatter;return{...e,title:t.title||e.title,ready:t.ready===!0,theme:t.theme,video:t.video,imageCount:t.images?.filter(Boolean).length??0}})}function pt(t){let n=ut();try{ue(n,[`r2`,`object`,`put`,`content-creation/${H}`,`--pipe`,`--content-type`,`text/calendar; charset=utf-8`,`--remote`],{cwd:V,stdio:[`pipe`,`inherit`,`inherit`],env:e.env,input:t})}catch(t){r.error(`Failed to upload LinkedIn calendar: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}async function mt(e,t){let n=ft(e);if(n.length===0)return r.info(`No LinkedIn publications found. Skipping calendar upload.`),null;let i=dt(t);return pt(lt(n)),r.success(`✓ LinkedIn calendar uploaded to R2 as ${H}`),r.info(`Subscription URL: ${i}`),i}function ht(e){let t=[];try{let n=f(e);for(let r of n){if(!p(g(e,r)).isFile())continue;let n=r.toLowerCase().substring(r.lastIndexOf(`.`));ke.includes(n)&&t.push(r)}}catch(e){M(`Error reading directory: ${e instanceof Error?e.message:String(e)}`)}return t.sort()}function gt(e,t){try{let{content:n,frontmatter:i}=P(e.path);F(e.path,{content:n,frontmatter:{...i,images:t.length>0?t:void 0}}),r.success(`LinkedIn publication updated: ${e.relativePath} (${t.length} image(s))`)}catch(e){M(`Failed to update LinkedIn publication: ${e instanceof Error?e.message:String(e)}`)}}function _t(e){let t=ht(e.folderPath);t.length===0?r.info(`No images found for LinkedIn publication: ${e.relativePath}`):r.info(`Found ${t.length} image(s) for ${e.relativePath}: ${t.join(`, `)}`),gt(e,t),z({path:e.path,relativePath:e.relativePath},`linkedin publication`);let n=g(e.folderPath,`linkedin-script.md`),i=e.relativePath.slice(0,e.relativePath.lastIndexOf(`/`)),a=c(n);return a&&z({path:n,relativePath:`${i}/linkedin-script.md`},`linkedin script`),{imageCount:t.length,scriptMarked:a}}async function vt(e){if(t(`Content Creation - Publish LinkedIn Calendar`),await mt(e,await C())){a(`✓ LinkedIn calendar published successfully`);return}a(`✓ No LinkedIn publications found, calendar was not updated`)}function yt(e){let t=D.linkedin,n=e.relativePath.slice(0,e.relativePath.lastIndexOf(`/`)),r=[{kind:`publication`,path:e.path,relativePath:e.relativePath,isMain:!0}];for(let i of t.additionalFiles??[]){let t=g(e.folderPath,i);c(t)&&r.push({kind:i===`linkedin-script.md`?`script`:`publication`,path:t,relativePath:`${n}/${i}`,isMain:!1})}return r}async function bt(e){return Be({basePath:e,contentTypes:[`linkedin`],message:`Which LinkedIn publication do you want to polish and mark as ready?`,emptyMessage:`No LinkedIn publications found in dated folders`,sort:`date-desc`})}function G(t,n,r={}){return me(`git`,n,{throwOnError:!0,nodeOptions:{cwd:t,env:{...e.env,...r}}}).stdout.trim()}function xt(e,t,n){let r=u(g(pe(),`content-creation-linkedin-ready-`)),i={GIT_INDEX_FILE:g(r,`index`)};try{G(e,[`rev-parse`,`--is-inside-work-tree`]),G(e,[`read-tree`,`HEAD`],i);let r=n.map(t=>_(e,t.path));if(r.length===0)return{created:!1,message:`No related LinkedIn files found to checkpoint.`};G(e,[`add`,`--`,...r],i);try{return G(e,[`diff`,`--cached`,`--quiet`,`--exit-code`],i),{created:!1,message:`No changes detected in the LinkedIn publication files. Skipping checkpoint commit.`}}catch{let n=`chore(linkedin): checkpoint ${t} before ready polish`;return G(e,[`commit`,`--no-gpg-sign`,`-m`,n],i),{created:!0,commitHash:G(e,[`rev-parse`,`HEAD`]),message:n}}}catch(e){throw Error(`Failed to create LinkedIn checkpoint commit: ${ze(e)}`)}finally{te(r,{recursive:!0,force:!0})}}const St=`linkedin-ready-editor`;function Ct(e,t,n){let r=v(e,t);return c(r)||M(`Missing ${n}: ${r}`),r}function wt(){return[`Directly edit attached Markdown files.`,`Do not explain your changes.`].join(`
|
|
7
|
-
`)}function Tt(e,t){let n=t.filter(e=>!e.isMain).map(e=>e.relativePath),r=n.length>0?`Related files: ${n.join(`, `)}.`:void 0;return[`Edit ${e.relativePath} for LinkedIn publishing.`,r,`Fix typos, grammar, punctuation, spacing, and light formatting only.`,`Keep the meaning and voice. You may improve the frontmatter title, but keep all other frontmatter keys intact.`].filter(Boolean).join(`
|
|
8
|
-
`)}function Et(e){return[`Edit ${e.relativePath}.`,`Fix typos, grammar, punctuation, spacing, and light formatting only.`,`Keep the meaning, structure, tone, and voice.`].join(`
|
|
9
|
-
`)}async function Dt(e,t,n,r){let i=Ct(e,`.github/skills`,`Copilot skills directory`),a=new he({logLevel:`all`,telemetry:{exporterType:`file`,filePath:v(e,`copilot-telemetry.log`),captureContent:!0}});await a.start();let o;try{o=await a.createSession({...r.model?{model:r.model}:{},...r.reasoningEffort?{reasoningEffort:r.reasoningEffort}:{},workingDirectory:e,skillDirectories:[i],customAgents:[{name:St,displayName:`LinkedIn Ready Editor`,description:`Proofreads and polishes LinkedIn publication files before they are marked as ready.`,prompt:wt()}],agent:St,onPermissionRequest:ge});let s=n.find(e=>e.isMain);s||M(`Unable to find the main LinkedIn publication file to polish.`),await o.sendAndWait({prompt:Tt(t,n),attachments:[{type:`file`,path:s.path,displayName:h(s.path)}]});for(let e of n.filter(e=>!e.isMain))await o.sendAndWait({prompt:Et(e),attachments:[{type:`file`,path:e.path,displayName:h(e.path)}]})}catch(e){M(`Failed to polish the LinkedIn publication with Copilot: ${e instanceof Error?e.message:String(e)}`)}finally{await o?.disconnect(),await a.stop()}}async function Ot(e){t(`Content Creation - Polish and Mark LinkedIn Publication Ready`);let n=await C(),i=await bt(e),o=yt(i),s=_(e,i.folderPath);r.info(`Preparing ${i.relativePath} with ${o.length} Markdown file(s).`);let c=xt(e,s,o);c.created?r.success(`Checkpoint commit created: ${c.commitHash}`):r.warn(c.message),await Dt(e,i,o,n.linkedin.ready.copilot);let l=_t(i),u=l.scriptMarked?`script marked ready`:`no script found`,d=c.created?`checkpoint ${c.commitHash} created`:`no checkpoint commit created`;a(`✓ LinkedIn publication ready: ${i.relativePath} (${o.length} file(s) reviewed, ${l.imageCount} image(s), ${u}, ${d}; final changes left uncommitted)`)}function kt(e,t,n){let i=B(e),a=g(v(g(n,`resources`)),i),o=`${t}.md`,s=g(a,o);if(c(s))return r.info(`${o} already exists at: ${s}`),a;l(a,{recursive:!0}),r.success(`Created directory: ${a}`);let u={title:e,url:``,date:``};return m(s,x.stringify(``,u),`utf-8`),r.success(`Created ${o} at: ${s}`),a}async function At(){let e=N(await o({message:`What type of resource is this?`,options:[{label:`Article`,value:`article`,hint:`Create article.md`},{label:`Video`,value:`video`,hint:`Create video.md`},{label:`Audio`,value:`audio`,hint:`Create audio.md`},{label:`Tweet`,value:`tweet`,hint:`Create tweet.md`}]}));return[`article`,`video`,`audio`,`tweet`].includes(String(e))||M(`Unable to resolve resource type: ${String(e)}`),e}async function jt(){return I({message:`What is the title of your content?`,placeholder:`Enter content title`,requiredMessage:`Title is required`})}async function Mt(e){t(`Content Creation - Create Resource`);let n=await jt(),r=await At();kt(n,r,e),a(`✓ Resource created: resources/${B(n)}/${r}.md`)}const Nt=`## Index`,K=`<!-- content-creation:series-index:start -->`,q=`<!-- content-creation:series-index:end -->`,Pt=/^(\d+)\.([^.]+(?:\.[^.]+)*)\.md$/;function J(e){return v(g(e,`series`))}function Ft(e){let t=J(e);return c(t)?f(t).filter(e=>zt(g(t,e))).map(e=>Bt(t,e)).sort((e,t)=>e.title.localeCompare(t.title,`fr`)):[]}function Y(e){return f(e).map(Wt).filter(e=>e!==null).map(t=>Vt(e,t)).sort((e,t)=>e.index-t.index)}function X(e){let t=Y(e),n=g(e,`README.md`);m(n,It(c(n)?d(n,`utf-8`):``,t,e),`utf-8`),r.success(`Generated series index: ${n}`)}function It(e,t,n){let r=Lt(t);if(e.includes(K)&&e.includes(q))return e.replace(Rt(),r);let i=e.trimEnd();return i.length>0?`${i}\n\n${r}\n`:`# ${L(n.split(`/`).pop()||`series`)}\n\n${r}\n`}function Lt(e){let t=[Nt,``,K];if(e.length===0)t.push(``,`_No articles yet._`);else{t.push(``);for(let n of e)t.push(`${n.index}. [${n.title}](./${n.fileName})`)}return t.push(``,q),t.join(`
|
|
10
|
-
`)}function Rt(){return RegExp(`${R(Nt)}\n\n${R(K)}[\\s\\S]*?${R(q)}`)}function zt(e){return p(e).isDirectory()}function Bt(e,t){let n=g(e,t);return{name:t,path:n,title:Ht(n,t)}}function Vt(e,t){let n=g(e,t.fileName);return{...t,path:n,title:Ut(n,t.slug)}}function Ht(e,t){let n=g(e,`README.md`);if(c(n)){let e=d(n,`utf-8`).split(`
|
|
11
|
-
`).map(e=>e.trim()).find(e=>e.startsWith(`# `));if(e)return e.slice(2).trim()}return L(t)}function Ut(e,t){try{let{data:t}=x(d(e,`utf-8`)),n=typeof t.title==`string`?t.title.trim():``;if(n.length>0)return n}catch(t){r.warn(`Unable to read article title from ${e}: ${t}`)}return L(t)}function Wt(e){let t=e.match(Pt);return t?{index:Number(t[1]),slug:t[2],fileName:e}:null}function Gt(e,t,n){let i=Y(e),a=qt(n,i.length),o=g(e,`${a}.${B(t)}.md`);if(c(o))throw Error(`Article already exists at ${o}`);return Kt(e,i,a),m(o,x.stringify(``,{title:t}),`utf-8`),r.success(`Created article: ${o}`),X(e),o}function Kt(e,t,n){let i=t.filter(e=>e.index>=n).sort((e,t)=>t.index-e.index);for(let t of i){let n=`${t.index+1}.${t.slug}.md`,i=g(e,n);ee(t.path,i),r.info(`Renamed ${t.fileName} → ${n}`)}}function qt(e,t){if(!Number.isInteger(e))throw TypeError(`Article index must be an integer`);if(e<1||e>t+1)throw RangeError(`Article index must be between 1 and ${t+1}`);return e}function Jt(e,t){let n=t.trim();if(n.length===0)throw Error(`Series title is required`);let i=B(n),a=J(e),o=g(a,i);if(l(a,{recursive:!0}),c(o))throw Error(`Series already exists: series/${i}`);return l(o),m(g(o,`README.md`),`# ${n}\n`,`utf-8`),X(o),r.success(`Created series: ${o}`),{name:i,path:o,title:n}}function Z(e,t){let{content:n,frontmatter:i}=P(e),a=Je(n,t);return F(e,{content:n,frontmatter:{...i,time:a}}),r.success(`Estimated time set to ${a} min: ${e}`),a}function Q(e){z({path:e.path,relativePath:e.fileName},`series article`)}async function Yt(e){let t=Y(e);if(t.length===0&&M(`No series articles found`),t.length===1)return t[0];let n=N(await o({message:`Which series article do you want to update?`,options:t.map(e=>({label:`#${e.index} · ${e.title}`,value:e.path,hint:e.fileName}))}));typeof n!=`string`&&M(`Unable to resolve selected series article: ${String(n)}`);let r=t.find(e=>e.path===n);return r||M(`Unable to resolve selected series article: ${n}`),r}async function Xt(){return I({message:`What is the title of your article?`,placeholder:`Enter article title`,requiredMessage:`Title is required`})}async function $(e){let t=Ft(e);if(t.length===0&&M(`No series directories found`),t.length===1)return t[0];let n=N(await o({message:`Which series do you want to work on?`,options:t.map(e=>({label:e.title,value:e.path,hint:`series/${e.name}`}))}));typeof n!=`string`&&M(`Unable to resolve selected series: ${String(n)}`);let r=t.find(e=>e.path===n);return r||M(`Unable to resolve selected series: ${n}`),r}async function Zt(e){let t=Y(e),n=t.map(e=>({label:`Insert at #${e.index} · before ${e.title}`,value:String(e.index),hint:e.fileName}));n.push({label:`Append as #${t.length+1}`,value:String(t.length+1),hint:`Add the new article at the end of the series`});let r=N(await o({message:`Where should the new article be inserted?`,options:n}));return typeof r!=`string`&&M(`Unable to resolve selected insert position: ${String(r)}`),Number(r)}async function Qt(){return I({message:`What is the title of your series?`,placeholder:`Enter series title`,requiredMessage:`Title is required`})}function $t(e){return en(e,e).sort((e,t)=>e.relativePath.localeCompare(t.relativePath))}function en(e,t){let n=f(t,{withFileTypes:!0}),r=[];for(let i of n){let n=g(t,i.name);if(i.isDirectory()){r.push(...en(e,n));continue}!i.isFile()||re(i.name).toLowerCase()!==`.mp4`||r.push({name:i.name,path:n,relativePath:_(e,n)})}return r}async function tn(e){let t=$t(e);if(t.length===0&&M(`No .mp4 files found in series directory: ${e}`),t.length===1)return t[0];let n=N(await o({message:`Which series video do you want to process?`,options:t.map(e=>({label:e.relativePath,value:e.path,hint:e.name}))}));typeof n!=`string`&&M(`Unable to resolve selected series video: ${String(n)}`);let r=t.find(e=>e.path===n);return r||M(`Unable to resolve selected series video: ${n}`),r}async function nn(){let e=await s({message:`Enter text for the thumbnail (or press Enter to skip):`,placeholder:`Optional thumbnail text`});if(typeof e!=`symbol`)return e.trim()||void 0}async function rn(e){let t=an(e);if(t.length===0){r.error(`No templates found in ${e||`the configured templates directory`}`),r.info(`Please add SVG templates before generating thumbnails.`);return}let n=await o({message:`Choose a template:`,options:t.map(e=>({label:e,value:e}))});if(typeof n!=`symbol`)return n}function an(e){if(!e||!c(e))return[];try{return f(e,{withFileTypes:!0}).filter(e=>e.isFile()&&re(e.name).toLowerCase()===`.svg`).map(e=>e.name.replace(/\.svg$/i,``)).sort((e,t)=>e.localeCompare(t))}catch{return[]}}async function on(e){t(`Content Creation - Estimate Series Article Time`);let n=await C(),r=await $(e),i=await Yt(r.path),o=Z(i.path,n.reading.wordsPerMinute);a(`✓ Estimated time updated for ${r.name}/${i.fileName}: ${o} min`)}async function sn(e){t(`Content Creation - Create Series`),a(`✓ Series created: series/${Jt(e,await Qt()).name}`)}async function cn(e){t(`Content Creation - Create Series Article`);let n=await $(e),r=await Xt(),i=await Zt(n.path),o=Gt(n.path,r,i);a(`✓ Series article created: ${n.name}/${h(o)}`)}async function ln(e){let n=await C();n.videoProcessing.openaiApiKey||M(`OpenAI API key is required. Set OPENAI_API_KEY in .env or configure videoProcessing.openaiApiKey in content-creation.config.{ts,js,mjs,json}.`);let i=await $(e),o=await tn(i.path);t(`Content Creation - Process Series Video: ${h(o.path)}`);let{videoPath:s,audioPath:c}=await ve(o.path),{srtPath:l}=await be(s,c,{openaiApiKey:n.videoProcessing.openaiApiKey,language:n.videoProcessing.language,model:n.videoProcessing.model,templatesDir:n.templatesDir});_e(c,`audio file`);let u=await nn();if(!u){a(`✓ Transcription saved to: ${l}`);return}let d=await rn(n.templatesDir);if(!d){r.warn(`No template selected. Skipping thumbnail generation.`),a(`✓ Transcription saved to: ${l}`);return}let{thumbnailPaths:f}=await ye(s,u,d,n.templatesDir);a(`✓ Series video processed: series/${i.name}/${o.relativePath}\n✓ Transcription saved to: ${l}\n✓ Thumbnails saved:\n - ${f.join(`
|
|
12
|
-
- `)}`)}async function un(e){t(`Content Creation - Mark Series Article Ready`);let n=await C(),r=await $(e),i=await Yt(r.path);Q(i);let o=Z(i.path,n.reading.wordsPerMinute);a(`✓ Series article updated for ${r.name}/${i.fileName}: ready + ${o} min`)}async function dn(e,t){let n=e.path,{content:i,frontmatter:a}=P(n);if(a.scheduled===!0)return r.warn(`This X publication is already scheduled. Skipping.`),!1;(!t.scheduling.automationEndpoint||!t.scheduling.cfAccessClientId||!t.scheduling.cfAccessClientSecret)&&M(`Scheduling configuration is missing. Please set AUTOMATION_ENDPOINT, CF_ACCESS_CLIENT_ID, and CF_ACCESS_CLIENT_SECRET in your .env file or config.`);let o=new Date(e.date);o=oe(o,O.hours),o=se(o,O.minutes),o=ce(o,O.seconds);let s={content:i.trim(),scheduleAt:o.getTime()};try{let e=await fetch(t.scheduling.automationEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`,"CF-Access-Client-Id":t.scheduling.cfAccessClientId,"CF-Access-Client-Secret":t.scheduling.cfAccessClientSecret},body:JSON.stringify(s)});if(!e.ok){let t=await e.text();M(`Failed to schedule reminder: ${e.status} ${e.statusText}\n${t}`)}let o=await e.json();F(n,{content:i,frontmatter:{...a,scheduled:!0}});let c=new Date(o.scheduledAt);return r.success(`✓ X publication reminder scheduled`),r.info(`Reminder scheduled for: ${b(c,`yyyy-MM-dd HH:mm:ss`)} UTC`),!0}catch(e){M(`Failed to schedule X publication reminder: ${e instanceof Error?e.message:String(e)}`)}}async function fn(e){return Be({basePath:e,contentTypes:[`x`],message:`Which X publication do you want to schedule a reminder for?`,emptyMessage:`No X publications found in dated folders`,sort:`date-desc`})}async function pn(e){t(`Content Creation - Schedule X Publication Reminder`);let n=await C();a(await dn(await fn(e),n)?`✓ X publication reminder scheduled`:`✓ X publication was already scheduled`)}export{Je as A,$e as C,Ye as D,z as E,A as F,Ae as I,C as L,P as M,F as N,R as O,j as P,et as S,B as T,kt as _,cn as a,_t as b,Q as c,Gt as d,X as f,Mt as g,J as h,ln as i,I as j,L as k,Z as l,Ft as m,dn as n,sn as o,Y as p,un as r,on as s,pn as t,Jt as u,Ot as v,Qe as w,mt as x,vt as y};
|