@glittering0717/ai-browser 2.1.7 → 2.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittering0717/ai-browser",
3
- "version": "2.1.7",
3
+ "version": "2.1.8",
4
4
  "description": "AI-native browser — renders pixels for humans, structured semantic-tree API for agents. No screenshots, no OCR, no coordinate guessing.",
5
5
  "main": "src/main/index.js",
6
6
  "bin": {
@@ -27,7 +27,11 @@ const args = process.argv.slice(2);
27
27
  const dry = args.includes('--dry');
28
28
  const bumpKind = args.find((a) => ['patch', 'minor', 'major'].includes(a)) || 'patch';
29
29
  const notesIdx = args.indexOf('--notes');
30
- const notesFile = notesIdx >= 0 ? args[notesIdx + 1] : null;
30
+ const notesArg = notesIdx >= 0 ? args[notesIdx + 1] : null;
31
+ // --notes 既接受仓库内相对路径,也接受绝对路径(写手稿时通常放在 /tmp)。
32
+ // path.join(ROOT, '/tmp/x') 会拼出 ROOT/tmp/x 这种不存在的路径,所以先判绝对。
33
+ const notesPath = notesArg ? (path.isAbsolute(notesArg) ? notesArg : path.join(ROOT, notesArg)) : null;
34
+ const notesFile = notesPath && fs.existsSync(notesPath) ? notesPath : null;
31
35
  // 全量测试 4 分钟起,发版时默认只跑"会拦住错误发版"的那几个:
32
36
  // 版本一致性、导入完整性、schema 预算。其余靠 CI / 平时。
33
37
  const fullTests = args.includes('--full-test');
@@ -125,8 +129,8 @@ mark('测试');
125
129
 
126
130
  // ---- 3. commit + tag ------------------------------------------------------
127
131
  step('提交并打 tag');
128
- const notes = notesFile && fs.existsSync(path.join(ROOT, notesFile))
129
- ? fs.readFileSync(path.join(ROOT, notesFile), 'utf8').trim()
132
+ const notes = notesFile
133
+ ? fs.readFileSync(notesFile, 'utf8').trim()
130
134
  : `v${to}`;
131
135
  git('add', 'package.json', 'server.json', 'src/shared/version.js');
132
136
  git('commit', '-m', `chore(release): v${to}`);
@@ -160,6 +164,46 @@ try {
160
164
  warn(`本地 commit 与 tag 都在,网络恢复后执行:git push origin main dev v${to}`);
161
165
  }
162
166
 
167
+ // ---- 4.5 GitHub Release ---------------------------------------------------
168
+ // **tag ≠ Release**。git tag 只是仓库里的一个 ref,GitHub 的 /releases 页面
169
+ // 读的是 Release 对象;只打 tag 不建 Release,页面会一直停在很久以前的版本
170
+ // (2026-10-24 用户就是在这里发现"releases 还是 2.1.1",而 tag 已经有 2.1.7)。
171
+ //
172
+ // 用 --generate-notes:GitHub 按 commit 自动生成 changelog,比手写更容易保持。
173
+ // 幂等:重复跑发版时该 tag 的 Release 已存在就跳过,不报错。
174
+ step('创建 GitHub Release');
175
+ let releaseOk = false;
176
+ {
177
+ const hasGh = spawnSync('gh', ['--version'], { encoding: 'utf8' }).status === 0;
178
+ if (!hasGh) {
179
+ warn('未找到 gh CLI,跳过 Release 创建。补救:gh release create v' + to + ' --generate-notes');
180
+ } else {
181
+ const exists = spawnSync('gh', ['release', 'view', `v${to}`], { cwd: ROOT, encoding: 'utf8' }).status === 0;
182
+ if (exists) {
183
+ ok(`v${to} 的 Release 已存在,跳过`);
184
+ releaseOk = true;
185
+ mark('github release');
186
+ } else {
187
+ const argv = ['release', 'create', `v${to}`, '--verify-tag', '--latest', '--title', `v${to}`];
188
+ if (notesFile) {
189
+ argv.push('--notes-file', notesFile);
190
+ } else {
191
+ argv.push('--generate-notes');
192
+ }
193
+ const r = spawnSync('gh', argv, { cwd: ROOT, encoding: 'utf8' });
194
+ const out = `${r.stdout || ''}${r.stderr || ''}`.trim();
195
+ if (r.status === 0) {
196
+ ok('Release 已创建:' + out.split('\n').filter(Boolean).pop());
197
+ releaseOk = true;
198
+ mark('github release');
199
+ } else {
200
+ fail('创建 Release 失败:' + out.split('\n').filter(Boolean).slice(-3).join(' | '));
201
+ warn(`补救:gh release create v${to} --verify-tag --latest --generate-notes`);
202
+ }
203
+ }
204
+ }
205
+ }
206
+
163
207
  // ---- 5. 打包(桌面 App)---------------------------------------------------
164
208
  // 放在 npm 之前:打包失败就不该把版本发出去(npm 上发出去的版本撤不回来,
165
209
  // 只能靠再发一个版本补救)。但 MCP registry 又要求 npm 先有版本,所以顺序
@@ -201,6 +245,24 @@ step('打包桌面 App');
201
245
  console.log((v.stdout || '').split('\n').map((l) => ' ' + l).join('\n'));
202
246
  process.exit(1);
203
247
  }
