chore(release): bump version to FlowPilot1.0

This commit is contained in:
QLHazyCoder
2026-05-16 06:56:38 +08:00
parent 8a11b91c5a
commit 4ccec28d13
16 changed files with 158 additions and 122 deletions
+5 -5
View File
@@ -1,4 +1,4 @@
# Multi-Page Automation
# FlowPilot
一个用于批量跑通 ChatGPT OAuth 注册/登录流程的 Chrome 扩展。
@@ -20,11 +20,11 @@
## Star History
<a href="https://www.star-history.com/?repos=QLHazyCoder%2Fcodex-oauth-automation-extension&type=timeline&logscale&legend=top-left">
<a href="https://www.star-history.com/?repos=QLHazyCoder%2FFlowPilot&type=timeline&logscale&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=QLHazyCoder/codex-oauth-automation-extension&type=timeline&logscale&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=QLHazyCoder/codex-oauth-automation-extension&type=timeline&logscale&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=QLHazyCoder/codex-oauth-automation-extension&type=timeline&logscale&legend=top-left" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=QLHazyCoder/FlowPilot&type=timeline&logscale&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=QLHazyCoder/FlowPilot&type=timeline&logscale&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=QLHazyCoder/FlowPilot&type=timeline&logscale&legend=top-left" />
</picture>
</a>
+1 -1
View File
@@ -7244,7 +7244,7 @@ async function fetchIcloudHideMyEmail(options = {}) {
: {}),
hme: generatedAlias,
label: getIcloudAliasLabel(),
note: 'Generated through Multi-Page Automation',
note: 'Generated through FlowPilot',
};
let alias = '';
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

