dsh-mobile-plugin 0.1.62 → 0.1.63
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/lib/update-install.cjs +56 -23
- package/package.json +1 -1
package/lib/update-install.cjs
CHANGED
|
@@ -32,16 +32,22 @@
|
|
|
32
32
|
* (`cordis`、`dsh-tools`、`dsh-client-runtime`…),它们是 **DSH 运行时注入的、npm 上
|
|
33
33
|
* 根本没有**。pnpm 8 起 `auto-install-peers` 默认开,会去 npm 上拉这些 peer 然后 404。
|
|
34
34
|
* profile 里通常已经关掉了,但那是市场帮它写的 —— 换台机器不一定有,所以这里显式带上。
|
|
35
|
-
* · `--config.minimum-release-age=0
|
|
36
|
-
*
|
|
35
|
+
* · `--config.minimum-release-age=0`:**撞上 pnpm 的「新发布先晾一会儿」策略时才带**。
|
|
36
|
+
* 强制更新(`force`)一开始就带;平时不带,但**失败信息确实是这类策略时会带开关重试一次**
|
|
37
|
+
* —— 那道策略查的是**整份 lockfile**,别的插件刚发不久也会把整份锁文件判成「含未过期
|
|
38
|
+
* 条目」,于是我们自己的更新整整一天都做不了,而原因跟我们这个包毫无关系(2026-10-11
|
|
39
|
+
* 真机撞上的就是 `billion-context@0.1.192`)。市场那边对这种拒绝同样是带开关重试一次。
|
|
37
40
|
* · `CI=1`:没有终端可交互。包管理器在无人值守的子进程里问问题是没法回答的。
|
|
38
41
|
*
|
|
39
|
-
* ## 装之前先看有没有 agent
|
|
42
|
+
* ## 装之前先看有没有 agent 在跑(但「有没有新版」排在它前面)
|
|
40
43
|
*
|
|
41
44
|
* 装是**就地替换插件文件**,正在干活的对话可能读到半个包。市场那条路有一道这样的闸
|
|
42
45
|
* (`AGENTS_RUNNING`),这里也补上了 —— 判到有人跑就**不动盘**,如实报成「可再试」。
|
|
43
46
|
* `force`(面板上的「强制更新」)是用户明说「我知道,装吧」的那一挡,会跳过它。
|
|
44
47
|
*
|
|
48
|
+
* 顺序上**先答「有没有新版」**:没东西可装时拿一句「有对话在跑」挡在前面是误导,用户会以为
|
|
49
|
+
* 是那个原因,而真正的答案是「已经是最新」。
|
|
50
|
+
*
|
|
45
51
|
* ## 边界(照实说)
|
|
46
52
|
*
|
|
47
53
|
* · **只动 profile 的依赖树**(`pnpm add` 会顺手把 profile 的 `package.json` 里的依赖声明
|
|
@@ -317,17 +323,39 @@ async function installLatest({
|
|
|
317
323
|
}
|
|
318
324
|
|
|
319
325
|
const spec = `${name}@${version}`;
|
|
326
|
+
const releaseAgeFlag = '--config.minimum-release-age=0';
|
|
320
327
|
const args = picked.manager === 'pnpm'
|
|
321
|
-
? ['add', spec, '--config.auto-install-peers=false', ...(force ? [
|
|
328
|
+
? ['add', spec, '--config.auto-install-peers=false', ...(force ? [releaseAgeFlag] : [])]
|
|
322
329
|
: ['install', spec, '--no-audit', '--no-fund'];
|
|
323
|
-
|
|
330
|
+
let command = [picked.manager, ...args].join(' ');
|
|
324
331
|
onEvent({ phase: 'install-started', command, manager: picked.kind });
|
|
325
332
|
|
|
326
|
-
const
|
|
333
|
+
const spawnAdd = extra => run(picked.command, [...picked.prefix, ...args, ...extra], {
|
|
327
334
|
cwd: profileDir,
|
|
328
335
|
env: { ...childEnv, ...picked.env },
|
|
329
336
|
timeoutMs,
|
|
330
337
|
});
|
|
338
|
+
let result = await spawnAdd([]);
|
|
339
|
+
let output = result.output;
|
|
340
|
+
/**
|
|
341
|
+
* 撞上 pnpm 的「新发布先晾一会儿」策略:**带上开关重试一次**。
|
|
342
|
+
*
|
|
343
|
+
* 为什么非重试不可:那道策略查的是**整份 lockfile**,不是我们要装的这个包。这台机器上
|
|
344
|
+
* 实测过一次 —— 另一个插件(`billion-context@0.1.192`)刚发不久,整份 lockfile 就被判
|
|
345
|
+
* 「含未过期的条目」而拒绝,于是我们自己的更新整整一天都做不了,而原因跟我们这个包毫无
|
|
346
|
+
* 关系。市场那边对这种拒绝也是带开关重试一次(`RELEASE_AGE_OVERRIDE`)。
|
|
347
|
+
*
|
|
348
|
+
* 只在**第一次没带开关**、且失败信息确实是这类策略时才重试:别的失败(网络、权限、盘满)
|
|
349
|
+
* 重试一次只会把同一条错误再说一遍,还多花一次等待。
|
|
350
|
+
*/
|
|
351
|
+
const releaseAgeLike = /MINIMUM_RELEASE_AGE|minimumReleaseAge|minimum-release-age|release age/i;
|
|
352
|
+
const retried = !result.ok && picked.manager === 'pnpm' && !force && releaseAgeLike.test(output);
|
|
353
|
+
if (retried) {
|
|
354
|
+
onEvent({ phase: 'release-age-retry' });
|
|
355
|
+
result = await spawnAdd([releaseAgeFlag]);
|
|
356
|
+
command = [picked.manager, ...args, releaseAgeFlag].join(' ');
|
|
357
|
+
output = `${output}\n--- 撞上 pnpm 的发布时长策略,带上 ${releaseAgeFlag} 重试一次 ---\n${result.output}`;
|
|
358
|
+
}
|
|
331
359
|
const after = installedVersion(profileNodeModules, name);
|
|
332
360
|
if (!result.ok) {
|
|
333
361
|
return {
|
|
@@ -338,7 +366,8 @@ async function installLatest({
|
|
|
338
366
|
source: declaredSource(profileDir, name),
|
|
339
367
|
command,
|
|
340
368
|
manager: picked.kind,
|
|
341
|
-
|
|
369
|
+
retried,
|
|
370
|
+
output,
|
|
342
371
|
// 超时与「真的失败了」分开说:前者可以再试,后者多半要看输出。
|
|
343
372
|
failure: {
|
|
344
373
|
code: result.code === null ? 'INSTALL_ABORTED' : 'INSTALL_FAILED',
|
|
@@ -356,7 +385,8 @@ async function installLatest({
|
|
|
356
385
|
source: declaredSource(profileDir, name),
|
|
357
386
|
command,
|
|
358
387
|
manager: picked.kind,
|
|
359
|
-
|
|
388
|
+
retried,
|
|
389
|
+
output,
|
|
360
390
|
failure: changed
|
|
361
391
|
? null
|
|
362
392
|
: { code: 'VERSION_UNCHANGED', message: `${command} 跑完了,但盘上还是 ${after ?? '读不到版本'},这一次不算数` },
|
|
@@ -382,6 +412,22 @@ async function installLatest({
|
|
|
382
412
|
async function installLatestIfNeeded(options = {}) {
|
|
383
413
|
const { profileNodeModules, force = false, name = DEFAULT_NAME, agents = null } = options;
|
|
384
414
|
const beforeVersion = installedVersion(profileNodeModules, name);
|
|
415
|
+
const check = await checkForUpdate({ installedVersion: beforeVersion, source: declaredSource(path.dirname(profileNodeModules), name) });
|
|
416
|
+
// 「有没有新版」排在「有没有人在跑」**前面**:没东西可装的时候,拿一句「有对话在跑」挡住
|
|
417
|
+
// 真正的答案(已经是最新)是误导 —— 2026-10-11 真机上就是这么被绊了一下。
|
|
418
|
+
if (!force && !check.updateAvailable) {
|
|
419
|
+
return {
|
|
420
|
+
installed: false,
|
|
421
|
+
skipped: true,
|
|
422
|
+
beforeVersion,
|
|
423
|
+
installedVersion: beforeVersion,
|
|
424
|
+
source: check.source,
|
|
425
|
+
command: null,
|
|
426
|
+
output: '',
|
|
427
|
+
failure: null,
|
|
428
|
+
check,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
385
431
|
if (!force) {
|
|
386
432
|
const running = runningAgentIds(agents);
|
|
387
433
|
if (running.length > 0) {
|
|
@@ -390,11 +436,12 @@ async function installLatestIfNeeded(options = {}) {
|
|
|
390
436
|
skipped: false,
|
|
391
437
|
beforeVersion,
|
|
392
438
|
installedVersion: beforeVersion,
|
|
393
|
-
source:
|
|
439
|
+
source: check.source,
|
|
394
440
|
command: null,
|
|
395
441
|
output: '',
|
|
396
442
|
runningAgents: running,
|
|
397
443
|
retryable: true,
|
|
444
|
+
check,
|
|
398
445
|
failure: {
|
|
399
446
|
code: 'AGENTS_RUNNING',
|
|
400
447
|
message:
|
|
@@ -405,20 +452,6 @@ async function installLatestIfNeeded(options = {}) {
|
|
|
405
452
|
};
|
|
406
453
|
}
|
|
407
454
|
}
|
|
408
|
-
const check = await checkForUpdate({ installedVersion: beforeVersion, source: declaredSource(path.dirname(profileNodeModules), name) });
|
|
409
|
-
if (!force && !check.updateAvailable) {
|
|
410
|
-
return {
|
|
411
|
-
installed: false,
|
|
412
|
-
skipped: true,
|
|
413
|
-
beforeVersion,
|
|
414
|
-
installedVersion: beforeVersion,
|
|
415
|
-
source: check.source,
|
|
416
|
-
command: null,
|
|
417
|
-
output: '',
|
|
418
|
-
failure: null,
|
|
419
|
-
check,
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
455
|
const result = await installLatest({ ...options, version: check.latestVersion });
|
|
423
456
|
return { ...result, check };
|
|
424
457
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-mobile-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.63",
|
|
4
4
|
"description": "Mobile API (/api/mobile/v1) and relay client for the DSH mobile app — desktop profile build (0.2.0-rc.2 compatible, peers resolved by the host)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|