@noob-stupid/dsh-plugin-console 0.5.12 → 0.5.13

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/client.js CHANGED
@@ -6,7 +6,7 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  //#region styles
9
- const css = ".pc_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}.pc_section h3{margin:0;font-size:13px;font-weight:600;line-height:20px}.pc_message{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;margin:0}.pc_messageRow{display:flex;align-items:center;gap:10px}.pc_spinner{width:14px;height:14px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}@keyframes pcspin{to{transform:rotate(360deg)}}.pc_message[data-error=true]{color:var(--dsw-alias-state-error-primary)}.pc_message[data-warn=true]{color:var(--dsw-alias-state-warning-primary)}.pc_list{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none;display:grid}.pc_row{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_rowTop{align-items:center;gap:8px;display:flex}.pc_name{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.pc_tag{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:16px;flex:none}.pc_tag[data-enabled=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-user=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_tag[data-pending=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_status[data-pending=true]{color:var(--dsw-alias-state-warning-primary)}.pc_toggle[data-pending=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-skill=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-system=true]{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}.pc_tag[data-disabled=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tag[data-suite=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-error=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tabGroup{display:inline-flex;gap:4px;align-items:center}.pc_meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;gap:6px;display:flex;align-items:center}.pc_phase[data-phase=failed]{color:var(--dsw-alias-state-error-primary)}.pc_phase[data-phase=active]{color:var(--dsw-alias-state-success-primary)}.pc_toggle{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_toggle:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_toggle:disabled{opacity:.5;cursor:default}.pc_search{gap:8px;display:flex}.pc_search input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;flex:1;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;outline:none;padding:0 12px;font-size:13px}.pc_search input:focus-visible{border-color:var(--dsw-alias-state-business-primary)}.pc_search button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:0 14px;font-size:13px}.pc_search button:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_market{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.pc_item{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_itemTop{align-items:center;gap:8px;display:flex}.pc_itemTop a{color:var(--dsw-alias-state-business-primary);font-size:13px;font-weight:600;text-decoration:none;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pc_stars{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none}.pc_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_item button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:3px 10px;align-self:flex-start;font-size:12px}.pc_item button:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_item button:disabled{opacity:.5;cursor:default}.pc_detail{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_status{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_note{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0}.pc_marketHead{display:flex;align-items:center;justify-content:space-between;gap:10px}.pc_descWrap{flex-direction:column;display:flex}.pc_installedHead{display:flex;align-items:center;gap:10px}.pc_backTopWrap{position:fixed;top:56px;right:20px;z-index:100;display:flex;flex-direction:column;gap:8px}.pc_backTop{width:34px;height:34px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:rgba(22,27,34,.72);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;backdrop-filter:blur(4px);opacity:.7}.pc_backTop:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_descTopbar{display:flex;justify-content:flex-end;margin:2px 0}.pc_ghpill{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 10px;font-size:11px;line-height:18px}.pc_ghpill[data-on=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_extraBtn{opacity:.75;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 10px;font-size:12px;line-height:16px;flex:none;transition:opacity .15s ease,border-color 1.5s ease,color 1.5s ease}.pc_extraBtn:hover{opacity:1}.pc_extraBtn[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_extraBtn[data-flash=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_view{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:2px 10px;font-size:12px;line-height:18px;flex:none}.pc_floatPanel{position:fixed;left:20px;top:50%;transform:translateY(-50%);width:340px;max-height:75vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:50;flex-direction:column;gap:8px;display:flex}.pc_refresh{position:sticky;bottom:14px;align-self:flex-end;margin-top:8px;z-index:60;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_refresh:hover{opacity:.9}.pc_date{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;flex:none}.pc_searchInner{position:relative;flex:1;display:flex;align-items:center;min-width:0}.pc_searchInner input{padding-right:32px;width:100%}.pc_starBtn{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:13px;line-height:1;padding:4px 8px;border-radius:6px;opacity:.75;transition:opacity .15s ease;flex:none}.pc_starBtn:hover{opacity:1}.pc_starBtn[data-active=true]{opacity:1;color:var(--dsw-alias-state-business-primary)}.pc_restartBtn{position:fixed;right:20px;top:50%;transform:translateY(-50%);z-index:60;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_restartBtn:hover{opacity:.9}.pc_relaunchFloat{position:fixed;left:20px;top:50%;transform:translateY(-50%);z-index:70;opacity:.85;border:1px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_relaunchFloat:hover{opacity:1}.pc_aiToggle{position:fixed;right:20px;top:20px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiToggle:hover{opacity:1}.pc_aiToggle[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_sourcesFloat{position:fixed;right:20px;top:104px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_sourcesFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat{position:fixed;right:20px;top:204px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_modeFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_trashBtn{border:0;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:16px;padding:2px 6px;flex:none;display:flex;align-items:center;border-radius:6px;transition:color .15s ease}.pc_trashBtn:hover{color:var(--dsw-alias-state-error-primary)}.pc_consentRemember{display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary);cursor:pointer}.pc_modalBackdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:flex;align-items:center;justify-content:center}.pc_modalCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;padding:16px 18px;width:min(760px,calc(100vw - 64px));max-height:calc(100vh - 96px);overflow-y:auto;overscroll-behavior:contain;flex-direction:column;gap:10px;display:flex;box-shadow:0 12px 32px rgba(0,0,0,.25)}.pc_modalCard::-webkit-scrollbar{width:8px}.pc_modalCard::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:4px}.pc_srcUrl{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%}.pc_accWrap{display:flex;flex-direction:column;gap:8px}.pc_acc{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;overflow:hidden;background:var(--dsw-alias-bg-layer-2)}.pc_accHead{width:100%;text-align:left;background:transparent;border:0;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;font-weight:600;line-height:20px;padding:8px 12px;cursor:pointer;display:flex;align-items:center;gap:6px}.pc_accHead:hover{background:var(--dsw-alias-bg-layer-3)}.pc_accHead[data-open=true]{border-bottom:1px solid var(--dsw-alias-border-l2)}.pc_accBody{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.pc_ghpill{background:0 0;cursor:pointer}.pc_ghwrap{position:relative;display:inline-flex}.pc_sourceMenu{position:absolute;right:0;top:calc(100% + 6px);z-index:90;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:4px;flex-direction:column;display:flex;min-width:120px;box-shadow:0 8px 24px rgba(0,0,0,.18)}.pc_sourceOpt{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 10px;text-align:left;font-size:12px;line-height:16px}.pc_sourceOpt[data-active=true]{color:var(--dsw-alias-state-business-primary);font-weight:600}.pc_sourceOpt:hover{background:var(--dsw-alias-bg-layer-3)}.pc_aiEmpower{position:fixed;right:20px;top:48px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiEmpower:hover{opacity:.9}.pc_aiEmpower[data-busy=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_aiEmpower[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_compCard{position:fixed;left:8px;top:20px;width:320px;max-height:70vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:45;flex-direction:column;gap:8px;display:flex}.pc_compCard .pc_rowTop{flex-wrap:wrap}.pc_compCard .pc_toggle{padding:3px 8px;font-size:11px}.pc_compCard[data-hidden=true]{display:none}.pc_compDrop{background:rgba(24,28,34,.92);backdrop-filter:blur(6px);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,.35);flex-direction:column;display:flex;gap:2px}.pc_compDropRow{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 8px;text-align:left;font-size:12px;line-height:16px;flex-direction:column;display:flex;gap:2px}.pc_compDropRow:hover{background:var(--dsw-alias-bg-layer-2)}.pc_compDropUrl{color:var(--dsw-alias-label-tertiary);font-size:11px;word-break:break-all}.pc_compToggle{position:fixed;left:336px;top:20px;z-index:46;opacity:.55;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:rgba(22,27,34,.72);font:inherit;cursor:pointer;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;line-height:1;backdrop-filter:blur(4px);transition:opacity .15s ease}.pc_compToggle:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_toolbarMain{position:fixed;right:70px;top:20px;z-index:71;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:border-color .15s ease}.pc_toolbarMain:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_toolbarMain[data-active=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_toolbarGroup{position:fixed;right:70px;top:56px;z-index:70;flex-direction:column;gap:8px;display:flex;transform-origin:top right;animation:pcDrawerIn .3s cubic-bezier(.34,1.56,.64,1)}@media (max-width:1160px){.pc_toolbarMain{top:104px}.pc_toolbarGroup{top:140px}.pc_aiToggle{top:104px}.pc_aiEmpower{top:132px}.pc_sourcesFloat{top:188px}.pc_modeFloat{top:288px}}@keyframes pcDrawerIn{from{opacity:0;transform:translateX(28px) scale(.94)}to{opacity:1;transform:translateX(0) scale(1)}}.pc_toolItem{opacity:.85;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_toolItem:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_toolItem[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_toolItem[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_switch{width:34px;height:18px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);position:relative;cursor:pointer;flex:none;transition:background .15s ease,border-color .15s ease}.pc_switch:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_switch[data-on=true]{background:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.pc_switchKnob{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#fff;transition:left .15s ease}.pc_switch[data-on=true] .pc_switchKnob{left:18px}.pc_aiSpinner{width:12px;height:12px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}.pc_aiSpinBtn{position:fixed;right:152px;top:20px;z-index:71;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px}.pc_aiSpinBtn:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_tag[data-bad=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_progress{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;margin:0;font-variant-numeric:tabular-nums}.pc_consentCard{border:2px solid var(--dsw-alias-state-warning-primary);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:12px 14px;flex-direction:column;gap:8px;display:flex;box-shadow:0 0 0 3px rgba(255,176,32,.10)}.pc_consentCountdown{margin-left:auto;flex:none;font-size:14px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--dsw-alias-state-warning-primary)}.pc_consentApprove{border:1px solid var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:0 0;font:inherit;font-weight:600;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_consentApprove:hover{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}";
9
+ const css = ".pc_section{width:100%;max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:14px;display:flex}.pc_section h3{margin:0;font-size:13px;font-weight:600;line-height:20px}.pc_message{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;margin:0}.pc_messageRow{display:flex;align-items:center;gap:10px}.pc_spinner{width:14px;height:14px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}@keyframes pcspin{to{transform:rotate(360deg)}}.pc_message[data-error=true]{color:var(--dsw-alias-state-error-primary)}.pc_message[data-warn=true]{color:var(--dsw-alias-state-warning-primary)}.pc_list{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none;display:grid}.pc_row{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_rowTop{align-items:center;gap:8px;display:flex}.pc_name{font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.pc_tag{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:16px;flex:none}.pc_tag[data-enabled=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-user=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_tag[data-pending=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_status[data-pending=true]{color:var(--dsw-alias-state-warning-primary)}.pc_toggle[data-pending=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-skill=true]{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_tag[data-system=true]{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}.pc_tag[data-disabled=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tag[data-suite=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_tag[data-error=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_tabGroup{display:inline-flex;gap:4px;align-items:center}.pc_meta{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;gap:6px;display:flex;align-items:center}.pc_phase[data-phase=failed]{color:var(--dsw-alias-state-error-primary)}.pc_phase[data-phase=active]{color:var(--dsw-alias-state-success-primary)}.pc_toggle{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_toggle:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_toggle:disabled{opacity:.5;cursor:default}.pc_search{gap:8px;display:flex}.pc_search input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);height:34px;flex:1;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;outline:none;padding:0 12px;font-size:13px}.pc_search input:focus-visible{border-color:var(--dsw-alias-state-business-primary)}.pc_search button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:0 14px;font-size:13px}.pc_search button:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_market{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.pc_item{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;min-width:0;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_itemTop{align-items:center;gap:8px;display:flex}.pc_itemTop a{color:var(--dsw-alias-state-business-primary);font-size:13px;font-weight:600;text-decoration:none;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pc_stars{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none}.pc_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_item button{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:3px 10px;align-self:flex-start;font-size:12px}.pc_item button:hover:not(:disabled){border-color:var(--dsw-alias-state-business-primary)}.pc_item button:disabled{opacity:.5;cursor:default}.pc_detail{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:10px 12px;flex-direction:column;gap:6px;display:flex}.pc_status{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0}.pc_note{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;margin:0}.pc_marketHead{display:flex;align-items:center;justify-content:space-between;gap:10px}.pc_descWrap{flex-direction:column;display:flex}.pc_installedHead{display:flex;align-items:center;gap:10px}.pc_backTopWrap{position:fixed;top:56px;right:20px;z-index:100;display:flex;flex-direction:column;gap:8px}.pc_backTop{width:34px;height:34px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:rgba(22,27,34,.72);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;backdrop-filter:blur(4px);opacity:.7}.pc_backTop:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_descTopbar{display:flex;justify-content:flex-end;margin:2px 0}.pc_ghpill{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 10px;font-size:11px;line-height:18px}.pc_ghpill[data-on=true]{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary)}.pc_extraBtn{opacity:.75;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:4px 10px;font-size:12px;line-height:16px;flex:none;transition:opacity .15s ease,border-color 1.5s ease,color 1.5s ease}.pc_extraBtn:hover{opacity:1}.pc_extraBtn[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_extraBtn[data-flash=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_view{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0;font:inherit;cursor:pointer;border-radius:6px;padding:2px 10px;font-size:12px;line-height:18px;flex:none}.pc_floatPanel{position:fixed;left:20px;top:50%;transform:translateY(-50%);width:340px;max-height:75vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:50;flex-direction:column;gap:8px;display:flex}.pc_refresh{position:sticky;bottom:14px;align-self:flex-end;margin-top:8px;z-index:60;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_refresh:hover{opacity:.9}.pc_date{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;flex:none}.pc_searchInner{position:relative;flex:1;display:flex;align-items:center;min-width:0}.pc_searchInner input{padding-right:32px;width:100%}.pc_starBtn{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:13px;line-height:1;padding:4px 8px;border-radius:6px;opacity:.75;transition:opacity .15s ease;flex:none}.pc_starBtn:hover{opacity:1}.pc_starBtn[data-active=true]{opacity:1;color:var(--dsw-alias-state-business-primary)}.pc_restartBtn{position:fixed;right:20px;top:50%;transform:translateY(-50%);z-index:60;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_restartBtn:hover{opacity:.9}.pc_relaunchFloat{position:fixed;left:20px;top:50%;transform:translateY(-50%);z-index:70;opacity:.85;border:1px solid var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_relaunchFloat:hover{opacity:1}.pc_aiToggle{position:fixed;right:20px;top:20px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiToggle:hover{opacity:1}.pc_aiToggle[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_sourcesFloat{position:fixed;right:20px;top:104px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_sourcesFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat{position:fixed;right:20px;top:204px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_modeFloat:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_modeFloat[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_trashBtn{border:0;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:16px;padding:2px 6px;flex:none;display:flex;align-items:center;border-radius:6px;transition:color .15s ease}.pc_trashBtn:hover{color:var(--dsw-alias-state-error-primary)}.pc_consentRemember{display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary);cursor:pointer}.pc_modalBackdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1000;display:flex;align-items:center;justify-content:center}.pc_modalCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;padding:16px 18px;width:min(760px,calc(100vw - 64px));max-height:calc(100vh - 96px);overflow-y:auto;overscroll-behavior:contain;flex-direction:column;gap:10px;display:flex;box-shadow:0 12px 32px rgba(0,0,0,.25)}.pc_modalCard::-webkit-scrollbar{width:8px}.pc_modalCard::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l2);border-radius:4px}.pc_srcUrl{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%}.pc_accWrap{display:flex;flex-direction:column;gap:8px}.pc_acc{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;overflow:hidden;background:var(--dsw-alias-bg-layer-2)}.pc_accHead{width:100%;text-align:left;background:transparent;border:0;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;font-weight:600;line-height:20px;padding:8px 12px;cursor:pointer;display:flex;align-items:center;gap:6px}.pc_accHead:hover{background:var(--dsw-alias-bg-layer-3)}.pc_accHead[data-open=true]{border-bottom:1px solid var(--dsw-alias-border-l2)}.pc_accBody{padding:10px 12px;display:flex;flex-direction:column;gap:8px}.pc_ghpill{background:0 0;cursor:pointer}.pc_ghwrap{position:relative;display:inline-flex}.pc_sourceMenu{position:absolute;right:0;top:calc(100% + 6px);z-index:90;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:4px;flex-direction:column;display:flex;min-width:120px;box-shadow:0 8px 24px rgba(0,0,0,.18)}.pc_sourceOpt{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 10px;text-align:left;font-size:12px;line-height:16px}.pc_sourceOpt[data-active=true]{color:var(--dsw-alias-state-business-primary);font-weight:600}.pc_sourceOpt:hover{background:var(--dsw-alias-bg-layer-3)}.pc_aiEmpower{position:fixed;right:20px;top:48px;z-index:70;opacity:1;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_aiEmpower:hover{opacity:.9}.pc_aiEmpower[data-busy=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_aiEmpower[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_compCard{position:fixed;left:8px;top:20px;width:320px;max-height:70vh;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:12px;box-shadow:0 8px 24px rgba(0,0,0,.18);z-index:45;flex-direction:column;gap:8px;display:flex}.pc_compCard .pc_rowTop{flex-wrap:wrap}.pc_compCard .pc_toggle{padding:3px 8px;font-size:11px}.pc_compCard[data-hidden=true]{display:none}.pc_compDrop{background:rgba(24,28,34,.92);backdrop-filter:blur(6px);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px;box-shadow:0 8px 24px rgba(0,0,0,.35);flex-direction:column;display:flex;gap:2px}.pc_compDropRow{border:0;background:0 0;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:6px;padding:6px 8px;text-align:left;font-size:12px;line-height:16px;flex-direction:column;display:flex;gap:2px}.pc_compDropRow:hover{background:var(--dsw-alias-bg-layer-2)}.pc_compDropUrl{color:var(--dsw-alias-label-tertiary);font-size:11px;word-break:break-all}.pc_compToggle{position:fixed;left:336px;top:20px;z-index:46;opacity:.55;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:rgba(22,27,34,.72);font:inherit;cursor:pointer;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:11px;line-height:1;backdrop-filter:blur(4px);transition:opacity .15s ease}.pc_compToggle:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_toolbarMain{position:fixed;right:70px;top:max(88px,21vh);z-index:71;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:border-color .15s ease}.pc_toolbarMain:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_toolbarMain[data-active=true]{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_toolbarGroup{position:fixed;right:70px;top:calc(max(88px,21vh) + 36px);z-index:70;flex-direction:column;gap:8px;display:flex;transform-origin:top right;animation:pcDrawerIn .3s cubic-bezier(.34,1.56,.64,1)}@media (max-width:1160px){.pc_toolbarMain{top:max(150px,30vh)}.pc_toolbarGroup{top:calc(max(150px,30vh) + 36px)}.pc_aiToggle{top:104px}.pc_aiEmpower{top:132px}.pc_sourcesFloat{top:188px}.pc_modeFloat{top:288px}}@keyframes pcDrawerIn{from{opacity:0;transform:translateX(28px) scale(.94)}to{opacity:1;transform:translateX(0) scale(1)}}.pc_toolItem{opacity:.85;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);font:inherit;cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px;transition:opacity .15s ease}.pc_toolItem:hover{opacity:1;border-color:var(--dsw-alias-state-business-primary)}.pc_toolItem[data-active=true]{opacity:1;border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}.pc_toolItem[data-mode=true]{opacity:1;border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary)}.pc_switch{width:34px;height:18px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);position:relative;cursor:pointer;flex:none;transition:background .15s ease,border-color .15s ease}.pc_switch:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_switch[data-on=true]{background:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.pc_switchKnob{position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:50%;background:#fff;transition:left .15s ease}.pc_switch[data-on=true] .pc_switchKnob{left:18px}.pc_aiSpinner{width:12px;height:12px;border:2px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-state-business-primary);border-radius:50%;animation:pcspin 1s linear infinite;flex:none}.pc_aiSpinBtn{position:fixed;right:152px;top:20px;z-index:71;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;border-radius:8px;padding:6px 10px;display:flex;align-items:center;gap:6px;font-size:12px;line-height:16px}.pc_aiSpinBtn:hover{border-color:var(--dsw-alias-state-business-primary)}.pc_tag[data-bad=true]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.pc_progress{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;margin:0;font-variant-numeric:tabular-nums}.pc_consentCard{border:2px solid var(--dsw-alias-state-warning-primary);background:var(--dsw-alias-bg-layer-2);border-radius:10px;padding:12px 14px;flex-direction:column;gap:8px;display:flex;box-shadow:0 0 0 3px rgba(255,176,32,.10)}.pc_consentCountdown{margin-left:auto;flex:none;font-size:14px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--dsw-alias-state-warning-primary)}.pc_consentApprove{border:1px solid var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:0 0;font:inherit;font-weight:600;cursor:pointer;border-radius:6px;padding:4px 12px;align-self:flex-start}.pc_consentApprove:hover{border-color:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-state-business-primary)}";
10
10
  const tagId = "@noob-stupid/dsh-plugin-console/PluginConsoleTab.css";
