dsh-waifu-agents 0.2.0 → 0.2.2
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/client.js +83 -5
- package/index.js +177 -28
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -149,6 +149,9 @@ window.__ModuleLoader__.load({
|
|
|
149
149
|
updateInstallingFrom: '正在从 {registry} 安装(第 {index}/{total} 个源)…',
|
|
150
150
|
updateApplying: '正在应用配置,请稍候…',
|
|
151
151
|
updateCancelling: '正在停止安装…',
|
|
152
|
+
updateRemoving: '正在卸载旧版本…',
|
|
153
|
+
updateReinstalling: '正在安装新版本…',
|
|
154
|
+
updateHandedOff: '插件已随更新重启;到「插件」页确认版本即可。',
|
|
152
155
|
updateWaiting: '安装进行中,别关掉这个窗口',
|
|
153
156
|
updateElapsed: '已用 {s} 秒',
|
|
154
157
|
updateQuiet: '已经 {s} 秒没等到平台的进度回报,但 pnpm 还在跑',
|
|
@@ -307,6 +310,7 @@ window.__ModuleLoader__.load({
|
|
|
307
310
|
gone, so the strings are gone: a key left behind is a key a future
|
|
308
311
|
reader will wire up again. */
|
|
309
312
|
teamUnitPerson: '人',
|
|
313
|
+
teamUnresolved: '解析失败',
|
|
310
314
|
teamUnitGroup: '组',
|
|
311
315
|
teamGone: '已经不存在了',
|
|
312
316
|
teamClipped: '超出上限被截断',
|
|
@@ -394,6 +398,9 @@ window.__ModuleLoader__.load({
|
|
|
394
398
|
updateInstallingFrom: 'Installing from {registry} (source {index} of {total})…',
|
|
395
399
|
updateApplying: 'Applying the configuration, please wait…',
|
|
396
400
|
updateCancelling: 'Stopping the installation…',
|
|
401
|
+
updateRemoving: 'Removing the old version…',
|
|
402
|
+
updateReinstalling: 'Installing the new version…',
|
|
403
|
+
updateHandedOff: 'The plugin restarted with the update; confirm the version on the Plugins page.',
|
|
397
404
|
updateWaiting: 'Installing — leave this window open',
|
|
398
405
|
updateElapsed: '{s}s elapsed',
|
|
399
406
|
updateQuiet: 'no progress report from the platform for {s}s, but pnpm is still running',
|
|
@@ -546,6 +553,7 @@ window.__ModuleLoader__.load({
|
|
|
546
553
|
teamAddPersona: 'add persona',
|
|
547
554
|
teamAddGroup: 'add group',
|
|
548
555
|
teamUnitPerson: 'members',
|
|
556
|
+
teamUnresolved: 'unresolved',
|
|
549
557
|
teamUnitGroup: 'group',
|
|
550
558
|
teamGone: 'no longer exists',
|
|
551
559
|
teamClipped: 'clipped by the cap',
|
|
@@ -1399,7 +1407,16 @@ window.__ModuleLoader__.load({
|
|
|
1399
1407
|
const phase = String(job.phase ?? 'starting');
|
|
1400
1408
|
let floor = 3;
|
|
1401
1409
|
let ceiling = 10;
|
|
1402
|
-
if (phase === '
|
|
1410
|
+
if (phase === 'removing') {
|
|
1411
|
+
// The self-removal before the reinstall: brief, but not instant.
|
|
1412
|
+
floor = 6;
|
|
1413
|
+
ceiling = 16;
|
|
1414
|
+
} else if (phase === 'reinstalling') {
|
|
1415
|
+
// The fresh install after the self-removal, before the platform's own
|
|
1416
|
+
// per-registry phases take over and redraw the band.
|
|
1417
|
+
floor = 14;
|
|
1418
|
+
ceiling = 24;
|
|
1419
|
+
} else if (phase === 'installing') {
|
|
1403
1420
|
// One band per registry tried, so a fallback to a second source is
|
|
1404
1421
|
// visible as progress instead of as a bar going backwards.
|
|
1405
1422
|
const index = Math.max(1, Number(job.attempt?.index ?? 1) || 1);
|
|
@@ -1429,10 +1446,13 @@ window.__ModuleLoader__.load({
|
|
|
1429
1446
|
*/
|
|
1430
1447
|
function progressHeadline(job, t) {
|
|
1431
1448
|
if (job === null || job === undefined) return '';
|
|
1449
|
+
if (job.handedOff === true) return t('updateHandedOff');
|
|
1432
1450
|
if (job.done === true) {
|
|
1433
1451
|
return typeof job.error === 'string' && job.error !== '' ? t('updateFailedTitle') : t('updateDoneTitle');
|
|
1434
1452
|
}
|
|
1435
1453
|
const phase = String(job.phase ?? 'starting');
|
|
1454
|
+
if (phase === 'removing') return t('updateRemoving');
|
|
1455
|
+
if (phase === 'reinstalling') return t('updateReinstalling');
|
|
1436
1456
|
if (phase === 'installing') {
|
|
1437
1457
|
const attempt = job.attempt;
|
|
1438
1458
|
const total = Number(attempt?.total ?? 1) || 1;
|
|
@@ -1493,6 +1513,7 @@ window.__ModuleLoader__.load({
|
|
|
1493
1513
|
.replace('{s}', String(seconds));
|
|
1494
1514
|
}
|
|
1495
1515
|
if (typeof job?.error === 'string' && job.error !== '') return t('updateStripFailed');
|
|
1516
|
+
if (job?.handedOff === true) return t('updateHandedOff');
|
|
1496
1517
|
return t('updateStripDone').replace('{v}', String(job?.result?.version ?? job?.version ?? ''));
|
|
1497
1518
|
}
|
|
1498
1519
|
|
|
@@ -1545,7 +1566,17 @@ window.__ModuleLoader__.load({
|
|
|
1545
1566
|
{ style: { ...L.dialog, maxWidth: '480px', width: '100%' } },
|
|
1546
1567
|
h('span', { style: L.rowTitle }, t('updateProgressTitle')),
|
|
1547
1568
|
h('div', { style: L.progressTrack }, h('div', { style: fill })),
|
|
1548
|
-
h(
|
|
1569
|
+
h(
|
|
1570
|
+
'span',
|
|
1571
|
+
{ style: failed ? L.error : L.name },
|
|
1572
|
+
failed
|
|
1573
|
+
? String(job.error)
|
|
1574
|
+
: job.handedOff === true
|
|
1575
|
+
? t('updateHandedOff')
|
|
1576
|
+
: done
|
|
1577
|
+
? progressOutcome(job, t)
|
|
1578
|
+
: progressHeadline(job, t),
|
|
1579
|
+
),
|
|
1549
1580
|
note === '' ? null : h('span', { style: L.meta }, note),
|
|
1550
1581
|
exit === '' ? null : h('span', { style: L.meta }, exit),
|
|
1551
1582
|
log === ''
|
|
@@ -2118,6 +2149,25 @@ window.__ModuleLoader__.load({
|
|
|
2118
2149
|
(one.kind === 'group' && groups.includes(one.ref))
|
|
2119
2150
|
),
|
|
2120
2151
|
);
|
|
2152
|
+
/*
|
|
2153
|
+
* The header counts PEOPLE, not picked rows: one group entry can carry
|
|
2154
|
+
* three wives, so `selection.length` printed 「1 人」 for a trio — the
|
|
2155
|
+
* exact number the user then corrected by hand. Resolved the same way
|
|
2156
|
+
* the Host expands: `group:` equality for group entries, the id for
|
|
2157
|
+
* persona entries (the picker only ever stores ids). A stale entry
|
|
2158
|
+
* resolves to nothing here, which is consistent with the grey block
|
|
2159
|
+
* below that says it is gone.
|
|
2160
|
+
*/
|
|
2161
|
+
const picked = new Set();
|
|
2162
|
+
for (const one of draft.selection) {
|
|
2163
|
+
if (one.kind === 'group') {
|
|
2164
|
+
for (const row of rows) {
|
|
2165
|
+
if (one.ref !== '' && groupOf(row) === one.ref) picked.add(row.id);
|
|
2166
|
+
}
|
|
2167
|
+
} else if (rows.some((row) => row.id === one.ref)) {
|
|
2168
|
+
picked.add(one.ref);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2121
2171
|
const named = String(draft.name ?? '') !== '';
|
|
2122
2172
|
return h(
|
|
2123
2173
|
'div',
|
|
@@ -2174,7 +2224,7 @@ window.__ModuleLoader__.load({
|
|
|
2174
2224
|
'div',
|
|
2175
2225
|
{ style: L.sectionHead },
|
|
2176
2226
|
h('span', { style: L.rowTitle }, t('teamSelection')),
|
|
2177
|
-
h('span', { style: L.meta }, `${
|
|
2227
|
+
h('span', { style: L.meta }, `${picked.size} ${t('teamUnitPerson')}`),
|
|
2178
2228
|
h('span', { style: L.hint }, t('teamSelectionHint')),
|
|
2179
2229
|
),
|
|
2180
2230
|
draft.selection.length === 0
|
|
@@ -2326,7 +2376,19 @@ window.__ModuleLoader__.load({
|
|
|
2326
2376
|
'div',
|
|
2327
2377
|
{ style: L.head },
|
|
2328
2378
|
h('span', { style: L.name }, entry.name),
|
|
2329
|
-
|
|
2379
|
+
/*
|
|
2380
|
+
* Resolved headcount, not selection entries: a team picked as one
|
|
2381
|
+
* group of three is 「3 人」, and a drifted group reports the
|
|
2382
|
+
* failure instead of a wrong number — the full error renders
|
|
2383
|
+
* right below in TeamPreview.
|
|
2384
|
+
*/
|
|
2385
|
+
h(
|
|
2386
|
+
'span',
|
|
2387
|
+
{ style: L.meta },
|
|
2388
|
+
entry.preview?.ok === true
|
|
2389
|
+
? `${entry.preview.members.length} ${t('teamUnitPerson')}`
|
|
2390
|
+
: t('teamUnresolved'),
|
|
2391
|
+
),
|
|
2330
2392
|
// No preset, no switch: a default has to name something the
|
|
2331
2393
|
// registry holds, and this team is not registered yet (the sync
|
|
2332
2394
|
// is in flight, or it failed). Saying which is `teamNoPreset`.
|
|
@@ -3323,7 +3385,23 @@ window.__ModuleLoader__.load({
|
|
|
3323
3385
|
const poll = () => {
|
|
3324
3386
|
post({ op: 'updateProgress', requestId })
|
|
3325
3387
|
.then((payload) => {
|
|
3326
|
-
if (!alive
|
|
3388
|
+
if (!alive) return;
|
|
3389
|
+
if (payload.idle === true) {
|
|
3390
|
+
/*
|
|
3391
|
+
* The host no longer knows this job. That is the two-step
|
|
3392
|
+
* update succeeding the loud way: the removal plus reinstall
|
|
3393
|
+
* ended in an HMR reload, the module that owned the job is
|
|
3394
|
+
* gone, and the fresh module answering polls has never heard
|
|
3395
|
+
* of this requestId. Report it as a hand-off to the restart
|
|
3396
|
+
* instead of letting the bar sit on its last phase forever.
|
|
3397
|
+
*/
|
|
3398
|
+
setJob((prev) =>
|
|
3399
|
+
prev !== null && prev !== undefined && prev.done !== true
|
|
3400
|
+
? { ...prev, done: true, handedOff: true, error: null }
|
|
3401
|
+
: prev,
|
|
3402
|
+
);
|
|
3403
|
+
return;
|
|
3404
|
+
}
|
|
3327
3405
|
setJob((prev) => (prev === null || prev === undefined ? prev : { ...prev, ...payload }));
|
|
3328
3406
|
})
|
|
3329
3407
|
.catch(() => {
|
package/index.js
CHANGED
|
@@ -40,12 +40,31 @@
|
|
|
40
40
|
*
|
|
41
41
|
* `updateApply` is what makes a published fix reachable without a shell: the
|
|
42
42
|
* panel posts it, `latestVersion` asks the registry, and the install goes
|
|
43
|
-
* through the very service the Plugins page uses
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
43
|
+
* through the very service the Plugins page uses. Three layers, tried in
|
|
44
|
+
* order:
|
|
45
|
+
*
|
|
46
|
+
* 1. TARBALL (updates of an installed package): the new version's tarball URL
|
|
47
|
+
* is the one spec shape the manager's HMR precheck cannot match by name —
|
|
48
|
+
* its package name is only known after pnpm runs — so the install reaches
|
|
49
|
+
* pnpm over the installed copy, and the HMR re-composition replaces the
|
|
50
|
+
* running plugin in place. No removal, no restart in the success path.
|
|
51
|
+
* This also rewrites the profile's saved dependency from a version range
|
|
52
|
+
* to the exact URL; the next update's URL differs again, so nothing is
|
|
53
|
+
* ever refused as already-installed on this road twice.
|
|
54
|
+
* 2. NAME (fresh installs, and the two-step's second half): the plain
|
|
55
|
+
* `name@version` spec keeps a fresh install's saved dependency a clean
|
|
56
|
+
* range.
|
|
57
|
+
* 3. TWO-STEP (fallback if the tarball road is somehow refused with
|
|
58
|
+
* `already-installed`): remove the running bundle, then install the new
|
|
59
|
+
* version from the same fiber — a resolved promise never interrupts the
|
|
60
|
+
* fiber that awaited it. Whether the fiber survives its own removal is
|
|
61
|
+
* what real-machine use finds out; every failure is caught and reported
|
|
62
|
+
* with the manual remove-then-install on the Plugins page as the last
|
|
63
|
+
* resort.
|
|
64
|
+
*
|
|
65
|
+
* The personas directory and `teams.json` live outside the package, so no
|
|
66
|
+
* path through any layer — success, half-done, or failure — touches user
|
|
67
|
+
* data.
|
|
49
68
|
*
|
|
50
69
|
* No configuration is required: `resolveDir` picks the directory from the row
|
|
51
70
|
* config, then from `<home>/personas`, then — on a machine that does not have it
|
|
@@ -228,6 +247,50 @@ async function latestVersion(registry) {
|
|
|
228
247
|
return { version: latest, registry: base };
|
|
229
248
|
}
|
|
230
249
|
|
|
250
|
+
/**
|
|
251
|
+
* The tarball URL of a published version.
|
|
252
|
+
*
|
|
253
|
+
* This is the one spec shape the manager's HMR precheck CANNOT refuse with
|
|
254
|
+
* `already-installed`: the precheck matches specs it can name before pnpm
|
|
255
|
+
* runs — a registry name, a local directory, or the exact git/tarball spec the
|
|
256
|
+
* dependency was saved with — and a fresh tarball URL names a package only
|
|
257
|
+
* after pnpm has fetched it. Installing version N+1 from its own tarball
|
|
258
|
+
* therefore reaches pnpm over an installed version N, and the HMR
|
|
259
|
+
* re-composition replaces the running plugin in place (the documented
|
|
260
|
+
* behavior). The abbreviated packument names the URL authoritatively
|
|
261
|
+
* (`dist.tarball`), so one small GET asks the registry; a registry that will
|
|
262
|
+
* not answer falls back to the npm tarball path convention, which every
|
|
263
|
+
* registry this plugin can be pointed at follows
|
|
264
|
+
* (`<base>/<name>/-/<name>-<version>.tgz`). Constructing never throws, so this
|
|
265
|
+
* helper always returns a usable URL.
|
|
266
|
+
* @param {string} [registry] registry base URL; the row config may override it
|
|
267
|
+
* @param {string} version exact published version
|
|
268
|
+
* @returns {Promise<string>}
|
|
269
|
+
*/
|
|
270
|
+
async function tarballUrlOf(registry, version) {
|
|
271
|
+
const base = String(registry ?? "").trim() || DEFAULT_REGISTRY;
|
|
272
|
+
const construct = () =>
|
|
273
|
+
new URL(
|
|
274
|
+
`${PACKAGE_NAME}/-/${PACKAGE_NAME}-${version}.tgz`,
|
|
275
|
+
base.endsWith("/") ? base : `${base}/`,
|
|
276
|
+
).href;
|
|
277
|
+
if (typeof fetch !== "function") return construct();
|
|
278
|
+
try {
|
|
279
|
+
const url = new URL(PACKAGE_NAME, base.endsWith("/") ? base : `${base}/`)
|
|
280
|
+
.href;
|
|
281
|
+
const response = await fetch(url, {
|
|
282
|
+
headers: { accept: "application/vnd.npm.install-v1+json" },
|
|
283
|
+
signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS),
|
|
284
|
+
});
|
|
285
|
+
if (!response.ok) return construct();
|
|
286
|
+
const meta = await response.json();
|
|
287
|
+
const tarball = meta?.versions?.[version]?.dist?.tarball;
|
|
288
|
+
return typeof tarball === "string" && tarball !== "" ? tarball : construct();
|
|
289
|
+
} catch {
|
|
290
|
+
return construct();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
231
294
|
/**
|
|
232
295
|
* The shared skeleton: a reserved name in the persona directory that is never a
|
|
233
296
|
* wife. It is the frame a `template:`-using persona is poured into, with
|
|
@@ -4295,20 +4358,20 @@ export function apply(ctx, config = {}) {
|
|
|
4295
4358
|
* Install the newest published version, through the same service the Plugins
|
|
4296
4359
|
* page uses.
|
|
4297
4360
|
*
|
|
4298
|
-
* The exact version is passed, never a range.
|
|
4299
|
-
*
|
|
4300
|
-
*
|
|
4301
|
-
*
|
|
4302
|
-
*
|
|
4303
|
-
*
|
|
4304
|
-
*
|
|
4305
|
-
*
|
|
4306
|
-
* first
|
|
4307
|
-
* `
|
|
4308
|
-
*
|
|
4309
|
-
*
|
|
4310
|
-
*
|
|
4311
|
-
*
|
|
4361
|
+
* The exact version is passed, never a range. The spec it installs FROM
|
|
4362
|
+
* depends on what is already on disk (see the comment at the choice below):
|
|
4363
|
+
* an update goes through the new version's tarball URL — the spec shape the
|
|
4364
|
+
* HMR precheck cannot refuse — and a fresh install keeps the plain
|
|
4365
|
+
* `name@version`, whose install rewrites the profile's declared range.
|
|
4366
|
+
*
|
|
4367
|
+
* `minimumReleaseAge`: a registry-name install is subject to the 24h
|
|
4368
|
+
* freshness policy and must be recorded in the workspace's
|
|
4369
|
+
* `minimumReleaseAgeExclude` first, or the lockfile check refuses outright
|
|
4370
|
+
* with `ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION`. A tarball URL install
|
|
4371
|
+
* fetches the package file directly and plausibly never consults publish
|
|
4372
|
+
* times — expected but NOT verified against the manager's pnpm invocation,
|
|
4373
|
+
* so the failure, should it come, is surfaced verbatim rather than promised
|
|
4374
|
+
* away.
|
|
4312
4375
|
*
|
|
4313
4376
|
* Returns as soon as the install is STARTED, not when it finishes, and hands
|
|
4314
4377
|
* back the `requestId` that makes the platform publish progress for it. The
|
|
@@ -4375,6 +4438,25 @@ export function apply(ctx, config = {}) {
|
|
|
4375
4438
|
},
|
|
4376
4439
|
};
|
|
4377
4440
|
}
|
|
4441
|
+
/*
|
|
4442
|
+
* Which spec to install from, decided by what is already on disk.
|
|
4443
|
+
*
|
|
4444
|
+
* An UPDATE of an installed package installs from the new version's
|
|
4445
|
+
* tarball URL: that is the one spec shape the HMR precheck cannot match by
|
|
4446
|
+
* name, so it reaches pnpm over the installed copy and the re-composition
|
|
4447
|
+
* replaces the running plugin in place — no removal, no fiber gamble. It
|
|
4448
|
+
* also rewrites the profile's saved dependency from a version range to the
|
|
4449
|
+
* exact URL, which is deliberate: the next update's URL differs again, so
|
|
4450
|
+
* no future install is ever refused as already-installed.
|
|
4451
|
+
*
|
|
4452
|
+
* A FRESH install (nothing readable on disk) keeps the registry name:
|
|
4453
|
+
* there is nothing installed for the precheck to refuse, and the saved
|
|
4454
|
+
* spec stays a plain dependency. If that judgement is wrong — the copy on
|
|
4455
|
+
* disk was unreadable, not absent — the name spec is refused with
|
|
4456
|
+
* `already-installed` and the two-step below takes over.
|
|
4457
|
+
*/
|
|
4458
|
+
let spec = `${PACKAGE_NAME}@${version}`;
|
|
4459
|
+
if (before !== null) spec = await tarballUrlOf(config.registry, version);
|
|
4378
4460
|
/*
|
|
4379
4461
|
* The id is minted here, or accepted from the panel when it sent one. It
|
|
4380
4462
|
* exists because the platform publishes progress ONLY for an installation
|
|
@@ -4411,25 +4493,92 @@ export function apply(ctx, config = {}) {
|
|
|
4411
4493
|
*/
|
|
4412
4494
|
void (async () => {
|
|
4413
4495
|
try {
|
|
4414
|
-
const outcome = await manager.installBundle(
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
);
|
|
4418
|
-
job.done = true;
|
|
4496
|
+
const outcome = await manager.installBundle(spec, {
|
|
4497
|
+
enabled: true,
|
|
4498
|
+
requestId,
|
|
4499
|
+
});
|
|
4419
4500
|
if (outcome?.application === "failed") {
|
|
4501
|
+
/*
|
|
4502
|
+
* The tarball spec should never be refused with `already-installed`
|
|
4503
|
+
* (its package name is only known after pnpm runs), and a fresh
|
|
4504
|
+
* install has nothing installed to refuse. Landing here anyway
|
|
4505
|
+
* means the manager named the installed package from the spec
|
|
4506
|
+
* after all, or the disk judgement above was wrong — either way the
|
|
4507
|
+
* documented cross-version path is remove-then-install, and the
|
|
4508
|
+
* fiber that awaited the removal is never interrupted by the
|
|
4509
|
+
* resolution itself, so this fallback performs the two-step right
|
|
4510
|
+
* here: remove, then install the new version from the same fiber
|
|
4511
|
+
* under its plain registry name. Whether the host lets the fiber
|
|
4512
|
+
* survive its own removal is what real-machine use finds out; every
|
|
4513
|
+
* way it can die is caught below and reported with the manual path
|
|
4514
|
+
* as the last resort. Nothing here can touch user data — personas
|
|
4515
|
+
* and teams.json are outside the package.
|
|
4516
|
+
*/
|
|
4517
|
+
const code = String(outcome.error?.code ?? outcome.error?.message ?? "");
|
|
4518
|
+
if (code.includes("already-installed")) {
|
|
4519
|
+
ctx.logger?.warn?.(
|
|
4520
|
+
`dsh-waifu-agents: installBundle refused ${version} (already-installed); performing remove-then-install.`,
|
|
4521
|
+
);
|
|
4522
|
+
job.phase = "removing";
|
|
4523
|
+
try {
|
|
4524
|
+
await manager.removeBundle(PACKAGE_NAME);
|
|
4525
|
+
} catch (removeFailure) {
|
|
4526
|
+
job.done = true;
|
|
4527
|
+
job.error = `卸载旧版本失败(${removeFailure?.message ?? removeFailure}),已安装的版本没有变动。请到「插件」页手动卸载后重新安装 ${version}。`;
|
|
4528
|
+
return;
|
|
4529
|
+
}
|
|
4530
|
+
/*
|
|
4531
|
+
* Still running — the fiber survived its own removal. The spec no
|
|
4532
|
+
* longer names an installed package, so the precheck lets the new
|
|
4533
|
+
* version through to pnpm.
|
|
4534
|
+
*/
|
|
4535
|
+
job.phase = "reinstalling";
|
|
4536
|
+
let retry = null;
|
|
4537
|
+
try {
|
|
4538
|
+
retry = await manager.installBundle(`${PACKAGE_NAME}@${version}`, {
|
|
4539
|
+
enabled: true,
|
|
4540
|
+
requestId,
|
|
4541
|
+
});
|
|
4542
|
+
} catch (installFailure) {
|
|
4543
|
+
job.done = true;
|
|
4544
|
+
job.error = `旧版本已卸载,但安装 ${version} 失败:${installFailure?.message ?? installFailure}。请到「插件」页重新安装——人设目录(personas)和团队配置(teams.json)不在包里,不受影响。`;
|
|
4545
|
+
return;
|
|
4546
|
+
}
|
|
4547
|
+
if (retry?.application === "failed") {
|
|
4548
|
+
job.done = true;
|
|
4549
|
+
job.error = `旧版本已卸载,但安装 ${version} 失败:${retry.error?.message ?? retry.error?.code ?? JSON.stringify(retry.error)}。请到「插件」页重新安装——人设目录(personas)和团队配置(teams.json)不在包里,不受影响。`;
|
|
4550
|
+
if (job.log === "") job.log = String(retry?.packageResult?.output ?? "");
|
|
4551
|
+
return;
|
|
4552
|
+
}
|
|
4553
|
+
job.done = true;
|
|
4554
|
+
job.result = {
|
|
4555
|
+
ok: true,
|
|
4556
|
+
version,
|
|
4557
|
+
previous: before,
|
|
4558
|
+
application: retry?.application ?? null,
|
|
4559
|
+
changed: retry?.changed === true,
|
|
4560
|
+
installed: readOwnVersion(),
|
|
4561
|
+
log: String(retry?.packageResult?.output ?? ""),
|
|
4562
|
+
twoStep: true,
|
|
4563
|
+
};
|
|
4564
|
+
return;
|
|
4565
|
+
}
|
|
4566
|
+
job.done = true;
|
|
4420
4567
|
job.error = `安装失败:${outcome.error?.message ?? outcome.error?.code ?? JSON.stringify(outcome.error)}`;
|
|
4421
4568
|
// The manager's own transcript is the useful half of this failure,
|
|
4422
4569
|
// and it is only here if the streaming feed never spoke.
|
|
4423
4570
|
if (job.log === "") job.log = String(outcome?.packageResult?.output ?? "");
|
|
4424
4571
|
return;
|
|
4425
4572
|
}
|
|
4573
|
+
job.done = true;
|
|
4426
4574
|
job.result = {
|
|
4427
4575
|
ok: true,
|
|
4428
4576
|
version,
|
|
4429
4577
|
previous: before,
|
|
4430
|
-
//
|
|
4431
|
-
//
|
|
4432
|
-
// rather than a promise this code cannot
|
|
4578
|
+
// Without HMR this is `restart-required`; with HMR a fresh install
|
|
4579
|
+
// (the two-step above included) may genuinely apply. The manager's
|
|
4580
|
+
// own word is passed through rather than a promise this code cannot
|
|
4581
|
+
// keep.
|
|
4433
4582
|
application: outcome?.application ?? null,
|
|
4434
4583
|
changed: outcome?.changed === true,
|
|
4435
4584
|
installed: readOwnVersion(),
|
package/package.json
CHANGED