248
+
249
+ // 把 dmg 挂到 Release 上 —— 否则 /releases 页面只有一段 changelog,用户还得
250
+ // 自己去找 App。上传放在校验之后:不一致的产物绝对不能对外发。
251
+ const dmg = path.join(ROOT, 'dist', `AI-Browser-${to}-arm64.dmg`);
252
+ if (releaseOk && fs.existsSync(dmg)) {
253
+ const up = spawnSync('gh', ['release', 'upload', `v${to}`, dmg, '--clobber'], {
254
+ cwd: ROOT, encoding: 'utf8',
255
+ });
256
+ if (up.status === 0) {
257
+ ok(`dmg 已挂到 Release(${(fs.statSync(dmg).size / 1024 / 1024).toFixed(1)} MB)`);
258
+ mark('release asset');
259
+ } else {
260
+ fail('dmg 上传失败:' + `${up.stdout || ''}${up.stderr || ''}`.trim().split('\n').slice(-2).join(' | '));
261
+ warn(`补救:gh release upload v${to} ${dmg} --clobber`);
262
+ }
263
+ } else if (!fs.existsSync(dmg)) {
264
+ warn('dmg 不存在,跳过上传');
265
+ }
204
266
  }
205
267
 
206
268
  // ---- 6. npm ---------------------------------------------------------------
@@ -319,7 +381,7 @@ if (process.platform !== 'darwin') {
319
381
  // ---- 摘要 -----------------------------------------------------------------
320
382
  console.log(`\n${c(36)}── v${to} 发版摘要 ──${c(0)}`);
321
383
  console.log(' 已完成: ' + (done.length ? done.join(' → ') : '(无)'));
322
- const skipped = ['版本号', '测试', 'commit', 'tag', 'git push', '打包', '打包校验', 'npm publish', 'mcp registry', '安装']
384
+ const skipped = ['版本号', '测试', 'commit', 'tag', 'git push', 'github release', '打包', '打包校验', 'release asset', 'npm publish', 'mcp registry', '安装']
323
385
  .filter((s) => !done.includes(s));
324
386
  if (skipped.length) console.log(` 未完成: ${skipped.join(', ')}`);
325
387
  process.exit(skipped.length ? 2 : 0);
@@ -2,4 +2,4 @@
2
2
  // Kept in JS (not imported from package.json) to avoid Node import-attribute
3
3
  // (JSON module) compatibility issues inside Electron. package.json "version"
4
4
  // is manually synced to this value; tests assert they match.
5
- export const VERSION = '2.1.7';
5
+ export const VERSION = '2.1.8';