11
11
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
12
12
  const tag = document.createElement("style");
@@ -243,6 +243,32 @@ window.__ModuleLoader__.load({
243
243
  installed: "已安装并启用",
244
244
  installNote: "HMR 正在生效;若插件带界面,请刷新页面",
245
245
  bundleNote: "该包是 bundle 层插件(如皮肤合集),重启 DSH 服务后生效",
246
+ // ── 安装诊断短句(2026-09-26 加法):面板只放短句;解释性长文留在服务端字段里,
247
+ // 前端只把它挂到 title 悬浮提示上,不铺进面板(用户明确要求)──
248
+ diagCardTitle: "安装诊断",
249
+ // 分隔符也进字典:英文界面用半角标点,避免中文全角标点混进英文句子
250
+ diagColon: ":",
251
+ diagSemi: ";",
252
+ diagDot: "。",
253
+ diagKindNetworkTimeout: "网络超时:可用更长超时重试",
254
+ diagKindAllowBuilds: "构建脚本未放行:需你手动确认,我们不会自动改白名单",
255
+ diagKindMinimumReleaseAge: "版本太新被安全间隔拦下:稍后再试",
256
+ diagKindMissingTool: "本机缺少命令(corepack/pnpm/git):装好再重试",
257
+ diagKindNotFound: "registry 上没有这个包或版本:核对包名或换源",
258
+ diagKindLocked: "文件被占用或无权限:关掉占用进程后重试",
259
+ diagKindOther: "未归类的安装失败:先原样重试,仍失败看原始输出",
260
+ diagRetried: "网络超时:已自动重试一次(更长超时)",
261
+ diagRetriedMark: "已重试一次",
262
+ diagEntryFail: "装后校验未通过",
263
+ diagEntryReasonEntry: "入口文件缺失(可能镜像未同步完整)",
264
+ diagEntryReasonPatch: "补丁行指向别的包",
265
+ diagEntryReasonBundle: "bundle 文件或引用缺失",
266
+ diagEntryAdviceMirror: "建议重装;仍失败请换源",
267
+ diagEntryAdvice: "建议重装一次",
268
+ diagIntegrityOk: "已校验 {algo} 摘要",
269
+ diagIntegrityNone: "来源无摘要,未校验",
270
+ diagIntegrityMismatch: "摘要不匹配(已安装,未拦截)",
271
+ diagIntegritySkip: "摘要未校验(读取失败,不影响安装)",
246
272
  direct: "(浏览器直连 GitHub;若你的浏览器打不开 GitHub,将自动回退到服务端通道)",
247
273
  details: "详情",
248
274
  expandDesc: "展开",
@@ -712,6 +738,32 @@ window.__ModuleLoader__.load({
712
738
  installed: "Installed and enabled",
713
739
  installNote: "HMR is applying; refresh the page if the plugin ships UI",
714
740
  bundleNote: "This package is a bundle-layer plugin (e.g. skin packs); restart the DSH service to activate it",
741
+ // ── install-diagnosis one-liners (2026-09-26, additive): the panel shows short sentences only;
742
+ // the long server-side explanations go to the title tooltip, never into the panel body ──
743
+ diagCardTitle: "Install diagnosis",
744
+ // separators live in the dictionary too: English uses half-width punctuation
745
+ diagColon: ": ",
746
+ diagSemi: "; ",
747
+ diagDot: ". ",
748
+ diagKindNetworkTimeout: "Network timeout: retry with a longer timeout",
749
+ diagKindAllowBuilds: "Build scripts not approved: your explicit approval is required, we never auto-edit the allowlist",
750
+ diagKindMinimumReleaseAge: "Version too new for the release-age gate: retry later",
751
+ diagKindMissingTool: "Missing local command (corepack/pnpm/git): install it, then retry",
752
+ diagKindNotFound: "Package or version not on the registry: check the name or switch source",
753
+ diagKindLocked: "File in use or no permission: close the holder, then retry",
754
+ diagKindOther: "Unclassified failure: retry as-is; if it fails again read the raw output",
755
+ diagRetried: "Network timeout: auto-retried once (longer timeout)",
756
+ diagRetriedMark: "already retried once",
757
+ diagEntryFail: "Post-install check failed",
758
+ diagEntryReasonEntry: "entry file missing (the mirror may be incomplete)",
759
+ diagEntryReasonPatch: "a patch row points to another package",
760
+ diagEntryReasonBundle: "bundle file or a referenced package is missing",
761
+ diagEntryAdviceMirror: "Reinstall; if it fails again, switch source",
762
+ diagEntryAdvice: "Reinstall once",
763
+ diagIntegrityOk: "Verified {algo} digest",
764
+ diagIntegrityNone: "No digest from source; not verified",
765
+ diagIntegrityMismatch: "Digest mismatch (installed, not blocked)",
766
+ diagIntegritySkip: "Digest not verified (read failed; install unaffected)",
715
767
  direct: "(The browser calls GitHub directly; the server channel is used as fallback.)",
716
768
  details: "Details",
717
769
  expandDesc: "Expand",
@@ -2708,7 +2760,7 @@ window.__ModuleLoader__.load({
2708
2760
  } else if (data.kind === "suite") {
2709
2761
  // 套装安装:保存组件报告;有 bundle 组件需重启,否则刷新页面
2710
2762
  if (Array.isArray(data.suiteReport)) setSuiteReports((prev) => ({ ...prev, [jobId]: data.suiteReport }));
2711
- setMessage(t("suiteInstalledMsg") + ":" + (data.suiteNote ?? ""));
2763
+ setMessage(t("suiteInstalledMsg") + ":" + (data.suiteNote ?? "") + diagMessage(data));
2712
2764
  if ((data.suiteReport ?? []).some((r) => r.type === "bundle" && r.ok)) {
2713
2765
  setRestartHint(true);
2714
2766
  } else {
@@ -2716,9 +2768,9 @@ window.__ModuleLoader__.load({
2716
2768
  window.setTimeout(() => window.location.reload(), 2500);
2717
2769
  }
2718
2770
  } else if (data.ai === true) {
2719
- setMessage(t("installed") + ":" + data.packageName + "。" + (data.aiNote ?? t("aiRepaired")));
2771
+ setMessage(t("installed") + ":" + data.packageName + "。" + (data.aiNote ?? t("aiRepaired")) + diagMessage(data));
2720
2772
  } else {
2721
- setMessage(t("installed") + ":" + data.packageName + "(" + (data.entryId ?? "") + ")。" + (data.curlNote ?? (data.bundle ? t("bundleNote") : t("installNote"))) + (data.bundleNote ? "。" + data.bundleNote : "") + (data.lockNote ? "。⚠️ " + data.lockNote : "") + (data.depNote ? "。" + data.depNote : ""));
2773
+ setMessage(t("installed") + ":" + data.packageName + "(" + (data.entryId ?? "") + ")。" + (data.curlNote ?? (data.bundle ? t("bundleNote") : t("installNote"))) + (data.bundleNote ? "。" + data.bundleNote : "") + (data.lockNote ? "。⚠️ " + data.lockNote : "") + (data.depNote ? "。" + data.depNote : "") + diagMessage(data));
2722
2774
  }
2723
2775
  if (data.kind === "skill") {
2724
2776
  // 技能无需页面刷新
@@ -2734,7 +2786,7 @@ window.__ModuleLoader__.load({
2734
2786
  window.setTimeout(() => window.location.reload(), 2500);
2735
2787
  }
2736
2788
  } else {
2737
- setMessage(t("failed") + ":" + (data.error ?? "未知错误"));
2789
+ setMessage(t("failed") + ":" + (data.error ?? "未知错误") + diagMessage(data));
2738
2790
  }
2739
2791
  setInstalling(null);
2740
2792
  setTimeout(refresh, 1500);
@@ -2778,6 +2830,96 @@ window.__ModuleLoader__.load({
2778
2830
  const timeoutMs = job?.aiConsent?.timeoutMs;
2779
2831
  return String(Math.max(1, Math.round((typeof timeoutMs === "number" && timeoutMs > 0 ? timeoutMs : 600000) / 60000)));
2780
2832
  };
2833
+ //#region install-diagnosis-lines
2834
+ /** 安装诊断短句(2026-09-26 加法):把服务端下发的 diagnosis / entryCheck / integrity 折成
2835
+ * **一两行短句**(解释性长文一律不进面板 —— 用户明确要求;长文案只挂 title 悬浮提示)。
2836
+ * 返回 [{ key, text, level, title }]:level 为 null(中性)| "warn" | "error",
2837
+ * 直接映射既有 styles.message + data-warn / data-error 约定,不新增徽章 / 按钮 / class。
2838
+ * 无问题时不产出任何行(不占版面);摘要「已校验 …」按需求作为极短一行保留。 */
2839
+ const diagLines = (job) => {
2840
+ const view = job === null || job === undefined ? {} : job;
2841
+ const lines = [];
2842
+ // ① 装后校验(install-verify.js: { ok, problems[], mirrorHint, suggestions[], entry, patch, bundle, version })
2843
+ // 只有 ok !== true 才算问题;面板只放一句短原因,完整长句挂 title
2844
+ const check = view.entryCheck ?? null;
2845
+ if (check !== null && check.ok !== true) {
2846
+ const problem = Array.isArray(check.problems) && typeof check.problems[0] === "string" ? check.problems[0] : "";
2847
+ const reason = /入口文件缺失/u.test(problem) ? t("diagEntryReasonEntry")
2848
+ : /补丁行/u.test(problem) ? t("diagEntryReasonPatch")
2849
+ : /bundle/u.test(problem) ? t("diagEntryReasonBundle")
2850
+ : "";
2851
+ const problems = Array.isArray(check.problems) ? check.problems.filter((p) => typeof p === "string") : [];
2852
+ lines.push({
2853
+ key: "entry",
2854
+ text: t("diagEntryFail") + (reason === "" ? "" : t("diagColon") + reason),
2855
+ level: "error",
2856
+ title: problems.length === 0 ? null : problems.join("\n"),
2857
+ });
2858
+ const suggestions = Array.isArray(check.suggestions) ? check.suggestions.filter((s) => typeof s === "string") : [];
2859
+ lines.push({
2860
+ key: "entryAdvice",
2861
+ text: check.mirrorHint !== null && check.mirrorHint !== undefined ? t("diagEntryAdviceMirror") : t("diagEntryAdvice"),
2862
+ level: "warn",
2863
+ title: suggestions.length === 0 ? null : suggestions.join("\n"),
2864
+ });
2865
+ }
2866
+ // ② 下载物摘要(verifyTarballDigest: { checked, ok, algorithm, expected, actual, sha256, note }):
2867
+ // 三种短句 + 读取失败一种;不匹配只提示「未拦截」(服务端语义就是不拒装,这里不改)
2868
+ const integrity = view.integrity ?? null;
2869
+ if (integrity !== null) {
2870
+ const text = integrity.checked !== true ? t("diagIntegritySkip")
2871
+ : integrity.ok === true ? t("diagIntegrityOk").replace("{algo}", String(integrity.algorithm ?? "sha256"))
2872
+ : integrity.ok === false ? t("diagIntegrityMismatch")
2873
+ : t("diagIntegrityNone");
2874
+ lines.push({ key: "integrity", text: text, level: integrity.ok === false ? "warn" : null, title: typeof integrity.note === "string" ? integrity.note : null });
2875
+ }
2876
+ // ③ 失败分类(install-diagnose.js: { kind, hint, retry },定向重试后带 retried/retryTimeoutMs):
2877
+ // 失败给「短分类句」;网络类定向重试后**成功**的作业只留一句「已自动重试一次」
2878
+ const diagnosis = view.diagnosis ?? null;
2879
+ if (diagnosis !== null && (view.status === "failed" || diagnosis.retried === true)) {
2880
+ const kindText = diagnosis.kind === "network-timeout" ? t("diagKindNetworkTimeout")
2881
+ : diagnosis.kind === "allowBuilds" ? t("diagKindAllowBuilds")
2882
+ : diagnosis.kind === "minimumReleaseAge" ? t("diagKindMinimumReleaseAge")
2883
+ : diagnosis.kind === "missing-tool" ? t("diagKindMissingTool")
2884
+ : diagnosis.kind === "not-found" ? t("diagKindNotFound")
2885
+ : diagnosis.kind === "locked" ? t("diagKindLocked")
2886
+ : t("diagKindOther");
2887
+ lines.push({
2888
+ key: "diagnosis",
2889
+ text: view.status === "failed"
2890
+ ? kindText + (diagnosis.retried === true ? t("diagSemi") + t("diagRetriedMark") : "")
2891
+ : t("diagRetried"),
2892
+ level: "warn",
2893
+ title: typeof diagnosis.hint === "string" ? diagnosis.hint : null,
2894
+ });
2895
+ }
2896
+ return lines;
2897
+ };
2898
+ /** 诊断短句 → 消息栏后缀(空串 = 没问题):只在既有文案**末尾追加**,一个字都不替换。 */
2899
+ const diagMessage = (job) => {
2900
+ const lines = diagLines(job);
2901
+ return lines.length === 0 ? "" : t("diagDot") + lines.map((line) => line.text).join(t("diagSemi"));
2902
+ };
2903
+ /** 单条诊断行 → <p className={styles.message} data-error|data-warn>(沿用既有约定,不加新 class)。 */
2904
+ const diagLineEl = (line) => el("p", {
2905
+ key: "diag-" + line.key,
2906
+ className: styles.message,
2907
+ "data-error": line.level === "error" ? "true" : undefined,
2908
+ "data-warn": line.level === "warn" ? "true" : undefined,
2909
+ title: line.title === null || line.title === undefined ? undefined : line.title,
2910
+ }, line.text);
2911
+ /** 安装诊断卡(加法):**装成功但装后校验 / 摘要校验有问题**的作业 → 一张卡片;
2912
+ * 无问题(或只有「已校验 …」这类中性行)返回 null,不占版面、不加按钮/徽章。 */
2913
+ const diagCardEl = (job) => {
2914
+ const lines = diagLines(job).filter((line) => line.level !== null);
2915
+ if (lines.length === 0) return null;
2916
+ return el("div", { key: "check-" + job.jobId, className: styles.detail },
2917
+ el("div", { className: styles.rowTop },
2918
+ el("span", { className: styles.phase, "data-phase": lines.some((line) => line.level === "error") ? "failed" : undefined }, t("diagCardTitle")),
2919
+ el("strong", { className: styles.name }, job.packageName ?? job.repo)),
2920
+ ...lines.map(diagLineEl));
2921
+ };
2922
+ //#endregion
2781
2923
  /** 软件源管理:打开时拉取当前配置。 */
2782
2924
  const openSources = () => {
2783
2925
  setSourcesOpen(true);
@@ -4261,7 +4403,7 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
4261
4403
  ? { left: (dragPos ?? toolbarPos).left, top: (dragPos ?? toolbarPos).top, right: "auto", touchAction: "none", userSelect: "none" }
4262
4404
  : { touchAction: "none", userSelect: "none" },
4263
4405
  onPointerDown: (e) => {
4264
- const cur = dragPos ?? toolbarPos ?? { left: Math.max(8, window.innerWidth - 70 - 110), top: 20 };
4406
+ const cur = dragPos ?? toolbarPos ?? { left: Math.max(8, window.innerWidth - 70 - 110), top: Math.max(88, Math.round(window.innerHeight * 0.21)) };
4265
4407
  dragRef.current.grabX = e.clientX - cur.left;
4266
4408
  dragRef.current.grabY = e.clientY - cur.top;
4267
4409
  dragRef.current.base = cur;
@@ -4538,6 +4680,9 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
4538
4680
  el("p", { className: styles.status },
4539
4681
  t("stageLabel") + ":" + t(stageKey) + " · " + t("elapsed") + " " + elapsed + "s"));
4540
4682
  }),
4683
+ // 安装诊断卡(2026-09-26 加法):只有**装成功但装后校验 / 摘要校验有问题**的作业才出现
4684
+ // (无问题时 diagCardEl 返回 null,不占版面)。数据只用本地 jobs 状态,不新增任何接口字段。
4685
+ Object.values(jobs).filter((job) => job.status === "done").map((job) => diagCardEl(job)),
4541
4686
  Object.entries(suiteReports).map(([jobId, report]) =>
4542
4687
  el("div", { key: "suite-" + jobId, className: styles.detail },
4543
4688
  el("p", { className: styles.status }, t("suiteComponentType") + ":"),
@@ -4562,6 +4707,7 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
4562
4707
  onClick: () => setDismissedFailures((prev) => ({ ...prev, [job.jobId]: true })),
4563
4708
  }, "✕")),
4564
4709
  el("p", { className: styles.message, "data-error": "true" }, job.error ?? ""),
4710
+ ...diagLines(job).map(diagLineEl),
4565
4711
  el("div", { className: styles.rowTop },
4566
4712
  el("button", { type: "button", className: styles.toggle, onClick: () => startJob(job.repo, job.packageName, job.source ?? "github") }, t("retry")),
4567
4713
  job.hint === "repo-land"
@@ -13,7 +13,7 @@ import { frameworkCheckPromptText } from './compat.js'
13
13
  import { compFind, compUpsert } from './components.js'
14
14
  import { jobLog, waitHealth } from './jobs.js'
15
15
  import { gitCloneUrls } from './sources.js'
16
- import { execFileAsync, gitEnv, resolvePnpmRunners, runPnpmWithFallback } from '../infra/exec.js'
16
+ import { buildPnpmEnv, execFileAsync, gitEnv, resolvePnpmRunners, runPnpmWithFallback } from '../infra/exec.js'
17
17
  import { maskUrl } from '../infra/mask.js'
18
18
 
19
19
  /**
@@ -368,7 +368,8 @@ async function runAiStep(job, step, ports, profileDir, signal) {
368
368
  // 2026-09-20 另一位用户的 install-npm 失败现场就是这一行),改为按优先级尝试可用的执行方式
369
369
  const args = ['add', pkg, '--dir', dir, '--registry', 'https://registry.npmmirror.com']
370
370
  const { runner } = await runPnpmWithFallback(args, {
371
- execOpts: { timeout: 900000, windowsHide: true, maxBuffer: 4 * 1024 * 1024, signal },
371
+ // 2026-09-26:与安装主链路同一套 pnpm env(fetch 超时/重试 + CI=true 非交互),见 infra/exec.js
372
+ execOpts: { timeout: 900000, windowsHide: true, maxBuffer: 4 * 1024 * 1024, env: buildPnpmEnv('https://registry.npmmirror.com'), signal },
372
373
  })
373
374
  jobLog(job, ` 运行: ${runner.note} ${args.join(' ')}`)
374
375
  return
@@ -0,0 +1,113 @@
1
+ // L1 · domain —— install-diagnose.js(安装失败分类 → 定向重试 / 提示;**纯函数**:无 IO、不认识 ctx、不读进程)
2
+ //
3
+ // 为什么(2026-09-26):安装失败时面板只给一句原始 stderr,用户与后续流程都不知道"能不能重试、怎么重试"。
4
+ // 这里把失败文本归成 { kind, hint, retry },调用方(install-job.js)据此只做两件事:
5
+ // ① retry === 'longer-timeout' 的失败(网络类超时/连接重置)→ **定向重试一次**、且用更长超时;
6
+ // ② 其余分类 → 只把 hint 写进 job.diagnosis 供面板展示,**不新增任何自动重试**(重试上限语义不变)。
7
+ //
8
+ // ⚠️ 安全边界(不许越线,写死在这里):
9
+ // `allowBuilds` / `ignored build scripts` 这一类**只提示,绝不自动写**用户的 allowBuilds / 构建白名单。
10
+ // allowBuilds 决定哪些依赖可以执行安装期脚本(postinstall),自动放行 = 替用户授权任意代码执行;
11
+ // 这是必须由人做的安全决策,所以本模块对它只产出文案,且 retry 恒为 null。
12
+ //
13
+ // 分类依据是**失败文本**(pnpm 的 stderr / ExecError message),同一个 kind 的文案只维护一份。
14
+
15
+ /** 分类规则:**顺序敏感**(具体在前、宽泛在后;network-timeout 最宽,放最后兜住超时类文本)。 */
16
+ const DIAGNOSIS_RULES = [
17
+ {
18
+ kind: 'minimumReleaseAge',
19
+ // pnpm 的供应链安全间隔(默认关闭,可配置):版本"太新"时拒装
20
+ re: /minimumReleaseAge|minimum release age|ERR_PNPM_MINIMUM_RELEASE_AGE|发布年龄|安全间隔/iu,
21
+ retry: 'later',
22
+ hint: 'registry 的 minimumReleaseAge(供应链安全间隔)拦下了这个版本:稍后重试即可(版本"过龄"后自动放行);'
23
+ + '确有紧急需要时由你自行放宽(pnpm 侧 --config.minimum-release-age=0)——本控制台不替你放宽安全间隔。',
24
+ },
25
+ {
26
+ kind: 'allowBuilds',
27
+ re: /allowBuilds|allow-builds|ignored build scripts|Ignored build scripts|approve-builds|ERR_PNPM_IGNORED_BUILDS/iu,
28
+ retry: null,
29
+ hint: '该包带安装脚本(postinstall 一类)且被 pnpm 拦下(allowBuilds / ignored build scripts):'
30
+ + '本控制台**只提示,绝不自动写 allowBuilds / 构建白名单**(放行等于允许该依赖执行安装期任意代码);'
31
+ + '确认该包可信后,请自行执行 `pnpm approve-builds` 或在 profile 配置里显式放行。',
32
+ },
33
+ {
34
+ kind: 'missing-tool',
35
+ re: /ENOENT|Cannot find module|MODULE_NOT_FOUND|is not recognized as an internal or external command|command not found/iu,
36
+ retry: null,
37
+ hint: '本机缺少要用的命令/模块(corepack、pnpm、git、tar 之一):确认 Node 安装完整、PATH 里能找到 corepack 与 git,'
38
+ + '必要时执行 `corepack enable`;本控制台已按多套布局回退查找,真的找不到才会报到这里。',
39
+ },
40
+ {
41
+ kind: 'not-found',
42
+ re: /ERR_PNPM_FETCH_404|E404|404 Not Found|not found in the registry|is not in the npm registry/iu,
43
+ retry: null,
44
+ hint: 'registry 上没有这个包/这个版本(包名拼错、子包未发布,或镜像未同步):核对包名与版本后重试;'
45
+ + '若确认已发布,多半是镜像未同步完整 —— 换一个 registry 或稍后再试。',
46
+ },
47
+ {
48
+ kind: 'locked',
49
+ re: /EPERM|EACCES|EBUSY|ERR_PNPM_EEXIST|operation not permitted|being used by another process/iu,
50
+ retry: null,
51
+ hint: '文件被占用/无权限(Windows 上多为杀软或残留句柄占着 node_modules):本控制台在安装前已清理陈旧包目录与'
52
+ + ' pnpm `_tmp_` 残留;仍失败请关掉占用该目录的进程(编辑器、资源管理器预览)后再重试。',
53
+ },
54
+ {
55
+ kind: 'network-timeout',
56
+ // 注意:最后一条兜底最宽 —— 超时/连接类文本很多(含我们自己的"超时 Nms:已终止整棵进程树")
57
+ re: /ETIMEDOUT|ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|EPIPE|socket hang up|fetch timeout|timed out|timeout|TIMEOUT|超时|network|ERR_PNPM_\w*FETCH_FAIL|request to .* failed/iu,
58
+ retry: 'longer-timeout',
59
+ hint: '网络类失败(超时/连接被重置/抓取失败):已用更长超时**自动重试一次**;仍失败多为镜像抖动或代理问题 ——'
60
+ + '稍后重试,或在「源管理」里换一个 registry(本控制台主源是 registry.npmmirror.com)。',
61
+ },
62
+ ]
63
+
64
+ /** 失败文本 → { kind, hint, retry }。
65
+ * retry 取值:'longer-timeout'(调用方**只对它**做一次定向长超时重试)| 'later'(提示"稍后再试",不自动重试)| null。 */
66
+ function classifyInstallFailure(stderrOrMessage) {
67
+ const text = typeof stderrOrMessage === 'string' ? stderrOrMessage : String(stderrOrMessage ?? '')
68
+ for (const rule of DIAGNOSIS_RULES) {
69
+ if (rule.re.test(text)) return { kind: rule.kind, hint: rule.hint, retry: rule.retry }
70
+ }
71
+ return {
72
+ kind: 'other',
73
+ hint: '未归类的安装失败:请把下面的原始输出用于排查;可先原样重试一次,仍失败再换源或看作业详情里的完整 stderr。',
74
+ retry: null,
75
+ }
76
+ }
77
+
78
+ /** 定向重试用的更长超时(纯函数):默认翻倍,至少 +15s,封顶 4 分钟 —— 避免"重试"变成"再卡一次很久"。
79
+ * 非法输入回落到 90s(= 安装通道默认值)后再翻倍。 */
80
+ function longerTimeoutFor(timeout, { cap = 240000 } = {}) {
81
+ const base = Number.isFinite(timeout) && timeout > 0 ? timeout : 90000
82
+ return Math.min(Math.max(Math.round(base * 2), base + 15000), cap)
83
+ }
84
+
85
+ /** 一行可展示文案(面板/日志用;纯函数):说清分类、是否已自动重试、以及下一步建议。 */
86
+ function diagnosisText(diagnosis) {
87
+ if (diagnosis == null || typeof diagnosis !== 'object') return null
88
+ const retried = diagnosis.retried === true ? '(已自动重试一次:更长超时)' : ''
89
+ return `失败分类:${diagnosis.kind}${retried}${diagnosis.hint ? `;${diagnosis.hint}` : ''}`
90
+ }
91
+
92
+ /** ③ 的定向重试编排(**本文件唯一的非纯函数**,只被 install-job.js 调用):
93
+ * pnpm 通道的失败若分类为 network-timeout(retry === 'longer-timeout'),用**更长超时**再试**一次**;
94
+ * 其余分类只把 hint 写进 job.diagnosis,不重试。返回 { installedName, error, diagnosis }:
95
+ * 重试成功给 installedName,失败给 error(调用方据此更新 lastError,语义与原来一致)。 */
96
+ async function retryPnpmOnce({ ch, profileDir, name, registries, pnpmError, job, baseTimeoutMs = 90000 }) {
97
+ const diagnosis = classifyInstallFailure(pnpmError?.message)
98
+ job.diagnosis = diagnosis
99
+ if (diagnosis.retry !== 'longer-timeout') return { installedName: null, error: null, diagnosis }
100
+ const retryTimeoutMs = longerTimeoutFor(baseTimeoutMs)
101
+ try {
102
+ await ch.pnpmInstall(profileDir, name, registries[0], retryTimeoutMs)
103
+ const ok = { ...diagnosis, retried: true, retryTimeoutMs }
104
+ job.diagnosis = ok
105
+ return { installedName: name, error: null, diagnosis: ok }
106
+ } catch (error) {
107
+ const failed = { ...classifyInstallFailure(error?.message), retried: true, retryTimeoutMs }
108
+ job.diagnosis = failed
109
+ return { installedName: null, error, diagnosis: failed }
110
+ }
111
+ }
112
+
113
+ export { classifyInstallFailure, longerTimeoutFor, diagnosisText, retryPnpmOnce, DIAGNOSIS_RULES }
@@ -7,7 +7,9 @@ import { readFile } from 'node:fs/promises'
7
7
  import { basename, dirname, join, resolve } from 'node:path'
8
8
  import { aiRepair } from './ai-run.js'
9
9
  import { maybeAutoAdaptCompat } from './compat.js'
10
- import { DEFAULT_BUNDLES, AI_CONSENT_TIMEOUT_MS, addBundleToManifest, backfillMissingDeps, curlManualInstall, detectBundleOnly, ensureBundlePatchIntegrity, githubReleaseInstall, pnpmInstall, raceInstallChannels, readBundlePatchRefNames, readGithubAuth, syncAggregateSubpackageVersions } from './install.js'
10
+ import { DEFAULT_BUNDLES, AI_CONSENT_TIMEOUT_MS, PNPM_INSTALL_TIMEOUT_MS, addBundleToManifest, backfillMissingDeps, curlManualInstall, detectBundleOnly, ensureBundlePatchIntegrity, githubReleaseInstall, pnpmInstall, raceInstallChannels, readBundlePatchRefNames, readGithubAuth, syncAggregateSubpackageVersions } from './install.js'
11
+ import { classifyInstallFailure, retryPnpmOnce } from './install-diagnose.js'
12
+ import { verifyInstalledEntry } from './install-verify.js'
11
13
  import { fetchRepoPackage, fetchRepoPackageEx, fetchSubpackageNames, packageProbeErrorText, subpackageCandidates } from './market.js'
12
14
  import { declareProfileDependency } from './manifest.js'
13
15
  import { appendInsert, readPatchState, syncNameFromNote } from './patch.js'
@@ -16,7 +18,7 @@ import { detectSkillRepo, runSkillInstallJob } from './skills.js'
16
18
  import { gitCloneUrls, orderedRegistries, readSources } from './sources.js'
17
19
  import { reconcileLockfile } from './selfupdate.js'
18
20
  import { probeGitmodules, resolveInstallKind, runSuiteInstallJob } from './suite.js'
19
- import { execFileAsync, runPnpmWithFallback } from '../infra/exec.js'
21
+ import { buildPnpmEnv, execFileAsync, runPnpmWithFallback } from '../infra/exec.js'
20
22
  import { cleanupStalePackageDir, removeDirVerified } from '../infra/fsx.js'
21
23
  import { GITHUB_API, META_BUDGET_MS, curlJson, githubJson } from '../infra/http.js'
22
24
  import { findPatchPath, packageNameOf, resolvePackageJson } from '../infra/paths.js'
@@ -134,18 +136,18 @@ async function tryCandidateChannels({ job, ch, name, profileDir, registries, rep
134
136
  const stillMissing = await ch.backfillMissingDeps(profileDir, racedInfo.missingDeps, registries)
135
137
  job.curlNote = `已通过并行 curl 通道安装 v${racedInfo.version}${stillMissing.length > 0 ? `(依赖仍未补齐:${stillMissing.join('、')},网络恢复后建议重新安装)` : '(捆绑依赖已补齐)'}`
136
138
  if (racedInfo.boxNote) job.curlNote += `(盒子验证:${racedInfo.boxNote})`
139
+ job.integrity = racedInfo.integrity ?? null // ④ 下载物摘要校验结果(加法;见 install-verify.js)
137
140
  }
138
141
  }
139
142
  }
140
143
  // 通道 1..n:配置的软件源依次尝试(每源 90 秒封顶)
144
+ let pnpmError = null
141
145
  for (let ri = 0; ri < registries.length && installedName === null; ri += 1) {
142
146
  try {
143
147
  await ch.pnpmInstall(profileDir, name, registries[ri])
144
148
  installedName = name
145
149
  break
146
- } catch (error) {
147
- lastError = error
148
- }
150
+ } catch (error) { lastError = error; pnpmError = error }
149
151
  }
150
152
  if (installedName === null) {
151
153
  // 通道 n+1:curl 手动安装(node 网络黑洞时 pnpm 下载卡死、curl 可用)——下载 registry tarball
@@ -156,6 +158,7 @@ async function tryCandidateChannels({ job, ch, name, profileDir, registries, rep
156
158
  const stillMissing = await ch.backfillMissingDeps(profileDir, info.missingDeps, registries)
157
159
  job.curlNote = `已通过 curl 通道安装 v${info.version}${stillMissing.length > 0 ? `(依赖仍未补齐:${stillMissing.join('、')},网络恢复后建议重新安装)` : '(捆绑依赖已补齐)'}`
158
160
  if (info.boxNote) job.curlNote += `(盒子验证:${info.boxNote})`
161
+ job.integrity = info.integrity ?? null // ④ 下载物摘要校验结果(加法;见 install-verify.js)
159
162
  } catch (curlError) {
160
163
  lastError = curlError
161
164
  }
@@ -202,6 +205,12 @@ async function tryCandidateChannels({ job, ch, name, profileDir, registries, rep
202
205
  }
203
206
  }
204
207
  }
208
+ // ③ 失败分类 → 只对网络类超时用更长超时定向重试**一次**;其余分类只写 job.diagnosis 提示(见 install-diagnose.js)
209
+ if (installedName === null && pnpmError !== null) {
210
+ const retry = await retryPnpmOnce({ ch, profileDir, name, registries, pnpmError, job, baseTimeoutMs: PNPM_INSTALL_TIMEOUT_MS })
211
+ installedName = retry.installedName ?? installedName
212
+ if (retry.error != null) lastError = retry.error
213
+ }
205
214
  if (installedName !== null) return { installedName, lastError }
206
215
  // Windows 原子替换失败(陈旧目录 / _tmp_ 残留)是 EPERM 类错误的根因:
207
216
  // 清理后用主源重试一次(把 AI 人工修复经验自动化,减少 AI 兜底触发)
@@ -392,6 +401,7 @@ async function runInstallJob(job, ports) {
392
401
  }
393
402
  }
394
403
  if (installedName === null) {
404
+ if (job.diagnosis == null) job.diagnosis = classifyInstallFailure(lastError?.message) // 分类提示兜底(加法)
395
405
  // 本地 AI 兜底会调用模型 API、产生费用:挂起等待用户明确同意后再执行
396
406
  job.stage = 'ai-consent'
397
407
  job.aiPending = { lastError: lastError?.message ?? null }
@@ -422,6 +432,9 @@ async function runInstallJob(job, ports) {
422
432
  job.candidateDone = true
423
433
  job.candidateName = installedName
424
434
  job.packageName = installedName
435
+ // ④ 装后校验(2026-09-26 加法,见 install-verify.js):只**提示**、绝不改成功判定 ——
436
+ // 入口文件缺失 / 补丁行指向别的包 / bundle 引用缺失 / 镜像未同步,都会写进 job.entryCheck 供面板显示
437
+ job.entryCheck = verifyInstalledEntry(profileDir, installedName)
425
438
  // 聚合包子包版本对齐(确保更新后所有子包也到新版声明版本)
426
439
  let syncedNames = []
427
440
  try {
@@ -533,7 +546,10 @@ async function pnpmRemove(profileDir, packageName) {
533
546
  timeout: 120000,
534
547
  windowsHide: true,
535
548
  maxBuffer: 4 * 1024 * 1024,
536
- env: { ...process.env, COREPACK_NPM_REGISTRY: 'https://registry.npmmirror.com', GIT_TERMINAL_PROMPT: '0', GCM_INTERACTIVE: 'never' },
549
+ // 2026-09-26:env 统一走 buildPnpmEnv(在既有 COREPACK_NPM_REGISTRY / 禁交互之上追加
550
+ // npm_config_fetch_timeout / npm_config_fetch_retries / CI=true)。**不加** --fetch-timeout 类
551
+ // CLI 选项:实测 `pnpm remove --fetch-timeout=…` 会 `[ERROR] Unknown options` 直接失败。
552
+ env: buildPnpmEnv('https://registry.npmmirror.com'),
537
553
  }
538
554
  // 与 pnpmInstall 同一套跨平台定位(Linux 的 corepack 不在 <node bin>/node_modules 下)
539
555
  await runPnpmWithFallback(args, { execOpts: opts })