dsh-plugin-update 0.1.2 → 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/dist/host.js CHANGED
@@ -1,8 +1,7 @@
1
1
  // AUTO-GENERATED by node build.mjs — DO NOT EDIT. Source: src/host.ts
2
2
  import { realpath } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
- import { join, sep } from "node:path";
5
- import { fileURLToPath } from "node:url";
4
+ import { sep } from "node:path";
6
5
  import {
7
6
  DEFAULT_CONFIRMATION_TTL_MS,
8
7
  DEFAULT_CHECK_TIMEOUT_MS,
@@ -11,13 +10,126 @@ import {
11
10
  resolveUpdateConfig
12
11
  } from "./config.js";
13
12
  import { manualCommand } from "./commands.js";
14
- import { validVersion } from "./service.js";
15
- import { createUpdateDiskPorts, createUpdateExecutor } from "./store.js";
16
- import { containingPackage, createUpdateReader, defaultHomeDir, resolveProfileName } from "./reader.js";
13
+ import { buildDiag } from "./diag.js";
14
+ import { isVersionAllowedInChannel, validRequestId } from "./service.js";
15
+ import { createUpdateDiskPorts, createUpdateExecutor, createUpdateQueuePorts } from "./store.js";
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";
17
30
  import { buildPhoneName, buildPhoneNames as buildPhoneNames2, resolveUpdateConfig as resolveUpdateConfig2 } from "./config.js";
18
31
  import { parseEventListManifest, checkEventFields, checkEventCounts } from "./gate.js";
19
- import { containingPackage as containingPackage2, defaultHomeDir as defaultHomeDir2, profileNameValid, registrySpec } from "./reader.js";
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";
20
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";
21
133
  let sharedReader = null;
22
134
  let sharedReaderKey = "";
23
135
  let sharedDesktopPnpm = null;
@@ -71,10 +183,11 @@ function hash8(s) {
71
183
  return "00000000";
72
184
  }
73
185
  }
