← back to Ventura Corridor
iter 135: IG-autopost dry-run job — writes daily JSON+HTML drafts under tmp/ig-drafts/, no posting yet
85d3731dd25838fd0a6822bcd608ddbb8d2ea681 · 2026-05-06 18:55:21 -0700 · SteveStudio2
Files touched
A src/jobs/ig_autopost_dryrun.tsA tmp/ig-drafts/2026-05-07.htmlA tmp/ig-drafts/2026-05-07.jsonA tmp/ig-drafts/index.html
Diff
commit 85d3731dd25838fd0a6822bcd608ddbb8d2ea681
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 6 18:55:21 2026 -0700
iter 135: IG-autopost dry-run job — writes daily JSON+HTML drafts under tmp/ig-drafts/, no posting yet
---
src/jobs/ig_autopost_dryrun.ts | 148 +++++++++++++++++++++++++++++++++++++++++
tmp/ig-drafts/2026-05-07.html | 51 ++++++++++++++
tmp/ig-drafts/2026-05-07.json | 22 ++++++
tmp/ig-drafts/index.html | 24 +++++++
4 files changed, 245 insertions(+)
diff --git a/src/jobs/ig_autopost_dryrun.ts b/src/jobs/ig_autopost_dryrun.ts
new file mode 100644
index 0000000..9abf813
--- /dev/null
+++ b/src/jobs/ig_autopost_dryrun.ts
@@ -0,0 +1,148 @@
+// Dry-run for daily IG autopost. Pulls /api/magazine/today, writes a JSON draft
+// + preview HTML. No actual Instagram posting until Steve flips the switch.
+//
+// Output:
+// ~/Projects/ventura-corridor/tmp/ig-drafts/YYYY-MM-DD.json ← machine-readable
+// ~/Projects/ventura-corridor/tmp/ig-drafts/YYYY-MM-DD.html ← visual preview
+// ~/Projects/ventura-corridor/tmp/ig-drafts/index.html ← all drafts (newest first)
+//
+// Run manually:
+// pnpm tsx src/jobs/ig_autopost_dryrun.ts
+//
+// Once Steve approves the format, we wire `instagram-agent` skill to consume
+// these drafts at 9 AM each morning.
+
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+
+const PORT = process.env.PORT || '9780';
+const ADMIN_USER = process.env.ADMIN_USER || 'admin';
+const ADMIN_PASS = process.env.ADMIN_PASS || 'DWSecure2024!';
+const BASE = `http://127.0.0.1:${PORT}`;
+const PUBLIC_BASE = process.env.PUBLIC_BASE || BASE; // when actually posting, swap to public domain
+const DRAFTS_DIR = path.resolve(process.cwd(), 'tmp', 'ig-drafts');
+
+function escHtml(s: any): string {
+ return String(s ?? '').replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]!));
+}
+
+async function main() {
+ if (!fs.existsSync(DRAFTS_DIR)) fs.mkdirSync(DRAFTS_DIR, { recursive: true });
+
+ const auth = 'Basic ' + Buffer.from(`${ADMIN_USER}:${ADMIN_PASS}`).toString('base64');
+ const r = await fetch(`${BASE}/api/magazine/today`, { headers: { Authorization: auth } });
+ if (!r.ok) {
+ console.error(`[ig-dryrun] /api/magazine/today returned ${r.status}`);
+ process.exit(1);
+ }
+ const data: any = await r.json();
+ if (!data.feature) {
+ console.error(`[ig-dryrun] no feature available today`);
+ process.exit(0);
+ }
+
+ const date = data.date;
+ const draft = {
+ date,
+ feature_id: data.feature.id,
+ headline: data.feature.headline,
+ biz_name: data.feature.biz_name,
+ category: data.feature.category_tag,
+ image_url: `${PUBLIC_BASE}${data.share_card}`,
+ permalink: `${PUBLIC_BASE}${data.permalink}`,
+ audio_url: `${PUBLIC_BASE}${data.audio}`,
+ caption: data.caption,
+ caption_chars: data.caption_chars,
+ tags: data.tags,
+ suggested_post_time: `${date}T09:00:00-07:00`,
+ status: 'draft',
+ generated_at: new Date().toISOString(),
+ };
+
+ const jsonPath = path.join(DRAFTS_DIR, `${date}.json`);
+ fs.writeFileSync(jsonPath, JSON.stringify(draft, null, 2));
+
+ const htmlPath = path.join(DRAFTS_DIR, `${date}.html`);
+ fs.writeFileSync(htmlPath, `<!doctype html>
+<html><head><meta charset="utf-8"><title>IG draft · ${escHtml(date)} · The Corridor</title>
+<style>
+@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;1,400;1,500&family=Inter:wght@300;400&display=swap');
+*{box-sizing:border-box}body{margin:0;background:#0a0a0c;color:#f0ece2;font-family:Inter,sans-serif;font-weight:300;padding:36px}
+.wrap{max-width:920px;margin:0 auto;display:grid;grid-template-columns:360px 1fr;gap:36px}
+h1{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:42px;margin:0 0 8px}
+.kicker{font-size:9px;letter-spacing:.4em;text-transform:uppercase;color:#8a6d3b}
+.card iframe{width:360px;height:640px;border:1px solid #2a2622;background:#000}
+.meta{font-family:'JetBrains Mono',monospace;font-size:11px;color:#888475;line-height:1.6}
+.meta a{color:#b89968;text-decoration:none}
+pre{background:rgba(184,153,104,0.06);border:1px solid #2a2622;padding:18px;font-family:'Inter',sans-serif;font-size:14px;line-height:1.5;white-space:pre-wrap;color:#f0ece2}
+.tags{margin-top:14px}.tags span{display:inline-block;color:#b89968;font-size:11px;margin-right:8px}
+.cta{margin-top:18px;display:flex;gap:8px;flex-wrap:wrap}
+.cta a{font-size:9px;letter-spacing:.32em;text-transform:uppercase;color:#b89968;border:1px solid #2a2622;padding:8px 14px;text-decoration:none}
+.cta a:hover{border-color:#b89968}
+</style></head><body>
+<div class="wrap">
+ <div class="card">
+ <div class="kicker" style="margin-bottom:10px">Share-card preview · 9:16</div>
+ <iframe src="${escHtml(data.share_card)}" loading="lazy"></iframe>
+ </div>
+ <div>
+ <div class="kicker">${escHtml(date)} · IG draft</div>
+ <h1>${escHtml(data.feature.headline)}</h1>
+ <div class="meta">
+ ${escHtml(data.feature.biz_name)} · ${escHtml(data.feature.category_tag || '—')}<br>
+ <a href="${escHtml(data.permalink)}">${escHtml(data.permalink)}</a><br>
+ Suggested post time: ${escHtml(draft.suggested_post_time)}<br>
+ Caption: ${data.caption_chars} chars
+ </div>
+ <h3 style="font-family:'Cormorant Garamond',serif;font-style:italic;color:#b89968;margin:20px 0 6px">Caption</h3>
+ <pre>${escHtml(data.caption)}</pre>
+ <div class="tags">${(data.tags as string[]).map(t => `<span>${escHtml(t)}</span>`).join('')}</div>
+ <div class="cta">
+ <a href="${escHtml(data.permalink)}">Read feature</a>
+ <a href="${escHtml(data.share_card)}">Share card</a>
+ <a href="${escHtml(data.audio)}">Audio</a>
+ <a href="index.html">All drafts</a>
+ </div>
+ </div>
+</div>
+</body></html>`);
+
+ // index.html — all drafts newest-first
+ const drafts = fs.readdirSync(DRAFTS_DIR)
+ .filter(n => n.endsWith('.json'))
+ .sort()
+ .reverse()
+ .map(n => JSON.parse(fs.readFileSync(path.join(DRAFTS_DIR, n), 'utf8')));
+ fs.writeFileSync(path.join(DRAFTS_DIR, 'index.html'), `<!doctype html>
+<html><head><meta charset="utf-8"><title>IG drafts · The Corridor</title>
+<style>
+@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;1,400;1,500&family=Inter:wght@300;400&display=swap');
+body{margin:0;background:#0a0a0c;color:#f0ece2;font-family:Inter,sans-serif;font-weight:300;padding:32px;max-width:900px;margin:0 auto}
+h1{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:42px;margin:0 0 24px;color:#b89968}
+.row{display:grid;grid-template-columns:120px 1fr auto;gap:18px;padding:18px 0;border-bottom:1px solid #2a2622;align-items:center}
+.row .date{font-family:'JetBrains Mono',monospace;font-size:13px;color:#b89968}
+.row .head{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:18px}
+.row .biz{font-size:11px;color:#888475;letter-spacing:.04em}
+.row a{color:#b89968;text-decoration:none;font-size:9px;letter-spacing:.32em;text-transform:uppercase;border:1px solid #2a2622;padding:6px 12px}
+.row a:hover{border-color:#b89968}
+</style></head><body>
+<h1>IG <em style="color:#f0ece2">drafts</em> · The Corridor</h1>
+${drafts.map(d => `
+<div class="row">
+ <span class="date">${escHtml(d.date)}</span>
+ <div>
+ <div class="head">${escHtml(d.headline)}</div>
+ <div class="biz">${escHtml(d.biz_name)} · ${escHtml(d.category || '—')} · ${d.caption_chars} chars</div>
+ </div>
+ <a href="${escHtml(d.date)}.html">preview →</a>
+</div>`).join('')}
+</body></html>`);
+
+ console.log(`[ig-dryrun] wrote draft for ${date}`);
+ console.log(` ${jsonPath}`);
+ console.log(` ${htmlPath}`);
+ console.log(` feature_id=${draft.feature_id} · "${draft.headline}"`);
+ console.log(` ${draft.caption_chars} char caption · ${draft.tags.length} tags`);
+}
+
+main().catch(e => { console.error(e); process.exit(1); });
diff --git a/tmp/ig-drafts/2026-05-07.html b/tmp/ig-drafts/2026-05-07.html
new file mode 100644
index 0000000..65d2e83
--- /dev/null
+++ b/tmp/ig-drafts/2026-05-07.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<html><head><meta charset="utf-8"><title>IG draft · 2026-05-07 · The Corridor</title>
+<style>
+@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;1,400;1,500&family=Inter:wght@300;400&display=swap');
+*{box-sizing:border-box}body{margin:0;background:#0a0a0c;color:#f0ece2;font-family:Inter,sans-serif;font-weight:300;padding:36px}
+.wrap{max-width:920px;margin:0 auto;display:grid;grid-template-columns:360px 1fr;gap:36px}
+h1{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:42px;margin:0 0 8px}
+.kicker{font-size:9px;letter-spacing:.4em;text-transform:uppercase;color:#8a6d3b}
+.card iframe{width:360px;height:640px;border:1px solid #2a2622;background:#000}
+.meta{font-family:'JetBrains Mono',monospace;font-size:11px;color:#888475;line-height:1.6}
+.meta a{color:#b89968;text-decoration:none}
+pre{background:rgba(184,153,104,0.06);border:1px solid #2a2622;padding:18px;font-family:'Inter',sans-serif;font-size:14px;line-height:1.5;white-space:pre-wrap;color:#f0ece2}
+.tags{margin-top:14px}.tags span{display:inline-block;color:#b89968;font-size:11px;margin-right:8px}
+.cta{margin-top:18px;display:flex;gap:8px;flex-wrap:wrap}
+.cta a{font-size:9px;letter-spacing:.32em;text-transform:uppercase;color:#b89968;border:1px solid #2a2622;padding:8px 14px;text-decoration:none}
+.cta a:hover{border-color:#b89968}
+</style></head><body>
+<div class="wrap">
+ <div class="card">
+ <div class="kicker" style="margin-bottom:10px">Share-card preview · 9:16</div>
+ <iframe src="/share/32" loading="lazy"></iframe>
+ </div>
+ <div>
+ <div class="kicker">2026-05-07 · IG draft</div>
+ <h1>Relieving Pain with Compassionate Care</h1>
+ <div class="meta">
+ PAIN RELEASE CENTER · medical<br>
+ <a href="/magazine/32">/magazine/32</a><br>
+ Suggested post time: 2026-05-07T09:00:00-07:00<br>
+ Caption: 634 chars
+ </div>
+ <h3 style="font-family:'Cormorant Garamond',serif;font-style:italic;color:#b89968;margin:20px 0 6px">Caption</h3>
+ <pre>Relieving Pain with Compassionate Care
+
+Nestled in Studio City’s vibrant multi-tenant complex, this clinic offers holistic pain management solutions.
+
+Tucked inside the bustling building at 12215 Ventura Blvd, the Pain Release Center blends modern medical expertise with a calming, approachable atmosphere. Patients describe the space as both professional and welcoming, with practitioners who prioritize personalized care. The clinic’s focus on integrative treatments—ranging from physical therapy to lifestyle guidance—resonates…
+
+Read the full feature → /magazine/32
+
+#TheCorridor #VenturaBoulevard #LosAngeles #medical #STUDIOCITY</pre>
+ <div class="tags"><span>#TheCorridor</span><span>#VenturaBoulevard</span><span>#LosAngeles</span><span>#medical</span><span>#STUDIOCITY</span></div>
+ <div class="cta">
+ <a href="/magazine/32">Read feature</a>
+ <a href="/share/32">Share card</a>
+ <a href="/api/magazine/32/audio.m4a">Audio</a>
+ <a href="index.html">All drafts</a>
+ </div>
+ </div>
+</div>
+</body></html>
\ No newline at end of file
diff --git a/tmp/ig-drafts/2026-05-07.json b/tmp/ig-drafts/2026-05-07.json
new file mode 100644
index 0000000..d47aa1d
--- /dev/null
+++ b/tmp/ig-drafts/2026-05-07.json
@@ -0,0 +1,22 @@
+{
+ "date": "2026-05-07",
+ "feature_id": "32",
+ "headline": "Relieving Pain with Compassionate Care",
+ "biz_name": "PAIN RELEASE CENTER",
+ "category": "medical",
+ "image_url": "http://127.0.0.1:9780/share/32",
+ "permalink": "http://127.0.0.1:9780/magazine/32",
+ "audio_url": "http://127.0.0.1:9780/api/magazine/32/audio.m4a",
+ "caption": "Relieving Pain with Compassionate Care\n\nNestled in Studio City’s vibrant multi-tenant complex, this clinic offers holistic pain management solutions.\n\nTucked inside the bustling building at 12215 Ventura Blvd, the Pain Release Center blends modern medical expertise with a calming, approachable atmosphere. Patients describe the space as both professional and welcoming, with practitioners who prioritize personalized care. The clinic’s focus on integrative treatments—ranging from physical therapy to lifestyle guidance—resonates…\n\nRead the full feature → /magazine/32\n\n#TheCorridor #VenturaBoulevard #LosAngeles #medical #STUDIOCITY",
+ "caption_chars": 634,
+ "tags": [
+ "#TheCorridor",
+ "#VenturaBoulevard",
+ "#LosAngeles",
+ "#medical",
+ "#STUDIOCITY"
+ ],
+ "suggested_post_time": "2026-05-07T09:00:00-07:00",
+ "status": "draft",
+ "generated_at": "2026-05-07T01:55:09.828Z"
+}
\ No newline at end of file
diff --git a/tmp/ig-drafts/index.html b/tmp/ig-drafts/index.html
new file mode 100644
index 0000000..9b62c6b
--- /dev/null
+++ b/tmp/ig-drafts/index.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html><head><meta charset="utf-8"><title>IG drafts · The Corridor</title>
+<style>
+@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;1,400;1,500&family=Inter:wght@300;400&display=swap');
+body{margin:0;background:#0a0a0c;color:#f0ece2;font-family:Inter,sans-serif;font-weight:300;padding:32px;max-width:900px;margin:0 auto}
+h1{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:42px;margin:0 0 24px;color:#b89968}
+.row{display:grid;grid-template-columns:120px 1fr auto;gap:18px;padding:18px 0;border-bottom:1px solid #2a2622;align-items:center}
+.row .date{font-family:'JetBrains Mono',monospace;font-size:13px;color:#b89968}
+.row .head{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:18px}
+.row .biz{font-size:11px;color:#888475;letter-spacing:.04em}
+.row a{color:#b89968;text-decoration:none;font-size:9px;letter-spacing:.32em;text-transform:uppercase;border:1px solid #2a2622;padding:6px 12px}
+.row a:hover{border-color:#b89968}
+</style></head><body>
+<h1>IG <em style="color:#f0ece2">drafts</em> · The Corridor</h1>
+
+<div class="row">
+ <span class="date">2026-05-07</span>
+ <div>
+ <div class="head">Relieving Pain with Compassionate Care</div>
+ <div class="biz">PAIN RELEASE CENTER · medical · 634 chars</div>
+ </div>
+ <a href="2026-05-07.html">preview →</a>
+</div>
+</body></html>
\ No newline at end of file
← f40d96e iter 134: /api/magazine/today unified payload — feature + ca
·
back to Ventura Corridor
·
iter 136: launchd for ig-dryrun at 8:02 AM daily — preview t 9b315a5 →