dsh-nextsteps 1.4.5 → 1.4.7

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.
Files changed (2) hide show
  1. package/client.js +27 -38
  2. package/package.json +1 -1
package/client.js CHANGED
@@ -10,6 +10,8 @@ window.__ModuleLoader__.load({
10
10
  const MAX_FOLLOWUPS = 6;
11
11
  const PROMPT_TRUNCATE = 60;
12
12
  const CLICKED_KEY = 'dsh-nextsteps.clicked';
13
+ const UPDATE_KEY = 'dsh-nextsteps.updated';
14
+ const remembered = () => { try { return localStorage.getItem(UPDATE_KEY); } catch { return null; } };
13
15
 
14
16
 
15
17
  const CSS = `
@@ -369,6 +371,8 @@ window.__ModuleLoader__.load({
369
371
  'zh-CN': {"header":"建议的后续操作","srFollowupSent":"后续操作已发送","sendFailed":"发送失败:","intro":"在每个智能体回合完成后的下方推荐下一步提示——单击即可发送。","setLanguage":"语言","setLanguageTip":"后续操作提示将以此语言生成。“自动”会跟随对话语言。","optAuto":"自动(跟随对话)","setModel":"模型","setModelTip":"生成建议的模型。自动会跟随对话所用的模型。","optModelAuto":"自动(与对话相同的模型)","modelUnavailable":"此安装没有可用的模型列表","setMax":"每回合提示数","setMaxTip":"模型应生成的建议提示数量(1-6)。","setHeader":"显示标题","setHeaderTip":"卡片上方的小标题“建议的后续操作”。","updates":"更新","current":"版本","checking":"检查中…","upToDate":"已是最新","available":"{v} 可用","manualCopy":"开发或重命名的副本——请手动更新。","installing":"正在安装 {v}…","restartMsg":"版本 {v} 已安装。请重启 dsh 以使用。","noReleases":"尚未发布任何版本","checkFailed":"检查更新失败({e})","updateNow":"立即更新","check":"检查更新","openRelease":"打开发布页","releaseNotes":"发布说明","repo":"GitHub 仓库","issues":"反馈问题"}
370
372
  };
371
373
  let pluginSettings = { lang: 'auto', maxFollowups: 3, showHeader: true };
374
+ let updateManager = undefined;
375
+
372
376
  let pluginVersion;
373
377
 
374
378
  async function responseJson(response) {
@@ -555,58 +559,34 @@ window.__ModuleLoader__.load({
555
559
  const response = await fetch(UPDATE_ROUTE, { cache: 'no-store' });
556
560
  const info = await responseJson(response);
557
561
  if (!response.ok || info === null || typeof info !== 'object' || info.error !== undefined) throw new Error(info?.error ?? 'HTTP ' + response.status);
558
- setUpdate({ status: info.newer === true ? (info.managed ? 'available' : 'manual') : 'current', info });
562
+ const status = info.newer !== true ? 'current' : remembered() === info.latest ? 'restart' : info.managed ? 'available' : 'manual';
563
+ setUpdate({ status, info });
559
564
  } catch (error) {
560
565
  setUpdate((previous) => previous.status === 'restart' ? previous : { status: 'error', info: previous.info, error: String(error?.message ?? error) });
561
566
  }
562
567
  };
563
568
  const install = async () => {
564
569
  const { info } = update;
565
- let pluginManager;
566
- try {
567
- pluginManager = ctx.remote?.pluginManager ?? ctx.get?.('remote')?.pluginManager;
568
- } catch { pluginManager = undefined; }
569
- if (info === undefined) return;
570
- if (pluginManager === undefined || typeof pluginManager.installBundle !== 'function') {
570
+ if (info === undefined || updateManager === undefined) {
571
571
  setUpdate({ status: 'error', info, error: 'the plugin manager is not available in this install' });
572
572
  return;
573
573
  }
574
574
  setUpdate({ status: 'installing', info });
575
575
  try {
576
576
  const requestId = NS + '-update-' + info.latest + '-' + Date.now();
577
- const recoverInstall = async (manager, id) => {
578
- if (typeof manager.waitForInstall !== 'function') return undefined;
579
- try {
580
- const reply = await manager.waitForInstall(id);
581
- if (reply === null || typeof reply !== 'object' || reply.ok !== true) return undefined;
582
- return reply.value ?? undefined;
583
- } catch { return undefined; }
584
- };
585
- const attempt = async (spec) => {
586
- const reply = await pluginManager.installBundle(spec, { enabled: true, requestId });
587
- if (reply === null || typeof reply !== 'object' || reply.ok !== true) throw new Error(reply?.error?.message ?? 'plugin manager did not answer');
577
+ const installSpec = async (spec) => {
578
+ const reply = await updateManager.installBundle(spec, { requestId });
579
+ if (!reply?.ok) throw new Error(reply?.error?.message ?? 'plugin manager did not answer');
588
580
  return reply.value;
589
581
  };
590
- let result;
591
- if (typeof pluginManager.updateBundle === 'function') {
592
- const reply = await pluginManager.updateBundle(NPM_NAME, info.latest, { requestId });
593
- if (reply === null || typeof reply !== 'object' || reply.ok !== true) {
594
- const recovered = await recoverInstall(pluginManager, requestId);
595
- if (recovered === undefined) throw new Error(reply?.error?.message ?? 'plugin manager did not answer');
596
- result = recovered;
597
- } else {
598
- result = reply.value;
599
- }
600
- } else {
601
- result = await attempt(NPM_NAME + '@' + info.latest);
602
- if (result !== null && typeof result === 'object' && result.error?.code === 'already-installed') result = await attempt('https://registry.npmjs.org/' + NPM_NAME + '/-/' + NPM_NAME + '-' + info.latest + '.tgz');
603
- }
604
- const applied = result !== null && typeof result === 'object' && (result.application === 'applied' || result.application === 'restart-required' || result.version === info.latest);
605
- if (!applied) {
606
- const diagnostic = typeof result?.error?.diagnostic === 'string' ? result.error.diagnostic.split('\n').find((entry) => entry.trim() !== '') : undefined;
607
- throw new Error(diagnostic ?? result?.error?.code ?? 'install failed');
608
- }
609
- setUpdate(result.application === 'applied'
582
+ let result = await installSpec(NPM_NAME + '@' + info.latest);
583
+ if (result?.error?.code === 'already-installed') result = await installSpec('https://registry.npmjs.org/' + NPM_NAME + '/-/' + NPM_NAME + '-' + info.latest + '.tgz');
584
+ const firstLine = () => result?.error?.diagnostic?.split(String.fromCharCode(10)).find((line) => line.trim() !== '') ?? result?.error?.code ?? result?.application;
585
+ const live = result?.application === 'applied';
586
+ if (!live && result?.application !== 'restart-required' && result?.version !== info.latest) throw new Error(firstLine());
587
+ if (result?.application === 'failed') console.warn(`dsh-nextsteps: ${info.latest} is installed; dsh could not start it before a restart: ${firstLine()}`);
588
+ try { localStorage.setItem(UPDATE_KEY, info.latest); } catch { }
589
+ setUpdate(live
610
590
  ? { status: 'current', info: { ...info, current: info.latest, newer: false } }
611
591
  : { status: 'restart', info });
612
592
  } catch (error) {
@@ -665,6 +645,15 @@ window.__ModuleLoader__.load({
665
645
  return {
666
646
  inject: ['slots', 'locale', 'remote'],
667
647
  apply(ctx) {
648
+ const injectScope = (deps, setup) => {
649
+ if (ctx.inject === undefined) return [];
650
+ const fiber = ctx.inject(deps, setup);
651
+ return [() => fiber.dispose()];
652
+ };
653
+ for (const off of injectScope(['remote.pluginManager'], (scope) => {
654
+ updateManager = scope.remote?.pluginManager;
655
+ scope.effect(() => () => { updateManager = undefined; }, 'dsh-nextsteps: update installer');
656
+ })) if (ctx.effect !== undefined) ctx.effect(() => off, 'dsh-nextsteps: update installer scope');
668
657
  installStyles(ctx);
669
658
  rehydrateCaptures();
670
659
  refreshPluginSettings();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-nextsteps",
3
- "version": "1.4.5",
3
+ "version": "1.4.7",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",