74
- async function inferProfileDir(loaded, homeDirDefault, targetPackageName) {
186
+ async function tryInferProfileDir(targetDir, targetPackageName) {
75
187
  try {
76
- const dir = loaded && loaded.directory ? String(loaded.directory) : "";
77
- const marker = `${sep}node_modules${sep}${targetPackageName}`;
188
+ const dir = String(targetDir || "");
189
+ if (!dir) return null;
190
+ const marker = `${sep}node_modules${sep}${String(targetPackageName || "").split("/").join(sep)}`;
78
191
  const at = dir.lastIndexOf(marker);
79
192
  if (at > 0) {
80
193
  const candidate = dir.slice(0, at);
@@ -86,26 +199,148 @@ async function inferProfileDir(loaded, homeDirDefault, targetPackageName) {
86
199
  }
87
200
  } catch {
88
201
  }
89
- return join(homeDirDefault, "profiles", "web");
202
+ return null;
203
+ }
204
+ function unknownProfile() {
205
+ return Object.assign(new Error("unknown-profile"), { code: "unknown-profile" });
90
206
  }
91
207
  async function getSharedReader(pluginId, config, overrides = {}) {
92
208
  const env = overrides.env ?? process.env;
93
209
  const osHome = overrides.osHome ?? homedir();
94
210
  const homeDirDefault = defaultHomeDir(env, osHome);
95
211
  const targetPackageName = overrides.targetPackageName ?? config.targetPackageName;
96
- const loaded = await containingPackage(fileURLToPath(import.meta.url), targetPackageName).catch(() => null);
97
- const runningVersion = overrides.runningVersion ?? (loaded && validVersion(loaded.manifest.version) ? String(loaded.manifest.version) : null);
98
- if (!runningVersion) throw Object.assign(new Error("unknown-profile"), { code: "unknown-profile" });
99
- const profileDirInput = overrides.profileDir ?? await inferProfileDir(loaded, homeDirDefault, targetPackageName);
212
+ const releaseChannel = overrides.releaseChannel ?? config.releaseChannel ?? "stable";
213
+ const explicitTargetDir = typeof overrides.targetPackageDir === "string" && overrides.targetPackageDir ? String(overrides.targetPackageDir) : "";
214
+ const loaded = await resolveTargetPackage(targetPackageName, explicitTargetDir ? { targetPackageDir: explicitTargetDir } : {}).catch(
215
+ () => null
216
+ );
217
+ const runningVersion = overrides.runningVersion ?? (loaded && isVersionAllowedInChannel(loaded.manifest.version, releaseChannel) ? String(loaded.manifest.version) : null);
218
+ if (!runningVersion) throw unknownProfile();
219
+ let profileDirInput;
220
+ if (overrides.profileDir) {
221
+ profileDirInput = String(overrides.profileDir);
222
+ } else {
223
+ const anchorDir = (loaded && loaded.directory ? String(loaded.directory) : "") || explicitTargetDir;
224
+ const inferred = anchorDir ? await tryInferProfileDir(anchorDir, targetPackageName) : null;
225
+ if (!inferred) throw unknownProfile();
226
+ profileDirInput = inferred;
227
+ }
100
228
  const homeDirInput = overrides.homeDir ?? config.homeDir ?? homeDirDefault;
101
229
  const environmentKind = overrides.environmentKind ?? detectEnvironmentKind(overrides.ctx, resolveProfileName(overrides.profileName, profileDirInput));
102
230
  const checkTimeoutMs = overrides.checkTimeoutMs ?? config.checkTimeoutMs;
103
231
  const confirmationTtlMs = overrides.confirmationTtlMs ?? config.confirmationTtlMs;
104
232
  const installTimeoutMs = overrides.installTimeoutMs ?? config.installTimeoutMs;
105
233
  const registryUrl = overrides.registryUrl ?? config.registryUrl;
106
- const key = `${pluginId}\0${config.prefix}\0${runningVersion}\0${profileDirInput}\0${overrides.profileName ?? ""}\0${homeDirInput}\0${overrides.runInstall ? "exec" : ""}\0${environmentKind}\0${targetPackageName}`;
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}`;
107
235
  if (sharedReader && sharedReaderKey === key) return sharedReader;
108
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
+ }
109
344
  const defaultRun = createUpdateExecutor({
110
345
  profileName: overrides.profileName ?? null,
111
346
  environmentKind,
@@ -123,31 +358,37 @@ async function getSharedReader(pluginId, config, overrides = {}) {
123
358
  pluginId,
124
359
  log: emitInstallLog
125
360
  });
126
- sharedReader = createUpdateReader({
127
- runningVersion,
128
- profileDir: profileDirInput,
129
- pluginId,
130
- profileName: overrides.profileName ?? void 0,
131
- homeDir: overrides.homeDir,
132
- env,
133
- osHome,
134
- fetchImpl: overrides.fetchImpl,
135
- now: overrides.now,
136
- randomId: overrides.randomId,
137
- nodeVersion: overrides.nodeVersion,
138
- environmentKind,
139
- targetPackageName,
140
- registryUrl,
141
- checkTimeoutMs,
142
- confirmationTtlMs,
143
- readInstalled: overrides.readInstalled,
144
- readJob: overrides.readJob,
145
- writeJob: overrides.writeJob,
146
- tryAcquireLock: overrides.tryAcquireLock ?? disk?.tryAcquireLock,
147
- releaseLock: overrides.releaseLock ?? disk?.releaseLock,
148
- backupJob: overrides.backupJob ?? disk?.backupJob,
149
- runInstall: overrides.runInstall ?? defaultRun
150
- });
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
+ );
151
392
  sharedReaderKey = key;
152
393
  return sharedReader;
153
394
  }
@@ -195,6 +436,22 @@ async function snapWithManual(reader, snapshot, config) {
195
436
  return { snapshot, manual: null };
196
437
  }
197
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
+ }
198
455
  let phoneLogCtx = null;
199
456
  function emitInstallLog(level, event, fields) {
200
457
  try {
@@ -202,7 +459,19 @@ function emitInstallLog(level, event, fields) {
202
459
  } catch {
203
460
  }
204
461
  }
205
- function loggedPhone(method, kind, pluginId, fn) {
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) {
206
475
  return async function(args) {
207
476
  const t0 = Date.now();
208
477
  const emit = (level, event, fields) => {
@@ -211,15 +480,47 @@ function loggedPhone(method, kind, pluginId, fn) {
211
480
  } catch {
212
481
  }
213
482
  };
483
+ const safeArgs = args && typeof args === "object" ? args : {};
214
484
  try {
215
- const out = await fn(args);
485
+ const out = await fn(safeArgs);
216
486
  const snapshot = out && out.snapshot ? out.snapshot : out;
217
487
  emit("info", "host.call", { method, latencyMs: Date.now() - t0, ok: true, kind, pluginId });
218
- return { ok: true, snapshot, manual: out && out.snapshot ? out.manual ?? null : null, receipt: out && out.receipt ? out.receipt : null };
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
+ };
219
498
  } catch (error) {
220
499
  const payload = toUpdateErrorPayload(error);
221
500
  emit("warn", "host.call.fail", { method, kind, errorHash: hash8(String(error?.message || payload.error)), pluginId });
222
- return { ok: false, ...payload };
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 } : {} };
223
524
  }
224
525
  };
225
526
  }
@@ -237,7 +538,12 @@ function createHostUpdate(deps = {}, configInput) {
237
538
  ...readerOverrides,
238
539
  profileDir: args && args.profileDir ? String(args.profileDir) : readerOverrides.profileDir
239
540
  });
240
- return snapWithManual(reader, await reader.status(), config);
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) };
241
547
  }
242
548
  async function readCheck(args) {
243
549
  const reader = await getSharedReader(pluginId, config, {
@@ -246,21 +552,85 @@ function createHostUpdate(deps = {}, configInput) {
246
552
  });
247
553
  const result = await reader.check();
248
554
  const withManual = await snapWithManual(reader, result.snapshot, config);
249
- return { snapshot: withManual.snapshot, manual: withManual.manual, receipt: result.receipt ?? null };
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
+ };
250
566
  }
251
567
  async function runInstall(args) {
252
568
  const checkId = args && typeof args.checkId === "string" ? args.checkId : "";
253
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;
254
573
  const reader = await getSharedReader(pluginId, config, {
255
574
  ...readerOverrides,
256
575
  profileDir: args && args.profileDir ? String(args.profileDir) : readerOverrides.profileDir
257
576
  });
258
- return snapWithManual(reader, await reader.install({ checkId, requestId }), config);
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
+ }
259
620
  }
260
621
  const handlers = {
261
- [phoneNames.updateStatus]: loggedPhone(phoneNames.updateStatus, "update-status", pluginId, readStatus),
262
- [phoneNames.updateCheck]: loggedPhone(phoneNames.updateCheck, "update-check", pluginId, readCheck),
263
- [phoneNames.updateInstall]: loggedPhone(phoneNames.updateInstall, "update-install", pluginId, runInstall)
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
+ })
264
634
  };
265
635
  return { phoneNames, handlers };
266
636
  }
@@ -273,21 +643,105 @@ function __resetSharedUpdateReaderForTests() {
273
643
  sharedDesktopPnpm = null;
274
644
  }
275
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,
276
683
  __resetSharedUpdateReaderForTests,
684
+ addSkipped,
685
+ buildDiag2 as buildDiag,
277
686
  buildPhoneName,
278
687
  buildPhoneNames2 as buildPhoneNames,
688
+ cancelEnqueuedInQueue2 as cancelEnqueuedInQueue,
689
+ changelogForUpdate,
279
690
  checkEventCounts,
280
691
  checkEventFields,
281
- containingPackage2 as containingPackage,
692
+ clearSkipped,
693
+ compareReleaseVersions,
694
+ compareVersions,
695
+ containingPackage,
282
696
  createHostUpdate,
697
+ createSkipDiskPorts,
283
698
  createUpdateDiskPorts2 as createUpdateDiskPorts,
284
699
  createUpdateExecutor2 as createUpdateExecutor,
700
+ createUpdateQueuePorts2 as createUpdateQueuePorts,
285
701
  defaultHomeDir2 as defaultHomeDir,
702
+ deriveAction,
703
+ deriveRouteMethod,
704
+ deriveStage,
286
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,
287
722
  parseEventListManifest,
288
723
  pathsForUpdate,
289
724
  profileNameValid,
725
+ pruneExpiredIntents2 as pruneExpiredIntents,
726
+ queuePathsForUpdate,
727
+ queuePositionOf2 as queuePositionOf,
728
+ readInstalledChangelogText,
290
729
  registrySpec,
730
+ releaseOwnerInQueue2 as releaseOwnerInQueue,
731
+ renderChangelogHTML,
732
+ renderChangelogNeutral,
733
+ renderChangelogSection,
291
734
  resolveCliEntry,
292
- resolveUpdateConfig2 as resolveUpdateConfig
735
+ resolveRegistryHost,
736
+ resolveTargetPackage2 as resolveTargetPackage,
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
293
747
  };