+1 -1
View File
@@ -1,4 +1,4 @@
# Codex 注册扩展相关项目、更新、邮箱、PayPal 与 Clash Verge 配置教程
# FlowPilot 相关项目、更新、邮箱、PayPal 与 Clash Verge 配置教程
本教程用于说明相关项目地址、扩展更新方式、`Cloudflare Temp Email``iCloud 隐私邮箱``QQ 邮箱` 的使用方法、`HeroSMS` 手机接码扩展能力、`PayPal` 注册绑卡流程、扩展内置动态 IP 代理、节点检测与纯净度检查,以及 [Clash Verge](https://github.com/clash-verge-rev/clash-verge-rev) 的 `🔁 非港轮询` 配置方法。
+1 -1
View File
@@ -1,6 +1,6 @@
# 使用教程总索引
本目录用于维护 `Codex 注册扩展` 的分部分使用教程。
本目录用于维护 `FlowPilot` 的分部分使用教程。
从现在开始,教程正文不再直接堆到这一个文件里,而是拆到 `分部分/` 目录下分别维护。
---
+2 -2
View File
@@ -1,6 +1,6 @@
# 公告书写标准
本文件用于约束 AI 生成“账号贡献站 / Codex 注册扩展”相关公告时的 Markdown 格式。
本文件用于约束 AI 生成“账号贡献站 / FlowPilot”相关公告时的 Markdown 格式。
使用方式:
@@ -184,7 +184,7 @@ AI 生成公告时禁止:
把下面这段直接发给 AI
```md
请帮我写一份“账号贡献站 / Codex 注册扩展”公告,严格遵守以下要求:
请帮我写一份“账号贡献站 / FlowPilot”公告,严格遵守以下要求:
1. 输出必须是 Markdown
2. 不要使用 HTML
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 17 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

After

Width:  |  Height:  |  Size: 763 B

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

+3 -3
View File
@@ -1,8 +1,8 @@
{
"manifest_version": 3,
"name": "codex-oauth-automation-extension",
"version": "9.99",
"version_name": "Ultra9.99",
"name": "FlowPilot",
"version": "1.0",
"version_name": "FlowPilot1.0",
"description": "用于自动执行多步骤 OAuth 注册流程",
"permissions": [
"sidePanel",
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "codex-oauth-automation-extension",
"name": "flowpilot",
"license": "MIT",
"private": true,
"scripts": {
+69 -62
View File
@@ -1,5 +1,5 @@
/* ============================================================
MultiPage Automation — Side Panel
FlowPilot — Side Panel
Design: Swiss Modernism + Developer Tool
Font: Inter (UI) + JetBrains Mono (code)
Themes: Light (default) + Dark (toggle)
@@ -7,35 +7,36 @@
/* ---- Light Theme (default) ---- */
:root {
--bg-base: #ffffff;
--bg-surface: #f7f8fa;
--bg-elevated: #eef0f4;
--bg-hover: #e4e7ec;
--bg-active: #dce0e8;
--bg-base: #eef6f4;
--bg-surface: #fbfefd;
--bg-field: #f7fbfa;
--bg-elevated: #e7f1ee;
--bg-hover: #dce9e6;
--bg-active: #d0e0dc;
--border: #d8dce3;
--border-subtle: #e8ecf1;
--border: #c8d9d5;
--border-subtle: #dce9e6;
--text-primary: #1a1d24;
--text-secondary: #5c6370;
--text-muted: #9ca3af;
--text-primary: #162025;
--text-secondary: #516368;
--text-muted: #7f9194;
--blue: #2563eb;
--blue-soft: rgba(37, 99, 235, 0.08);
--blue-glow: rgba(37, 99, 235, 0.12);
--green: #16a34a;
--green-soft: rgba(22, 163, 74, 0.08);
--orange: #ea580c;
--orange-soft: rgba(234, 88, 12, 0.08);
--amber: #d97706;
--amber-soft: rgba(217, 119, 6, 0.08);
--red: #dc2626;
--red-soft: rgba(220, 38, 38, 0.08);
--cyan: #0891b2;
--purple: #7c3aed;
--blue: #1473e6;
--blue-soft: rgba(20, 115, 230, 0.09);
--blue-glow: rgba(20, 115, 230, 0.16);
--green: #5eaf00;
--green-soft: rgba(94, 175, 0, 0.10);
--orange: #d8661f;
--orange-soft: rgba(216, 102, 31, 0.10);
--amber: #b98200;
--amber-soft: rgba(185, 130, 0, 0.10);
--red: #d63c3c;
--red-soft: rgba(214, 60, 60, 0.10);
--cyan: #0aa5bd;
--purple: #7160d7;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.04);
--shadow-md: 0 2px 6px rgba(0,0,0,0.06);
--shadow-sm: 0 1px 3px rgba(22, 32, 37, 0.06);
--shadow-md: 0 6px 18px rgba(22, 32, 37, 0.10);
--radius-sm: 6px;
--radius-md: 8px;
@@ -44,35 +45,36 @@
/* ---- Dark Theme ---- */
[data-theme="dark"] {
--bg-base: #0f1117;
--bg-surface: #181a21;
--bg-elevated: #21242d;
--bg-hover: #2a2e38;
--bg-active: #323844;
--bg-base: #0c1214;
--bg-surface: #141b1e;
--bg-field: #101719;
--bg-elevated: #1d272a;
--bg-hover: #263236;
--bg-active: #2f3d42;
--border: #2a2e38;
--border-subtle: #21242d;
--border: #2a383c;
--border-subtle: #1f2b2f;
--text-primary: #e4e6eb;
--text-secondary: #8b919e;
--text-muted: #565c6a;
--text-primary: #edf5f3;
--text-secondary: #9cada9;
--text-muted: #748583;
--blue: #3b82f6;
--blue-soft: rgba(59, 130, 246, 0.12);
--blue-glow: rgba(59, 130, 246, 0.18);
--green: #22c55e;
--green-soft: rgba(34, 197, 94, 0.12);
--orange: #f97316;
--orange-soft: rgba(249, 115, 22, 0.12);
--amber: #f59e0b;
--amber-soft: rgba(245, 158, 11, 0.12);
--red: #ef4444;
--red-soft: rgba(239, 68, 68, 0.12);
--cyan: #06b6d4;
--purple: #a78bfa;
--blue: #4d9aff;
--blue-soft: rgba(77, 154, 255, 0.14);
--blue-glow: rgba(77, 154, 255, 0.22);
--green: #8bdc2d;
--green-soft: rgba(139, 220, 45, 0.14);
--orange: #ff9148;
--orange-soft: rgba(255, 145, 72, 0.14);
--amber: #ffc247;
--amber-soft: rgba(255, 194, 71, 0.14);
--red: #ff6b6b;
--red-soft: rgba(255, 107, 107, 0.14);
--cyan: #38d5e6;
--purple: #a997ff;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.2);
--shadow-md: 0 2px 8px rgba(0,0,0,0.3);
--shadow-sm: 0 1px 2px rgba(0,0,0,0.22);
--shadow-md: 0 8px 24px rgba(0,0,0,0.34);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
@@ -109,7 +111,7 @@ header {
margin-bottom: 14px;
padding-top: 4px;
padding-bottom: 12px;
background: color-mix(in srgb, var(--bg-base) 94%, transparent);
background: color-mix(in srgb, var(--bg-surface) 92%, transparent);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--border-subtle);
}
@@ -447,7 +449,7 @@ header {
width: 42px;
padding: 6px 4px;
text-align: center;
background: var(--bg-base);
background: var(--bg-field);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
@@ -1176,7 +1178,7 @@ header {
padding: 2px 6px;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--bg-base);
background: var(--bg-field);
color: var(--text-primary);
font-size: 11px;
line-height: 1.2;
@@ -1238,7 +1240,7 @@ header {
.data-input {
flex: 1;
padding: 7px 10px;
background: var(--bg-base);
background: var(--bg-field);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
@@ -1256,7 +1258,7 @@ header {
min-height: 88px;
resize: vertical;
padding: 8px 10px;
background: var(--bg-base);
background: var(--bg-field);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
@@ -1890,7 +1892,7 @@ header {
.data-select {
flex: 1;
padding: 7px 10px;
background: var(--bg-base);
background: var(--bg-field);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
@@ -1904,6 +1906,11 @@ header {
.data-select:focus { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft); }
[data-theme="dark"] .data-select { color-scheme: dark; }
#select-flow {
text-align: center;
text-align-last: center;
}
.data-input[readonly] {
color: var(--text-secondary);
background: var(--bg-surface);
@@ -1955,7 +1962,7 @@ header {
padding: 4px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-base);
background: var(--bg-surface);
box-shadow: var(--shadow-md);
}
@@ -2158,7 +2165,7 @@ header {
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--bg-base);
background: var(--bg-field);
box-shadow: var(--shadow-sm);
transition: transform var(--transition), background var(--transition);
}
@@ -2268,7 +2275,7 @@ header {
padding: 6px;
max-height: 180px;
overflow-y: auto;
background: var(--bg-base);
background: var(--bg-field);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
box-shadow: var(--shadow-md);
@@ -2755,7 +2762,7 @@ header {
.step-manual-btn:disabled {
color: var(--text-muted);
border-color: var(--border-subtle);
background: var(--bg-base);
background: var(--bg-field);
cursor: not-allowed;
opacity: 0.5;
}
@@ -3269,7 +3276,7 @@ header {
.modal-card {
width: min(100%, 320px);
background: var(--bg-base);
background: var(--bg-surface);
border: 1px solid var(--border);
border-radius: 12px;
box-shadow: var(--shadow-md), 0 18px 36px rgba(0, 0, 0, 0.18);
+4 -4
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>codex-oauth-automation-extension</title>
<title>FlowPilot</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
@@ -25,7 +25,7 @@
<div class="header-version-block">
<div class="header-version-main">
<button id="extension-update-status" class="header-version-title header-version-link" type="button"
aria-label="打开 GitHub Releases 页面" title="打开 GitHub Releases 页面">Ultra0.0</button>
aria-label="打开 GitHub Releases 页面" title="打开 GitHub Releases 页面">FlowPilot0.0</button>
<button id="btn-release-log" class="header-link-btn" type="button" hidden>更新日志</button>
</div>
<span id="extension-version-meta" class="header-version-meta" hidden></span>
@@ -111,10 +111,10 @@
<section id="data-section">
<div id="settings-card" class="data-card">
<div class="data-row">
<span class="data-label">Flow</span>
<span class="data-label">注册</span>
<select id="select-flow" class="data-select" aria-label="Flow 选择">
<option value="codex" selected>codex</option>
<option value="pending-1" disabled>待添加</option>
<option value="pending-1" disabled>kiro</option>
<option value="pending-2" disabled>待添加</option>
</select>
</div>
+9 -9
View File
@@ -9729,7 +9729,7 @@ function getRepositoryHomeUrl() {
return releasesPageUrl.replace(/\/releases\/?$/, '');
}
return 'https://github.com/QLHazyCoder/codex-oauth-automation-extension';
return 'https://github.com/QLHazyCoder/FlowPilot';
}
function getReleaseListUrl() {
@@ -9823,7 +9823,7 @@ function renderUpdateReleaseList(releases = []) {
const version = document.createElement('span');
version.className = 'update-release-version';
version.textContent = release.displayVersion || `Ultra${release.version}`;
version.textContent = release.displayVersion || `FlowPilot${release.version}`;
titleRow.appendChild(version);
if (release.title) {
@@ -9925,21 +9925,21 @@ function renderReleaseSnapshot(snapshot) {
}
case 'ignored': {
extensionUpdateStatus.textContent = localVersionText || 'Ultra0.0';
extensionUpdateStatus.textContent = localVersionText || 'FlowPilot0.0';
extensionUpdateStatus.classList.add('is-version-label');
resetUpdateCard();
break;
}
case 'latest': {
extensionUpdateStatus.textContent = localVersionText || 'Ultra0.0';
extensionUpdateStatus.textContent = localVersionText || 'FlowPilot0.0';
extensionUpdateStatus.classList.add('is-version-label');
resetUpdateCard();
break;
}
case 'empty': {
extensionUpdateStatus.textContent = localVersionText || 'Ultra0.0';
extensionUpdateStatus.textContent = localVersionText || 'FlowPilot0.0';
extensionUpdateStatus.classList.add('is-version-label');
resetUpdateCard();
break;
@@ -9947,7 +9947,7 @@ function renderReleaseSnapshot(snapshot) {
case 'error':
default: {
extensionUpdateStatus.textContent = localVersionText || 'Ultra0.0';
extensionUpdateStatus.textContent = localVersionText || 'FlowPilot0.0';
extensionUpdateStatus.classList.add('is-version-label', 'is-check-failed');
extensionVersionMeta.textContent = snapshot?.errorMessage || 'GitHub Releases 检查失败';
extensionVersionMeta.hidden = false;
@@ -9958,7 +9958,7 @@ function renderReleaseSnapshot(snapshot) {
}
async function initializeReleaseInfo() {
const fallbackReleaseUrl = sidepanelUpdateService?.releasesPageUrl || 'https://github.com/QLHazyCoder/codex-oauth-automation-extension/releases';
const fallbackReleaseUrl = sidepanelUpdateService?.releasesPageUrl || 'https://github.com/QLHazyCoder/FlowPilot/releases';
if (btnReleaseLog) {
btnReleaseLog.onclick = () => openExternalUrl(currentReleaseSnapshot?.logUrl || fallbackReleaseUrl);
@@ -9970,8 +9970,8 @@ async function initializeReleaseInfo() {
const localVersion = sidepanelUpdateService?.getLocalVersionLabel?.(chrome.runtime.getManifest())
|| chrome.runtime.getManifest()?.version_name
|| (chrome.runtime.getManifest()?.version ? `Ultra${chrome.runtime.getManifest().version}` : '');
extensionUpdateStatus.textContent = localVersion || 'Ultra0.0';
|| (chrome.runtime.getManifest()?.version ? `FlowPilot${chrome.runtime.getManifest().version}` : '');
extensionUpdateStatus.textContent = localVersion || 'FlowPilot0.0';
extensionUpdateStatus.classList.remove('is-update-available', 'is-check-failed');
extensionUpdateStatus.classList.add('is-version-label');
extensionVersionMeta.hidden = true;
+18 -7
View File
@@ -1,6 +1,6 @@
(() => {
const GITHUB_OWNER = 'QLHazyCoder';
const GITHUB_REPO = 'codex-oauth-automation-extension';
const GITHUB_REPO = 'FlowPilot';
const RELEASES_PAGE_URL = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/releases`;
const RELEASES_API_URL = `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases?per_page=10`;
const CACHE_KEY = 'multipage-release-snapshot-v1';
@@ -9,12 +9,16 @@
const FETCH_TIMEOUT_MS = 8000;
const MAX_RELEASES = 10;
const MAX_NOTES_PER_RELEASE = 5;
const VERSION_FAMILY_FLOWPILOT = 'flowpilot';
const VERSION_FAMILY_ULTRA = 'ultra';
const VERSION_FAMILY_PRO = 'pro';
const VERSION_FAMILY_LEGACY = 'legacy';
function getVersionFamily(version, fallbackFamily = VERSION_FAMILY_LEGACY) {
const trimmed = String(version || '').trim();
if (/^flowpilot/i.test(trimmed)) {
return VERSION_FAMILY_FLOWPILOT;
}
if (/^ultra/i.test(trimmed)) {
return VERSION_FAMILY_ULTRA;
}
@@ -28,7 +32,7 @@
}
function stripVersionPrefix(version) {
return String(version || '').trim().replace(/^(?:ultra|pro|v)\s*/i, '');
return String(version || '').trim().replace(/^(?:flowpilot|ultra|pro|v)\s*/i, '');
}
function extractVersionCore(version) {
@@ -63,6 +67,9 @@
}
function getVersionFamilyPrefix(family) {
if (family === VERSION_FAMILY_FLOWPILOT) {
return 'FlowPilot';
}
if (family === VERSION_FAMILY_ULTRA) {
return 'Ultra';
}
@@ -73,6 +80,9 @@
}
function getVersionFamilyRank(family) {
if (family === VERSION_FAMILY_FLOWPILOT) {
return 4;
}
if (family === VERSION_FAMILY_ULTRA) {
return 3;
}
@@ -206,7 +216,7 @@
}
function getComparableReleaseVersion(release) {
const fallbackFamily = [VERSION_FAMILY_ULTRA, VERSION_FAMILY_PRO].includes(release?.family)
const fallbackFamily = [VERSION_FAMILY_FLOWPILOT, VERSION_FAMILY_ULTRA, VERSION_FAMILY_PRO].includes(release?.family)
? release.family
: VERSION_FAMILY_LEGACY;
const displayVersion = String(release?.displayVersion || '').trim();
@@ -267,7 +277,7 @@
}
function setIgnoredUpdateVersion(version) {
const normalized = formatDisplayVersion(version, VERSION_FAMILY_ULTRA) || String(version || '').trim();
const normalized = formatDisplayVersion(version, VERSION_FAMILY_FLOWPILOT) || String(version || '').trim();
try {
if (normalized) {
localStorage.setItem(IGNORED_UPDATE_VERSION_KEY, normalized);
@@ -387,17 +397,17 @@
}
function getLocalVersionLabel(manifest = chrome.runtime.getManifest()) {
const versionName = formatDisplayVersion(manifest?.version_name, VERSION_FAMILY_ULTRA);
const versionName = formatDisplayVersion(manifest?.version_name, VERSION_FAMILY_FLOWPILOT);
if (versionName) {
return versionName;
}
const versionCore = extractVersionCore(manifest?.version || '');
return versionCore ? formatDisplayVersion(`Ultra${versionCore}`, VERSION_FAMILY_ULTRA) : '';
return versionCore ? formatDisplayVersion(`FlowPilot${versionCore}`, VERSION_FAMILY_FLOWPILOT) : '';
}
async function getReleaseSnapshot(options = {}) {
const localVersion = getLocalVersionLabel(chrome.runtime.getManifest()) || 'Ultra0.0';
const localVersion = getLocalVersionLabel(chrome.runtime.getManifest()) || 'FlowPilot0.0';
try {
const releases = await loadReleases(options);
@@ -442,6 +452,7 @@
getLocalVersionLabel,
getReleaseSnapshot,
ignoreReleaseSnapshot,
repositoryUrl: `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}`,
releasesPageUrl: RELEASES_PAGE_URL,
stripVersionPrefix,
};
+44 -26
View File
@@ -7,7 +7,7 @@ const source = fs.readFileSync('sidepanel/update-service.js', 'utf8');
function createUpdateService(options = {}) {
const manifest = options.manifest || {
version: '1.0',
version_name: 'Ultra1.0',
version_name: 'FlowPilot1.0',
};
const cache = new Map();
const windowObject = {};
@@ -77,11 +77,11 @@ function createUpdateService(options = {}) {
};
}
test('getReleaseSnapshot keeps Ultra releases ahead of historical Pro and legacy v releases', async () => {
test('getReleaseSnapshot keeps FlowPilot releases ahead of historical Ultra, Pro, and legacy v releases', async () => {
const { api } = createUpdateService({
manifest: {
version: '1.0',
version_name: 'Ultra1.0',
version: '9.99',
version_name: 'Ultra9.99',
},
fetchImpl: async () => ({
ok: true,
@@ -106,10 +106,19 @@ test('getReleaseSnapshot keeps Ultra releases ahead of historical Pro and legacy
prerelease: false,
},
{
tag_name: 'Ultra1.1',
name: 'Ultra1.1',
html_url: 'https://example.com/Ultra1.1',
tag_name: 'Ultra9.99',
name: 'Ultra9.99',
html_url: 'https://example.com/Ultra9.99',
published_at: '2026-04-19T00:00:00.000Z',
body: '- historical ultra release',
draft: false,
prerelease: false,
},
{
tag_name: 'FlowPilot1.0',
name: 'FlowPilot1.0',
html_url: 'https://example.com/FlowPilot1.0',
published_at: '2026-04-20T00:00:00.000Z',
body: '- current release',
draft: false,
prerelease: false,
@@ -122,11 +131,11 @@ test('getReleaseSnapshot keeps Ultra releases ahead of historical Pro and legacy
const snapshot = await api.getReleaseSnapshot({ force: true });
assert.equal(snapshot.status, 'update-available');
assert.equal(snapshot.localVersion, 'Ultra1.0');
assert.equal(snapshot.latestVersion, 'Ultra1.1');
assert.equal(snapshot.localVersion, 'Ultra9.99');
assert.equal(snapshot.latestVersion, 'FlowPilot1.0');
assert.deepEqual(
snapshot.newerReleases.map((release) => release.displayVersion),
['Ultra1.1']
['FlowPilot1.0']
);
});
@@ -134,7 +143,7 @@ test('getReleaseSnapshot reorders cached releases before choosing latest version
const { api, getFetchCalls } = createUpdateService({
manifest: {
version: '1.0',
version_name: 'Ultra1.0',
version_name: 'FlowPilot1.0',
},
cachedSnapshot: {
fetchedAt: Date.now(),
@@ -158,14 +167,23 @@ test('getReleaseSnapshot reorders cached releases before choosing latest version
notes: [],
},
{
version: '1.1',
displayVersion: 'Ultra1.1',
version: '9.99',
displayVersion: 'Ultra9.99',
family: 'ultra',
title: '',
url: 'https://example.com/Ultra1.1',
url: 'https://example.com/Ultra9.99',
publishedAt: '2026-04-19T00:00:00.000Z',
notes: [],
},
{
version: '1.1',
displayVersion: 'FlowPilot1.1',
family: 'flowpilot',
title: '',
url: 'https://example.com/FlowPilot1.1',
publishedAt: '2026-04-20T00:00:00.000Z',
notes: [],
},
],
},
fetchImpl: async () => {
@@ -177,19 +195,19 @@ test('getReleaseSnapshot reorders cached releases before choosing latest version
assert.equal(getFetchCalls(), 0);
assert.equal(snapshot.status, 'update-available');
assert.equal(snapshot.latestVersion, 'Ultra1.1');
assert.equal(snapshot.latestVersion, 'FlowPilot1.1');
assert.deepEqual(
snapshot.newerReleases.map((release) => release.displayVersion),
['Ultra1.1']
['FlowPilot1.1']
);
});
test('getReleaseSnapshot suppresses an ignored latest update until a newer release appears', async () => {
let releases = [
{
tag_name: 'Ultra1.1',
name: 'Ultra1.1',
html_url: 'https://example.com/Ultra1.1',
tag_name: 'FlowPilot1.1',
name: 'FlowPilot1.1',
html_url: 'https://example.com/FlowPilot1.1',
published_at: '2026-04-19T00:00:00.000Z',
body: '- current release',
draft: false,
@@ -199,7 +217,7 @@ test('getReleaseSnapshot suppresses an ignored latest update until a newer relea
const { api } = createUpdateService({
manifest: {
version: '1.0',
version_name: 'Ultra1.0',
version_name: 'FlowPilot1.0',
},
fetchImpl: async () => ({
ok: true,
@@ -211,17 +229,17 @@ test('getReleaseSnapshot suppresses an ignored latest update until a newer relea
const firstSnapshot = await api.getReleaseSnapshot({ force: true });
assert.equal(firstSnapshot.status, 'update-available');
assert.equal(api.ignoreReleaseSnapshot(firstSnapshot), 'Ultra1.1');
assert.equal(api.ignoreReleaseSnapshot(firstSnapshot), 'FlowPilot1.1');
const ignoredSnapshot = await api.getReleaseSnapshot({ force: true });
assert.equal(ignoredSnapshot.status, 'ignored');
assert.equal(ignoredSnapshot.ignoredVersion, 'Ultra1.1');
assert.equal(ignoredSnapshot.ignoredVersion, 'FlowPilot1.1');
releases = [
{
tag_name: 'Ultra1.2',
name: 'Ultra1.2',
html_url: 'https://example.com/Ultra1.2',
tag_name: 'FlowPilot1.2',
name: 'FlowPilot1.2',
html_url: 'https://example.com/FlowPilot1.2',
published_at: '2026-04-20T00:00:00.000Z',
body: '- next release',
draft: false,
@@ -232,5 +250,5 @@ test('getReleaseSnapshot suppresses an ignored latest update until a newer relea
const newerSnapshot = await api.getReleaseSnapshot({ force: true });
assert.equal(newerSnapshot.status, 'update-available');
assert.equal(newerSnapshot.latestVersion, 'Ultra1.2');
assert.equal(newerSnapshot.latestVersion, 'FlowPilot1.2');
});