storebound/site/desktop/index.html
CRT 236977940e flows, listing translate, scan 2.2 + --originality, prices export with subscriptions, site sections
- storebound flow list|validate|run: repo-local workflows (store/workflows/*.yml, storebound.workflows.*), writes dry unless --apply, owner steps never run, --from / --resume; init templates for app and game; MCP flow_list + dry flow_run; release-flows skill
- storebound listing translate|check|reviewed: per-language listing drafts with store limits and keyword rules, agent or --translate hook, marked until reviewed; MCP listing_translate / listing_check; listing-localization skill
- scan: Apple 2.2 beta/demo/test wording rule; scan --originality [--against]
- prices export includes subscriptions; prices import skips their rows
- site: rejection scan section, Desktop app section with fixture-mode screenshots, Everything else grid, FAQ 4.3 answer
- README, CHANGELOG, adapters, API tables
2026-10-12 01:24:21 +08:00

212 lines
13 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>storebound desktop · download</title>
<meta name="description" content="storebound desktop: every app and game on Google Play and the App Store in one window, driving the same storebound CLI. Downloads for macOS, Windows and Linux with checksums and signatures.">
<link rel="canonical" href="https://storebound.dev/desktop/">
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#f8fafc" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#0b1120" media="(prefers-color-scheme: dark)">
<link rel="icon" href="../favicon.ico?v=2" sizes="16x16 32x32 48x48">
<link rel="manifest" href="../manifest.webmanifest?v=2">
<link rel="apple-touch-icon" href="../apple-touch-icon.png?v=2">
<meta property="og:type" content="website">
<meta property="og:site_name" content="storebound">
<meta property="og:url" content="https://storebound.dev/desktop/">
<meta property="og:title" content="storebound desktop · download">
<meta property="og:description" content="Every app and game on both stores in one window. macOS, Windows, Linux.">
<meta property="og:image" content="https://storebound.dev/og.png?v=2">
<link rel="stylesheet" href="../style.css?v=20261012">
<script>document.documentElement.classList.add('js')</script>
<style>
/* page-only parts; colours and type come from ../style.css tokens (fallbacks keep the page readable alone) */
.dl-hero { padding: 56px 0 40px; }
.dl-hero h1 { font-size: clamp(30px, 5vw, 46px); font-weight: 760; }
.dl-main { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-top: 28px; }
.dl-main .meta { color: var(--muted, #64748b); font-size: 14.5px; }
.dl-notice { margin-top: 22px; max-width: 46em; padding: 14px 16px; border-radius: 12px; border: 1px solid var(--ui-ink, #92400e); background: var(--ui-bg, #fef3c7); color: var(--ui-ink, #92400e); font-size: 15px; }
.dl-notice b { font-weight: 700; }
.dl-notice ol { margin: 8px 0 0; padding-left: 20px; }
.dl-table { width: 100%; border-collapse: collapse; margin-top: 24px; font-size: 15px; }
.dl-table th, .dl-table td { text-align: left; padding: 12px 10px; border-bottom: 1px solid var(--line, #e2e8f0); vertical-align: top; }
.dl-table th { font: 600 12.5px var(--mono, monospace); text-transform: uppercase; letter-spacing: .08em; color: var(--muted, #64748b); }
.dl-table tr.here td { background: var(--accent-soft, #eef2ff); }
.dl-table .sha { font: 12.5px/1.5 var(--mono, monospace); color: var(--ink-2, #334155); overflow-wrap: anywhere; display: block; max-width: 34em; }
.dl-table .fname { font-weight: 600; overflow-wrap: anywhere; }
.dl-table .small { font-size: 13.5px; color: var(--muted, #64748b); }
.dl-steps { margin: 16px 0 0; padding-left: 20px; color: var(--ink-2, #334155); max-width: 50em; }
.dl-steps li { margin: 6px 0; }
.dl-key { display: block; margin-top: 8px; font: 13px/1.6 var(--mono, monospace); overflow-wrap: anywhere; }
@media (max-width: 720px) {
.dl-table thead { display: none; }
.dl-table, .dl-table tbody, .dl-table tr, .dl-table td { display: block; width: 100%; }
.dl-table tr { border-bottom: 1px solid var(--line, #e2e8f0); padding: 8px 0; }
.dl-table td { border: 0; padding: 4px 0; }
}
</style>
</head>
<body>
<a class="skip" href="#main">Skip to content</a>
<header class="top">
<div class="wrap top-in">
<a class="brand" href="../" aria-label="storebound home">
<img class="mark" src="../img/logo-mark.png?v=2" srcset="../img/logo-mark.png?v=2 1x, ../img/logo-mark@2x.png?v=2 2x" width="28" height="28" alt="storebound logo">
<span>storebound</span>
</a>
<nav aria-label="Primary">
<a href="../">Home</a>
<a href="#all">All downloads</a>
<a href="#verify">Verify</a>
<a class="nav-git" href="https://git.crt.fyi/CRT/storebound" target="_blank" rel="noopener">git<span class="ext" aria-hidden="true">↗</span><span class="sr-only"> (opens in a new tab)</span></a>
</nav>
</div>
</header>
<main id="main">
<section class="dl-hero">
<div class="wrap">
<p class="kicker">Desktop app · preview</p>
<h1>storebound desktop</h1>
<p class="sub">Every app and game you ship, on Google Play and the App Store, in one window: releases on both stores, catalog parity, listings, crash health, reviews and TestFlight. It drives the same open source storebound CLI, keeps store keys in the system keychain, shows a dry run before every change, and never presses Submit.</p>
<div class="dl-main" id="primary" aria-live="polite">
<p class="meta" id="status">Looking up the latest release…</p>
</div>
<noscript><p class="note">The download list needs JavaScript. Without it: <a href="latest.json">latest.json</a> lists every file with its SHA-256.</p></noscript>
<div id="notice"></div>
<p class="note">Already use the CLI? <code>storebound desktop</code> opens the app in your app folder, or prints the download for your system; <code>storebound desktop --download</code> saves the installer to ~/Downloads after checking its signature (it never installs anything).</p>
</div>
</section>
<section class="sec alt" id="all">
<div class="wrap">
<p class="kicker">All downloads</p>
<h2>Every system, with checksums</h2>
<p class="sub" id="all-sub">The app updates itself from this site: each update is signed, and the app checks the signature before it replaces anything. It asks before installing.</p>
<table class="dl-table" id="table" hidden>
<thead><tr><th scope="col">System</th><th scope="col">File</th><th scope="col">SHA-256</th></tr></thead>
<tbody id="rows"></tbody>
</table>
</div>
</section>
<section class="sec" id="verify">
<div class="wrap">
<p class="kicker">Verify</p>
<h2>Check a download yourself</h2>
<ol class="dl-steps">
<li>Checksum: <code>shasum -a 256 &lt;file&gt;</code> (macOS, Linux) or <code>Get-FileHash &lt;file&gt;</code> (Windows) must print the SHA-256 above.</li>
<li>Signature: every file has a <code>.sig</code> next to it (the app's update key, minisign). With <a href="https://jedisct1.github.io/minisign/" target="_blank" rel="noopener">minisign</a>: <code>base64 -d &lt;file&gt;.sig &gt; f.minisig &amp;&amp; minisign -Vm &lt;file&gt; -x f.minisig -P &lt;key&gt;</code>, with the public key
<code class="dl-key">RWQqOO/ZeIr3RBmolt/fx108m89QNEUndRTFrucjx0CBMoeFb7/xhrvW</code></li>
<li>Or let the CLI do both: <code>storebound desktop --download</code>.</li>
</ol>
<p class="note">The npm package <code>storebound</code> stays the CLI, MCP server and agent skills; it does not contain this app. The app brings its own copy of the CLI.</p>
</div>
</section>
</main>
<footer class="foot">
<div class="wrap foot-in">
<div>
<a class="brand" href="../" aria-label="storebound home">
<img class="mark" src="../img/logo-mark.png?v=2" srcset="../img/logo-mark.png?v=2 1x, ../img/logo-mark@2x.png?v=2 2x" width="28" height="28" alt="storebound logo">
<span>storebound</span>
</a>
<p class="fine">MIT · made by CRTkafa · not affiliated with Google or Apple.<br>App Store is a trademark of Apple Inc.; Google Play is a trademark of Google LLC.</p>
</div>
<nav aria-label="Footer">
<a href="https://git.crt.fyi/CRT/storebound/src/branch/main/docs/desktop.md" target="_blank" rel="noopener">desktop docs<span class="ext" aria-hidden="true">↗</span><span class="sr-only"> (opens in a new tab)</span></a>
<a href="https://git.crt.fyi/CRT/storebound/src/branch/main/CHANGELOG.md" target="_blank" rel="noopener">CHANGELOG<span class="ext" aria-hidden="true">↗</span><span class="sr-only"> (opens in a new tab)</span></a>
<a href="https://git.crt.fyi/CRT/storebound/src/branch/main/SECURITY.md" target="_blank" rel="noopener">SECURITY<span class="ext" aria-hidden="true">↗</span><span class="sr-only"> (opens in a new tab)</span></a>
</nav>
</div>
</footer>
<script>
// Reads latest.json (same origin, published with the binaries) and fills the page. Everything from it is set as
// text, never as HTML; only storebound.dev/desktop/dl/ links are shown.
(function () {
var LABEL = {
'macos-universal-dmg': ['macOS 11 or later', 'Apple silicon and Intel, .dmg'],
'windows-x86_64-nsis': ['Windows 10 / 11', 'x64 installer, installs for your user only'],
'linux-x86_64-appimage': ['Linux', 'x64 AppImage (Ubuntu 22.04+, Debian 12+)'],
'linux-x86_64-deb': ['Linux (Debian, Ubuntu)', 'x64 .deb package']
};
var ORDER = ['macos-universal-dmg', 'windows-x86_64-nsis', 'linux-x86_64-appimage', 'linux-x86_64-deb'];
function el(tag, attrs, text) { var e = document.createElement(tag); for (var k in attrs || {}) e.setAttribute(k, attrs[k]); if (text != null) e.textContent = text; return e; }
function mb(n) { return (n / 1e6).toFixed(1) + ' MB'; }
function ours(u) { try { var x = new URL(u, location.href); return x.protocol === 'https:' && x.host === 'storebound.dev' && x.pathname.indexOf('/desktop/dl/') === 0; } catch (e) { return false; } }
function system() {
var p = ((navigator.userAgentData && navigator.userAgentData.platform) || navigator.platform || '') + ' ' + navigator.userAgent;
if (/android|iphone|ipad|ipod/i.test(p)) return 'mobile';
if (/mac/i.test(p)) return 'macos-universal-dmg';
if (/win/i.test(p)) return 'windows-x86_64-nsis';
if (/linux|x11/i.test(p)) return 'linux-x86_64-appimage';
return null;
}
var status = document.getElementById('status');
var primary = document.getElementById('primary');
var notice = document.getElementById('notice');
fetch('latest.json', { cache: 'no-cache' }).then(function (r) {
if (r.status === 404) throw new Error('none');
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
}).then(function (m) {
var d = m.downloads || {};
var keys = ORDER.filter(function (k) { return d[k] && ours(d[k].url); });
if (!keys.length) throw new Error('none');
var here = system();
var pick = d[here] && keys.indexOf(here) >= 0 ? here : null;
primary.textContent = '';
if (pick) {
var a = el('a', { 'class': 'btn btn-primary', href: d[pick].url }, 'Download for ' + LABEL[pick][0]);
primary.appendChild(a);
} else {
primary.appendChild(el('a', { 'class': 'btn btn-primary', href: '#all' }, 'Choose your system'));
}
var meta = 'Version ' + m.version + (m.pub_date ? ' · ' + m.pub_date.slice(0, 10) : '') + (pick ? ' · ' + mb(d[pick].size) : '') + (here === 'mobile' ? ' · the desktop app runs on macOS, Windows and Linux' : '');
primary.appendChild(el('p', { 'class': 'meta' }, meta));
var unsigned = keys.filter(function (k) { return !d[k].codeSigned && (k === 'macos-universal-dmg' || k === 'windows-x86_64-nsis'); });
if (unsigned.length) {
var n = el('div', { 'class': 'dl-notice', role: 'note' });
n.appendChild(el('b', null, 'Unsigned preview. '));
n.appendChild(document.createTextNode('This early build is not yet signed by Apple or Microsoft, so your system asks once before opening it. Check the SHA-256 below first.'));
var ol = el('ol');
if (unsigned.indexOf('macos-universal-dmg') >= 0) ol.appendChild(el('li', null, 'macOS: drag storebound to Applications and open it; when macOS refuses, open System Settings > Privacy & Security and choose Open Anyway (once).'));
if (unsigned.indexOf('windows-x86_64-nsis') >= 0) ol.appendChild(el('li', null, 'Windows: when SmartScreen shows "Windows protected your PC", choose More info > Run anyway.'));
n.appendChild(ol);
notice.appendChild(n);
}
var rows = document.getElementById('rows');
keys.forEach(function (k) {
var x = d[k];
var tr = el('tr', k === pick ? { 'class': 'here' } : {});
var sys = el('td');
sys.appendChild(el('b', null, LABEL[k][0]));
sys.appendChild(el('div', { 'class': 'small' }, LABEL[k][1] + ((k === 'macos-universal-dmg' || k === 'windows-x86_64-nsis') && !x.codeSigned ? ' · unsigned preview' : '')));
var file = el('td');
file.appendChild(el('a', { 'class': 'fname', href: x.url }, x.file));
var sub = el('div', { 'class': 'small' }, mb(x.size) + ' · ');
sub.appendChild(el('a', { href: x.url + '.sig' }, 'signature'));
file.appendChild(sub);
var sha = el('td');
sha.appendChild(el('code', { 'class': 'sha' }, x.sha256));
tr.appendChild(sys); tr.appendChild(file); tr.appendChild(sha);
rows.appendChild(tr);
});
document.getElementById('table').hidden = false;
}).catch(function (e) {
var none = e && e.message === 'none';
status.textContent = none
? 'The first desktop release is not published yet. Until then the storebound CLI does everything the app will show.'
: 'Could not read the release list (' + (e && e.message) + '). latest.json has every file and its SHA-256.';
if (none) primary.appendChild(el('a', { 'class': 'btn btn-ghost', href: '../#start' }, 'Get the CLI'));
else primary.appendChild(el('a', { 'class': 'btn btn-ghost', href: 'latest.json' }, 'latest.json'));
});
})();
</script>
</body>
</html>