← back to Secrets Manager
auto-save: 2026-07-21T14:07:54 (3 files) — alias-content-token.py find-content-token.sh publish-creatures-article.py
95b77ef59414c484fcbf011fe8eb82539be5b0eb · 2026-07-21 14:07:55 -0700 · Steve Abrams
Files touched
A alias-content-token.pyA find-content-token.shA publish-creatures-article.py
Diff
commit 95b77ef59414c484fcbf011fe8eb82539be5b0eb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Jul 21 14:07:55 2026 -0700
auto-save: 2026-07-21T14:07:54 (3 files) — alias-content-token.py find-content-token.sh publish-creatures-article.py
---
alias-content-token.py | 30 ++++++++++++++
find-content-token.sh | 31 +++++++++++++++
publish-creatures-article.py | 95 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 156 insertions(+)
diff --git a/alias-content-token.py b/alias-content-token.py
new file mode 100644
index 0000000..d16748a
--- /dev/null
+++ b/alias-content-token.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+"""
+alias-content-token.py — give the DW content-capable Shopify token a name that
+reflects its REAL scope. The token currently stored as SHOPIFY_DRAFT_TOKEN (…a43b,
+DW-Write-Content app) actually carries read_content + write_content, so a future
+session shouldn't have to probe to rediscover that.
+
+Adds SHOPIFY_CONTENT_TOKEN = (same value) to the canonical .env with a scope comment,
+keeping SHOPIFY_DRAFT_TOKEN intact for backward-compat. Then run `node cli.js sync`
+to fan the new key out to the registered destinations.
+Run: ! python3 ~/Projects/secrets-manager/alias-content-token.py
+"""
+import re
+ENV = "/Users/macstudio3/Projects/secrets-manager/.env"
+env = open(ENV).read()
+
+m = re.search(r'^SHOPIFY_DRAFT_TOKEN=(.+)$', env, re.M)
+if not m:
+ print("SHOPIFY_DRAFT_TOKEN not found — nothing to alias"); raise SystemExit(1)
+val = m.group(1).strip()
+
+if re.search(r'^SHOPIFY_CONTENT_TOKEN=', env, re.M):
+ print("SHOPIFY_CONTENT_TOKEN already exists — no change")
+else:
+ with open(ENV, "a") as f:
+ f.write(f"\n# DW-Write-Content app (…{val.strip(chr(34)+chr(39))[-4:]}); "
+ f"products + read_content/write_content. Alias of SHOPIFY_DRAFT_TOKEN.\n")
+ f.write(f"SHOPIFY_CONTENT_TOKEN={val}\n")
+ print(f"added SHOPIFY_CONTENT_TOKEN (…{val.strip(chr(34)+chr(39))[-4:]}) to canonical .env")
+print("next: run node ~/Projects/secrets-manager/cli.js sync to fan out to registered .env/MCP dests")
diff --git a/find-content-token.sh b/find-content-token.sh
new file mode 100755
index 0000000..d31d133
--- /dev/null
+++ b/find-content-token.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+# find-content-token.sh — identify which already-stored DW Shopify token carries
+# read_content/write_content scope, by probing the blogs.json endpoint.
+# READ-ONLY: prints only the var NAME + last-4 + HTTP result. Never prints a full token.
+# Purpose-built so a single narrow permission rule can allow it (Steve, 2026-07-21, DTD-B).
+set -euo pipefail
+cd "$(dirname "$0")"
+STORE="designer-laboratory-sandbox.myshopify.com"
+API="2024-10"
+CANDIDATES=(SHOPIFY_ADMIN_TOKEN SHOPIFY_DRAFT_TOKEN SHOPIFY_ORDERS_TOKEN \
+ SHOPIFY_FULL_ACCESS_TOKEN SHOPIFY_CONTENT_TOKEN SHOPIFY_WRITE_CONTENT_TOKEN \
+ SHOPIFY_WRITECONTENT_TOKEN SHOPIFY_BLOG_TOKEN)
+WINNER=""
+for n in "${CANDIDATES[@]}"; do
+ line=$(grep -m1 "^${n}=" .env 2>/dev/null || true)
+ [ -z "$line" ] && { printf '%-30s — not stored\n' "$n"; continue; }
+ tok="${line#*=}"; tok="${tok%\"}"; tok="${tok#\"}"; tok="${tok%\'}"; tok="${tok#\'}"
+ code=$(curl -s -o /dev/null -w '%{http_code}' \
+ "https://$STORE/admin/api/$API/blogs.json" \
+ -H "X-Shopify-Access-Token: $tok")
+ if [ "$code" = "200" ]; then
+ printf '%-30s …%s → ✅ CONTENT OK\n' "$n" "${tok: -4}"
+ [ -z "$WINNER" ] && WINNER="$n"
+ elif [ "$code" = "403" ]; then
+ printf '%-30s …%s → 403 (no content scope)\n' "$n" "${tok: -4}"
+ else
+ printf '%-30s …%s → HTTP %s\n' "$n" "${tok: -4}" "$code"
+ fi
+done
+echo "---"
+[ -n "$WINNER" ] && echo "USABLE_CONTENT_TOKEN=$WINNER" || echo "USABLE_CONTENT_TOKEN=none"
diff --git a/publish-creatures-article.py b/publish-creatures-article.py
new file mode 100644
index 0000000..ab0528d
--- /dev/null
+++ b/publish-creatures-article.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+"""
+publish-creatures-article.py — one-shot: publish the AEO-optimized "Creatures of the
+Night by Zak and Fox" DW blog article via the Shopify Admin API.
+
+SAFE BY DESIGN:
+ • Backs up the current body_html to /tmp/dw-drafts/creatures-ORIGINAL-body-backup.html
+ BEFORE writing (instant revert path).
+ • PRESERVES the existing 3 images + the poem block verbatim — wraps AEO content
+ (direct-answer line + editorial above, question-H2s + FAQ + FAQPage JSON-LD below).
+ • Sets the SEO title_tag + description_tag metafields.
+Run it yourself: ! python3 ~/Projects/secrets-manager/publish-creatures-article.py
+"""
+import re, json, urllib.request, sys
+
+env = open('/Users/macstudio3/Projects/secrets-manager/.env').read()
+tok = re.search(r'^SHOPIFY_DRAFT_TOKEN=(.+)$', env, re.M).group(1).strip().strip('"').strip("'")
+STORE = "designer-laboratory-sandbox.myshopify.com"; API = "2024-10"
+BLOG = 9243689072; ART = 561218191411
+
+def get(path):
+ req = urllib.request.Request(f"https://{STORE}/admin/api/{API}/{path}",
+ headers={"X-Shopify-Access-Token": tok})
+ return json.load(urllib.request.urlopen(req, timeout=30))
+
+def put(path, payload):
+ data = json.dumps(payload).encode()
+ req = urllib.request.Request(f"https://{STORE}/admin/api/{API}/{path}", data=data,
+ method="PUT", headers={"X-Shopify-Access-Token": tok, "Content-Type": "application/json"})
+ return json.load(urllib.request.urlopen(req, timeout=30))
+
+art = get(f"blogs/{BLOG}/articles/{ART}.json")['article']
+orig_body = art.get('body_html') or ''
+import os
+os.makedirs("/tmp/dw-drafts", exist_ok=True)
+open("/tmp/dw-drafts/creatures-ORIGINAL-body-backup.html", "w").write(orig_body)
+print(f"backup saved ({len(orig_body)} chars) -> /tmp/dw-drafts/creatures-ORIGINAL-body-backup.html")
+
+intro = '''<p>Creatures of the Night is a dark, nocturnal luxury wallcovering by the New York design house Zak and Fox.</p>
+
+<p>The pattern reads like a midnight tapestry — shadowed forms and glinting eyes woven into a moody, hand-drawn motif. It belongs to Zak and Fox's signature vocabulary of artisanal, globally-inspired prints on natural grounds, and <a href="https://www.zakandfox.com">Zak and Fox</a> is carried by Designer Wallcoverings as an authorized luxury source with sampling and made-to-order production.</p>
+
+<p>Because the design leans deep and atmospheric, it rewards spaces you want to feel intimate and theatrical: a powder room, a dining room, a study, or a single accent wall behind a bed. Paired with warm, low lighting and rich textures — velvet, aged brass, dark timber — the nocturnal motif comes alive rather than reading flat. Designers often reach for a statement pattern like this exactly where a quieter paper would disappear.</p>
+
+<p>Installation follows standard luxury-wallcovering practice: professional hanging is recommended, and because the goods are made to order, lead times run longer than a stock roll. Order enough to cover the full run plus pattern-match overage in one batch so every drop comes from the same dye lot, and prep the walls properly before hanging.</p>
+
+<p>As with most luxury wallcoverings, Creatures of the Night is produced to order. Designer Wallcoverings offers up to 10 free samples so you can test the colorway and scale in your own light before committing, and can custom design and print a coordinating pattern if you need a bespoke variation. For trade accounts, pricing and lead times are quoted per project — <a href="https://designerwallcoverings.com/pages/contact">contact the DW team</a> to start an order.</p>
+'''
+
+faq_pairs = [
+ ("What is Creatures of the Night wallpaper by Zak and Fox?", "Creatures of the Night is a dark, nocturnal-themed luxury wallcovering from the New York design house Zak and Fox, known for artisanal, globally-inspired patterns. Its moody, tapestry-like motif is built for dramatic, statement interiors."),
+ ("Who is Zak and Fox?", "Zak and Fox is a New York-based textile and wallcovering design studio founded by Zak Profera, recognized for hand-crafted, ethnographically-inspired prints on natural grounds. Designer Wallcoverings carries the line as an authorized luxury source."),
+ ("Can I order free samples of Creatures of the Night before buying?", "Yes. Designer Wallcoverings offers up to 10 free samples so you can check the colorway and scale against your own lighting and furnishings before committing to a full order."),
+ ("Is Creatures of the Night made to order?", "Yes. Like most luxury wallcoverings, it is produced to order, and Designer Wallcoverings can also custom design and print a coordinating pattern if you need a bespoke variation."),
+ ("What rooms suit a dark, dramatic wallpaper like Creatures of the Night?", "Deep, atmospheric patterns work best in spaces you want to feel enveloping: powder rooms, dining rooms, studies, bedrooms, and accent walls. Pair with warm lighting and rich textures to amplify the nocturnal mood."),
+]
+h2s = '''<h2>What is Creatures of the Night wallpaper?</h2>
+<p>A dark, tapestry-like luxury wallcovering from Zak and Fox, built around a nocturnal motif of shadowed creatures — designed to make a dramatic statement rather than sit quietly in the background.</p>
+
+<h2>Who designs Creatures of the Night?</h2>
+<p>Zak and Fox, a New York textile and wallcovering studio founded by Zak Profera, known for hand-crafted, ethnographically-inspired patterns printed on natural grounds.</p>
+
+<h2>How do you order Creatures of the Night by Zak and Fox?</h2>
+<p>Start with up to 10 free samples from Designer Wallcoverings to confirm the colorway and scale, then request a quote for made-to-order production. Contact the DW team for trade pricing and lead times before placing the full order.</p>
+
+<h2>What rooms suit a dark wallpaper like this?</h2>
+<p>Enveloping patterns work best in powder rooms, dining rooms, studies, bedrooms, and accent walls — spaces where a bold, moody backdrop adds drama instead of overwhelming an already-bright, high-traffic room.</p>
+'''
+faq_html = '<h2>Frequently Asked Questions</h2>\n' + ''.join(f'<h3>{q}</h3>\n<p>{a}</p>\n' for q, a in faq_pairs)
+schema = {"@context": "https://schema.org", "@type": "FAQPage",
+ "mainEntity": [{"@type": "Question", "name": q,
+ "acceptedAnswer": {"@type": "Answer", "text": a}} for q, a in faq_pairs]}
+schema_html = '<script type="application/ld+json">\n' + json.dumps(schema, ensure_ascii=False, indent=2) + '\n</script>'
+
+new_body = intro + "\n" + orig_body + "\n" + h2s + "\n" + faq_html + "\n" + schema_html
+
+payload = {"article": {"id": ART,
+ "title": "Creatures of the Night Wallpaper by Zak and Fox",
+ "body_html": new_body,
+ "metafields": [
+ {"namespace": "global", "key": "title_tag",
+ "value": "Creatures of the Night Wallpaper by Zak and Fox | Designer Wallcoverings",
+ "type": "single_line_text_field"},
+ {"namespace": "global", "key": "description_tag",
+ "value": "Creatures of the Night by Zak and Fox is a dark, nocturnal luxury wallcovering for dramatic rooms. Order up to 10 free samples and get trade pricing.",
+ "type": "single_line_text_field"},
+ ]}}
+
+res = put(f"blogs/{BLOG}/articles/{ART}.json", payload)['article']
+print("PUBLISHED OK updated_at:", res.get('updated_at'))
+print("title :", res.get('title'))
+print("body :", len(res.get('body_html') or ''), "chars (was", len(orig_body), ")")
+nb = res.get('body_html') or ''
+print("images kept:", len(re.findall(r'<img', nb)), "| poem kept:", 'midnight' in nb.lower(),
+ "| faq schema:", 'FAQPage' in nb)
← be72efe auto-save: 2026-07-20T13:32:24 (1 files) — registry.json
·
back to Secrets Manager
·
auto-save: 2026-07-21T14:38:01 (3 files) — add-blog-images.p 48f095f →