diff --git a/.github/workflows/deploy-download-worker.yml b/.github/workflows/deploy-download-worker.yml
index e3a72ae..124181c 100644
--- a/.github/workflows/deploy-download-worker.yml
+++ b/.github/workflows/deploy-download-worker.yml
@@ -7,17 +7,38 @@ on:
paths:
- 'workers/**'
- '.github/workflows/deploy-download-worker.yml'
+ pull_request:
+ paths:
+ - 'workers/**'
+ - '.github/workflows/deploy-download-worker.yml'
workflow_dispatch:
permissions:
contents: read
concurrency:
- group: download-worker
+ group: download-worker-${{ github.ref }}
cancel-in-progress: false
jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+
+ - name: Test download worker
+ run: node --test workers/*.test.js
+
deploy:
+ if: github.event_name != 'pull_request'
+ needs: test
runs-on: ubuntu-latest
steps:
diff --git a/README.md b/README.md
index 4655e65..34d7a9b 100644
--- a/README.md
+++ b/README.md
@@ -78,6 +78,8 @@ Desktop installers are served through a Cloudflare Worker at:
The Worker resolves the latest `memohai/Memoh` release, redirects to a same-domain versioned URL, then caches the release asset at Cloudflare edge on first download. Users never download from a GitHub release URL directly.
+Release assets are discovered by the stable platform suffix in each route and proxied through the `browser_download_url` returned by GitHub. Do not reconstruct asset URLs or couple the Worker to a product-name prefix such as `Memoh` or `Memoh-Local`.
+
Deploy the Worker after configuring the `memoh.ai/downloads/desktop/*` route:
```bash
diff --git a/index.html b/index.html
index 5daeb8f..5440f6c 100644
--- a/index.html
+++ b/index.html
@@ -5,19 +5,20 @@
-
MemohAI - The Agent Orchestrator
-
+
+ Memoh — The multi-agent platform
+
-
-
-
+
+
+
-
-
+
+
diff --git a/package.json b/package.json
index 858fd3f..0b3fe76 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,8 @@
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build && node scripts/create-spa-fallback.mjs",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "test:worker": "node --test workers/*.test.js"
},
"dependencies": {
"@iconify/vue": "^5.0.0",
diff --git a/scripts/create-spa-fallback.mjs b/scripts/create-spa-fallback.mjs
index 01187c0..cb5cd86 100644
--- a/scripts/create-spa-fallback.mjs
+++ b/scripts/create-spa-fallback.mjs
@@ -5,19 +5,27 @@ const distDir = resolve('dist')
const indexPath = resolve(distDir, 'index.html')
const metaPattern = /[\s\S]*?/
+// 静态 meta 必须与 src/locales 里的 seo.* 文案保持同一定位;home 同时是 index.html 的兜底内容
const pages = {
home: {
- title: 'MemohAI - The Agent Orchestrator',
- description: 'Memoh is a self-hosted agent orchestrator for hackers and AI developers, with container isolation, long-term memory, high concurrency, and zero telemetry.',
- socialDescription: 'Self-hosted agent orchestration with container isolation, long-term memory, high concurrency, and zero telemetry.',
+ title: 'Memoh — The multi-agent platform',
+ description: 'Every agent gets its own cloud computer with a desktop, filesystem, and network. Always on, always there.',
+ socialDescription: 'Every agent gets its own cloud computer with a desktop, filesystem, and network. Always on, always there.',
url: 'https://memoh.ai/',
},
+ // /desktop 已重定向到 /download,但保留静态入口让旧链接拿到 200 而不是 404
desktop: {
title: 'Memoh Desktop - Download for macOS, Windows & Linux',
description: 'Download the Memoh desktop app for macOS, Windows, and Linux. Run your agent workspace natively on your computer.',
socialDescription: 'Download Memoh Desktop for macOS, Windows, and Linux.',
url: 'https://memoh.ai/desktop',
},
+ download: {
+ title: 'Download Memoh Desktop - macOS, Windows & Linux',
+ description: 'Pick the Memoh desktop installer for your system. Available for macOS (Apple Silicon & Intel), Windows, and Linux.',
+ socialDescription: 'Download Memoh Desktop for macOS, Windows, and Linux.',
+ url: 'https://memoh.ai/download',
+ },
waitlist: {
title: 'Join the Memoh waitlist',
description: 'Join the Memoh waitlist for early access to cloud agent workspaces.',
@@ -49,7 +57,7 @@ const renderMeta = ({ title, description, socialDescription, url }) => {
-
+
@@ -73,7 +81,8 @@ const indexHtml = await readFile(indexPath, 'utf8')
await writeFile(indexPath, withMeta(indexHtml, pages.home))
await writeFile(resolve(distDir, '404.html'), withMeta(indexHtml, pages.home))
-for (const route of ['desktop', 'waitlist', 'blogs']) {
+// 每个 SPA 路由都必须在这里有静态入口,否则 GitHub Pages 用 404.html 兜底、返回 404 状态码(SEO 直接判死)
+for (const route of ['desktop', 'download', 'waitlist', 'blogs']) {
const routeDir = resolve(distDir, route)
await mkdir(routeDir, { recursive: true })
await writeFile(resolve(routeDir, 'index.html'), withMeta(indexHtml, pages[route]))
diff --git a/src/App.vue b/src/App.vue
index c40839f..509b786 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -80,15 +80,16 @@ const telecomLicenseUrl = 'https://dxzhgl.miit.gov.cn/'
class="flex items-center justify-center w-9 h-9 rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors">
-
+
-
-