- 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
212 lines
13 KiB
HTML
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 <file></code> (macOS, Linux) or <code>Get-FileHash <file></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 <file>.sig > f.minisig && minisign -Vm <file> -x f.minisig -P <key></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>
|