← back to Cred Launcher
cred-launcher: localhost credential launcher served on :51858, launchd keep-alive
ae8f127c1964b81995f72ce77464a6144cd5dbd0 · 2026-09-22 16:49:12 -0700 · Steve Abrams
Serves the 132-credential launcher page at http://127.0.0.1:51858/ so its
provider-console Launch links open in a real browser. Kept alive by
com.steve.credlauncher (KeepAlive=true, RunAtLoad=true).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A .gitignoreA page.htmlA server.js
Diff
commit ae8f127c1964b81995f72ce77464a6144cd5dbd0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Sep 22 16:49:12 2026 -0700
cred-launcher: localhost credential launcher served on :51858, launchd keep-alive
Serves the 132-credential launcher page at http://127.0.0.1:51858/ so its
provider-console Launch links open in a real browser. Kept alive by
com.steve.credlauncher (KeepAlive=true, RunAtLoad=true).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
.gitignore | 8 +
page.html | 596 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
server.js | 44 +++++
3 files changed, 648 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1924158
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+.next/
diff --git a/page.html b/page.html
new file mode 100644
index 0000000..293a112
--- /dev/null
+++ b/page.html
@@ -0,0 +1,596 @@
+<title>Credential Launcher</title>
+<link rel="preconnect" href="https://fonts.googleapis.com">
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap">
+<style>
+ :root{
+ --bg:#f4f6f8; --surface:#ffffff; --surface-2:#eef1f5; --text:#171c23; --muted:#5c6773;
+ --border:#dde2e9; --border-strong:#c6cdd6;
+ --accent:#0e7490; --accent-ink:#ffffff; --accent-soft:#d7eef3;
+ --warn:#b45309; --warn-soft:#fbebd5; --warn-bd:#e6b877;
+ --good:#15803d; --good-soft:#dcf1e2; --good-bd:#8fd0a4;
+ --danger:#be123c; --danger-soft:#fbe0e6; --danger-bd:#eaa3b3;
+ --shadow:0 1px 2px rgba(20,28,38,.06), 0 4px 16px rgba(20,28,38,.05);
+ --radius:12px;
+ color-scheme:light;
+ }
+ :root:not([data-theme="light"]){ @media (prefers-color-scheme: dark){
+ --bg:#0d1117; --surface:#161b22; --surface-2:#1c232d; --text:#e6ebf2; --muted:#9aa5b3;
+ --border:#2a313c; --border-strong:#3a434f;
+ --accent:#22d3ee; --accent-ink:#04222a; --accent-soft:#0f3a44;
+ --warn:#f59e0b; --warn-soft:#3a2b0f; --warn-bd:#7a5a1c;
+ --good:#4ade80; --good-soft:#123222; --good-bd:#1f5a3a;
+ --danger:#fb7185; --danger-soft:#3a1620; --danger-bd:#7a2a3a;
+ --shadow:0 1px 2px rgba(0,0,0,.4), 0 6px 20px rgba(0,0,0,.35);
+ color-scheme:dark;
+ }}
+ :root[data-theme="dark"]{
+ --bg:#0d1117; --surface:#161b22; --surface-2:#1c232d; --text:#e6ebf2; --muted:#9aa5b3;
+ --border:#2a313c; --border-strong:#3a434f;
+ --accent:#22d3ee; --accent-ink:#04222a; --accent-soft:#0f3a44;
+ --warn:#f59e0b; --warn-soft:#3a2b0f; --warn-bd:#7a5a1c;
+ --good:#4ade80; --good-soft:#123222; --good-bd:#1f5a3a;
+ --danger:#fb7185; --danger-soft:#3a1620; --danger-bd:#7a2a3a;
+ --shadow:0 1px 2px rgba(0,0,0,.4), 0 6px 20px rgba(0,0,0,.35);
+ color-scheme:dark;
+ }
+
+ *{box-sizing:border-box}
+ body{background:var(--bg);color:var(--text);font-family:"IBM Plex Sans",system-ui,-apple-system,sans-serif;line-height:1.45;-webkit-font-smoothing:antialiased}
+ .wrap{max-width:1080px;margin:0 auto;padding-inline:16px;padding-block:0 120px}
+ .mono{font-family:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace}
+
+ /* header */
+ header.top{position:sticky;top:env(safe-area-inset-top,0px);z-index:20;background:var(--bg);
+ border-bottom:1px solid var(--border);padding-block:14px 10px}
+ .title-row{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap}
+ h1{font-size:1.35rem;font-weight:700;margin:0;letter-spacing:-.01em}
+ .sub{color:var(--muted);font-size:.82rem}
+ .stats{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
+ .stat{display:flex;align-items:center;gap:6px;background:var(--surface);border:1px solid var(--border);
+ border-radius:999px;padding:4px 11px;font-size:.76rem;font-weight:500}
+ .stat b{font-weight:700;font-variant-numeric:tabular-nums}
+ .dot{width:8px;height:8px;border-radius:50%;flex:none}
+ .dot.warn{background:var(--warn)} .dot.good{background:var(--good)}
+ .dot.ext{background:var(--accent)} .dot.int{background:var(--border-strong)}
+
+ .controls{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;align-items:center}
+ .search{flex:1;min-width:190px;display:flex;align-items:center;gap:8px;background:var(--surface);
+ border:1px solid var(--border-strong);border-radius:10px;padding:8px 12px}
+ .search input{border:0;background:transparent;color:var(--text);font:inherit;width:100%;outline:none}
+ .search svg{flex:none;color:var(--muted)}
+ select,.seg button{font:inherit;color:var(--text)}
+ select{background:var(--surface);border:1px solid var(--border-strong);border-radius:10px;padding:8px 10px;font-size:.82rem}
+ .seg{display:flex;background:var(--surface);border:1px solid var(--border-strong);border-radius:10px;overflow:hidden}
+ .seg button{background:transparent;border:0;padding:8px 11px;font-size:.78rem;cursor:pointer;color:var(--muted)}
+ .seg button[aria-pressed="true"]{background:var(--accent);color:var(--accent-ink);font-weight:600}
+
+ .chips{display:flex;gap:7px;flex-wrap:wrap;margin-top:10px}
+ .chip{background:var(--surface);border:1px solid var(--border-strong);border-radius:999px;
+ padding:5px 12px;font-size:.78rem;cursor:pointer;color:var(--muted);white-space:nowrap}
+ .chip[aria-pressed="true"]{background:var(--text);color:var(--bg);border-color:var(--text);font-weight:600}
+ .chip .n{opacity:.6;font-variant-numeric:tabular-nums;margin-left:2px}
+
+ /* sections */
+ .sec-label{display:flex;align-items:center;gap:8px;margin:22px 2px 10px;font-size:.72rem;
+ font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
+ .sec-label .rule{flex:1;height:1px;background:var(--border)}
+ .sec-label.attn{color:var(--warn)}
+
+ /* rows */
+ .list{display:flex;flex-direction:column;gap:8px}
+ .row{display:grid;grid-template-columns:auto 1fr auto;gap:12px;align-items:center;
+ background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
+ padding:13px 15px;box-shadow:var(--shadow);cursor:pointer;transition:border-color .12s,transform .06s}
+ .row:hover{border-color:var(--border-strong)}
+ .row.sel{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft),var(--shadow)}
+ .row.attn{border-left:4px solid var(--warn)}
+ .row.done{opacity:.5}
+ .row.done .kname{text-decoration:line-through}
+
+ .rmark{width:20px;height:20px;border-radius:50%;border:2px solid var(--border-strong);flex:none;
+ display:grid;place-items:center;color:transparent}
+ .row.sel .rmark{border-color:var(--accent);background:var(--accent);color:var(--accent-ink)}
+
+ .rmid{min-width:0}
+ .kname{font-family:"IBM Plex Mono",monospace;font-weight:600;font-size:.9rem;word-break:break-all}
+ .meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:3px}
+ .prov{font-size:.8rem;font-weight:600;color:var(--accent)}
+ .badge{font-size:.66rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
+ padding:2px 7px;border-radius:6px;border:1px solid transparent;white-space:nowrap}
+ .badge.rot{background:var(--warn-soft);color:var(--warn);border-color:var(--warn-bd)}
+ .badge.live{background:var(--good-soft);color:var(--good);border-color:var(--good-bd)}
+ .badge.alias{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-bd)}
+ .badge.int{background:var(--surface-2);color:var(--muted);border-color:var(--border)}
+ .label{font-size:.8rem;color:var(--muted);margin-top:4px;overflow:hidden;text-overflow:ellipsis;
+ display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
+ .dest{font-size:.72rem;color:var(--muted);margin-top:4px;opacity:.85}
+
+ .rright{display:flex;flex-direction:column;align-items:flex-end;gap:8px}
+ a.launch{display:inline-flex;align-items:center;gap:6px;text-decoration:none;font-size:.8rem;font-weight:600;
+ background:var(--accent);color:var(--accent-ink);border:1px solid var(--accent);
+ padding:7px 13px;border-radius:9px;white-space:nowrap}
+ a.launch.ghost{background:transparent;color:var(--muted);border-color:var(--border-strong)}
+ a.launch:hover{filter:brightness(1.06)}
+ .nocon{font-size:.72rem;color:var(--muted);font-style:italic;padding:7px 4px}
+ .donebtn{display:inline-flex;align-items:center;gap:5px;font-size:.72rem;color:var(--muted);
+ background:transparent;border:0;cursor:pointer;padding:2px 4px}
+ .donebtn:hover{color:var(--good)}
+ .doneck{width:14px;height:14px;border:1.5px solid var(--border-strong);border-radius:4px;display:grid;place-items:center;color:transparent}
+ .row.done .doneck{background:var(--good);border-color:var(--good);color:#fff}
+
+ .empty{text-align:center;color:var(--muted);padding:40px 0;font-size:.9rem}
+
+ /* launch bar */
+ .launchbar{position:fixed;left:0;right:0;bottom:0;z-index:30;
+ background:var(--surface);border-top:1px solid var(--border-strong);
+ padding:12px 16px calc(12px + env(safe-area-inset-bottom,0px));
+ box-shadow:0 -4px 20px rgba(0,0,0,.08);transform:translateY(120%);transition:transform .18s ease}
+ .launchbar.show{transform:translateY(0)}
+ .lb-inner{max-width:1080px;margin:0 auto;display:flex;align-items:center;gap:12px}
+ .lb-txt{min-width:0;flex:1}
+ .lb-k{font-family:"IBM Plex Mono",monospace;font-weight:600;font-size:.85rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+ .lb-p{font-size:.75rem;color:var(--muted)}
+ a.lb-go{display:inline-flex;align-items:center;gap:8px;text-decoration:none;font-weight:700;font-size:.9rem;
+ background:var(--accent);color:var(--accent-ink);padding:11px 20px;border-radius:11px;white-space:nowrap}
+ a.lb-go[aria-disabled="true"]{opacity:.4;pointer-events:none}
+ .lb-close{background:transparent;border:0;color:var(--muted);font-size:1.3rem;cursor:pointer;line-height:1;padding:4px 6px}
+
+ @media (max-width:560px){
+ .row{grid-template-columns:auto 1fr;grid-template-areas:"mark mid" "right right"}
+ .rmark{grid-area:mark}.rmid{grid-area:mid}.rright{grid-area:right;flex-direction:row;justify-content:flex-end;align-items:center}
+ }
+ @media (prefers-reduced-motion:reduce){*{transition:none!important}}
+
+ /* compact density */
+ body.compact .row{padding:8px 13px}
+ body.compact .label,body.compact .dest{display:none}
+ body.compact .meta{margin-top:1px}
+</style>
+
+<div class="wrap">
+ <header class="top">
+ <div class="title-row">
+ <h1>Credential Launcher</h1>
+ <span class="sub">click a credential → open its provider console to view / rotate</span>
+ </div>
+ <div class="stats" id="stats"></div>
+ <div class="controls">
+ <label class="search">
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
+ <input id="q" type="text" placeholder="Search key, provider, or note…" autocomplete="off" spellcheck="false">
+ </label>
+ <select id="sort" aria-label="Sort">
+ <option value="attn">Sort: Attention first</option>
+ <option value="prov">Sort: Provider A→Z</option>
+ <option value="key">Sort: Key A→Z</option>
+ <option value="type">Sort: Type</option>
+ </select>
+ <div class="seg" role="group" aria-label="Density">
+ <button id="d-comf" aria-pressed="true">Comfortable</button>
+ <button id="d-comp" aria-pressed="false">Compact</button>
+ </div>
+ </div>
+ <div class="chips" id="chips"></div>
+ </header>
+
+ <main id="main"></main>
+</div>
+
+<div class="launchbar" id="lbar">
+ <div class="lb-inner">
+ <div class="lb-txt">
+ <div class="lb-k" id="lb-k">—</div>
+ <div class="lb-p" id="lb-p"></div>
+ </div>
+ <a class="lb-go" id="lb-go" target="_blank" rel="noopener" aria-disabled="true">Launch ↗</a>
+ <button class="lb-close" id="lb-close" aria-label="Clear selection">✕</button>
+ </div>
+</div>
+
+<script type="application/json" id="creds">
+[
+ {"key":"IG_AGENT_AUTH","label":"Norma instagram-agent HTTP Authorization header for DW reels publisher","provider":"Instagram","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"SI_API_KEY","label":"api.data.gov key (Smithsonian Open Access + other data.gov APIs) — CelebritySignatures artists harvest; account steve@designerwallcoverings.com, created 2025-08-25","provider":"data.gov / Smithsonian","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"PRINTIFY_API_TOKEN","label":"Printify personal access token — CelebritySignatures standalone wear fulfillment","provider":"Printify","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"PG_DW_ADMIN_PASSWORD","label":"Postgres dw_admin role password (local Mac2 + prod via prestage 3b). DSN_REWRITE rewrites DATABASE_URL pw in each env_file dest.","provider":"PostgreSQL (local/Kamatera)","type":"internal","status":"registered","dest":"4 project/env files"},
+ {"key":"REPLICATE_API_TOKEN","label":"Replicate API Token — wallco.ai SDXL gen fallback + animate-museum-posts + interiordesignershowroom scene/hero regen","provider":"Replicate","type":"external_api","status":"registered","dest":"3 project/env files, ssh to Kamatera (1)"},
+ {"key":"GOOGLE_PLACES_API_KEY_GOVARB","label":"Google Places API Key - govarbitrage ONLY (Places API New)","provider":"Google Cloud (Places)","type":"external_api","status":"registered","dest":"1 project/env file [clean replacement for a prior leaked shared key]"},
+ {"key":"GOOGLE_PLACES_API_KEY_RESTAURANT","label":"Google Places API Key - restaurant-directory ONLY (Places API Legacy)","provider":"Google Cloud (Places)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"BRAVE_SEARCH_API_KEY","label":"Brave Search API (Web)","provider":"Brave Search","type":"external_api","status":"registered","dest":"2 project/env files"},
+ {"key":"MOONSHOT_API_KEY","label":"Moonshot AI / Kimi K2 API Key","provider":"Moonshot AI (Kimi)","type":"external_api","status":"registered","dest":"1 project/env file, 1 skill env, ssh to Kamatera (2)"},
+ {"key":"CLOUDFLARE_API_TOKEN","label":"Cloudflare API Token","provider":"Cloudflare","type":"external_api","status":"registered","dest":"7 project/env files, 3 skill envs"},
+ {"key":"STRIPE_TEST_SECRET_KEY","label":"Stripe test secret key — Charge & Explore test-mode billing only","provider":"Stripe","type":"external_api","status":"registered","dest":"ssh to Kamatera (1)"},
+ {"key":"STRIPE_TEST_WEBHOOK_SECRET","label":"Stripe test webhook signing secret — Charge & Explore","provider":"Stripe","type":"external_api","status":"registered","dest":"ssh to Kamatera (1)"},
+ {"key":"STRIPE_SECRET_KEY","label":"Stripe Secret Key","provider":"Stripe","type":"external_api","status":"registered","dest":"10 project/env files, 3 site-local domains, ssh to Kamatera (2)"},
+ {"key":"STRIPE_PUBLISHABLE_KEY","label":"Stripe Publishable Key","provider":"Stripe","type":"external_api","status":"registered","dest":"6 project/env files, 3 site-local domains, ssh to Kamatera (2)"},
+ {"key":"STRIPE_WEBHOOK_SECRET","label":"Stripe Webhook Signing Secret","provider":"Stripe","type":"external_api","status":"registered","dest":"2 project/env files, 3 site-local domains"},
+ {"key":"STRIPE_TIER_WEBHOOK_SECRET","label":"Stripe Tier-Subscription Webhook Signing Secret (Counsel & Bar Lawyer Pro $29/mo)","provider":"Stripe","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"STRIPE_PRICE_LAWYER_PRO_MONTHLY","label":"Stripe Price ID (public): Counsel & Bar Lawyer Pro $29/mo recurring","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"STRIPE_VCL_WEBHOOK_SECRET","label":"Stripe webhook signing secret — VCL endpoint we_1TUF1Y63uNmiRsMbNr1kLu52","provider":"Stripe","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_PRICE_VCL_STARTER_MONTH","label":"Stripe Price ID — VCL Starter $49/mo","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_PRICE_VCL_STANDARD_MONTH","label":"Stripe Price ID — VCL Standard $99/mo","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_PRICE_VCL_PREMIER_MONTH","label":"Stripe Price ID — VCL Premier $199/mo","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"HEYGEN_API_KEY","label":"HeyGen API key — talking-head avatar video render (session-debrief skill)","provider":"HeyGen","type":"external_api","status":"registered","dest":"4 project/env files, 4 skill envs"},
+ {"key":"INFO_VENTURACLAW_COM_PASSWORD","label":"Purelymail mailbox password — info@venturaclaw.com","provider":"PurelyMail","type":"internal","status":"registered","dest":"2 project/env files, ssh to Kamatera (2)"},
+ {"key":"INFO_NATIONALPAPERHANGERS_COM_PASSWORD","label":"Purelymail mailbox password — info@nationalpaperhangers.com","provider":"PurelyMail","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_NPH_WEBHOOK_SECRET","label":"Stripe Webhook Signing Secret — NPH endpoint we_1TUDHV63uNmiRsMbRJea7EqU","provider":"Stripe","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_PRICE_PRO_MONTH","label":"Stripe Price ID — NPH Pro $39/mo","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_PRICE_PRO_YEAR","label":"Stripe Price ID — NPH Pro $399/yr","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_PRICE_SIGNATURE_MONTH","label":"Stripe Price ID — NPH Signature $149/mo","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_PRICE_SIGNATURE_YEAR","label":"Stripe Price ID — NPH Signature $1500/yr","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"STRIPE_PRICE_ENTERPRISE_MONTH","label":"Stripe Price ID — NPH Enterprise $399/mo","provider":"Stripe","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"VERCEL_TOKEN","label":"Vercel API Token","provider":"Vercel","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"PURELYMAIL_API_TOKEN","label":"PurelyMail API Token","provider":"PurelyMail","type":"external_api","status":"registered","dest":"3 project/env files, 1 MCP server"},
+ {"key":"GODADDY_API_KEY","label":"GoDaddy API Key","provider":"GoDaddy","type":"external_api","status":"registered","dest":"1 MCP server"},
+ {"key":"GODADDY_API_SECRET","label":"GoDaddy API Secret","provider":"GoDaddy","type":"external_api","status":"registered","dest":"1 MCP server"},
+ {"key":"BROWSERBASE_API_KEY","label":"Browserbase API Key","provider":"Browserbase","type":"external_api","status":"registered","dest":"5 project/env files, 1 skill env, ssh to Kamatera (1)"},
+ {"key":"BROWSERBASE_PROJECT_ID","label":"Browserbase Project ID","provider":"Browserbase","type":"internal","status":"registered","dest":"5 project/env files, 1 skill env, ssh to Kamatera (1)"},
+ {"key":"ANTHROPIC_API_KEY","label":"Anthropic API Key — COMPROMISED via git history in DW-Agents/ecosystem.config.cjs. Rotate at console.anthropic.com, then route via secrets skill + set on Kamatera /root/DW-Agents/.env.","provider":"Anthropic","type":"external_api","status":"needs_rotation","dest":"2 project/env files"},
+ {"key":"OPENAI_API_KEY","label":"OpenAI API Key","provider":"OpenAI","type":"external_api","status":"registered","dest":"8 project/env files, ssh to Kamatera (3)"},
+ {"key":"GEMINI_API_KEY","label":"Google Gemini API Key","provider":"Google AI Studio (Gemini)","type":"external_api","status":"registered","dest":"2 project/env files, 2 skill envs"},
+ {"key":"GEMINI_API_KEY_DW_ENRICH","label":"Gemini key DEDICATED to DW image-enrichment / vendor-command-center. Pre-staged empty; rotation target for prior leaked orphan keys.","provider":"Google AI Studio (Gemini)","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1) [pre-staged rotation alias]"},
+ {"key":"GEMINI_API_KEY_WALLCO","label":"Gemini key DEDICATED to wallco.ai gen pipeline (nano-banana / gemini-2.5-flash-image fallback).","provider":"Google AI Studio (Gemini)","type":"external_api","status":"registered","dest":"2 project/env files, ssh to Kamatera (1)"},
+ {"key":"GOOGLE_GENERATIVE_AI_API_KEY","label":"Google Generative AI API Key (alias of GEMINI_API_KEY)","provider":"Google AI Studio (Gemini)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"SHOPIFY_ADMIN_TOKEN","label":"Shopify Admin API Access Token — COMPROMISED via git history in DW-Agents/ecosystem.config.cjs. Regenerate in the DW store admin (Apps → Develop apps).","provider":"Shopify","type":"external_api","status":"needs_rotation","dest":"5 project/env files"},
+ {"key":"SHOPIFY_ADMIN_ACCESS_TOKEN","label":"Shopify Admin token — ACCESS-name alias of SHOPIFY_ADMIN_TOKEN (same DW live store). Rotate TOGETHER with SHOPIFY_ADMIN_TOKEN.","provider":"Shopify","type":"external_api","status":"alias_rotate","dest":"2 project/env files"},
+ {"key":"SHOPIFY_API_KEY","label":"Shopify API Key","provider":"Shopify","type":"external_api","status":"registered","dest":"no destinations registered"},
+ {"key":"RESEND_API_KEY","label":"Resend API Key","provider":"Resend","type":"external_api","status":"registered","dest":"no destinations registered"},
+ {"key":"STRIPE_RESTRICTED_KEY","label":"Stripe Restricted Key (site-factory)","provider":"Stripe","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"DPLA_API_KEY","label":"Digital Public Library of America API Key","provider":"DPLA","type":"external_api","status":"registered","dest":"3 project/env files, 1 skill env"},
+ {"key":"GEMINI_API_KEY_RALPH","label":"Gemini key (dedicated to Letsbegin Ralph runner)","provider":"Google AI Studio (Gemini)","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"ELEVENLABS_API_KEY","label":"ElevenLabs Voice Synthesis API Key","provider":"ElevenLabs","type":"external_api","status":"registered","dest":"2 project/env files, ssh to Kamatera (1)"},
+ {"key":"NEXT_PUBLIC_GOOGLE_MAPS_API_KEY","label":"Google Maps JavaScript API Key (browser-exposed; needs HTTP-referrer restrictions)","provider":"Google Cloud (Maps)","type":"external_api","status":"registered","dest":"6 project/env files, ssh to Kamatera (2)"},
+ {"key":"LINKEDIN_CLIENT_ID","label":"LinkedIn OAuth Client ID — 'For Claude - 5-6-26' app, bound to Designer Wallcoverings Page","provider":"LinkedIn","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"LINKEDIN_CLIENT_SECRET","label":"LinkedIn OAuth Client Secret — 'For Claude - 5-6-26' app","provider":"LinkedIn","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"GOOGLE_OAUTH_CLIENT_ID","label":"Google OAuth Client ID — DW Activation Calendar (local :9765)","provider":"Google Cloud (OAuth)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"GOOGLE_OAUTH_CLIENT_SECRET","label":"Google OAuth Client Secret — DW Activation Calendar (local :9765)","provider":"Google Cloud (OAuth)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"NPH_GOOGLE_OAUTH_CLIENT_ID","label":"Google OAuth Client ID — buyer sign-in on National Paper Hangers","provider":"Google Cloud (OAuth)","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"NPH_GOOGLE_OAUTH_CLIENT_SECRET","label":"Google OAuth Client Secret — buyer sign-in on National Paper Hangers","provider":"Google Cloud (OAuth)","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"GEORGE_AUTH","label":"George Gmail Basic Auth (DW microsite leads)","provider":"George Gmail relay (own service)","type":"internal","status":"registered","dest":"52 project/env files"},
+ {"key":"CLOUDFLARE_PAGES_TOKEN","label":"Cloudflare Pages API Token","provider":"Cloudflare","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"SHOPIFY_THEME_TOKEN","label":"Shopify full-access theme/files token (read_themes+files)","provider":"Shopify","type":"external_api","status":"registered","dest":"3 project/env files"},
+ {"key":"SLACK_APP_ID","label":"Slack app 'Claude Access to Slack' — App ID (A0BDER23QRK). Not secret; registered for completeness.","provider":"Slack","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"SLACK_CLIENT_ID","label":"Slack app Client ID — used with Client Secret in oauth.v2.access requests.","provider":"Slack","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"SLACK_CLIENT_SECRET","label":"Slack app Client Secret — OAuth token exchange. NOT the bot token.","provider":"Slack","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"SLACK_SIGNING_SECRET","label":"Slack app Signing Secret — verifies inbound request signatures.","provider":"Slack","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"SLACK_VERIFICATION_TOKEN","label":"Slack DEPRECATED verification token — prefer SLACK_SIGNING_SECRET.","provider":"Slack","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"SLACK_APP_TOKEN","label":"Slack app-level token (xapp-1-…) for Socket Mode — connections:write. NOT the bot token.","provider":"Slack","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"CENSUS_API_KEY","label":"US Census Bureau Data API key — commercialrealestate ACS5 by ZCTA. Verifier can't probe (302); verify via ACS5 curl.","provider":"US Census Bureau","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"POSTIZ_API_KEY","label":"Postiz public API key — DW marketing slideshow engine + Postiz MCP","provider":"Postiz","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"X_API_KEY","label":"X (Twitter) API v2 — Consumer/API key. Pairs with X_API_SECRET.","provider":"X (Twitter)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"X_API_SECRET","label":"X (Twitter) API v2 — Consumer/API secret.","provider":"X (Twitter)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"X_BEARER_TOKEN","label":"X (Twitter) API v2 — app-only Bearer token (use RAW/as-issued).","provider":"X (Twitter)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"MAGIC_API_KEY","label":"21st.dev Magic (Four Horsemen) API Key","provider":"21st.dev Magic","type":"external_api","status":"registered","dest":"1 MCP server"},
+ {"key":"ADOBE_CLIENT_ID","label":"Adobe Firefly Services / Photoshop API client id (model-arena)","provider":"Adobe","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"ADOBE_CLIENT_SECRET","label":"Adobe Firefly Services / Photoshop API client secret (model-arena)","provider":"Adobe","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"FIGMA_TOKEN","label":"Figma personal access token, read-only (model-arena + hyperframes imports)","provider":"Figma","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"BUTLR_OWNER_TOKEN","label":"BUTLR_OWNER_TOKEN","provider":"Butlr","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"DEEPSEEK_API_KEY","label":"DeepSeek platform API key — DeepSeek Harness CLI (dsh)","provider":"DeepSeek","type":"external_api","status":"registered","dest":"no destinations registered"},
+ {"key":"TYPESAFE_API_KEY","label":"TypeSafe (typesafe.ai) Jev /v1/systemone API key — jev-ultrafast; created 2026-09-22","provider":"TypeSafe","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"TEXT_MODEL_API_KEY","label":"OpenRouter API key (sk-or-v1) — jev-ultrafast TYPE_TEXT helper; created 2026-09-22","provider":"OpenRouter","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"CLOUDFLARE_ACCOUNT_ID","label":"Cloudflare Account ID (identifier, not secret)","provider":"Cloudflare","type":"internal","status":"registered","dest":"7 project/env files"},
+ {"key":"SMB_BUILDER_ADMIN_TOKEN","label":"Small Business Builder admin token (own app)","provider":"Small Business Builder (own app)","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"SHOPIFY_ORDERS_TOKEN","label":"Shopify Admin token (orders+products scope, weekend-report1-1 Partner app)","provider":"Shopify","type":"external_api","status":"registered","dest":"1 skill env"},
+ {"key":"SPOONFLOWER_EMAIL","label":"Spoonflower login email","provider":"Spoonflower (login)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"SPOONFLOWER_PASSWORD","label":"Spoonflower login password","provider":"Spoonflower (login)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"GOOGLE_DRIVE_CLIENT_ID","label":"Google Drive OAuth Client ID — TSD photo-import-cli desktop app","provider":"Google Cloud (Drive OAuth)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"GOOGLE_DRIVE_CLIENT_SECRET","label":"Google Drive OAuth Client Secret — TSD photo-import-cli","provider":"Google Cloud (Drive OAuth)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"GOOGLE_DRIVE_REFRESH_TOKEN","label":"Google Drive OAuth refresh token — read-only, steveabramsdesigns@gmail.com","provider":"Google Cloud (Drive OAuth)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"TWILIO_ACCOUNT_SID","label":"Twilio Account SID","provider":"Twilio","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"TWILIO_AUTH_TOKEN","label":"Twilio Auth Token","provider":"Twilio","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"TWILIO_PHONE_NUMBER","label":"Twilio phone number (identifier)","provider":"Twilio","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"TMDB_API_KEY","label":"TMDB v3 API key — asseeninmovies Phase B drain","provider":"TMDB","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"TMDB_READ_ACCESS_TOKEN","label":"TMDB v4 read-access token (bearer) — alt to v3 api_key","provider":"TMDB","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"VAPI_API_KEY","label":"Vapi API key","provider":"Vapi","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"VAPI_PUBLIC_KEY","label":"Vapi public key","provider":"Vapi","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"AYRSHARE_API_KEY","label":"Ayrshare API key (social posting)","provider":"Ayrshare","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"META_ACCESS_TOKEN","label":"Meta Graph API access token","provider":"Meta","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"META_IG_USER_ID","label":"Meta / Instagram user id (identifier)","provider":"Meta","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"META_FB_PAGE_ID","label":"Meta / Facebook page id (identifier)","provider":"Meta","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"SHOPIFY_DRAFT_TOKEN","label":"Shopify Admin token (write_draft_orders) — DW mural PDP custom-size checkout","provider":"Shopify","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"SHOPIFY_FULL_ACCESS_TOKEN","label":"Shopify Admin token — DW full Online Store pages, content, navigation, theme","provider":"Shopify","type":"external_api","status":"registered","dest":"2 project/env files"},
+ {"key":"SHOPIFY_STORE_DOMAIN","label":"Shopify store domain for DW vendor microsites Memo-Sample checkout (identifier)","provider":"Shopify","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"SHOPIFY_SAMPLE_VARIANT_ID","label":"Shopify Memo Sample variant id (identifier)","provider":"Shopify","type":"internal","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"SHOPIFY_STOREFRONT_TOKEN","label":"Shopify Storefront API token for DW vendor microsites (unauthenticated checkout)","provider":"Shopify","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"TWOCAPTCHA_API_KEY","label":"2Captcha API key (captcha solving for trade-portal scrapers)","provider":"2Captcha","type":"external_api","status":"registered","dest":"2 skill envs"},
+ {"key":"DEDAR_LOGIN_URL","label":"Dedar trade-portal login URL (supervised outbound-tier)","provider":"Dedar (trade portal)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"DEDAR_USERNAME","label":"Dedar trade-portal username","provider":"Dedar (trade portal)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"DEDAR_PASSWORD","label":"Dedar trade-portal password","provider":"Dedar (trade portal)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"YORKWALL_USER","label":"York Wallcoverings trade-portal username","provider":"York Wallcoverings (trade portal)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"YORKWALL_PASS","label":"York Wallcoverings trade-portal password","provider":"York Wallcoverings (trade portal)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"GOVAUCTIONS_API_KEY","label":"GovAuctions API key","provider":"GovAuctions","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"APIFY_TOKEN","label":"Apify API token","provider":"Apify","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"IG_USER_ID","label":"Instagram user id (identifier)","provider":"Instagram","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"TIKTOK_CLIENT_KEY","label":"TikTok client key","provider":"TikTok","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"TIKTOK_CLIENT_SECRET","label":"TikTok client secret","provider":"TikTok","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"THREADS_APP_ID","label":"Meta Threads app id (identifier)","provider":"Meta (Threads)","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"THREADS_APP_SECRET","label":"Meta Threads app secret","provider":"Meta (Threads)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"THREADS_ACCESS_TOKEN","label":"Meta Threads access token","provider":"Meta (Threads)","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"THREADS_USER_ID","label":"Meta Threads user id (identifier)","provider":"Meta (Threads)","type":"internal","status":"registered","dest":"1 project/env file"},
+ {"key":"DATAFORSEO_API_KEY","label":"DataForSEO API key (base64 login:api-password) — OpenSEO backend","provider":"DataForSEO","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"OSBORNE_USERNAME","label":"Osborne & Little trade portal (tradenew.osborneandlittle.com) login","provider":"Osborne & Little (trade portal)","type":"internal","status":"registered","dest":"1 skill env"},
+ {"key":"OSBORNE_PASSWORD","label":"Osborne & Little trade portal password","provider":"Osborne & Little (trade portal)","type":"internal","status":"registered","dest":"1 skill env"},
+ {"key":"OPENROUTER_API_KEY","label":"OpenRouter API key","provider":"OpenRouter","type":"external_api","status":"registered","dest":"1 project/env file"},
+ {"key":"SHOPIFY_DW_APP_CLIENT_SECRET","label":"Shopify DW app API secret (OAuth + webhook HMAC). Rotated 2026-08-10.","provider":"Shopify","type":"external_api","status":"registered","dest":"master .env only"},
+ {"key":"SHOPIFY_FULFILLMENT_TOKEN","label":"Shopify dw-signup-fulfillment offline Admin API token.","provider":"Shopify","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"SHOPIFY_SIGNUP_APP_CLIENT_SECRET","label":"Shopify dw-signup-fulfillment installed app secret — OAuth + webhook HMAC.","provider":"Shopify","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"SHOPIFY_FREE_SAMPLES_APP_CLIENT_SECRET","label":"Shopify DW Free Samples app secret — orders/paid webhook HMAC.","provider":"Shopify","type":"external_api","status":"registered","dest":"1 project/env file, ssh to Kamatera (1)"},
+ {"key":"XAI_API_KEY","label":"xAI / Grok API key","provider":"xAI (Grok)","type":"external_api","status":"registered","dest":"ssh to Kamatera (2)"},
+ {"key":"SLACK_BOT_TOKEN","label":"Slack bot token (dw_reports_bot @ Designerwallcoverings, bot_id B0BC0EB3GPP) — slack MCP server. Rotated + verified live 2026-09-22.","provider":"Slack","type":"external_api","status":"rotated_live_today","dest":"1 MCP server"},
+ {"key":"INNOVATIONS_USERNAME","label":"Innovations USA trade-portal username (innovationsusa.com, DW acct 58315 'Net Less 20%').","provider":"Innovations USA (trade portal)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"INNOVATIONS_PASSWORD","label":"Innovations USA trade-portal password (DW acct 58315).","provider":"Innovations USA (trade portal)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"INNOVATIONS_LOGIN_URL","label":"Innovations USA trade-portal login URL (https://www.innovationsusa.com/login; reCAPTCHA, use browserbase+2captcha).","provider":"Innovations USA (trade portal)","type":"internal","status":"registered","dest":"2 project/env files"},
+ {"key":"DW_SESSION_SECRET","label":"DW room-setting-app session secret (own app)","provider":"DW room-setting-app (own app)","type":"internal","status":"registered","dest":"1 project/env file"}
+]
+</script>
+
+<script>
+(function(){
+ "use strict";
+ var LS_DONE="credlauncher.done.v1", LS_PREF="credlauncher.pref.v1";
+
+ // provider -> its actual console / key-management page. "" = no external console.
+ var CONSOLE = {
+ "Anthropic":"https://console.anthropic.com/settings/keys",
+ "OpenAI":"https://platform.openai.com/api-keys",
+ "Slack":"https://api.slack.com/apps/A0BDER23QRK",
+ "Shopify":"https://admin.shopify.com/store/designer-laboratory-sandbox/settings/apps/development",
+ "Google AI Studio (Gemini)":"https://aistudio.google.com/app/apikey",
+ "Google Cloud (Places)":"https://console.cloud.google.com/apis/credentials",
+ "Google Cloud (Maps)":"https://console.cloud.google.com/apis/credentials",
+ "Google Cloud (OAuth)":"https://console.cloud.google.com/apis/credentials",
+ "Google Cloud (Drive OAuth)":"https://console.cloud.google.com/apis/credentials",
+ "Cloudflare":"https://dash.cloudflare.com/profile/api-tokens",
+ "Stripe":"https://dashboard.stripe.com/apikeys",
+ "Twilio":"https://console.twilio.com/us1/account/keys-credentials/api-keys",
+ "Replicate":"https://replicate.com/account/api-tokens",
+ "ElevenLabs":"https://elevenlabs.io/app/settings/api-keys",
+ "HeyGen":"https://app.heygen.com/settings?nav=API",
+ "Vercel":"https://vercel.com/account/tokens",
+ "PurelyMail":"https://purelymail.com/manage/account",
+ "GoDaddy":"https://developer.godaddy.com/keys",
+ "Browserbase":"https://www.browserbase.com/settings",
+ "LinkedIn":"https://www.linkedin.com/developers/apps",
+ "Brave Search":"https://api-dashboard.search.brave.com/app/keys",
+ "Moonshot AI (Kimi)":"https://platform.moonshot.ai/console/api-keys",
+ "xAI (Grok)":"https://console.x.ai/",
+ "OpenRouter":"https://openrouter.ai/keys",
+ "DeepSeek":"https://platform.deepseek.com/api_keys",
+ "Figma":"https://www.figma.com/settings",
+ "Adobe":"https://developer.adobe.com/console/projects",
+ "Printify":"https://printify.com/app/account/api",
+ "Vapi":"https://dashboard.vapi.ai/keys",
+ "TMDB":"https://www.themoviedb.org/settings/api",
+ "X (Twitter)":"https://developer.x.com/en/portal/dashboard",
+ "Meta":"https://developers.facebook.com/apps/",
+ "Meta (Threads)":"https://developers.facebook.com/apps/",
+ "Instagram":"https://developers.facebook.com/apps/",
+ "TikTok":"https://developers.tiktok.com/apps/",
+ "Postiz":"https://app.postiz.com/settings",
+ "Ayrshare":"https://app.ayrshare.com/api",
+ "Apify":"https://console.apify.com/account/integrations/api",
+ "2Captcha":"https://2captcha.com/enterpage",
+ "DataForSEO":"https://app.dataforseo.com/api-access",
+ "US Census Bureau":"https://api.census.gov/data/key_signup.html",
+ "data.gov / Smithsonian":"https://api.data.gov/signup/",
+ "DPLA":"https://pro.dp.la/developers/policies",
+ "21st.dev Magic":"https://21st.dev/settings/billing",
+ "TypeSafe":"https://typesafe.ai/",
+ "Butlr":"https://portal.butlr.io/",
+ "Resend":"https://resend.com/api-keys",
+ "GovAuctions":"",
+ "Spoonflower (login)":"https://www.spoonflower.com/login",
+ "Osborne & Little (trade portal)":"https://tradenew.osborneandlittle.com/",
+ "Innovations USA (trade portal)":"https://www.innovationsusa.com/login",
+ "Dedar (trade portal)":"",
+ "York Wallcoverings (trade portal)":"",
+ "PostgreSQL (local/Kamatera)":"",
+ "George Gmail relay (own service)":"",
+ "Small Business Builder (own app)":"",
+ "DW room-setting-app (own app)":""
+ };
+ var SELF_MANAGED = {
+ "PostgreSQL (local/Kamatera)":1,"George Gmail relay (own service)":1,
+ "Small Business Builder (own app)":1,"DW room-setting-app (own app)":1
+ };
+ function launchFor(rec){
+ var u = CONSOLE[rec.provider];
+ if(u) return {url:u, kind:(rec.type==="internal" && /trade portal|login/.test(rec.provider))?"login":"console"};
+ if(SELF_MANAGED[rec.provider]) return {url:"", kind:"none"};
+ return {url:"https://www.google.com/search?q="+encodeURIComponent(rec.provider+" API key console"), kind:"search"};
+ }
+
+ var STATUS_RANK = {needs_rotation:0, alias_rotate:1, rotated_live_today:2, registered:3};
+ function statusBadge(s){
+ if(s==="needs_rotation") return {cls:"rot", txt:"Needs rotation"};
+ if(s==="alias_rotate") return {cls:"alias", txt:"Rotate w/ Shopify token"};
+ if(s==="rotated_live_today") return {cls:"live", txt:"Rotated today"};
+ return null;
+ }
+
+ var DATA;
+ try { DATA = JSON.parse(document.getElementById("creds").textContent); }
+ catch(e){ DATA = []; }
+
+ // ---- persisted prefs / done set ----
+ var pref = {filter:"all", sort:"attn", q:"", compact:false};
+ try{ var p=JSON.parse(localStorage.getItem(LS_PREF)||"{}"); Object.assign(pref,p);}catch(e){}
+ var done = {};
+ try{ done = JSON.parse(localStorage.getItem(LS_DONE)||"{}")||{}; }catch(e){}
+ function savePref(){ try{ localStorage.setItem(LS_PREF, JSON.stringify(pref)); }catch(e){} }
+ function saveDone(){ try{ localStorage.setItem(LS_DONE, JSON.stringify(done)); }catch(e){} }
+
+ var selKey = null;
+
+ // ---- header stats ----
+ function renderStats(){
+ var ext=0,int=0,rot=0,live=0;
+ DATA.forEach(function(r){
+ if(r.type==="external_api") ext++; else int++;
+ if(r.status==="needs_rotation"||r.status==="alias_rotate") rot++;
+ if(r.status==="rotated_live_today") live++;
+ });
+ var el=document.getElementById("stats");
+ el.innerHTML =
+ stat("", DATA.length+" total")+
+ stat("ext", "<b>"+ext+"</b> external API")+
+ stat("int", "<b>"+int+"</b> internal")+
+ (rot?stat("warn","<b>"+rot+"</b> need attention"):"")+
+ (live?stat("good","<b>"+live+"</b> rotated today"):"");
+ }
+ function stat(dot,html){ return '<span class="stat">'+(dot?'<span class="dot '+dot+'"></span>':'')+html+'</span>'; }
+
+ // ---- filter chips (dynamic providers with counts) ----
+ var FILTERS;
+ function buildFilters(){
+ var provCounts={};
+ DATA.forEach(function(r){ provCounts[r.provider]=(provCounts[r.provider]||0)+1; });
+ FILTERS=[
+ {id:"all", label:"All", n:DATA.length},
+ {id:"attn", label:"Needs attention", n:DATA.filter(function(r){return r.status==="needs_rotation"||r.status==="alias_rotate";}).length},
+ {id:"external_api", label:"External APIs", n:DATA.filter(function(r){return r.type==="external_api";}).length},
+ {id:"internal", label:"Internal", n:DATA.filter(function(r){return r.type==="internal";}).length}
+ ];
+ var el=document.getElementById("chips");
+ el.innerHTML = FILTERS.map(function(f){
+ return '<button class="chip" data-f="'+f.id+'" aria-pressed="'+(pref.filter===f.id)+'">'+f.label+'<span class="n">'+f.n+'</span></button>';
+ }).join("");
+ el.querySelectorAll(".chip").forEach(function(b){
+ b.addEventListener("click",function(){ pref.filter=b.getAttribute("data-f"); savePref(); syncChips(); render(); });
+ });
+ }
+ function syncChips(){
+ document.querySelectorAll("#chips .chip").forEach(function(b){
+ b.setAttribute("aria-pressed", String(pref.filter===b.getAttribute("data-f")));
+ });
+ }
+
+ function esc(s){ return String(s==null?"":s).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];}); }
+
+ function passFilter(r){
+ if(pref.filter==="all") return true;
+ if(pref.filter==="attn") return r.status==="needs_rotation"||r.status==="alias_rotate";
+ return r.type===pref.filter;
+ }
+ function passSearch(r){
+ var q=pref.q.trim().toLowerCase(); if(!q) return true;
+ return (r.key+" "+r.provider+" "+(r.label||"")).toLowerCase().indexOf(q)>=0;
+ }
+ function sortRows(a,b){
+ if(pref.sort==="attn"){
+ var d=STATUS_RANK[a.status]-STATUS_RANK[b.status]; if(d) return d;
+ return a.provider.localeCompare(b.provider)||a.key.localeCompare(b.key);
+ }
+ if(pref.sort==="prov") return a.provider.localeCompare(b.provider)||a.key.localeCompare(b.key);
+ if(pref.sort==="key") return a.key.localeCompare(b.key);
+ if(pref.sort==="type") return a.type.localeCompare(b.type)||a.provider.localeCompare(b.provider);
+ return 0;
+ }
+
+ function rowHTML(r){
+ var lf=launchFor(r), b=statusBadge(r.status);
+ var isDone=!!done[r.key];
+ var right;
+ if(lf.kind==="none"){
+ right='<span class="nocon">self-managed<br>no console</span>';
+ } else {
+ var lbl = lf.kind==="login"?"Log in ↗" : lf.kind==="search"?"Find console ↗" : "Launch ↗";
+ var cls = lf.kind==="console"?"launch":"launch ghost";
+ right='<a class="'+cls+'" href="'+esc(lf.url)+'" target="_blank" rel="noopener" data-launch="1">'+lbl+'</a>';
+ }
+ right += '<button class="donebtn" data-done="'+esc(r.key)+'"><span class="doneck">✓</span>'+(isDone?"done":"mark done")+'</button>';
+ var badges='<span class="badge '+(r.type==="external_api"?"":"int")+'" style="'+(r.type==="external_api"?"display:none":"")+'">internal</span>';
+ if(b) badges='<span class="badge '+b.cls+'">'+b.txt+'</span>'+(r.type==="internal"?'<span class="badge int">internal</span>':'');
+ else if(r.type==="internal") badges='<span class="badge int">internal</span>';
+ else badges='';
+ return '<div class="row'+(selKey===r.key?" sel":"")+((r.status==="needs_rotation"||r.status==="alias_rotate")?" attn":"")+(isDone?" done":"")+'" data-key="'+esc(r.key)+'" tabindex="0">'+
+ '<span class="rmark">✓</span>'+
+ '<div class="rmid">'+
+ '<div class="kname">'+esc(r.key)+'</div>'+
+ '<div class="meta"><span class="prov">'+esc(r.provider)+'</span>'+badges+'</div>'+
+ (r.label?'<div class="label">'+esc(r.label)+'</div>':'')+
+ '<div class="dest">→ '+esc(r.dest)+'</div>'+
+ '</div>'+
+ '<div class="rright">'+right+'</div>'+
+ '</div>';
+ }
+
+ function render(){
+ var rows=DATA.filter(function(r){return passFilter(r)&&passSearch(r);}).slice().sort(sortRows);
+ var main=document.getElementById("main");
+ if(!rows.length){ main.innerHTML='<div class="empty">No credentials match.</div>'; return; }
+
+ var attn=rows.filter(function(r){return r.status==="needs_rotation"||r.status==="alias_rotate";});
+ var rest=rows.filter(function(r){return !(r.status==="needs_rotation"||r.status==="alias_rotate");});
+ var html="";
+ if(attn.length && pref.sort==="attn" && pref.filter!=="internal"){
+ html+='<div class="sec-label attn"><span>⚠ Action needed — '+attn.length+'</span><span class="rule"></span></div>';
+ html+='<div class="list">'+attn.map(rowHTML).join("")+'</div>';
+ if(rest.length){
+ html+='<div class="sec-label"><span>All credentials — '+rest.length+'</span><span class="rule"></span></div>';
+ html+='<div class="list">'+rest.map(rowHTML).join("")+'</div>';
+ }
+ } else {
+ html+='<div class="list">'+rows.map(rowHTML).join("")+'</div>';
+ }
+ main.innerHTML=html;
+ wireRows();
+ }
+
+ function wireRows(){
+ document.querySelectorAll(".row").forEach(function(row){
+ row.addEventListener("click",function(ev){
+ if(ev.target.closest("[data-launch]")) return; // let launch anchor work
+ if(ev.target.closest("[data-done]")){ toggleDone(ev.target.closest("[data-done]").getAttribute("data-done")); return; }
+ select(row.getAttribute("data-key"));
+ });
+ row.addEventListener("keydown",function(ev){
+ if(ev.key==="Enter"||ev.key===" "){ ev.preventDefault(); select(row.getAttribute("data-key")); }
+ });
+ });
+ }
+
+ function byKey(k){ for(var i=0;i<DATA.length;i++) if(DATA[i].key===k) return DATA[i]; return null; }
+
+ function select(k){
+ selKey = (selKey===k)?null:k;
+ document.querySelectorAll(".row").forEach(function(r){ r.classList.toggle("sel", r.getAttribute("data-key")===selKey); });
+ updateBar();
+ }
+ function updateBar(){
+ var bar=document.getElementById("lbar"), go=document.getElementById("lb-go");
+ if(!selKey){ bar.classList.remove("show"); return; }
+ var r=byKey(selKey), lf=launchFor(r);
+ document.getElementById("lb-k").textContent=r.key;
+ document.getElementById("lb-p").textContent=r.provider+(lf.kind==="none"?" · self-managed (no console)":lf.kind==="search"?" · console unknown — web search":"");
+ if(lf.url){ go.setAttribute("href",lf.url); go.setAttribute("aria-disabled","false");
+ go.textContent=(lf.kind==="login"?"Log in":lf.kind==="search"?"Search":"Launch")+" ↗"; }
+ else { go.removeAttribute("href"); go.setAttribute("aria-disabled","true"); go.textContent="No console"; }
+ bar.classList.add("show");
+ }
+ function toggleDone(k){
+ if(done[k]) delete done[k]; else done[k]=Date.now();
+ saveDone(); render();
+ if(selKey) updateBar();
+ }
+
+ // ---- controls ----
+ var qEl=document.getElementById("q"); qEl.value=pref.q||"";
+ qEl.addEventListener("input",function(){ pref.q=qEl.value; savePref(); render(); });
+ var sortEl=document.getElementById("sort"); sortEl.value=pref.sort;
+ sortEl.addEventListener("change",function(){ pref.sort=sortEl.value; savePref(); render(); });
+ function setCompact(on){
+ pref.compact=on; savePref();
+ document.body.classList.toggle("compact",on);
+ document.getElementById("d-comf").setAttribute("aria-pressed",String(!on));
+ document.getElementById("d-comp").setAttribute("aria-pressed",String(on));
+ }
+ document.getElementById("d-comf").addEventListener("click",function(){setCompact(false);});
+ document.getElementById("d-comp").addEventListener("click",function(){setCompact(true);});
+ document.getElementById("lb-close").addEventListener("click",function(){ selKey=null; document.querySelectorAll(".row.sel").forEach(function(r){r.classList.remove("sel");}); updateBar(); });
+
+ // ---- boot ----
+ renderStats(); buildFilters(); setCompact(!!pref.compact); render();
+})();
+</script>
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..94f5e8e
--- /dev/null
+++ b/server.js
@@ -0,0 +1,44 @@
+#!/usr/bin/env node
+// Credential Launcher — tiny static server, localhost only, kept alive by launchd.
+// Serves page.html (authored for the Artifact skeleton) wrapped in a standalone
+// HTML document so it renders in a normal browser at http://127.0.0.1:51858/.
+const http = require('http');
+const fs = require('fs');
+const path = require('path');
+
+const PORT = Number(process.env.PORT) || 51858;
+const HOST = '127.0.0.1';
+const PAGE = path.join(__dirname, 'page.html');
+
+const HEAD = '<!doctype html><html lang="en"><head>' +
+ '<meta charset="utf-8">' +
+ '<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">' +
+ '<style>html,body{margin:0}img{max-width:100%}[hidden]{display:none!important}</style>';
+
+function render() {
+ // Read fresh each request so editing page.html shows up on reload.
+ const body = fs.readFileSync(PAGE, 'utf8');
+ return HEAD + body + '</html>';
+}
+
+const server = http.createServer((req, res) => {
+ if (req.url === '/health') {
+ res.writeHead(200, { 'content-type': 'text/plain' });
+ return res.end('ok');
+ }
+ try {
+ const html = render();
+ res.writeHead(200, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' });
+ res.end(html);
+ } catch (e) {
+ res.writeHead(500, { 'content-type': 'text/plain' });
+ res.end('cred-launcher error: ' + e.message);
+ }
+});
+
+server.listen(PORT, HOST, () => {
+ console.log(`[${new Date().toISOString()}] cred-launcher listening on http://${HOST}:${PORT}/`);
+});
+
+// Fail loud but let launchd KeepAlive restart us.
+process.on('uncaughtException', (e) => { console.error('uncaught', e); process.exit(1); });
(oldest)
·
back to Cred Launcher
·
(newest)