dsh-plugin-update 0.2.0 → 0.3.0
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/CHANGELOG.md +49 -0
- package/README.md +483 -279
- package/dist/batch-run.d.ts +80 -0
- package/dist/batch-run.js +86 -0
- package/dist/batch.d.ts +111 -0
- package/dist/batch.js +183 -0
- package/dist/changelog-io.d.ts +56 -0
- package/dist/changelog-io.js +258 -0
- package/dist/changelog.d.ts +96 -0
- package/dist/changelog.js +287 -0
- package/dist/client.d.ts +16 -0
- package/dist/client.js +75 -1
- package/dist/commands.d.ts +59 -0
- package/dist/commands.js +60 -4
- package/dist/config.d.ts +48 -0
- package/dist/config.js +9 -1
- package/dist/diag.d.ts +125 -0
- package/dist/diag.js +243 -0
- package/dist/entry.d.ts +60 -0
- package/dist/entry.js +305 -0
- package/dist/gate.d.ts +35 -0
- package/dist/host-batch.d.ts +113 -0
- package/dist/host-batch.js +778 -0
- package/dist/host.d.ts +129 -0
- package/dist/host.js +479 -41
- package/dist/panel-batch.d.ts +151 -0
- package/dist/panel-batch.js +927 -0
- package/dist/panel.d.ts +256 -0
- package/dist/panel.js +1300 -0
- package/dist/ports.d.ts +208 -0
- package/dist/queue.d.ts +109 -0
- package/dist/queue.js +182 -0
- package/dist/reader.d.ts +84 -0
- package/dist/reader.js +6 -4
- package/dist/redaction.d.ts +120 -0
- package/dist/redaction.js +205 -0
- package/dist/service.d.ts +72 -0
- package/dist/service.js +100 -6
- package/dist/store.d.ts +229 -0
- package/dist/store.js +251 -12
- package/package.json +27 -2
package/dist/host.js
CHANGED
|
@@ -10,13 +10,126 @@ import {
|
|
|
10
10
|
resolveUpdateConfig
|
|
11
11
|
} from "./config.js";
|
|
12
12
|
import { manualCommand } from "./commands.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { buildDiag } from "./diag.js";
|
|
14
|
+
import { isVersionAllowedInChannel, validRequestId } from "./service.js";
|
|
15
|
+
import { createUpdateDiskPorts, createUpdateExecutor, createUpdateQueuePorts } from "./store.js";
|
|
15
16
|
import { createUpdateReader, defaultHomeDir, resolveProfileName, resolveTargetPackage } from "./reader.js";
|
|
17
|
+
import {
|
|
18
|
+
QUEUE_INTENT_TTL_MS,
|
|
19
|
+
cancelEnqueuedInQueue,
|
|
20
|
+
emptyQueueState,
|
|
21
|
+
enqueueInQueue,
|
|
22
|
+
isHeadOfQueue,
|
|
23
|
+
normalizeQueueState,
|
|
24
|
+
pruneExpiredIntents,
|
|
25
|
+
queuePositionOf,
|
|
26
|
+
releaseOwnerInQueue,
|
|
27
|
+
setOwnerIfFree,
|
|
28
|
+
visibleQueueFor
|
|
29
|
+
} from "./queue.js";
|
|
16
30
|
import { buildPhoneName, buildPhoneNames as buildPhoneNames2, resolveUpdateConfig as resolveUpdateConfig2 } from "./config.js";
|
|
17
31
|
import { parseEventListManifest, checkEventFields, checkEventCounts } from "./gate.js";
|
|
18
32
|
import { containingPackage, defaultHomeDir as defaultHomeDir2, profileNameValid, registrySpec, resolveTargetPackage as resolveTargetPackage2 } from "./reader.js";
|
|
33
|
+
import {
|
|
34
|
+
COPY_BUDGET_CHARS,
|
|
35
|
+
DETAIL_MAX_CHARS,
|
|
36
|
+
DIAG_DROP_ORDER,
|
|
37
|
+
DIAG_INLINE_BUDGET_BYTES,
|
|
38
|
+
DIAG_NEVER_DROP_KEYS,
|
|
39
|
+
ENDORSED_REGISTRY_HOSTS,
|
|
40
|
+
KNOWN_GAP_FILE_URL_PREFIX,
|
|
41
|
+
REDACTED_PATH,
|
|
42
|
+
REDACTED_SECRET,
|
|
43
|
+
REDACTION_RULE_NAMES,
|
|
44
|
+
diagByteLength,
|
|
45
|
+
enforceDiagBudget,
|
|
46
|
+
resolveRegistryHost,
|
|
47
|
+
sanitizeDetail,
|
|
48
|
+
sanitizeForCopy,
|
|
49
|
+
truncateToWordBoundary
|
|
50
|
+
} from "./redaction.js";
|
|
51
|
+
import {
|
|
52
|
+
DIAG_ACTIONS,
|
|
53
|
+
DIAG_KEYS,
|
|
54
|
+
DIAG_KNOWN_TYPES,
|
|
55
|
+
DIAG_STAGE_EXPECT,
|
|
56
|
+
DIAG_STAGES,
|
|
57
|
+
DIAG_VERSION,
|
|
58
|
+
buildDiag as buildDiag2,
|
|
59
|
+
deriveAction,
|
|
60
|
+
deriveRouteMethod,
|
|
61
|
+
deriveStage,
|
|
62
|
+
enforceBudget
|
|
63
|
+
} from "./diag.js";
|
|
64
|
+
import {
|
|
65
|
+
CHANGELOG_ALL_CATEGORIES,
|
|
66
|
+
CHANGELOG_FILENAME,
|
|
67
|
+
CHANGELOG_FOLDED,
|
|
68
|
+
CHANGELOG_MAX_BULLETS_PER_SECTION,
|
|
69
|
+
CHANGELOG_MAX_BULLET_CHARS,
|
|
70
|
+
CHANGELOG_MAX_CHARS,
|
|
71
|
+
CHANGELOG_MAX_ENTRIES,
|
|
72
|
+
CHANGELOG_MUST_SHOW,
|
|
73
|
+
CHANGELOG_NEUTRAL_HINT,
|
|
74
|
+
CHANGELOG_NEUTRAL_LINE,
|
|
75
|
+
changelogForUpdate,
|
|
76
|
+
hasVisibleSections,
|
|
77
|
+
isUnreleasedVersion,
|
|
78
|
+
parseChangelog,
|
|
79
|
+
renderChangelogHTML,
|
|
80
|
+
renderChangelogNeutral,
|
|
81
|
+
renderChangelogSection,
|
|
82
|
+
selectChangelogEntries
|
|
83
|
+
} from "./changelog.js";
|
|
84
|
+
import {
|
|
85
|
+
CHANGELOG_DEFAULT_REGISTRY,
|
|
86
|
+
CHANGELOG_FETCH_MAX_BYTES,
|
|
87
|
+
CHANGELOG_TAR_MAX_BYTES,
|
|
88
|
+
CHANGELOG_TARBALL_MAX_BYTES,
|
|
89
|
+
extractChangelogFromTar,
|
|
90
|
+
fetchReleaseChangelogText,
|
|
91
|
+
readInstalledChangelogText
|
|
92
|
+
} from "./changelog-io.js";
|
|
19
93
|
import { createUpdateDiskPorts as createUpdateDiskPorts2, createUpdateExecutor as createUpdateExecutor2, pathsForUpdate, resolveCliEntry } from "./store.js";
|
|
94
|
+
import { SKIPPED_FILE } from "./config.js";
|
|
95
|
+
import {
|
|
96
|
+
addSkipped,
|
|
97
|
+
clearSkipped,
|
|
98
|
+
createSkipDiskPorts,
|
|
99
|
+
isVersionSkipped,
|
|
100
|
+
MAX_SKIPPED_ENTRIES,
|
|
101
|
+
normalizeSkipped,
|
|
102
|
+
skipKey
|
|
103
|
+
} from "./store.js";
|
|
104
|
+
import {
|
|
105
|
+
compareReleaseVersions,
|
|
106
|
+
isPrereleaseVersion,
|
|
107
|
+
isVersionAllowedInChannel as isVersionAllowedInChannel2,
|
|
108
|
+
validReleaseVersion,
|
|
109
|
+
validVersion,
|
|
110
|
+
compareVersions
|
|
111
|
+
} from "./service.js";
|
|
112
|
+
import {
|
|
113
|
+
createUpdateQueuePorts as createUpdateQueuePorts2,
|
|
114
|
+
queuePathsForUpdate,
|
|
115
|
+
QUEUE_DIR_SEGMENT,
|
|
116
|
+
QUEUE_FILE,
|
|
117
|
+
QUEUE_LOCK_FILE,
|
|
118
|
+
QUEUE_LOCK_STALE_MS
|
|
119
|
+
} from "./store.js";
|
|
120
|
+
import {
|
|
121
|
+
QUEUE_INTENT_TTL_MS as QUEUE_INTENT_TTL_MS2,
|
|
122
|
+
cancelEnqueuedInQueue as cancelEnqueuedInQueue2,
|
|
123
|
+
emptyQueueState as emptyQueueState2,
|
|
124
|
+
enqueueInQueue as enqueueInQueue2,
|
|
125
|
+
isHeadOfQueue as isHeadOfQueue2,
|
|
126
|
+
normalizeQueueState as normalizeQueueState2,
|
|
127
|
+
pruneExpiredIntents as pruneExpiredIntents2,
|
|
128
|
+
queuePositionOf as queuePositionOf2,
|
|
129
|
+
releaseOwnerInQueue as releaseOwnerInQueue2,
|
|
130
|
+
setOwnerIfFree as setOwnerIfFree2,
|
|
131
|
+
visibleQueueFor as visibleQueueFor2
|
|
132
|
+
} from "./queue.js";
|
|
20
133
|
let sharedReader = null;
|
|
21
134
|
let sharedReaderKey = "";
|
|
22
135
|
let sharedDesktopPnpm = null;
|
|
@@ -96,11 +209,12 @@ async function getSharedReader(pluginId, config, overrides = {}) {
|
|
|
96
209
|
const osHome = overrides.osHome ?? homedir();
|
|
97
210
|
const homeDirDefault = defaultHomeDir(env, osHome);
|
|
98
211
|
const targetPackageName = overrides.targetPackageName ?? config.targetPackageName;
|
|
212
|
+
const releaseChannel = overrides.releaseChannel ?? config.releaseChannel ?? "stable";
|
|
99
213
|
const explicitTargetDir = typeof overrides.targetPackageDir === "string" && overrides.targetPackageDir ? String(overrides.targetPackageDir) : "";
|
|
100
214
|
const loaded = await resolveTargetPackage(targetPackageName, explicitTargetDir ? { targetPackageDir: explicitTargetDir } : {}).catch(
|
|
101
215
|
() => null
|
|
102
216
|
);
|
|
103
|
-
const runningVersion = overrides.runningVersion ?? (loaded &&
|
|
217
|
+
const runningVersion = overrides.runningVersion ?? (loaded && isVersionAllowedInChannel(loaded.manifest.version, releaseChannel) ? String(loaded.manifest.version) : null);
|
|
104
218
|
if (!runningVersion) throw unknownProfile();
|
|
105
219
|
let profileDirInput;
|
|
106
220
|
if (overrides.profileDir) {
|
|
@@ -117,9 +231,116 @@ async function getSharedReader(pluginId, config, overrides = {}) {
|
|
|
117
231
|
const confirmationTtlMs = overrides.confirmationTtlMs ?? config.confirmationTtlMs;
|
|
118
232
|
const installTimeoutMs = overrides.installTimeoutMs ?? config.installTimeoutMs;
|
|
119
233
|
const registryUrl = overrides.registryUrl ?? config.registryUrl;
|
|
120
|
-
const key = `${pluginId}\0${config.prefix}\0${runningVersion}\0${profileDirInput}\0${overrides.profileName ?? ""}\0${homeDirInput}\0${overrides.runInstall ? "exec" : ""}\0${environmentKind}\0${targetPackageName}\0${explicitTargetDir}`;
|
|
234
|
+
const key = `${pluginId}\0${config.prefix}\0${runningVersion}\0${profileDirInput}\0${overrides.profileName ?? ""}\0${homeDirInput}\0${overrides.runInstall ? "exec" : ""}\0${environmentKind}\0${targetPackageName}\0${explicitTargetDir}\0${releaseChannel}`;
|
|
121
235
|
if (sharedReader && sharedReaderKey === key) return sharedReader;
|
|
122
236
|
const disk = overrides.readJob && overrides.writeJob ? null : createUpdateDiskPorts(homeDirInput, pluginId, profileDirInput);
|
|
237
|
+
const queueTtlMs = typeof overrides.queueTtlMs === "number" && Number.isFinite(overrides.queueTtlMs) && overrides.queueTtlMs > 0 ? overrides.queueTtlMs : QUEUE_INTENT_TTL_MS;
|
|
238
|
+
const queueDisk = overrides.readQueue && overrides.writeQueue ? null : disk ? createUpdateQueuePorts(homeDirInput, profileDirInput) : null;
|
|
239
|
+
const queueNow = overrides.now ?? Date.now;
|
|
240
|
+
async function readQueueBestEffort() {
|
|
241
|
+
try {
|
|
242
|
+
if (overrides.readQueue) return normalizeQueueState(await overrides.readQueue());
|
|
243
|
+
if (queueDisk) return normalizeQueueState(await queueDisk.readQueue());
|
|
244
|
+
} catch {
|
|
245
|
+
}
|
|
246
|
+
return emptyQueueState();
|
|
247
|
+
}
|
|
248
|
+
async function writeQueueBestEffort(state) {
|
|
249
|
+
try {
|
|
250
|
+
if (overrides.writeQueue) await overrides.writeQueue(normalizeQueueState(state));
|
|
251
|
+
else if (queueDisk) await queueDisk.writeQueue(state);
|
|
252
|
+
} catch {
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function loadQueuePruned() {
|
|
256
|
+
const raw = await readQueueBestEffort();
|
|
257
|
+
const pruned = pruneExpiredIntents(raw, queueNow(), queueTtlMs);
|
|
258
|
+
if (pruned !== raw) await writeQueueBestEffort(pruned);
|
|
259
|
+
return pruned;
|
|
260
|
+
}
|
|
261
|
+
const baseTryAcquire = overrides.tryAcquireLock ?? disk?.tryAcquireLock;
|
|
262
|
+
const baseRelease = overrides.releaseLock ?? disk?.releaseLock;
|
|
263
|
+
const globalTryAcquire = overrides.tryAcquireGlobalLock ? (lockId) => overrides.tryAcquireGlobalLock(lockId, pluginId, { timeoutMs: installTimeoutMs }) : queueDisk ? (lockId) => queueDisk.tryAcquireGlobalLock(lockId, pluginId, { timeoutMs: installTimeoutMs }) : void 0;
|
|
264
|
+
const globalRelease = overrides.releaseGlobalLock ?? queueDisk?.releaseGlobalLock;
|
|
265
|
+
async function combinedTryAcquire(lockId) {
|
|
266
|
+
try {
|
|
267
|
+
if (globalTryAcquire && !await globalTryAcquire(lockId)) return false;
|
|
268
|
+
if (baseTryAcquire && !await baseTryAcquire(lockId)) {
|
|
269
|
+
if (globalRelease) {
|
|
270
|
+
try {
|
|
271
|
+
await globalRelease(lockId);
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
return true;
|
|
278
|
+
} catch {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function combinedRelease(lockId) {
|
|
283
|
+
try {
|
|
284
|
+
if (baseRelease) await baseRelease(lockId);
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
if (globalRelease) await globalRelease(lockId);
|
|
289
|
+
} catch {
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
const current = await readQueueBestEffort();
|
|
293
|
+
const next = releaseOwnerInQueue(current, pluginId, lockId);
|
|
294
|
+
if (next.released) await writeQueueBestEffort(next.state);
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
let memJobFallback = null;
|
|
299
|
+
async function mirroredWriteJob(job) {
|
|
300
|
+
if (overrides.writeJob) await overrides.writeJob(job);
|
|
301
|
+
else memJobFallback = job;
|
|
302
|
+
try {
|
|
303
|
+
if (!job) return;
|
|
304
|
+
const current = await readQueueBestEffort();
|
|
305
|
+
const pruned = pruneExpiredIntents(current, queueNow(), queueTtlMs);
|
|
306
|
+
if (job.state === "installing" || job.state === "verifying") {
|
|
307
|
+
let next = pruned;
|
|
308
|
+
if (next.owner && next.owner.jobId === job.id) {
|
|
309
|
+
next = {
|
|
310
|
+
version: 1,
|
|
311
|
+
owner: {
|
|
312
|
+
...next.owner,
|
|
313
|
+
requestId: job.requestId ?? next.owner.requestId,
|
|
314
|
+
targetVersion: job.targetVersion ?? next.owner.targetVersion
|
|
315
|
+
},
|
|
316
|
+
waiting: next.waiting.filter((e) => !(e.pluginId === pluginId && (e.requestId ?? null) === (job.requestId ?? null)))
|
|
317
|
+
};
|
|
318
|
+
} else {
|
|
319
|
+
let base = next;
|
|
320
|
+
const age = queueNow() - (typeof base.owner?.startedAt === "number" ? base.owner.startedAt : 0);
|
|
321
|
+
if (base.owner && age > installTimeoutMs) base = { version: 1, owner: null, waiting: base.waiting };
|
|
322
|
+
next = setOwnerIfFree(base, {
|
|
323
|
+
pluginId,
|
|
324
|
+
jobId: job.id,
|
|
325
|
+
requestId: job.requestId,
|
|
326
|
+
targetVersion: job.targetVersion,
|
|
327
|
+
startedAt: queueNow()
|
|
328
|
+
}).state;
|
|
329
|
+
}
|
|
330
|
+
if (next !== pruned) await writeQueueBestEffort(next);
|
|
331
|
+
else if (pruned !== current) await writeQueueBestEffort(pruned);
|
|
332
|
+
} else {
|
|
333
|
+
const released = releaseOwnerInQueue(pruned, pluginId, job.id);
|
|
334
|
+
const out = released.released ? released.state : pruned;
|
|
335
|
+
if (out !== current) await writeQueueBestEffort(out);
|
|
336
|
+
}
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async function mirroredReadJob() {
|
|
341
|
+
if (overrides.readJob) return await overrides.readJob() ?? null;
|
|
342
|
+
return memJobFallback;
|
|
343
|
+
}
|
|
123
344
|
const defaultRun = createUpdateExecutor({
|
|
124
345
|
profileName: overrides.profileName ?? null,
|
|
125
346
|
environmentKind,
|
|
@@ -137,32 +358,37 @@ async function getSharedReader(pluginId, config, overrides = {}) {
|
|
|
137
358
|
pluginId,
|
|
138
359
|
log: emitInstallLog
|
|
139
360
|
});
|
|
140
|
-
sharedReader =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
361
|
+
sharedReader = Object.assign(
|
|
362
|
+
createUpdateReader({
|
|
363
|
+
runningVersion,
|
|
364
|
+
profileDir: profileDirInput,
|
|
365
|
+
pluginId,
|
|
366
|
+
profileName: overrides.profileName ?? void 0,
|
|
367
|
+
homeDir: overrides.homeDir,
|
|
368
|
+
targetPackageDir: explicitTargetDir || void 0,
|
|
369
|
+
env,
|
|
370
|
+
osHome,
|
|
371
|
+
fetchImpl: overrides.fetchImpl,
|
|
372
|
+
now: overrides.now,
|
|
373
|
+
randomId: overrides.randomId,
|
|
374
|
+
nodeVersion: overrides.nodeVersion,
|
|
375
|
+
environmentKind,
|
|
376
|
+
targetPackageName,
|
|
377
|
+
registryUrl,
|
|
378
|
+
checkTimeoutMs,
|
|
379
|
+
confirmationTtlMs,
|
|
380
|
+
releaseChannel,
|
|
381
|
+
readInstalled: overrides.readInstalled,
|
|
382
|
+
readJob: mirroredReadJob,
|
|
383
|
+
writeJob: mirroredWriteJob,
|
|
384
|
+
tryAcquireLock: combinedTryAcquire,
|
|
385
|
+
releaseLock: combinedRelease,
|
|
386
|
+
backupJob: overrides.backupJob ?? disk?.backupJob,
|
|
387
|
+
runInstall: overrides.runInstall ?? defaultRun
|
|
388
|
+
}),
|
|
389
|
+
// 跨插件队列的面板侧读写口(#15):与本次解析出的使用范围绑定,面板经电话可选参数消费。
|
|
390
|
+
{ readQueuePruned: loadQueuePruned, writeQueueState: writeQueueBestEffort, queueTtlMs, queueNow }
|
|
391
|
+
);
|
|
166
392
|
sharedReaderKey = key;
|
|
167
393
|
return sharedReader;
|
|
168
394
|
}
|
|
@@ -210,6 +436,22 @@ async function snapWithManual(reader, snapshot, config) {
|
|
|
210
436
|
return { snapshot, manual: null };
|
|
211
437
|
}
|
|
212
438
|
}
|
|
439
|
+
async function readEnvForPanel(reader) {
|
|
440
|
+
try {
|
|
441
|
+
if (typeof reader.readEnv !== "function") return null;
|
|
442
|
+
const env = await reader.readEnv();
|
|
443
|
+
if (!env || typeof env !== "object") return null;
|
|
444
|
+
return {
|
|
445
|
+
profileName: typeof env.profileName === "string" && env.profileName ? env.profileName : null,
|
|
446
|
+
environmentKind: typeof env.environmentKind === "string" && env.environmentKind ? env.environmentKind : null
|
|
447
|
+
};
|
|
448
|
+
} catch {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function wantsEnv(args) {
|
|
453
|
+
return !!args && args["includeEnv"] === true;
|
|
454
|
+
}
|
|
213
455
|
let phoneLogCtx = null;
|
|
214
456
|
function emitInstallLog(level, event, fields) {
|
|
215
457
|
try {
|
|
@@ -217,7 +459,19 @@ function emitInstallLog(level, event, fields) {
|
|
|
217
459
|
} catch {
|
|
218
460
|
}
|
|
219
461
|
}
|
|
220
|
-
function
|
|
462
|
+
function queueArgsOf(args) {
|
|
463
|
+
return {
|
|
464
|
+
includeQueue: !!args && args["includeQueue"] === true,
|
|
465
|
+
showOthers: !!args && args["showOthers"] === true
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function checkExpiredError() {
|
|
469
|
+
return Object.assign(new Error("check-expired"), { code: "check-expired" });
|
|
470
|
+
}
|
|
471
|
+
function updateBusyError() {
|
|
472
|
+
return Object.assign(new Error("update-busy"), { code: "update-busy" });
|
|
473
|
+
}
|
|
474
|
+
function loggedPhone(method, kind, pluginId, fn, diagCtx) {
|
|
221
475
|
return async function(args) {
|
|
222
476
|
const t0 = Date.now();
|
|
223
477
|
const emit = (level, event, fields) => {
|
|
@@ -226,15 +480,47 @@ function loggedPhone(method, kind, pluginId, fn) {
|
|
|
226
480
|
} catch {
|
|
227
481
|
}
|
|
228
482
|
};
|
|
483
|
+
const safeArgs = args && typeof args === "object" ? args : {};
|
|
229
484
|
try {
|
|
230
|
-
const out = await fn(
|
|
485
|
+
const out = await fn(safeArgs);
|
|
231
486
|
const snapshot = out && out.snapshot ? out.snapshot : out;
|
|
232
487
|
emit("info", "host.call", { method, latencyMs: Date.now() - t0, ok: true, kind, pluginId });
|
|
233
|
-
return {
|
|
488
|
+
return {
|
|
489
|
+
ok: true,
|
|
490
|
+
snapshot,
|
|
491
|
+
manual: out && out.snapshot ? out.manual ?? null : null,
|
|
492
|
+
receipt: out && out.receipt ? out.receipt : null,
|
|
493
|
+
// 新增选填(#15):调用方没要时不带该键,老调用形状不变。
|
|
494
|
+
...out && out.queue ? { queue: out.queue } : {},
|
|
495
|
+
// 新增选填:使用范围一栏(只有调用方传了 includeEnv 才带,老调用形状不变)。
|
|
496
|
+
...out && out.env ? { env: out.env } : {}
|
|
497
|
+
};
|
|
234
498
|
} catch (error) {
|
|
235
499
|
const payload = toUpdateErrorPayload(error);
|
|
236
500
|
emit("warn", "host.call.fail", { method, kind, errorHash: hash8(String(error?.message || payload.error)), pluginId });
|
|
237
|
-
|
|
501
|
+
let diag;
|
|
502
|
+
try {
|
|
503
|
+
const cfg = diagCtx?.config;
|
|
504
|
+
const overrides = diagCtx?.readerOverrides ?? {};
|
|
505
|
+
const envRaw = overrides?.environmentKind;
|
|
506
|
+
const runningRaw = overrides?.runningVersion;
|
|
507
|
+
const built = buildDiag({
|
|
508
|
+
errorCode: payload.error,
|
|
509
|
+
phoneKind: kind,
|
|
510
|
+
error,
|
|
511
|
+
args: safeArgs,
|
|
512
|
+
targetPackageName: cfg?.targetPackageName,
|
|
513
|
+
registryUrl: cfg?.registryUrl,
|
|
514
|
+
runningVersion: typeof runningRaw === "string" ? runningRaw : void 0,
|
|
515
|
+
latestVersion: void 0,
|
|
516
|
+
environmentKind: typeof envRaw === "string" ? envRaw : void 0,
|
|
517
|
+
latencyMs: Date.now() - t0
|
|
518
|
+
});
|
|
519
|
+
if (built && typeof built === "object") diag = built;
|
|
520
|
+
} catch {
|
|
521
|
+
diag = void 0;
|
|
522
|
+
}
|
|
523
|
+
return { ok: false, ...payload, ...diag ? { diag } : {} };
|
|
238
524
|
}
|
|
239
525
|
};
|
|
240
526
|
}
|
|
@@ -252,7 +538,12 @@ function createHostUpdate(deps = {}, configInput) {
|
|
|
252
538
|
...readerOverrides,
|
|
253
539
|
profileDir: args && args.profileDir ? String(args.profileDir) : readerOverrides.profileDir
|
|
254
540
|
});
|
|
255
|
-
|
|
541
|
+
const out = await snapWithManual(reader, await reader.status(), config);
|
|
542
|
+
const env = wantsEnv(args) ? { env: await readEnvForPanel(reader) } : {};
|
|
543
|
+
const { includeQueue, showOthers } = queueArgsOf(args);
|
|
544
|
+
if (!includeQueue) return { ...out, ...env };
|
|
545
|
+
const requestId = args && typeof args.requestId === "string" ? args.requestId : void 0;
|
|
546
|
+
return { ...out, ...env, queue: visibleQueueFor(await reader.readQueuePruned(), pluginId, showOthers, requestId) };
|
|
256
547
|
}
|
|
257
548
|
async function readCheck(args) {
|
|
258
549
|
const reader = await getSharedReader(pluginId, config, {
|
|
@@ -261,21 +552,85 @@ function createHostUpdate(deps = {}, configInput) {
|
|
|
261
552
|
});
|
|
262
553
|
const result = await reader.check();
|
|
263
554
|
const withManual = await snapWithManual(reader, result.snapshot, config);
|
|
264
|
-
|
|
555
|
+
const env = wantsEnv(args) ? { env: await readEnvForPanel(reader) } : {};
|
|
556
|
+
const { includeQueue, showOthers } = queueArgsOf(args);
|
|
557
|
+
if (!includeQueue) return { snapshot: withManual.snapshot, manual: withManual.manual, receipt: result.receipt ?? null, ...env };
|
|
558
|
+
const requestId = args && typeof args.requestId === "string" ? args.requestId : void 0;
|
|
559
|
+
return {
|
|
560
|
+
snapshot: withManual.snapshot,
|
|
561
|
+
manual: withManual.manual,
|
|
562
|
+
receipt: result.receipt ?? null,
|
|
563
|
+
...env,
|
|
564
|
+
queue: visibleQueueFor(await reader.readQueuePruned(), pluginId, showOthers, requestId)
|
|
565
|
+
};
|
|
265
566
|
}
|
|
266
567
|
async function runInstall(args) {
|
|
267
568
|
const checkId = args && typeof args.checkId === "string" ? args.checkId : "";
|
|
268
569
|
const requestId = args && typeof args.requestId === "string" ? args.requestId : "";
|
|
570
|
+
const { includeQueue, showOthers } = queueArgsOf(args);
|
|
571
|
+
const enqueueOnly = !!args && args["enqueueOnly"] === true;
|
|
572
|
+
const cancelQueued = !!args && args["cancelQueued"] === true;
|
|
269
573
|
const reader = await getSharedReader(pluginId, config, {
|
|
270
574
|
...readerOverrides,
|
|
271
575
|
profileDir: args && args.profileDir ? String(args.profileDir) : readerOverrides.profileDir
|
|
272
576
|
});
|
|
273
|
-
|
|
577
|
+
const envPatch = wantsEnv(args) ? { env: await readEnvForPanel(reader) } : {};
|
|
578
|
+
if (cancelQueued) {
|
|
579
|
+
if (!validRequestId(requestId)) throw checkExpiredError();
|
|
580
|
+
const current = await reader.readQueuePruned();
|
|
581
|
+
const next = cancelEnqueuedInQueue(current, pluginId, requestId);
|
|
582
|
+
if (next.removed) await reader.writeQueueState(next.state);
|
|
583
|
+
const out = await snapWithManual(reader, await reader.status(), config);
|
|
584
|
+
return { ...out, ...envPatch, queue: visibleQueueFor(next.state, pluginId, showOthers, requestId) };
|
|
585
|
+
}
|
|
586
|
+
if (enqueueOnly) {
|
|
587
|
+
if (!validRequestId(requestId)) throw checkExpiredError();
|
|
588
|
+
const current = await reader.readQueuePruned();
|
|
589
|
+
const placed2 = enqueueInQueue(current, { pluginId, requestId, targetVersion: null, enqueuedAt: reader.queueNow() });
|
|
590
|
+
if (placed2.state !== current) await reader.writeQueueState(placed2.state);
|
|
591
|
+
const out = await snapWithManual(reader, await reader.status(), config);
|
|
592
|
+
return { ...out, ...envPatch, queue: visibleQueueFor(placed2.state, pluginId, showOthers, requestId) };
|
|
593
|
+
}
|
|
594
|
+
if (!checkId || !validRequestId(requestId)) {
|
|
595
|
+
const out = await snapWithManual(reader, await reader.install({ checkId, requestId }), config);
|
|
596
|
+
if (!includeQueue) return { ...out, ...envPatch };
|
|
597
|
+
return { ...out, ...envPatch, queue: visibleQueueFor(await reader.readQueuePruned(), pluginId, showOthers, requestId) };
|
|
598
|
+
}
|
|
599
|
+
const before = await reader.readQueuePruned();
|
|
600
|
+
const placed = enqueueInQueue(before, { pluginId, requestId, targetVersion: null, enqueuedAt: reader.queueNow() });
|
|
601
|
+
if (placed.state !== before) await reader.writeQueueState(placed.state);
|
|
602
|
+
if (queuePositionOf(placed.state, pluginId, requestId) !== 0 && !isHeadOfQueue(placed.state, pluginId, requestId)) {
|
|
603
|
+
throw updateBusyError();
|
|
604
|
+
}
|
|
605
|
+
try {
|
|
606
|
+
const out = await snapWithManual(reader, await reader.install({ checkId, requestId }), config);
|
|
607
|
+
if (!includeQueue) return { ...out, ...envPatch };
|
|
608
|
+
return { ...out, ...envPatch, queue: visibleQueueFor(await reader.readQueuePruned(), pluginId, showOthers, requestId) };
|
|
609
|
+
} catch (error) {
|
|
610
|
+
if (error?.code !== "update-busy") {
|
|
611
|
+
try {
|
|
612
|
+
const current = await reader.readQueuePruned();
|
|
613
|
+
const next = cancelEnqueuedInQueue(current, pluginId, requestId);
|
|
614
|
+
if (next.removed) await reader.writeQueueState(next.state);
|
|
615
|
+
} catch {
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
throw error;
|
|
619
|
+
}
|
|
274
620
|
}
|
|
275
621
|
const handlers = {
|
|
276
|
-
[phoneNames.updateStatus]: loggedPhone(phoneNames.updateStatus, "update-status", pluginId, readStatus
|
|
277
|
-
|
|
278
|
-
|
|
622
|
+
[phoneNames.updateStatus]: loggedPhone(phoneNames.updateStatus, "update-status", pluginId, readStatus, {
|
|
623
|
+
config,
|
|
624
|
+
readerOverrides
|
|
625
|
+
}),
|
|
626
|
+
[phoneNames.updateCheck]: loggedPhone(phoneNames.updateCheck, "update-check", pluginId, readCheck, {
|
|
627
|
+
config,
|
|
628
|
+
readerOverrides
|
|
629
|
+
}),
|
|
630
|
+
[phoneNames.updateInstall]: loggedPhone(phoneNames.updateInstall, "update-install", pluginId, runInstall, {
|
|
631
|
+
config,
|
|
632
|
+
readerOverrides
|
|
633
|
+
})
|
|
279
634
|
};
|
|
280
635
|
return { phoneNames, handlers };
|
|
281
636
|
}
|
|
@@ -288,22 +643,105 @@ function __resetSharedUpdateReaderForTests() {
|
|
|
288
643
|
sharedDesktopPnpm = null;
|
|
289
644
|
}
|
|
290
645
|
export {
|
|
646
|
+
CHANGELOG_ALL_CATEGORIES,
|
|
647
|
+
CHANGELOG_DEFAULT_REGISTRY,
|
|
648
|
+
CHANGELOG_FETCH_MAX_BYTES,
|
|
649
|
+
CHANGELOG_FILENAME,
|
|
650
|
+
CHANGELOG_FOLDED,
|
|
651
|
+
CHANGELOG_MAX_BULLETS_PER_SECTION,
|
|
652
|
+
CHANGELOG_MAX_BULLET_CHARS,
|
|
653
|
+
CHANGELOG_MAX_CHARS,
|
|
654
|
+
CHANGELOG_MAX_ENTRIES,
|
|
655
|
+
CHANGELOG_MUST_SHOW,
|
|
656
|
+
CHANGELOG_NEUTRAL_HINT,
|
|
657
|
+
CHANGELOG_NEUTRAL_LINE,
|
|
658
|
+
CHANGELOG_TARBALL_MAX_BYTES,
|
|
659
|
+
CHANGELOG_TAR_MAX_BYTES,
|
|
660
|
+
COPY_BUDGET_CHARS,
|
|
661
|
+
DETAIL_MAX_CHARS,
|
|
662
|
+
DIAG_ACTIONS,
|
|
663
|
+
DIAG_DROP_ORDER,
|
|
664
|
+
DIAG_INLINE_BUDGET_BYTES,
|
|
665
|
+
DIAG_KEYS,
|
|
666
|
+
DIAG_KNOWN_TYPES,
|
|
667
|
+
DIAG_NEVER_DROP_KEYS,
|
|
668
|
+
DIAG_STAGES,
|
|
669
|
+
DIAG_STAGE_EXPECT,
|
|
670
|
+
DIAG_VERSION,
|
|
671
|
+
ENDORSED_REGISTRY_HOSTS,
|
|
672
|
+
KNOWN_GAP_FILE_URL_PREFIX,
|
|
673
|
+
MAX_SKIPPED_ENTRIES,
|
|
674
|
+
QUEUE_DIR_SEGMENT,
|
|
675
|
+
QUEUE_FILE,
|
|
676
|
+
QUEUE_INTENT_TTL_MS2 as QUEUE_INTENT_TTL_MS,
|
|
677
|
+
QUEUE_LOCK_FILE,
|
|
678
|
+
QUEUE_LOCK_STALE_MS,
|
|
679
|
+
REDACTED_PATH,
|
|
680
|
+
REDACTED_SECRET,
|
|
681
|
+
REDACTION_RULE_NAMES,
|
|
682
|
+
SKIPPED_FILE,
|
|
291
683
|
__resetSharedUpdateReaderForTests,
|
|
684
|
+
addSkipped,
|
|
685
|
+
buildDiag2 as buildDiag,
|
|
292
686
|
buildPhoneName,
|
|
293
687
|
buildPhoneNames2 as buildPhoneNames,
|
|
688
|
+
cancelEnqueuedInQueue2 as cancelEnqueuedInQueue,
|
|
689
|
+
changelogForUpdate,
|
|
294
690
|
checkEventCounts,
|
|
295
691
|
checkEventFields,
|
|
692
|
+
clearSkipped,
|
|
693
|
+
compareReleaseVersions,
|
|
694
|
+
compareVersions,
|
|
296
695
|
containingPackage,
|
|
297
696
|
createHostUpdate,
|
|
697
|
+
createSkipDiskPorts,
|
|
298
698
|
createUpdateDiskPorts2 as createUpdateDiskPorts,
|
|
299
699
|
createUpdateExecutor2 as createUpdateExecutor,
|
|
700
|
+
createUpdateQueuePorts2 as createUpdateQueuePorts,
|
|
300
701
|
defaultHomeDir2 as defaultHomeDir,
|
|
702
|
+
deriveAction,
|
|
703
|
+
deriveRouteMethod,
|
|
704
|
+
deriveStage,
|
|
301
705
|
detectEnvironmentKind,
|
|
706
|
+
diagByteLength,
|
|
707
|
+
emptyQueueState2 as emptyQueueState,
|
|
708
|
+
enforceDiagBudget,
|
|
709
|
+
enforceBudget as enforceDiagBudgetForPhone,
|
|
710
|
+
enqueueInQueue2 as enqueueInQueue,
|
|
711
|
+
extractChangelogFromTar,
|
|
712
|
+
fetchReleaseChangelogText,
|
|
713
|
+
hasVisibleSections,
|
|
714
|
+
isHeadOfQueue2 as isHeadOfQueue,
|
|
715
|
+
isPrereleaseVersion,
|
|
716
|
+
isUnreleasedVersion,
|
|
717
|
+
isVersionAllowedInChannel2 as isVersionAllowedInChannel,
|
|
718
|
+
isVersionSkipped,
|
|
719
|
+
normalizeQueueState2 as normalizeQueueState,
|
|
720
|
+
normalizeSkipped,
|
|
721
|
+
parseChangelog,
|
|
302
722
|
parseEventListManifest,
|
|
303
723
|
pathsForUpdate,
|
|
304
724
|
profileNameValid,
|
|
725
|
+
pruneExpiredIntents2 as pruneExpiredIntents,
|
|
726
|
+
queuePathsForUpdate,
|
|
727
|
+
queuePositionOf2 as queuePositionOf,
|
|
728
|
+
readInstalledChangelogText,
|
|
305
729
|
registrySpec,
|
|
730
|
+
releaseOwnerInQueue2 as releaseOwnerInQueue,
|
|
731
|
+
renderChangelogHTML,
|
|
732
|
+
renderChangelogNeutral,
|
|
733
|
+
renderChangelogSection,
|
|
306
734
|
resolveCliEntry,
|
|
735
|
+
resolveRegistryHost,
|
|
307
736
|
resolveTargetPackage2 as resolveTargetPackage,
|
|
308
|
-
resolveUpdateConfig2 as resolveUpdateConfig
|
|
737
|
+
resolveUpdateConfig2 as resolveUpdateConfig,
|
|
738
|
+
sanitizeDetail,
|
|
739
|
+
sanitizeForCopy,
|
|
740
|
+
selectChangelogEntries,
|
|
741
|
+
setOwnerIfFree2 as setOwnerIfFree,
|
|
742
|
+
skipKey,
|
|
743
|
+
truncateToWordBoundary,
|
|
744
|
+
validReleaseVersion,
|
|
745
|
+
validVersion,
|
|
746
|
+
visibleQueueFor2 as visibleQueueFor
|
|
309
747
|
};
|