@noob-stupid/dsh-plugin-console 0.3.29 → 0.3.31
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 +200 -24
- package/lib/index.js +435 -34
- package/package.json +1 -1
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_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;max-width:420px;width:calc(100% - 48px);flex-direction:column;gap:10px;display:flex;box-shadow:0 12px 32px rgba(0,0,0,.25)}.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)}";
|
|
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)}";
|
|
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");
|
|
@@ -56,6 +56,11 @@ window.__ModuleLoader__.load({
|
|
|
56
56
|
pcSwitch: "pc_switch",
|
|
57
57
|
pcSwitchKnob: "pc_switchKnob",
|
|
58
58
|
sourcesFloat: "pc_sourcesFloat",
|
|
59
|
+
srcUrl: "pc_srcUrl",
|
|
60
|
+
accWrap: "pc_accWrap",
|
|
61
|
+
acc: "pc_acc",
|
|
62
|
+
accHead: "pc_accHead",
|
|
63
|
+
accBody: "pc_accBody",
|
|
59
64
|
modeFloat: "pc_modeFloat",
|
|
60
65
|
trashBtn: "pc_trashBtn",
|
|
61
66
|
consentRemember: "pc_consentRemember",
|
|
@@ -241,8 +246,9 @@ window.__ModuleLoader__.load({
|
|
|
241
246
|
toolbarOpenTitle: "展开功能包",
|
|
242
247
|
toolbarCloseTitle: "收起功能包",
|
|
243
248
|
repoLandLabel: "仓库落地",
|
|
244
|
-
repoLandTitle: "
|
|
245
|
-
|
|
249
|
+
repoLandTitle: "仓库落地:用 Git 源「{source}」克隆任意项目到本地(~/.dsh/repos/,不限于 DSH 插件)",
|
|
250
|
+
repoLandSourceUnknown: "已配置的 Git 源",
|
|
251
|
+
repoLandPlaceholder: "输入 owner/repo 或仓库链接,如 deepseek-ai/deepseek-harness",
|
|
246
252
|
repoLandBusy: "克隆中…(大仓库可能较慢)",
|
|
247
253
|
repoLandDone: "已落地到:",
|
|
248
254
|
repoLandOpenFolder: "打开文件夹",
|
|
@@ -266,9 +272,12 @@ window.__ModuleLoader__.load({
|
|
|
266
272
|
dismissFailure: "关闭此条提示",
|
|
267
273
|
sourcesBtn: "软件源",
|
|
268
274
|
sourcesTitle: "软件源管理",
|
|
269
|
-
sourcesDesc: "
|
|
275
|
+
sourcesDesc: "分工:① 软件源 = 下载 npm 包(安装用);② 索引源 = 市场首页清单 JSON;③ Git 源 = 克隆仓库代码(无 npm 包时);④ 搜索源 = 搜索时查哪个平台——搜到之后仍按 ①/③ 下载",
|
|
270
276
|
sourcePrimary: "主源",
|
|
271
277
|
setPrimary: "设为主源",
|
|
278
|
+
setPrimaryHint: "设为主源:优先使用它,失败时自动回退到其他源",
|
|
279
|
+
editSourceHint: "编辑:修改这个源的名称与地址",
|
|
280
|
+
removeSourceHint: "删除:从列表中移除这个源(不影响已安装的插件)",
|
|
272
281
|
removeSource: "删除",
|
|
273
282
|
sourceName: "名称",
|
|
274
283
|
sourceUrl: "地址(https://…)",
|
|
@@ -282,6 +291,21 @@ window.__ModuleLoader__.load({
|
|
|
282
291
|
searchSourcesTitle: "搜索源",
|
|
283
292
|
searchSourcesDesc: "搜索平台(点击登录标切换)。自定义源用 URL 模板:{q}=关键词、{page}=页码",
|
|
284
293
|
addSearchSource: "添加搜索源",
|
|
294
|
+
indexSourcesTitle: "索引源",
|
|
295
|
+
indexSourcesDesc: "市场静态索引的下载地址(主源优先,其余依次重试)。可换成公司内网自建服务——注意:索引「内容」由那份 JSON 决定,把公共索引与内网私有插件索引一并托管即可在内网浏览;想同时看到两边条目,请打开下面的「合并所有索引源」",
|
|
296
|
+
indexMergeLabel: "合并所有索引源",
|
|
297
|
+
indexMergeDesc: "开启后:所有索引源的结果合并去重展示(公共索引 + 公司内网私有索引同时可见);关闭时按主→备只用一个源(内网优先,更快)",
|
|
298
|
+
addIndexSource: "添加索引源",
|
|
299
|
+
indexUrlPlaceholder: "索引 JSON 地址(https:// 或本机/私网 http://)",
|
|
300
|
+
gitSourcesTitle: "Git 源",
|
|
301
|
+
gitSourcesDesc: "克隆仓库时用的地址模板({owner}/{repo} 占位符)。下面这个列表就是「自定义 Git 源」——内置两条是默认值,点「添加 Git 源」即可换成你自己的:Gitee = https://gitee.com/{owner}/{repo}.git;GitLab = https://gitlab.com/{owner}/{repo}.git;自建 Gitea = https://你的域名/{owner}/{repo}.git;镜像代理 = https://镜像站/https://github.com/{owner}/{repo}.git;本地裸仓库 = file:///D:/repos/{owner}/{repo}.git。主源优先,失败自动回退下一个",
|
|
302
|
+
addGitSource: "添加 Git 源",
|
|
303
|
+
gitUrlPlaceholder: "如 https://gitee.com/{owner}/{repo}.git",
|
|
304
|
+
gitSourceBtn: "Git 源",
|
|
305
|
+
marketIndexOffline: "离线数据 · 缓存于 {time}(索引源不可达,已回退上次成功结果)",
|
|
306
|
+
marketIndexFrom: "索引来源:{source}",
|
|
307
|
+
marketIndexFailed: "市场索引加载失败",
|
|
308
|
+
unknown: "未知",
|
|
285
309
|
searchUrlPlaceholder: "URL 模板(必须含 {q})",
|
|
286
310
|
invalidSearchUrl: "搜索 URL 模板必须包含 {q} 占位符",
|
|
287
311
|
headersPlaceholder: "请求头(可选):每行一个,格式 名称: 值,如 Authorization: Bearer 令牌",
|
|
@@ -558,8 +582,9 @@ window.__ModuleLoader__.load({
|
|
|
558
582
|
toolbarOpenTitle: "Expand feature pack",
|
|
559
583
|
toolbarCloseTitle: "Collapse feature pack",
|
|
560
584
|
repoLandLabel: "Repo land",
|
|
561
|
-
repoLandTitle: "Repo land: clone any
|
|
562
|
-
|
|
585
|
+
repoLandTitle: "Repo land: clone any project to ~/.dsh/repos/ via git source \"{source}\" (not limited to DSH plugins)",
|
|
586
|
+
repoLandSourceUnknown: "the configured git source",
|
|
587
|
+
repoLandPlaceholder: "owner/repo or repo URL, e.g. deepseek-ai/deepseek-harness",
|
|
563
588
|
repoLandBusy: "Cloning… (large repos may take a while)",
|
|
564
589
|
repoLandDone: "Landed at: ",
|
|
565
590
|
repoLandOpenFolder: "Open folder",
|
|
@@ -583,9 +608,12 @@ window.__ModuleLoader__.load({
|
|
|
583
608
|
dismissFailure: "Dismiss this notice",
|
|
584
609
|
sourcesBtn: "Sources",
|
|
585
610
|
sourcesTitle: "Software sources",
|
|
586
|
-
sourcesDesc: "
|
|
611
|
+
sourcesDesc: "Roles: ① registry = download npm packages (install); ② index sources = market list JSON; ③ git sources = clone repo code (when no npm package); ④ search sources = where to search — results are still downloaded via ①/③",
|
|
587
612
|
sourcePrimary: "Primary",
|
|
588
613
|
setPrimary: "Set primary",
|
|
614
|
+
setPrimaryHint: "Set primary: tried first, falls back to the others on failure",
|
|
615
|
+
editSourceHint: "Edit: change this source's name and URL",
|
|
616
|
+
removeSourceHint: "Remove: delete this source from the list (installed plugins unaffected)",
|
|
589
617
|
removeSource: "Remove",
|
|
590
618
|
sourceName: "Name",
|
|
591
619
|
sourceUrl: "URL (https://…)",
|
|
@@ -599,6 +627,21 @@ window.__ModuleLoader__.load({
|
|
|
599
627
|
searchSourcesTitle: "Search sources",
|
|
600
628
|
searchSourcesDesc: "Search platforms (switch via the pill). Custom sources use a URL template: {q}=keyword, {page}=page number",
|
|
601
629
|
addSearchSource: "Add search source",
|
|
630
|
+
indexSourcesTitle: "Index sources",
|
|
631
|
+
indexSourcesDesc: "Download URLs for the static market index (primary first, others retried in order). Point this at an intranet service — note the index CONTENT comes from that JSON, so host a merged public+private index there. To see both sides at once, turn on \"Merge all index sources\" below",
|
|
632
|
+
indexMergeLabel: "Merge all index sources",
|
|
633
|
+
indexMergeDesc: "On: results from all index sources are merged and de-duplicated (public index + company intranet private index visible together). Off: only one source is used, primary first (intranet-friendly, faster)",
|
|
634
|
+
addIndexSource: "Add index source",
|
|
635
|
+
indexUrlPlaceholder: "Index JSON URL (https:// or local/intranet http://)",
|
|
636
|
+
gitSourcesTitle: "Git sources",
|
|
637
|
+
gitSourcesDesc: "URL template for cloning ({owner}/{repo} placeholders). This list IS the custom git-source list — the two built-ins are just defaults; click Add git source to swap in your own: Gitee = https://gitee.com/{owner}/{repo}.git; GitLab = https://gitlab.com/{owner}/{repo}.git; self-hosted Gitea = https://your-domain/{owner}/{repo}.git; mirror proxy = https://mirror/https://github.com/{owner}/{repo}.git; local bare repo = file:///D:/repos/{owner}/{repo}.git. Primary first, falls back on failure",
|
|
638
|
+
addGitSource: "Add Git source",
|
|
639
|
+
gitUrlPlaceholder: "e.g. https://gitee.com/{owner}/{repo}.git",
|
|
640
|
+
gitSourceBtn: "Git sources",
|
|
641
|
+
marketIndexOffline: "Offline data · cached {time} (index sources unreachable, showing last successful result)",
|
|
642
|
+
marketIndexFrom: "Index source: {source}",
|
|
643
|
+
marketIndexFailed: "Failed to load market index",
|
|
644
|
+
unknown: "unknown",
|
|
602
645
|
searchUrlPlaceholder: "URL template (must contain {q})",
|
|
603
646
|
invalidSearchUrl: "Search URL template must contain the {q} placeholder",
|
|
604
647
|
headersPlaceholder: "Headers (optional): one per line, format Name: Value, e.g. Authorization: Bearer token",
|
|
@@ -1310,7 +1353,7 @@ window.__ModuleLoader__.load({
|
|
|
1310
1353
|
if (aiJob === null || aiJob.status !== "plan-ready") return;
|
|
1311
1354
|
const steps = (aiJob.steps ?? []).map((s) => s.index).filter((i) => aiSelected[i]);
|
|
1312
1355
|
if (steps.length === 0) {
|
|
1313
|
-
setMessage(t("
|
|
1356
|
+
setMessage(t("aiEmpowerNeedSteps"));
|
|
1314
1357
|
return;
|
|
1315
1358
|
}
|
|
1316
1359
|
if (!window.confirm(t("aiConfirmDeploy").replace("{n}", String(steps.length)).replace("{name}", aiJob.displayName ?? aiJob.source))) {
|
|
@@ -1394,8 +1437,18 @@ window.__ModuleLoader__.load({
|
|
|
1394
1437
|
const [sourcesBusy, setSourcesBusy] = react.useState(false);
|
|
1395
1438
|
// registry 行内编辑
|
|
1396
1439
|
const [editReg, setEditReg] = react.useState(null);
|
|
1440
|
+
const [indexSourceName, setIndexSourceName] = react.useState("");
|
|
1441
|
+
const [indexSourceUrl, setIndexSourceUrl] = react.useState("");
|
|
1442
|
+
const [editIndex, setEditIndex] = react.useState(null);
|
|
1443
|
+
const [gitSourceName, setGitSourceName] = react.useState("");
|
|
1444
|
+
const [gitSourceUrl, setGitSourceUrl] = react.useState("");
|
|
1445
|
+
const [editGit, setEditGit] = react.useState(null);
|
|
1446
|
+
// 软件源弹窗折叠分区:软件源默认展开,其余收起(避免弹窗过长)
|
|
1447
|
+
const [openSections, setOpenSections] = react.useState({ registries: true, index: false, git: false, search: false, gitee: false });
|
|
1397
1448
|
// 搜索源列表(内置 + 自定义,来自配置)
|
|
1398
1449
|
const [searchSourcesList, setSearchSourcesList] = react.useState(null);
|
|
1450
|
+
// Git 源列表(「仓库落地」提示动态显示当前主源,避免写死 GitHub)
|
|
1451
|
+
const [gitSourcesList, setGitSourcesList] = react.useState(null);
|
|
1399
1452
|
// 自定义搜索源添加表单
|
|
1400
1453
|
const [searchSourceName, setSearchSourceName] = react.useState("");
|
|
1401
1454
|
const [searchSourceUrl, setSearchSourceUrl] = react.useState("");
|
|
@@ -1436,16 +1489,26 @@ window.__ModuleLoader__.load({
|
|
|
1436
1489
|
// 挂载时拉取软件源配置(搜索源菜单用,含自定义源)
|
|
1437
1490
|
react.useEffect(() => {
|
|
1438
1491
|
call("/plugin-console/sources").then(
|
|
1439
|
-
(data) =>
|
|
1492
|
+
(data) => {
|
|
1493
|
+
setSearchSourcesList(data.sources.searchSources ?? []);
|
|
1494
|
+
setGitSourcesList(data.sources.gitSources ?? []);
|
|
1495
|
+
},
|
|
1440
1496
|
() => {},
|
|
1441
1497
|
);
|
|
1442
1498
|
}, []);
|
|
1443
1499
|
// 挂载时加载静态插件索引(GitHub 源空查询时秒开浏览,零 API 调用)
|
|
1444
1500
|
react.useEffect(() => {
|
|
1445
|
-
call("/plugin-console/market-index").then(
|
|
1501
|
+
call("/plugin-console/market-index", {}).then(
|
|
1446
1502
|
(data) => {
|
|
1447
1503
|
if (data && Array.isArray(data.items)) {
|
|
1448
|
-
setIndexData({
|
|
1504
|
+
setIndexData({
|
|
1505
|
+
items: data.items,
|
|
1506
|
+
skills: Array.isArray(data.skills) ? data.skills : [],
|
|
1507
|
+
loaded: true,
|
|
1508
|
+
offline: data.offline === true,
|
|
1509
|
+
cachedAt: typeof data.cachedAt === "number" ? data.cachedAt : null,
|
|
1510
|
+
sourceName: typeof data.sourceName === "string" ? data.sourceName : null,
|
|
1511
|
+
});
|
|
1449
1512
|
// ★「只看官方」需要官方标记:索引条目补标(网络/服务端此前判定失败时不动已有标记,避免 null 覆盖)
|
|
1450
1513
|
// 服务端 /enrich 已带 24h 结果缓存时,此处仅兜底浏览器直连失败部分。
|
|
1451
1514
|
const base = data.items;
|
|
@@ -1466,7 +1529,7 @@ window.__ModuleLoader__.load({
|
|
|
1466
1529
|
});
|
|
1467
1530
|
}
|
|
1468
1531
|
},
|
|
1469
|
-
() =>
|
|
1532
|
+
(error) => setMessage(t("marketIndexFailed") + ":" + (error?.message ?? t("unknown"))),
|
|
1470
1533
|
);
|
|
1471
1534
|
// 市场状态记忆:关闭前搜索过 → 重开时自动恢复该搜索(query 已从 localStorage 初始化)
|
|
1472
1535
|
if (query !== "") {
|
|
@@ -2100,6 +2163,46 @@ window.__ModuleLoader__.load({
|
|
|
2100
2163
|
if (editReg === null) return;
|
|
2101
2164
|
sourcesAction({ action: "edit", id: editReg.id, name: editReg.name, url: editReg.url }, () => setEditReg(null));
|
|
2102
2165
|
};
|
|
2166
|
+
const addIndexSource = () => {
|
|
2167
|
+
if (!isAllowedSourceUrl(indexSourceUrl.trim())) {
|
|
2168
|
+
setMessage(t("failed") + ":" + t("invalidSourceUrl"));
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
sourcesAction({ action: "add-index", name: indexSourceName.trim(), url: indexSourceUrl.trim() }, () => { setIndexSourceName(""); setIndexSourceUrl(""); });
|
|
2172
|
+
};
|
|
2173
|
+
const saveEditIndex = () => {
|
|
2174
|
+
if (editIndex === null) return;
|
|
2175
|
+
sourcesAction({ action: "edit-index", id: editIndex.id, name: editIndex.name, url: editIndex.url }, () => setEditIndex(null));
|
|
2176
|
+
};
|
|
2177
|
+
const addGitSource = () => {
|
|
2178
|
+
const tpl = gitSourceUrl.trim();
|
|
2179
|
+
if (!tpl.includes("{owner}") || !tpl.includes("{repo}")) {
|
|
2180
|
+
setMessage(t("failed") + ":" + t("gitUrlPlaceholder"));
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
if (!isAllowedSourceUrl(tpl)) {
|
|
2184
|
+
setMessage(t("failed") + ":" + t("invalidSourceUrl"));
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
sourcesAction({ action: "add-git", name: gitSourceName.trim(), urlTemplate: tpl }, () => { setGitSourceName(""); setGitSourceUrl(""); });
|
|
2188
|
+
};
|
|
2189
|
+
const saveEditGit = () => {
|
|
2190
|
+
if (editGit === null) return;
|
|
2191
|
+
sourcesAction({ action: "edit-git", id: editGit.id, name: editGit.name, urlTemplate: editGit.urlTemplate }, () => setEditGit(null));
|
|
2192
|
+
};
|
|
2193
|
+
/** 折叠分区(软件源弹窗):标题可点,展开/收起各自独立;软件源默认展开。 */
|
|
2194
|
+
const accSection = (key, title, children) => {
|
|
2195
|
+
const open = openSections[key] === true;
|
|
2196
|
+
return el("div", { key, className: styles.acc },
|
|
2197
|
+
el("button", {
|
|
2198
|
+
type: "button",
|
|
2199
|
+
className: styles.accHead,
|
|
2200
|
+
"data-open": open ? "true" : "false",
|
|
2201
|
+
"aria-expanded": open ? "true" : "false",
|
|
2202
|
+
onClick: () => setOpenSections((prev) => ({ ...prev, [key]: !prev[key] })),
|
|
2203
|
+
}, (open ? "▾ " : "▸ ") + title),
|
|
2204
|
+
open ? el("div", { className: styles.accBody }, ...children) : null);
|
|
2205
|
+
};
|
|
2103
2206
|
const addSearchSource = () => {
|
|
2104
2207
|
if (!isAllowedSourceUrl(searchSourceUrl.trim()) || !searchSourceUrl.includes("{q}")) {
|
|
2105
2208
|
setMessage(t("failed") + ":" + t("invalidSearchUrl"));
|
|
@@ -2284,6 +2387,12 @@ window.__ModuleLoader__.load({
|
|
|
2284
2387
|
: source === "gitee"
|
|
2285
2388
|
? "Gitee"
|
|
2286
2389
|
: ((searchSourcesList !== null ? searchSourcesList.find((s) => s.id === source)?.name : undefined) ?? source);
|
|
2390
|
+
// 仓库落地提示:动态带上当前主 Git 源名称(Git 源可自定义,不能写死 GitHub)
|
|
2391
|
+
const repoLandTitleText = () => {
|
|
2392
|
+
const primary = (gitSourcesList ?? []).find((s) => s.primary === true);
|
|
2393
|
+
const label = primary?.name ?? t("repoLandSourceUnknown");
|
|
2394
|
+
return t("repoLandTitle").replace("{source}", label);
|
|
2395
|
+
};
|
|
2287
2396
|
const extraIds = new Set((state.status === "ready" ? state.data.patch?.inserts ?? [] : []));
|
|
2288
2397
|
const extraCount = state.status === "ready" ? state.data.entries.filter((entry) => entry.extra === true || extraIds.has(entry.rowId)).length : 0;
|
|
2289
2398
|
const pkgRootOf = (m) => { const mm = String(m ?? ""); const x = mm.match(/^(@[^/]+\/[^/]+)(?:\/.*)?$/u); return x ? x[1] : mm; };
|
|
@@ -2655,7 +2764,7 @@ window.__ModuleLoader__.load({
|
|
|
2655
2764
|
? el("button", {
|
|
2656
2765
|
type: "button",
|
|
2657
2766
|
className: styles.toggle,
|
|
2658
|
-
title:
|
|
2767
|
+
title: repoLandTitleText(),
|
|
2659
2768
|
onClick: () => repoLandEntry(item.fullName),
|
|
2660
2769
|
}, repoLanded.some((r) => r.repo === item.fullName) ? t("repoLandLanded") + " ✓" : t("repoLandEntry"))
|
|
2661
2770
|
: null,
|
|
@@ -2953,7 +3062,7 @@ window.__ModuleLoader__.load({
|
|
|
2953
3062
|
el("div", { className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
2954
3063
|
el("strong", { className: styles.name }, t("repoLandLabel")),
|
|
2955
3064
|
el("button", { type: "button", className: styles.trashBtn, style: { fontSize: 14, padding: "4px 8px" }, title: t("closeModal"), "aria-label": t("closeModal"), onClick: () => { setRepoLandOpen(false); setRepoLandResult(null); } }, "✕")),
|
|
2956
|
-
el("p", { className: styles.message },
|
|
3065
|
+
el("p", { className: styles.message }, repoLandTitleText()),
|
|
2957
3066
|
el("div", { className: styles.rowTop, style: { flexWrap: "wrap" } },
|
|
2958
3067
|
el("span", { className: styles.message }, t("repoLandDirLabel") + ":" + (repoLandDir || "—")),
|
|
2959
3068
|
el("input", { type: "text", value: repoLandDirInput, onChange: (event) => setRepoLandDirInput(event.currentTarget.value), style: { flex: 1, minWidth: 120, font: "inherit", padding: "4px 8px", borderRadius: 6, border: "1px solid var(--dsw-alias-border-l2)", background: "var(--dsw-alias-bg-layer-1)", color: "var(--dsw-alias-label-primary)" } }),
|
|
@@ -3199,7 +3308,7 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
|
|
|
3199
3308
|
type: "button",
|
|
3200
3309
|
className: styles.toolItem,
|
|
3201
3310
|
"data-active": repoLandOn ? "true" : "false",
|
|
3202
|
-
title:
|
|
3311
|
+
title: repoLandTitleText(),
|
|
3203
3312
|
"aria-label": t("repoLandLabel"),
|
|
3204
3313
|
onClick: () => { setRepoLandOn((v) => { const n = !v; try { localStorage.setItem("pc-repo-land", n ? "1" : "0"); } catch {} return n; }); setRepoLandOpen(true); repoLandRefresh(); },
|
|
3205
3314
|
}, t("repoLandLabel")),
|
|
@@ -3276,6 +3385,11 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
|
|
|
3276
3385
|
? t("skillsNote")
|
|
3277
3386
|
: t("skillsGithubOnly"))
|
|
3278
3387
|
: (searchSource === "github" ? t("direct") : t("directOther").replace("{source}", sourceDisplayName(searchSource)))),
|
|
3388
|
+
indexData !== null && indexData.offline === true
|
|
3389
|
+
? el("p", { className: styles.message, "data-warn": "true" },
|
|
3390
|
+
t("marketIndexOffline").replace("{time}", indexData.cachedAt !== null ? new Date(indexData.cachedAt).toLocaleString() : "—")
|
|
3391
|
+
+ (indexData.sourceName !== null ? " · " + t("marketIndexFrom").replace("{source}", indexData.sourceName) : ""))
|
|
3392
|
+
: null,
|
|
3279
3393
|
el("div", { className: styles.search, id: "pc-market-search" },
|
|
3280
3394
|
el("div", { className: styles.searchInner },
|
|
3281
3395
|
el("input", {
|
|
@@ -3384,7 +3498,10 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
|
|
|
3384
3498
|
}, "✕")),
|
|
3385
3499
|
el("p", { className: styles.message, "data-error": "true" }, job.error ?? ""),
|
|
3386
3500
|
el("div", { className: styles.rowTop },
|
|
3387
|
-
el("button", { type: "button", className: styles.toggle, onClick: () => startJob(job.repo, job.packageName, job.source ?? "github") }, t("retry"))
|
|
3501
|
+
el("button", { type: "button", className: styles.toggle, onClick: () => startJob(job.repo, job.packageName, job.source ?? "github") }, t("retry")),
|
|
3502
|
+
job.hint === "repo-land"
|
|
3503
|
+
? el("button", { type: "button", className: styles.toggle, title: repoLandTitleText(), onClick: () => { setRepoLandOpen(true); repoLandEntry(job.repo); } }, t("repoLandEntry"))
|
|
3504
|
+
: null)),
|
|
3388
3505
|
),
|
|
3389
3506
|
repoInfo !== null
|
|
3390
3507
|
? el("aside", { className: styles.floatPanel },
|
|
@@ -3546,8 +3663,9 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
|
|
|
3546
3663
|
el("p", { className: styles.message }, t("sourcesDesc")),
|
|
3547
3664
|
sourcesData === null
|
|
3548
3665
|
? el("p", { className: styles.status }, t("loading"))
|
|
3549
|
-
: el("div", {
|
|
3550
|
-
|
|
3666
|
+
: el("div", { className: styles.accWrap },
|
|
3667
|
+
accSection("registries", t("sourcesBtn"), [
|
|
3668
|
+
...sourcesData.registries.map((src) =>
|
|
3551
3669
|
editReg !== null && editReg.id === src.id
|
|
3552
3670
|
? el("div", { key: src.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
3553
3671
|
el("input", { type: "text", value: editReg.name, onChange: (event) => setEditReg({ ...editReg, name: event.currentTarget.value }), style: { flex: 1, minWidth: 80 } }),
|
|
@@ -3559,13 +3677,68 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
|
|
|
3559
3677
|
el("strong", { className: styles.name, style: { flex: "none", maxWidth: "35%" } }, src.name + (src.primary ? "(" + t("sourcePrimary") + ")" : "")),
|
|
3560
3678
|
el("code", { className: styles.srcUrl }, src.url)),
|
|
3561
3679
|
el("div", { className: styles.rowTop },
|
|
3562
|
-
src.primary ? null : el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: () => sourcesAction({ action: "set-primary", id: src.id }) }, t("setPrimary")),
|
|
3563
|
-
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: () => setEditReg({ id: src.id, name: src.name, url: src.url }) }, t("editSource"))))),
|
|
3680
|
+
src.primary ? null : el("button", { type: "button", className: styles.toggle, title: t("setPrimaryHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "set-primary", id: src.id }) }, t("setPrimary")),
|
|
3681
|
+
el("button", { type: "button", className: styles.toggle, title: t("editSourceHint"), disabled: sourcesBusy, onClick: () => setEditReg({ id: src.id, name: src.name, url: src.url }) }, t("editSource"))))),
|
|
3564
3682
|
el("div", { className: styles.rowTop },
|
|
3565
3683
|
el("input", { type: "text", placeholder: t("sourceName"), value: sourceName, onChange: (event) => setSourceName(event.currentTarget.value), style: { flex: 1, minWidth: 0 } }),
|
|
3566
3684
|
el("input", { type: "text", placeholder: t("sourceUrl"), value: sourceUrl, onChange: (event) => setSourceUrl(event.currentTarget.value), style: { flex: 2, minWidth: 0 } }),
|
|
3567
3685
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addSource }, t("addSource"))),
|
|
3568
|
-
|
|
3686
|
+
]),
|
|
3687
|
+
accSection("index", t("indexSourcesTitle"), [
|
|
3688
|
+
el("p", { className: styles.message }, t("indexSourcesDesc")),
|
|
3689
|
+
(sourcesData.indexSources ?? []).map((s) =>
|
|
3690
|
+
editIndex !== null && editIndex.id === s.id
|
|
3691
|
+
? el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
3692
|
+
el("input", { type: "text", value: editIndex.name, onChange: (event) => setEditIndex({ ...editIndex, name: event.currentTarget.value }), style: { flex: 1, minWidth: 80 } }),
|
|
3693
|
+
el("input", { type: "text", value: editIndex.url, onChange: (event) => setEditIndex({ ...editIndex, url: event.currentTarget.value }), style: { flex: 2, minWidth: 120 } }),
|
|
3694
|
+
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditIndex }, t("saveSource")),
|
|
3695
|
+
el("button", { type: "button", className: styles.toggle, onClick: () => setEditIndex(null) }, t("cancelEdit")))
|
|
3696
|
+
: el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
3697
|
+
el("div", { className: styles.rowTop, style: { flex: 1, minWidth: 0 } },
|
|
3698
|
+
el("strong", { className: styles.name, style: { flex: "none", maxWidth: "35%" } }, s.name + (s.primary ? "(" + t("sourcePrimary") + ")" : "")),
|
|
3699
|
+
el("code", { className: styles.srcUrl }, s.url)),
|
|
3700
|
+
el("div", { className: styles.rowTop },
|
|
3701
|
+
s.primary ? null : el("button", { type: "button", className: styles.toggle, title: t("setPrimaryHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "set-index-primary", id: s.id }) }, t("setPrimary")),
|
|
3702
|
+
el("button", { type: "button", className: styles.toggle, title: t("editSourceHint"), disabled: sourcesBusy, onClick: () => setEditIndex({ id: s.id, name: s.name, url: s.url }) }, t("editSource")),
|
|
3703
|
+
el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-index", id: s.id }) }, t("removeSource"))))),
|
|
3704
|
+
el("div", { className: styles.rowTop },
|
|
3705
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: indexSourceName, onChange: (event) => setIndexSourceName(event.currentTarget.value), style: { flex: 1, minWidth: 0 } }),
|
|
3706
|
+
el("input", { type: "text", placeholder: t("indexUrlPlaceholder"), value: indexSourceUrl, onChange: (event) => setIndexSourceUrl(event.currentTarget.value), style: { flex: 2, minWidth: 0 } }),
|
|
3707
|
+
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addIndexSource }, t("addIndexSource"))),
|
|
3708
|
+
el("div", { className: styles.rowTop },
|
|
3709
|
+
el("button", {
|
|
3710
|
+
type: "button",
|
|
3711
|
+
className: styles.toggle,
|
|
3712
|
+
"data-active": sourcesData.indexMerge === true ? "true" : "false",
|
|
3713
|
+
title: t("indexMergeDesc"),
|
|
3714
|
+
disabled: sourcesBusy,
|
|
3715
|
+
onClick: () => sourcesAction({ action: "set-index-merge", merge: sourcesData.indexMerge !== true }),
|
|
3716
|
+
}, t("indexMergeLabel") + ":" + (sourcesData.indexMerge === true ? t("on") : t("off")))),
|
|
3717
|
+
el("p", { className: styles.message }, t("indexMergeDesc")),
|
|
3718
|
+
]),
|
|
3719
|
+
accSection("git", t("gitSourcesTitle"), [
|
|
3720
|
+
el("p", { className: styles.message }, t("gitSourcesDesc")),
|
|
3721
|
+
(sourcesData.gitSources ?? []).map((s) =>
|
|
3722
|
+
editGit !== null && editGit.id === s.id
|
|
3723
|
+
? el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
3724
|
+
el("input", { type: "text", value: editGit.name, onChange: (event) => setEditGit({ ...editGit, name: event.currentTarget.value }), style: { flex: 1, minWidth: 80 } }),
|
|
3725
|
+
el("input", { type: "text", value: editGit.urlTemplate, onChange: (event) => setEditGit({ ...editGit, urlTemplate: event.currentTarget.value }), style: { flex: 2, minWidth: 120 } }),
|
|
3726
|
+
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: saveEditGit }, t("saveSource")),
|
|
3727
|
+
el("button", { type: "button", className: styles.toggle, onClick: () => setEditGit(null) }, t("cancelEdit")))
|
|
3728
|
+
: el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
3729
|
+
el("div", { className: styles.rowTop, style: { flex: 1, minWidth: 0 } },
|
|
3730
|
+
el("strong", { className: styles.name, style: { flex: "none", maxWidth: "35%" } }, s.name + (s.primary ? "(" + t("sourcePrimary") + ")" : "")),
|
|
3731
|
+
el("code", { className: styles.srcUrl }, s.urlTemplate)),
|
|
3732
|
+
el("div", { className: styles.rowTop },
|
|
3733
|
+
s.primary ? null : el("button", { type: "button", className: styles.toggle, title: t("setPrimaryHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "set-git-primary", id: s.id }) }, t("setPrimary")),
|
|
3734
|
+
el("button", { type: "button", className: styles.toggle, title: t("editSourceHint"), disabled: sourcesBusy, onClick: () => setEditGit({ id: s.id, name: s.name, urlTemplate: s.urlTemplate }) }, t("editSource")),
|
|
3735
|
+
el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-git", id: s.id }) }, t("removeSource"))))),
|
|
3736
|
+
el("div", { className: styles.rowTop },
|
|
3737
|
+
el("input", { type: "text", placeholder: t("sourceName"), value: gitSourceName, onChange: (event) => setGitSourceName(event.currentTarget.value), style: { flex: 1, minWidth: 0 } }),
|
|
3738
|
+
el("input", { type: "text", placeholder: t("gitUrlPlaceholder"), value: gitSourceUrl, onChange: (event) => setGitSourceUrl(event.currentTarget.value), style: { flex: 2, minWidth: 0 } }),
|
|
3739
|
+
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: addGitSource }, t("addGitSource"))),
|
|
3740
|
+
]),
|
|
3741
|
+
accSection("search", t("searchSourcesTitle"), [
|
|
3569
3742
|
el("p", { className: styles.message }, t("searchSourcesDesc")),
|
|
3570
3743
|
(sourcesData.searchSources ?? []).map((s) =>
|
|
3571
3744
|
el("div", { key: s.id, className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
@@ -3576,7 +3749,7 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
|
|
|
3576
3749
|
? el("span", { className: styles.tag, title: t("headersPlaceholder") }, "🔒 " + Object.keys(s.headers).length)
|
|
3577
3750
|
: null),
|
|
3578
3751
|
s.type === "custom"
|
|
3579
|
-
? el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-search", id: s.id }) }, t("removeSource"))
|
|
3752
|
+
? el("button", { type: "button", className: styles.toggle, title: t("removeSourceHint"), disabled: sourcesBusy, onClick: () => sourcesAction({ action: "remove-search", id: s.id }) }, t("removeSource"))
|
|
3580
3753
|
: null)),
|
|
3581
3754
|
el("div", { className: styles.rowTop },
|
|
3582
3755
|
el("input", { type: "text", placeholder: t("sourceName"), value: searchSourceName, onChange: (event) => setSearchSourceName(event.currentTarget.value), style: { flex: 1, minWidth: 0 } }),
|
|
@@ -3589,7 +3762,8 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
|
|
|
3589
3762
|
onChange: (event) => setSearchSourceHeaders(event.currentTarget.value),
|
|
3590
3763
|
style: { width: "100%", boxSizing: "border-box", font: "inherit", resize: "vertical" },
|
|
3591
3764
|
}),
|
|
3592
|
-
|
|
3765
|
+
]),
|
|
3766
|
+
accSection("gitee", t("giteeTitle"), [
|
|
3593
3767
|
el("p", { className: styles.message }, t("giteeDesc").replace("{port}", String(window.location.port || "3080"))),
|
|
3594
3768
|
el("div", { className: styles.rowTop, style: { justifyContent: "space-between" } },
|
|
3595
3769
|
el("span", { className: styles.tag },
|
|
@@ -3604,7 +3778,9 @@ setMessage(t("failed") + ":" + (data?.reason ?? "未知"));
|
|
|
3604
3778
|
el("button", { type: "button", className: styles.toggle, disabled: giteeStatus !== null && !giteeStatus.clientConfigured, onClick: startGiteeOauth }, t("giteeLoginBtn")),
|
|
3605
3779
|
giteeStatus !== null && giteeStatus.hasToken
|
|
3606
3780
|
? el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: () => sourcesAction({ action: "gitee-clear" }) }, t("giteeClear"))
|
|
3607
|
-
: null)
|
|
3781
|
+
: null),
|
|
3782
|
+
]),
|
|
3783
|
+
),
|
|
3608
3784
|
el("div", { className: styles.rowTop },
|
|
3609
3785
|
el("button", { type: "button", className: styles.toggle, disabled: sourcesBusy, onClick: () => sourcesAction({ action: "reset" }) }, t("resetSources")),
|
|
3610
3786
|
el("button", { type: "button", className: styles.toggle, onClick: () => setSourcesOpen(false) }, t("closeModal")))))
|
package/lib/index.js
CHANGED
|
@@ -621,8 +621,24 @@ const DEFAULT_SOURCES = {
|
|
|
621
621
|
{ id: 'github', name: 'GitHub', type: 'builtin' },
|
|
622
622
|
{ id: 'gitee', name: 'Gitee', type: 'builtin' },
|
|
623
623
|
],
|
|
624
|
+
// 市场静态索引源(按主→备依次尝试;内网可整体替换为自建镜像,实现完全离线的市场浏览)
|
|
625
|
+
indexSources: [
|
|
626
|
+
{ id: 'jsdelivr', name: 'jsDelivr CDN', url: 'https://cdn.jsdelivr.net/gh/Noob-stupid/dsh-plugin-hub@main/marketplace/index.json', primary: true },
|
|
627
|
+
{ id: 'ghproxy', name: 'ghproxy 镜像', url: 'https://ghproxy.net/https://raw.githubusercontent.com/Noob-stupid/dsh-plugin-hub/main/marketplace/index.json', primary: false },
|
|
628
|
+
],
|
|
629
|
+
// Git 克隆源({owner}/{repo} 占位符;按主→备依次尝试)。
|
|
630
|
+
// 可替换为 Gitee / GitLab / 自建 Gitea / 任意镜像代理,实现「换一个网站下载仓库内容」。
|
|
631
|
+
gitSources: [
|
|
632
|
+
{ id: 'ghproxy-git', name: 'ghproxy 镜像', urlTemplate: 'https://ghproxy.net/https://github.com/{owner}/{repo}.git', primary: true },
|
|
633
|
+
{ id: 'github-git', name: 'GitHub 直连', urlTemplate: 'https://github.com/{owner}/{repo}.git', primary: false },
|
|
634
|
+
],
|
|
635
|
+
// 索引合并模式:true = 所有索引源结果合并去重(公共索引 + 内网私有索引同时可见);
|
|
636
|
+
// false = 主→备只用一个(内网优先,更快)
|
|
637
|
+
indexMerge: false,
|
|
624
638
|
gitee: { clientId: '', clientSecret: '', token: '', login: '' },
|
|
625
639
|
}
|
|
640
|
+
/** 市场索引落盘缓存(网络不可达时降级展示上次成功结果,避免内网/断网下市场空白)。 */
|
|
641
|
+
const MARKET_INDEX_CACHE_FILE = join(dshHome(), 'plugin-console-market-index-cache.json')
|
|
626
642
|
|
|
627
643
|
/** 源地址校验(模块顶层,readSources 与 sources 路由共用):https 任意;http 仅限私网/本机地址(内网 npm registry、内网搜索服务常用 http)。 */
|
|
628
644
|
function isAllowedSourceUrl(url) {
|
|
@@ -644,6 +660,12 @@ function isAllowedSourceUrl(url) {
|
|
|
644
660
|
}
|
|
645
661
|
}
|
|
646
662
|
|
|
663
|
+
/** Git 源地址校验:在通用校验之上额外允许 file:// 本地裸仓库(完全离线/内网共享盘场景)。 */
|
|
664
|
+
function isAllowedGitSourceUrl(url) {
|
|
665
|
+
if (/^file:\/\/\/\S+$/u.test(url)) return true
|
|
666
|
+
return isAllowedSourceUrl(url)
|
|
667
|
+
}
|
|
668
|
+
|
|
647
669
|
/** 读取 Gitee OAuth 配置(clientId/clientSecret/token/login)。 */
|
|
648
670
|
function readGiteeConfig(data) {
|
|
649
671
|
const gitee = data && typeof data === 'object' && data.gitee && typeof data.gitee === 'object' ? data.gitee : {}
|
|
@@ -700,6 +722,36 @@ function readSources() {
|
|
|
700
722
|
})
|
|
701
723
|
.filter((s) => s !== null)
|
|
702
724
|
const giteeBase = readGiteeConfig(data)
|
|
725
|
+
// 索引源:老配置无该字段时用默认;URL 必须是合法 https 或本机/私网 http
|
|
726
|
+
const indexSources = (Array.isArray(data.indexSources) ? data.indexSources : [])
|
|
727
|
+
.filter((s) => s && typeof s.url === 'string' && isAllowedSourceUrl(s.url))
|
|
728
|
+
.map((s) => ({
|
|
729
|
+
id: String(s.id ?? '').slice(0, 40) || `idx-${Math.random().toString(36).slice(2, 8)}`,
|
|
730
|
+
name: String(s.name ?? s.url).slice(0, 60) || s.url,
|
|
731
|
+
url: s.url,
|
|
732
|
+
primary: s.primary === true,
|
|
733
|
+
}))
|
|
734
|
+
const indexFinal = indexSources.length > 0
|
|
735
|
+
? (() => {
|
|
736
|
+
if (!indexSources.some((s) => s.primary)) indexSources[0].primary = true
|
|
737
|
+
return indexSources
|
|
738
|
+
})()
|
|
739
|
+
: defaults.indexSources
|
|
740
|
+
// Git 克隆源:模板必须同时含 {owner} 与 {repo};老配置无该字段时用默认
|
|
741
|
+
const gitSources = (Array.isArray(data.gitSources) ? data.gitSources : [])
|
|
742
|
+
.filter((s) => s && typeof s.urlTemplate === 'string' && s.urlTemplate.includes('{owner}') && s.urlTemplate.includes('{repo}') && isAllowedGitSourceUrl(s.urlTemplate))
|
|
743
|
+
.map((s) => ({
|
|
744
|
+
id: String(s.id ?? '').slice(0, 40) || `git-${Math.random().toString(36).slice(2, 8)}`,
|
|
745
|
+
name: String(s.name ?? s.urlTemplate).slice(0, 60) || s.urlTemplate,
|
|
746
|
+
urlTemplate: s.urlTemplate,
|
|
747
|
+
primary: s.primary === true,
|
|
748
|
+
}))
|
|
749
|
+
const gitFinal = gitSources.length > 0
|
|
750
|
+
? (() => {
|
|
751
|
+
if (!gitSources.some((s) => s.primary)) gitSources[0].primary = true
|
|
752
|
+
return gitSources
|
|
753
|
+
})()
|
|
754
|
+
: defaults.gitSources
|
|
703
755
|
const gitee = {
|
|
704
756
|
...giteeBase,
|
|
705
757
|
clientSecret: secrets.gitee?.clientSecret ?? giteeBase.clientSecret,
|
|
@@ -707,7 +759,7 @@ function readSources() {
|
|
|
707
759
|
}
|
|
708
760
|
if (registries.length > 0) {
|
|
709
761
|
if (!registries.some((r) => r.primary)) registries[0].primary = true
|
|
710
|
-
return { registries, searchSources, gitee }
|
|
762
|
+
return { registries, searchSources, indexSources: indexFinal, gitSources: gitFinal, indexMerge: data.indexMerge === true, gitee }
|
|
711
763
|
}
|
|
712
764
|
} catch {}
|
|
713
765
|
return defaults
|
|
@@ -769,6 +821,9 @@ function maskSources(sources) {
|
|
|
769
821
|
}
|
|
770
822
|
return {
|
|
771
823
|
registries: sources.registries,
|
|
824
|
+
indexSources: sources.indexSources ?? DEFAULT_SOURCES.indexSources,
|
|
825
|
+
indexMerge: sources.indexMerge === true,
|
|
826
|
+
gitSources: sources.gitSources ?? DEFAULT_SOURCES.gitSources,
|
|
772
827
|
searchSources: (sources.searchSources ?? []).map((s) => {
|
|
773
828
|
if (s && typeof s.headers === 'object' && Object.keys(s.headers).length > 0) {
|
|
774
829
|
const masked = {}
|
|
@@ -1588,6 +1643,7 @@ async function curlText(url, timeout = 10000) {
|
|
|
1588
1643
|
/** 任意 https JSON 接口(Gitee/自定义源用):curl 优先、node:https 兜底;4xx/5xx 确定性失败直接抛出。 */
|
|
1589
1644
|
async function fetchJsonUrl(url, timeout = 15000, headers = {}) {
|
|
1590
1645
|
let lastError
|
|
1646
|
+
const isHttps = /^https:\/\//u.test(url)
|
|
1591
1647
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
1592
1648
|
if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
1593
1649
|
try {
|
|
@@ -1597,6 +1653,9 @@ async function fetchJsonUrl(url, timeout = 15000, headers = {}) {
|
|
|
1597
1653
|
if (error instanceof Error && /^请求失败 \(HTTP \d+\)$/u.test(error.message)) throw error
|
|
1598
1654
|
lastError = error
|
|
1599
1655
|
}
|
|
1656
|
+
// node:https 兜底仅对 https 有效:私网 http 镜像只走 curl 通道,
|
|
1657
|
+
// 否则会抛出 "Protocol http: not supported" 掩盖 curl 的真实连接错误
|
|
1658
|
+
if (!isHttps) continue
|
|
1600
1659
|
try {
|
|
1601
1660
|
const res = await githubRequest(url, { headers })
|
|
1602
1661
|
const status = res.statusCode ?? 0
|
|
@@ -2166,6 +2225,30 @@ async function githubReleaseInstall(profileDir, repo, packageName) {
|
|
|
2166
2225
|
* 读已装包的 bundle patch → 检查每个 insert 引用的包是否存在 → 缺失补装(pnpm/curl)→
|
|
2167
2226
|
* 仍缺则在用户 patch 层自动禁用该行(防崩兜底)+ 返回报告供面板提示。
|
|
2168
2227
|
*/
|
|
2228
|
+
/** 读取聚合包 cordis.patch.yml 的 insert 行 name 列表(注册前校验用)。 */
|
|
2229
|
+
function readBundlePatchRefNames(profileDir, pkgName) {
|
|
2230
|
+
try {
|
|
2231
|
+
const pkgJsonPath = join(profileDir, 'node_modules', pkgName, 'package.json')
|
|
2232
|
+
if (!existsSync(pkgJsonPath)) return []
|
|
2233
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))
|
|
2234
|
+
const patchRel = pkg.dsh?.bundle?.patch
|
|
2235
|
+
if (typeof patchRel !== 'string') return []
|
|
2236
|
+
const text = readFileSync(join(profileDir, 'node_modules', pkgName, patchRel), 'utf8')
|
|
2237
|
+
const names = []
|
|
2238
|
+
const lines = text.split(/\r?\n/u)
|
|
2239
|
+
let inInsert = false
|
|
2240
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
2241
|
+
const line = lines[i]
|
|
2242
|
+
if (/^- insert:\s*$/u.test(line)) { inInsert = true; continue }
|
|
2243
|
+
if (/^- /u.test(line) && !/^ {4}- /u.test(line)) inInsert = false
|
|
2244
|
+
if (!inInsert) continue
|
|
2245
|
+
const nameM = (lines[i + 1] ?? '').match(/^ {6}name: ['"]([^'"]+)['"]\s*$/u)
|
|
2246
|
+
if (nameM) names.push(nameM[1])
|
|
2247
|
+
}
|
|
2248
|
+
return names
|
|
2249
|
+
} catch { return [] }
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2169
2252
|
async function ensureBundlePatchIntegrity(profileDir, pkgName, userPatchPath, transientAllow = []) {
|
|
2170
2253
|
const report = { checked: 0, installed: [], disabled: [], missing: [], pending: [] }
|
|
2171
2254
|
try {
|
|
@@ -2339,6 +2422,7 @@ function installJobView(job) {
|
|
|
2339
2422
|
skillNote: job.skillNote ?? null,
|
|
2340
2423
|
suiteReport: job.suiteReport ?? null,
|
|
2341
2424
|
suiteNote: job.suiteNote ?? null,
|
|
2425
|
+
hint: job.hint ?? null,
|
|
2342
2426
|
}
|
|
2343
2427
|
}
|
|
2344
2428
|
|
|
@@ -2424,18 +2508,45 @@ function packageEntryExists(dir, pkg) {
|
|
|
2424
2508
|
return candidates.some((c) => existsSync(join(dir, c)))
|
|
2425
2509
|
}
|
|
2426
2510
|
|
|
2511
|
+
/** git 非交互环境:禁止任何登录/凭据窗口弹出(私有仓库或不可达源直接失败,不做交互式重试)。 */
|
|
2512
|
+
function gitEnv() {
|
|
2513
|
+
return {
|
|
2514
|
+
...process.env,
|
|
2515
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
2516
|
+
GCM_INTERACTIVE: 'never',
|
|
2517
|
+
GIT_ASKPASS: 'echo',
|
|
2518
|
+
SSH_ASKPASS: 'echo',
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
/** Git 克隆 URL 列表(按主→备顺序)。来源在「功能包 → 软件源 → Git 源」中自定义,
|
|
2523
|
+
* 可替换为 Gitee / GitLab / 自建 Gitea / 任意镜像代理,实现「换一个网站下载仓库内容」。 */
|
|
2524
|
+
function gitCloneUrls(repoFullName, source = 'github') {
|
|
2525
|
+
if (source === 'gitee') return [`https://gitee.com/${repoFullName}.git`]
|
|
2526
|
+
const [owner = '', repo = ''] = String(repoFullName).split('/')
|
|
2527
|
+
let list = []
|
|
2528
|
+
try {
|
|
2529
|
+
list = readSources().gitSources ?? []
|
|
2530
|
+
} catch {
|
|
2531
|
+
list = DEFAULT_SOURCES.gitSources
|
|
2532
|
+
}
|
|
2533
|
+
const ordered = [...list].sort((a, b) => (b.primary === true ? 1 : 0) - (a.primary === true ? 1 : 0))
|
|
2534
|
+
const urls = ordered
|
|
2535
|
+
.map((s) => String(s.urlTemplate).replace(/\{owner\}/gu, owner).replace(/\{repo\}/gu, repo))
|
|
2536
|
+
.filter((u) => u !== '')
|
|
2537
|
+
return urls.length > 0 ? urls : [`https://github.com/${repoFullName}.git`]
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2427
2540
|
/** git clone(镜像→直连;gitee 直连),返回或抛错。 */
|
|
2428
2541
|
async function gitCloneRepo(repo, dest, source = 'github', timeout = 180000) {
|
|
2429
|
-
const urls = source
|
|
2430
|
-
? [`https://gitee.com/${repo}.git`]
|
|
2431
|
-
: [`https://ghproxy.net/https://github.com/${repo}.git`, `https://github.com/${repo}.git`]
|
|
2542
|
+
const urls = gitCloneUrls(repo, source)
|
|
2432
2543
|
let lastError = null
|
|
2433
2544
|
for (const url of urls) {
|
|
2434
2545
|
try {
|
|
2435
2546
|
await execFileAsync('git', ['clone', '--depth', '1', '--quiet', url, dest], {
|
|
2436
2547
|
timeout,
|
|
2437
2548
|
windowsHide: true,
|
|
2438
|
-
env:
|
|
2549
|
+
env: gitEnv(),
|
|
2439
2550
|
})
|
|
2440
2551
|
return
|
|
2441
2552
|
} catch (error) {
|
|
@@ -2598,9 +2709,7 @@ async function runSkillInstallJob(job, ctx) {
|
|
|
2598
2709
|
job.stage = 'preparing'
|
|
2599
2710
|
const shortName = String(job.repo).split('/').pop() || job.repo
|
|
2600
2711
|
mkdirSync(tmpDir, { recursive: true })
|
|
2601
|
-
const urls = job.source
|
|
2602
|
-
? [`https://gitee.com/${job.repo}.git`]
|
|
2603
|
-
: [`https://ghproxy.net/https://github.com/${job.repo}.git`, `https://github.com/${job.repo}.git`]
|
|
2712
|
+
const urls = gitCloneUrls(job.repo, job.source)
|
|
2604
2713
|
let cloned = false
|
|
2605
2714
|
let lastError = null
|
|
2606
2715
|
for (const url of urls) {
|
|
@@ -2608,7 +2717,7 @@ async function runSkillInstallJob(job, ctx) {
|
|
|
2608
2717
|
await execFileAsync('git', ['clone', '--depth', '1', '--quiet', url, tmpDir], {
|
|
2609
2718
|
timeout: 120000,
|
|
2610
2719
|
windowsHide: true,
|
|
2611
|
-
env:
|
|
2720
|
+
env: gitEnv(),
|
|
2612
2721
|
})
|
|
2613
2722
|
cloned = true
|
|
2614
2723
|
break
|
|
@@ -3173,6 +3282,30 @@ async function aiEmpowerPlan(job, ctx, profileDir) {
|
|
|
3173
3282
|
job.finishedAt = Date.now()
|
|
3174
3283
|
return
|
|
3175
3284
|
}
|
|
3285
|
+
// 优先用「Git 源」把目标仓库克隆到本地,让子代理读本地材料:
|
|
3286
|
+
// 内网/离线环境下 api.github.com 与 raw 通道不可达时,这是唯一可行的调研通道。
|
|
3287
|
+
let materialDir = null
|
|
3288
|
+
if (/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u.test(String(job.source ?? '').trim())) {
|
|
3289
|
+
const srcDir = join(tmpdir(), `dsh-ai-src-${job.id}-${Date.now().toString(36)}`)
|
|
3290
|
+
try {
|
|
3291
|
+
mkdirSync(srcDir, { recursive: true })
|
|
3292
|
+
for (const url of gitCloneUrls(job.source)) {
|
|
3293
|
+
try {
|
|
3294
|
+
await execFileAsync('git', ['clone', '--depth', '1', '--quiet', url, srcDir], { timeout: 120000, windowsHide: true, env: gitEnv() })
|
|
3295
|
+
materialDir = srcDir
|
|
3296
|
+
break
|
|
3297
|
+
} catch {}
|
|
3298
|
+
}
|
|
3299
|
+
} catch {}
|
|
3300
|
+
if (materialDir === null) {
|
|
3301
|
+
try { rmSync(srcDir, { recursive: true, force: true }) } catch {}
|
|
3302
|
+
} else {
|
|
3303
|
+
job.materialDir = srcDir
|
|
3304
|
+
// 30 分钟后自动清理(规划+执行通常早已结束)
|
|
3305
|
+
const timer = setTimeout(() => { try { rmSync(srcDir, { recursive: true, force: true }) } catch {} }, 30 * 60 * 1000)
|
|
3306
|
+
if (typeof timer?.unref === 'function') timer.unref()
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3176
3309
|
const prompt = [
|
|
3177
3310
|
'你是 DSH 插件控制台挂载的「AI 赋能」部署规划器。用户想把下面的组件完整部署到本机。',
|
|
3178
3311
|
`目标来源:${job.source}`,
|
|
@@ -3181,7 +3314,9 @@ async function aiEmpowerPlan(job, ctx, profileDir) {
|
|
|
3181
3314
|
frameworkCheckPromptText(job.frameworkCheck),
|
|
3182
3315
|
'',
|
|
3183
3316
|
'【任务】只做只读调研与规划,禁止执行任何安装/写文件/启动服务/杀进程命令(curl 的 GET 抓取、gh api 查询、读取文件是允许的)。',
|
|
3184
|
-
|
|
3317
|
+
materialDir !== null
|
|
3318
|
+
? `调研材料:目标仓库已由控制台通过「Git 源」克隆到本地目录 ${materialDir} —— 直接读取该目录下的 README / package.json / docs/ / install 说明即可(无需联网,内网/离线环境同样可用)。`
|
|
3319
|
+
: '调研材料:npm README / GitHub README / docs/ 目录 / package.json / install 说明。',
|
|
3185
3320
|
'全部调研完成后,输出且只输出一个 ```json ... ``` 代码块,内容为部署计划;代码块之外不要输出任何文字。',
|
|
3186
3321
|
'',
|
|
3187
3322
|
'计划 schema(严格遵循)',
|
|
@@ -3774,6 +3909,15 @@ function frameworkCheckPromptText(fc) {
|
|
|
3774
3909
|
async function runInstallJob(job, ctx) {
|
|
3775
3910
|
try {
|
|
3776
3911
|
job.stage = 'preparing'
|
|
3912
|
+
// 2026-09-06 事故(室友机器):对 deepseek-ai/deepseek-harness(框架本体仓库)点「添加到本地/安装」
|
|
3913
|
+
// 会按 bundle 规则注册其 patch,其中 deepseek-ai-dsh-root 等框架级行的包只存在于 npx 缓存/框架树,
|
|
3914
|
+
// profile node_modules 不存在 → 整服务启动崩溃。直接拦截。
|
|
3915
|
+
const repoNorm = String(job.repo ?? '').toLowerCase().replace(/^git\+/u, '').replace(/\.git$/u, '')
|
|
3916
|
+
if (repoNorm === 'deepseek-ai/deepseek-harness') {
|
|
3917
|
+
job.status = 'failed'
|
|
3918
|
+
job.error = '这是 DSH 框架本体仓库,不能按插件安装(其组件引用框架内部包,安装后会导致服务启动失败)。如需升级框架请用「框架升级」按钮;如需生态插件请选择 dsh-plugin 插件仓库。'
|
|
3919
|
+
return
|
|
3920
|
+
}
|
|
3777
3921
|
let candidates = [job.packageName].filter((name) => typeof name === 'string' && name !== '')
|
|
3778
3922
|
let subpackageMode = false
|
|
3779
3923
|
if (candidates.length === 0) {
|
|
@@ -3797,8 +3941,17 @@ async function runInstallJob(job, ctx) {
|
|
|
3797
3941
|
const branch = meta?.default_branch ?? 'main'
|
|
3798
3942
|
const pkg = await fetchRepoPackage(job.repo, branch)
|
|
3799
3943
|
if (pkg === null) {
|
|
3944
|
+
// 无 package.json:先探测是否技能仓库(含 SKILL.md)→ 自动转技能安装;
|
|
3945
|
+
// 否则标记 hint=repo-land,前端给出「仓库落地」一键入口(克隆到本地目录)。
|
|
3946
|
+
const skillProbe = await detectSkillRepo(job.repo, branch)
|
|
3947
|
+
if (skillProbe.hasSkill) {
|
|
3948
|
+
job.kind = 'skill'
|
|
3949
|
+
await runSkillInstallJob(job, ctx)
|
|
3950
|
+
return
|
|
3951
|
+
}
|
|
3800
3952
|
job.status = 'failed'
|
|
3801
|
-
job.
|
|
3953
|
+
job.hint = 'repo-land'
|
|
3954
|
+
job.error = `仓库 ${job.repo} 没有 package.json(也不是技能仓库),无法作为插件安装——可改用「仓库落地」克隆到本地目录。`
|
|
3802
3955
|
return
|
|
3803
3956
|
}
|
|
3804
3957
|
if (pkg.private === true) {
|
|
@@ -3928,7 +4081,7 @@ async function runInstallJob(job, ctx) {
|
|
|
3928
4081
|
const gitSpecs = job.source === 'gitee'
|
|
3929
4082
|
? [`git+https://gitee.com/${job.repo}.git`]
|
|
3930
4083
|
: [
|
|
3931
|
-
|
|
4084
|
+
...gitCloneUrls(job.repo).map((u) => `git+${u}`),
|
|
3932
4085
|
`github:${job.repo}`,
|
|
3933
4086
|
]
|
|
3934
4087
|
for (const spec of gitSpecs) {
|
|
@@ -4037,6 +4190,17 @@ async function runInstallJob(job, ctx) {
|
|
|
4037
4190
|
// 官方 dsh plugin add 行为:声明 dsh.bundle 的包追加为 profile bundle 层,
|
|
4038
4191
|
// 其 cordis.patch.yml 在下次启动时参与组合(含皮肤包与 web-ui-settings 这类有入口的包)
|
|
4039
4192
|
if (!alreadyServed) {
|
|
4193
|
+
// 通用防线(2026-09-06 事故:装 dsh-desktop 类插件时,其 bundle patch 引用 @deepseek-ai/dsh-root 等
|
|
4194
|
+
// 框架级行,包不在 profile node_modules → 注册后整服务启动崩溃):
|
|
4195
|
+
// 注册前校验 bundle patch 引用的行模块全部可解析(**含 @deepseek-ai/* —— 正是室友崩塌的那些框架级包**),
|
|
4196
|
+
// 缺失即拒绝注册并给出清单。
|
|
4197
|
+
const refs = readBundlePatchRefNames(profileDir, installedName)
|
|
4198
|
+
const missing = refs.filter((n) => resolvePackageJson(n, profileDir) === null)
|
|
4199
|
+
if (missing.length > 0) {
|
|
4200
|
+
job.status = 'failed'
|
|
4201
|
+
job.error = `该聚合包(${installedName})的插件组引用以下未安装模块:${missing.join('、')}。若这些是框架内部包(@deepseek-ai/*),该包不能作为插件安装(会导致服务启动失败);若为本应随包安装的依赖,请重试或检查网络。已取消注册。`
|
|
4202
|
+
return
|
|
4203
|
+
}
|
|
4040
4204
|
await addBundleToManifest(profileDir, installedName)
|
|
4041
4205
|
job.bundle = true
|
|
4042
4206
|
} else {
|
|
@@ -4325,7 +4489,10 @@ async function handle(ctx, req, res) {
|
|
|
4325
4489
|
return
|
|
4326
4490
|
}
|
|
4327
4491
|
|
|
4328
|
-
|
|
4492
|
+
// GET 兼容白名单:只读且无副作用的接口允许 GET 调用
|
|
4493
|
+
// (market-index 曾被客户端以 GET 调用,落进 405 后错误被静默吞掉,导致静态索引长期未生效)
|
|
4494
|
+
const GET_COMPAT = new Set([`${ROUTE_PREFIX}/market-index`])
|
|
4495
|
+
if (method !== 'POST' && !(method === 'GET' && GET_COMPAT.has(pathname))) {
|
|
4329
4496
|
sendError(res, 405, '不支持的方法')
|
|
4330
4497
|
return
|
|
4331
4498
|
}
|
|
@@ -4670,7 +4837,7 @@ async function handle(ctx, req, res) {
|
|
|
4670
4837
|
// 纯命令(无注释,CMD/PowerShell 通用);不再关闭 TLS 校验;
|
|
4671
4838
|
// 脚本用 powershell -File 调用,CMD 里也能跑
|
|
4672
4839
|
installCommand = [
|
|
4673
|
-
`git clone --recurse-submodules
|
|
4840
|
+
`git clone --recurse-submodules ${gitCloneUrls(repo)[0]}`,
|
|
4674
4841
|
`cd ${short}`,
|
|
4675
4842
|
hasInstallScript
|
|
4676
4843
|
? `powershell -ExecutionPolicy Bypass -File install.ps1`
|
|
@@ -4824,8 +4991,168 @@ async function handle(ctx, req, res) {
|
|
|
4824
4991
|
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
4825
4992
|
return
|
|
4826
4993
|
}
|
|
4994
|
+
if (action === 'add-index') {
|
|
4995
|
+
const name = typeof body.name === 'string' ? body.name.trim() : ''
|
|
4996
|
+
const url = typeof body.url === 'string' ? body.url.trim() : ''
|
|
4997
|
+
if (!isAllowedSourceUrl(url)) {
|
|
4998
|
+
sendError(res, 400, '索引地址必须是 https:// 开头(或本机/私网 http://)的合法 URL')
|
|
4999
|
+
return
|
|
5000
|
+
}
|
|
5001
|
+
if ((sources.indexSources ?? []).some((s) => s.url === url)) {
|
|
5002
|
+
sendError(res, 400, '该索引源已存在')
|
|
5003
|
+
return
|
|
5004
|
+
}
|
|
5005
|
+
sources.indexSources = [...(sources.indexSources ?? []), {
|
|
5006
|
+
id: `idx-${Date.now().toString(36)}`,
|
|
5007
|
+
name: name || url,
|
|
5008
|
+
url,
|
|
5009
|
+
primary: (sources.indexSources ?? []).length === 0,
|
|
5010
|
+
}]
|
|
5011
|
+
marketIndexCache = null
|
|
5012
|
+
await writeSources(sources)
|
|
5013
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5014
|
+
return
|
|
5015
|
+
}
|
|
5016
|
+
if (action === 'edit-index') {
|
|
5017
|
+
const id = typeof body.id === 'string' ? body.id : ''
|
|
5018
|
+
const name = typeof body.name === 'string' ? body.name.trim() : ''
|
|
5019
|
+
const url = typeof body.url === 'string' ? body.url.trim() : ''
|
|
5020
|
+
if (!isAllowedSourceUrl(url)) {
|
|
5021
|
+
sendError(res, 400, '索引地址必须是 https:// 开头(或本机/私网 http://)的合法 URL')
|
|
5022
|
+
return
|
|
5023
|
+
}
|
|
5024
|
+
const target = (sources.indexSources ?? []).find((s) => s.id === id)
|
|
5025
|
+
if (!target) {
|
|
5026
|
+
sendError(res, 404, '没有这个索引源')
|
|
5027
|
+
return
|
|
5028
|
+
}
|
|
5029
|
+
target.name = name || url
|
|
5030
|
+
target.url = url
|
|
5031
|
+
marketIndexCache = null
|
|
5032
|
+
await writeSources(sources)
|
|
5033
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5034
|
+
return
|
|
5035
|
+
}
|
|
5036
|
+
if (action === 'set-index-primary') {
|
|
5037
|
+
const id = typeof body.id === 'string' ? body.id : ''
|
|
5038
|
+
const list = sources.indexSources ?? []
|
|
5039
|
+
if (!list.some((s) => s.id === id)) {
|
|
5040
|
+
sendError(res, 404, '没有这个索引源')
|
|
5041
|
+
return
|
|
5042
|
+
}
|
|
5043
|
+
for (const s of list) s.primary = s.id === id
|
|
5044
|
+
marketIndexCache = null
|
|
5045
|
+
await writeSources(sources)
|
|
5046
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5047
|
+
return
|
|
5048
|
+
}
|
|
5049
|
+
if (action === 'remove-index') {
|
|
5050
|
+
const id = typeof body.id === 'string' ? body.id : ''
|
|
5051
|
+
const list = sources.indexSources ?? []
|
|
5052
|
+
if (!list.some((s) => s.id === id)) {
|
|
5053
|
+
sendError(res, 404, '没有这个索引源')
|
|
5054
|
+
return
|
|
5055
|
+
}
|
|
5056
|
+
const rest = list.filter((s) => s.id !== id)
|
|
5057
|
+
if (rest.length === 0) {
|
|
5058
|
+
sendError(res, 400, '至少保留一个索引源(可先添加自建镜像再删除默认源)')
|
|
5059
|
+
return
|
|
5060
|
+
}
|
|
5061
|
+
if (!rest.some((s) => s.primary)) rest[0].primary = true
|
|
5062
|
+
sources.indexSources = rest
|
|
5063
|
+
marketIndexCache = null
|
|
5064
|
+
await writeSources(sources)
|
|
5065
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5066
|
+
return
|
|
5067
|
+
}
|
|
5068
|
+
if (action === 'set-index-merge') {
|
|
5069
|
+
sources.indexMerge = body.merge === true
|
|
5070
|
+
marketIndexCache = null
|
|
5071
|
+
await writeSources(sources)
|
|
5072
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5073
|
+
return
|
|
5074
|
+
}
|
|
5075
|
+
if (action === 'add-git') {
|
|
5076
|
+
const name = typeof body.name === 'string' ? body.name.trim() : ''
|
|
5077
|
+
const urlTemplate = typeof body.urlTemplate === 'string' ? body.urlTemplate.trim() : ''
|
|
5078
|
+
if (!urlTemplate.includes('{owner}') || !urlTemplate.includes('{repo}')) {
|
|
5079
|
+
sendError(res, 400, 'Git 源模板必须同时包含 {owner} 与 {repo} 占位符')
|
|
5080
|
+
return
|
|
5081
|
+
}
|
|
5082
|
+
if (!isAllowedGitSourceUrl(urlTemplate)) {
|
|
5083
|
+
sendError(res, 400, 'Git 源地址必须是 https://(或本机/私网 http://、file:// 本地裸仓库)的合法 URL')
|
|
5084
|
+
return
|
|
5085
|
+
}
|
|
5086
|
+
if ((sources.gitSources ?? []).some((s) => s.urlTemplate === urlTemplate)) {
|
|
5087
|
+
sendError(res, 400, '该 Git 源已存在')
|
|
5088
|
+
return
|
|
5089
|
+
}
|
|
5090
|
+
sources.gitSources = [...(sources.gitSources ?? []), {
|
|
5091
|
+
id: `git-${Date.now().toString(36)}`,
|
|
5092
|
+
name: name || urlTemplate,
|
|
5093
|
+
urlTemplate,
|
|
5094
|
+
primary: (sources.gitSources ?? []).length === 0,
|
|
5095
|
+
}]
|
|
5096
|
+
await writeSources(sources)
|
|
5097
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5098
|
+
return
|
|
5099
|
+
}
|
|
5100
|
+
if (action === 'edit-git') {
|
|
5101
|
+
const id = typeof body.id === 'string' ? body.id : ''
|
|
5102
|
+
const name = typeof body.name === 'string' ? body.name.trim() : ''
|
|
5103
|
+
const urlTemplate = typeof body.urlTemplate === 'string' ? body.urlTemplate.trim() : ''
|
|
5104
|
+
if (!urlTemplate.includes('{owner}') || !urlTemplate.includes('{repo}')) {
|
|
5105
|
+
sendError(res, 400, 'Git 源模板必须同时包含 {owner} 与 {repo} 占位符')
|
|
5106
|
+
return
|
|
5107
|
+
}
|
|
5108
|
+
if (!isAllowedGitSourceUrl(urlTemplate)) {
|
|
5109
|
+
sendError(res, 400, 'Git 源地址必须是 https://(或本机/私网 http://、file:// 本地裸仓库)的合法 URL')
|
|
5110
|
+
return
|
|
5111
|
+
}
|
|
5112
|
+
const target = (sources.gitSources ?? []).find((s) => s.id === id)
|
|
5113
|
+
if (!target) {
|
|
5114
|
+
sendError(res, 404, '没有这个 Git 源')
|
|
5115
|
+
return
|
|
5116
|
+
}
|
|
5117
|
+
target.name = name || urlTemplate
|
|
5118
|
+
target.urlTemplate = urlTemplate
|
|
5119
|
+
await writeSources(sources)
|
|
5120
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5121
|
+
return
|
|
5122
|
+
}
|
|
5123
|
+
if (action === 'set-git-primary') {
|
|
5124
|
+
const id = typeof body.id === 'string' ? body.id : ''
|
|
5125
|
+
const list = sources.gitSources ?? []
|
|
5126
|
+
if (!list.some((s) => s.id === id)) {
|
|
5127
|
+
sendError(res, 404, '没有这个 Git 源')
|
|
5128
|
+
return
|
|
5129
|
+
}
|
|
5130
|
+
for (const s of list) s.primary = s.id === id
|
|
5131
|
+
await writeSources(sources)
|
|
5132
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5133
|
+
return
|
|
5134
|
+
}
|
|
5135
|
+
if (action === 'remove-git') {
|
|
5136
|
+
const id = typeof body.id === 'string' ? body.id : ''
|
|
5137
|
+
const list = sources.gitSources ?? []
|
|
5138
|
+
if (!list.some((s) => s.id === id)) {
|
|
5139
|
+
sendError(res, 404, '没有这个 Git 源')
|
|
5140
|
+
return
|
|
5141
|
+
}
|
|
5142
|
+
const rest = list.filter((s) => s.id !== id)
|
|
5143
|
+
if (rest.length === 0) {
|
|
5144
|
+
sendError(res, 400, '至少保留一个 Git 源(可先添加自建镜像再删除默认源)')
|
|
5145
|
+
return
|
|
5146
|
+
}
|
|
5147
|
+
if (!rest.some((s) => s.primary)) rest[0].primary = true
|
|
5148
|
+
sources.gitSources = rest
|
|
5149
|
+
await writeSources(sources)
|
|
5150
|
+
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
5151
|
+
return
|
|
5152
|
+
}
|
|
4827
5153
|
if (action === 'reset') {
|
|
4828
5154
|
const defaults = JSON.parse(JSON.stringify(DEFAULT_SOURCES))
|
|
5155
|
+
marketIndexCache = null
|
|
4829
5156
|
await writeSources(defaults)
|
|
4830
5157
|
sendJson(res, 200, { ok: true, sources: maskSources(defaults) })
|
|
4831
5158
|
return
|
|
@@ -4852,34 +5179,86 @@ async function handle(ctx, req, res) {
|
|
|
4852
5179
|
sendJson(res, 200, { ok: true, sources: maskSources(sources) })
|
|
4853
5180
|
return
|
|
4854
5181
|
}
|
|
4855
|
-
sendError(res, 400, '未知操作(add / edit / set-primary / remove / add-search / remove-search / gitee-setup / gitee-clear / reset)')
|
|
5182
|
+
sendError(res, 400, '未知操作(add / edit / set-primary / remove / add-search / remove-search / add-index / edit-index / set-index-primary / remove-index / set-index-merge / add-git / edit-git / set-git-primary / remove-git / gitee-setup / gitee-clear / reset)')
|
|
4856
5183
|
return
|
|
4857
5184
|
}
|
|
4858
5185
|
|
|
4859
5186
|
if (pathname === `${ROUTE_PREFIX}/market-index`) {
|
|
4860
|
-
//
|
|
5187
|
+
// 静态插件索引:按「软件源 → 索引源」主→备顺序拉取 + 10 分钟内存缓存(市场秒开、零 GitHub API 调用)。
|
|
5188
|
+
// 全部索引源失败时回退落盘缓存(内网/断网仍可浏览,响应带 offline 标记),无缓存则区分错误类型。
|
|
4861
5189
|
if (marketIndexCache !== null && Date.now() - marketIndexCache.at < 600000) {
|
|
4862
|
-
sendJson(res, 200, { ok: true, ...marketIndexCache.data })
|
|
5190
|
+
sendJson(res, 200, { ok: true, sourceName: marketIndexCache.sourceName ?? null, ...marketIndexCache.data })
|
|
4863
5191
|
return
|
|
4864
5192
|
}
|
|
4865
|
-
const
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
5193
|
+
const indexConf = readSources()
|
|
5194
|
+
let indexList = [...(indexConf.indexSources ?? [])].sort((a, b) => (b.primary === true ? 1 : 0) - (a.primary === true ? 1 : 0))
|
|
5195
|
+
// 合并模式:并发拉取所有索引源并去重合并(公共索引 + 内网私有索引同时可见)
|
|
5196
|
+
if (indexConf.indexMerge === true && indexList.length > 1) {
|
|
5197
|
+
const fetched = await Promise.all(indexList.map(async (src) => {
|
|
5198
|
+
try {
|
|
5199
|
+
// 各源独立短超时:单个慢源(被墙镜像/不可达内网)不该拖垮整体
|
|
5200
|
+
const data = await fetchJsonUrl(src.url, 8000)
|
|
5201
|
+
return data && Array.isArray(data.items) ? { src, data } : null
|
|
5202
|
+
} catch { return null }
|
|
5203
|
+
}))
|
|
5204
|
+
const good = fetched.filter((x) => x !== null)
|
|
5205
|
+
if (good.length > 0) {
|
|
5206
|
+
const seen = new Set()
|
|
5207
|
+
const skillSeen = new Set()
|
|
5208
|
+
const items = []
|
|
5209
|
+
const skills = []
|
|
5210
|
+
for (const { data } of good) {
|
|
5211
|
+
for (const it of data.items) {
|
|
5212
|
+
const key = typeof it?.fullName === 'string' ? it.fullName : JSON.stringify(it)
|
|
5213
|
+
if (seen.has(key)) continue
|
|
5214
|
+
seen.add(key)
|
|
5215
|
+
items.push(it)
|
|
5216
|
+
}
|
|
5217
|
+
for (const sk of (Array.isArray(data.skills) ? data.skills : [])) {
|
|
5218
|
+
const key = typeof sk?.fullName === 'string' ? sk.fullName : JSON.stringify(sk)
|
|
5219
|
+
if (skillSeen.has(key)) continue
|
|
5220
|
+
skillSeen.add(key)
|
|
5221
|
+
skills.push(sk)
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
const sourceName = good.map((g) => g.src.name).join(' + ')
|
|
5225
|
+
const merged = { items, skills, skillCount: skills.length, merged: true, sourceName }
|
|
5226
|
+
marketIndexCache = { at: Date.now(), data: merged, sourceName }
|
|
5227
|
+
try { await writeFile(MARKET_INDEX_CACHE_FILE, JSON.stringify({ at: Date.now(), data: merged, sourceName }), 'utf8') } catch {}
|
|
5228
|
+
sendJson(res, 200, { ok: true, ...merged })
|
|
5229
|
+
return
|
|
5230
|
+
}
|
|
5231
|
+
// 所有源都失败 → 跳过逐个重试,直接进入下方缓存兜底
|
|
5232
|
+
indexList = []
|
|
5233
|
+
}
|
|
4869
5234
|
let lastError = null
|
|
4870
|
-
|
|
5235
|
+
let formatError = null
|
|
5236
|
+
for (const src of indexList) {
|
|
4871
5237
|
try {
|
|
4872
|
-
const data = await fetchJsonUrl(url, 15000)
|
|
5238
|
+
const data = await fetchJsonUrl(src.url, 15000)
|
|
4873
5239
|
if (data && Array.isArray(data.items)) {
|
|
4874
|
-
marketIndexCache = { at: Date.now(), data }
|
|
4875
|
-
|
|
5240
|
+
marketIndexCache = { at: Date.now(), data, sourceName: src.name }
|
|
5241
|
+
try { await writeFile(MARKET_INDEX_CACHE_FILE, JSON.stringify({ at: Date.now(), data, sourceName: src.name }), 'utf8') } catch {}
|
|
5242
|
+
sendJson(res, 200, { ok: true, sourceName: src.name, ...data })
|
|
4876
5243
|
return
|
|
4877
5244
|
}
|
|
5245
|
+
formatError = `索引格式异常(${src.name} 未返回 items 数组)`
|
|
4878
5246
|
} catch (error) {
|
|
4879
5247
|
lastError = error
|
|
4880
5248
|
}
|
|
4881
5249
|
}
|
|
4882
|
-
|
|
5250
|
+
try {
|
|
5251
|
+
const cached = JSON.parse(readFileSync(MARKET_INDEX_CACHE_FILE, 'utf8'))
|
|
5252
|
+
if (cached && cached.data && Array.isArray(cached.data.items)) {
|
|
5253
|
+
marketIndexCache = { at: Date.now(), data: cached.data, sourceName: cached.sourceName ?? null }
|
|
5254
|
+
sendJson(res, 200, { ok: true, offline: true, cachedAt: typeof cached.at === 'number' ? cached.at : null, sourceName: cached.sourceName ?? null, ...cached.data })
|
|
5255
|
+
return
|
|
5256
|
+
}
|
|
5257
|
+
} catch {}
|
|
5258
|
+
const reason = formatError !== null
|
|
5259
|
+
? formatError
|
|
5260
|
+
: `网络不可达(${indexList.length} 个索引源全部失败):${lastError?.message ?? '未知错误'}`
|
|
5261
|
+
sendError(res, 500, `索引加载失败:${reason}`)
|
|
4883
5262
|
return
|
|
4884
5263
|
}
|
|
4885
5264
|
|
|
@@ -5997,16 +6376,22 @@ async function handle(ctx, req, res) {
|
|
|
5997
6376
|
}
|
|
5998
6377
|
|
|
5999
6378
|
if (pathname === `${ROUTE_PREFIX}/repo-clone`) {
|
|
6000
|
-
//
|
|
6379
|
+
// 仓库落地:克隆任意项目到 <配置路径>/<owner>/<name>(地址由「软件源 → Git 源」决定,
|
|
6380
|
+
// 因此这里接受任意平台的仓库链接:GitHub / Gitee / GitLab / 自建 Gitea / 镜像代理前缀)
|
|
6001
6381
|
const raw = typeof body.repo === 'string' ? body.repo.trim() : ''
|
|
6382
|
+
// 循环剥离协议/域名前缀:镜像代理链接可能叠两层(ghproxy.net/https://github.com/...)
|
|
6002
6383
|
let repo = raw
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6384
|
+
for (let guard = 0; guard < 4; guard += 1) {
|
|
6385
|
+
const next = repo
|
|
6386
|
+
.replace(/^https?:\/\/[^/]+\//u, '')
|
|
6387
|
+
.replace(/^git@[^:]+:/u, '')
|
|
6388
|
+
if (next === repo) break
|
|
6389
|
+
repo = next
|
|
6390
|
+
}
|
|
6391
|
+
repo = repo.replace(/\.git$/u, '').replace(/\/+$/u, '')
|
|
6007
6392
|
const m = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/u.exec(repo)
|
|
6008
6393
|
if (!m) {
|
|
6009
|
-
sendError(res, 400, '仓库格式不正确:请提供 owner/repo
|
|
6394
|
+
sendError(res, 400, '仓库格式不正确:请提供 owner/repo 或仓库链接(如 https://gitee.com/owner/repo)')
|
|
6010
6395
|
return
|
|
6011
6396
|
}
|
|
6012
6397
|
const [, owner, name] = m
|
|
@@ -6018,8 +6403,24 @@ async function handle(ctx, req, res) {
|
|
|
6018
6403
|
}
|
|
6019
6404
|
try {
|
|
6020
6405
|
mkdirSync(root, { recursive: true })
|
|
6021
|
-
|
|
6022
|
-
|
|
6406
|
+
const urls = gitCloneUrls(`${owner}/${name}`)
|
|
6407
|
+
let lastError = null
|
|
6408
|
+
let cloned = false
|
|
6409
|
+
let usedUrl = ''
|
|
6410
|
+
for (const url of urls) {
|
|
6411
|
+
try {
|
|
6412
|
+
await execFileAsync('git.exe', ['clone', '--depth', '1', url, target], { cwd: root, timeout: 600000, windowsHide: true, maxBuffer: 4 * 1024 * 1024, env: gitEnv() })
|
|
6413
|
+
cloned = true
|
|
6414
|
+
usedUrl = url
|
|
6415
|
+
break
|
|
6416
|
+
} catch (error) {
|
|
6417
|
+
lastError = error
|
|
6418
|
+
// 失败可能留下半成品目录,清理后再试下一个源,否则会因目标已存在而连环失败
|
|
6419
|
+
try { rmSync(target, { recursive: true, force: true }) } catch {}
|
|
6420
|
+
}
|
|
6421
|
+
}
|
|
6422
|
+
if (!cloned) throw lastError ?? new Error('未知错误')
|
|
6423
|
+
sendJson(res, 200, { ok: true, owner, name, path: target, url: usedUrl })
|
|
6023
6424
|
} catch (error) {
|
|
6024
6425
|
sendError(res, 500, `克隆失败:${error instanceof Error ? error.message : String(error)}`)
|
|
6025
6426
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noob-stupid/dsh-plugin-console",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.31",
|
|
4
4
|
"description": "DSH plugin management panel & marketplace: one-click enable/disable, multi-source market (GitHub/Gitee/custom), static-index market (500+ plugins / 300 skills), skills, suites, and one-click framework upgrade.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|