@mofeng2223/dsh-claude-provider 0.4.6 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -56,7 +56,7 @@ window.__ModuleLoader__.load({
56
56
  }
57
57
  //#endregion
58
58
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/ModelsSection.module.css.mjs
59
- const css$3 = ".zGbnIq_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.zGbnIq_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.zGbnIq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.zGbnIq_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.zGbnIq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.zGbnIq_rowCard{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.zGbnIq_rowHead{align-items:center;gap:10px;display:flex}.zGbnIq_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.zGbnIq_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_rowTag{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.zGbnIq_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.zGbnIq_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.zGbnIq_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.zGbnIq_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.zGbnIq_primaryButton,.zGbnIq_secondaryButton,.zGbnIq_addButton{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border:none;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.zGbnIq_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.zGbnIq_secondaryButton,.zGbnIq_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.zGbnIq_secondaryButton:hover:not(:disabled),.zGbnIq_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.zGbnIq_dangerButton{box-sizing:border-box;height:36px;color:var(--dsw-alias-state-error-primary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_rowActions .zGbnIq_secondaryButton,.zGbnIq_rowActions .zGbnIq_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.zGbnIq_primaryButton:disabled,.zGbnIq_secondaryButton:disabled,.zGbnIq_dangerButton:disabled,.zGbnIq_addButton:disabled,.zGbnIq_linkButton:disabled,.zGbnIq_addModelButton:disabled{opacity:.4;cursor:default}.zGbnIq_primaryButton:focus-visible,.zGbnIq_secondaryButton:focus-visible,.zGbnIq_dangerButton:focus-visible,.zGbnIq_addButton:focus-visible,.zGbnIq_linkButton:focus-visible,.zGbnIq_addModelButton:focus-visible,.zGbnIq_iconButton:focus-visible,.zGbnIq_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.zGbnIq_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.zGbnIq_editorHeader{align-items:baseline;gap:8px;display:flex}.zGbnIq_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_field{flex-direction:column;gap:6px;display:flex}.zGbnIq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.zGbnIq_linkButton{box-sizing:border-box;height:28px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:14px;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.zGbnIq_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_editorActions{justify-content:flex-end;gap:8px;display:flex}.zGbnIq_addBlock{flex-direction:column;gap:12px;display:flex}.zGbnIq_addActions{flex-wrap:wrap;gap:10px;display:flex}.zGbnIq_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.zGbnIq_addCard,.zGbnIq_setupCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.zGbnIq_addCard .zGbnIq_editor,.zGbnIq_setupCard .zGbnIq_editor{background:0 0;padding:0}.zGbnIq_customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px}.zGbnIq_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.zGbnIq_customizedSummary::-webkit-details-marker{display:none}.zGbnIq_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.zGbnIq_customized[open]>.zGbnIq_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.zGbnIq_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.zGbnIq_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.zGbnIq_modelCatalog{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.zGbnIq_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.zGbnIq_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.zGbnIq_modelCatalogMeta,.zGbnIq_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_modelList{flex-direction:column;gap:8px;display:flex}.zGbnIq_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.zGbnIq_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;padding:6px}.zGbnIq_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.zGbnIq_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.zGbnIq_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.zGbnIq_iconButton:disabled{cursor:default;opacity:.4}.zGbnIq_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.zGbnIq_modelAdvanced{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px 4px 2px;display:grid}.zGbnIq_modelField{flex-direction:column;gap:4px;min-width:0;display:flex}.zGbnIq_modelInputTypes{border:none;grid-column:1/-1;min-width:0;margin:0;padding:0}.zGbnIq_modelInputTypes legend{margin-bottom:4px;padding:0}.zGbnIq_modelInputChoices{flex-wrap:wrap;align-items:center;gap:16px;min-height:32px;display:flex}.zGbnIq_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.zGbnIq_addModelButton{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:14px;align-self:flex-start;align-items:center;gap:4px;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.zGbnIq_input{cursor:pointer;max-width:240px}.zGbnIq_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.zGbnIq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.zGbnIq_input:disabled{opacity:.6;cursor:default}.zGbnIq_selectInput{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.zGbnIq_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_deleteDialog{width:min(480px,100%)}.zGbnIq_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.zGbnIq_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.zGbnIq_customizedSummary:before,.zGbnIq_switchThumb{transition:none}}.zGbnIq_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.zGbnIq_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.zGbnIq_candidateSearch{flex:240px;min-width:0}.zGbnIq_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.zGbnIq_candidate{border-radius:6px}.zGbnIq_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.zGbnIq_candidateId{font-family:var(--ds-font-family-code);overflow-wrap:anywhere;flex:auto;font-size:13px}.zGbnIq_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
59
+ const css$3 = ".zGbnIq_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.zGbnIq_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.zGbnIq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.zGbnIq_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.zGbnIq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.zGbnIq_rowCard{border:.5px solid var(--dsw-alias-border-l4);border-radius:16px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.zGbnIq_rowHead{align-items:center;gap:10px;display:flex}.zGbnIq_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.zGbnIq_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_rowTag{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.zGbnIq_credentialDot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.zGbnIq_credentialDotConfigured{background:var(--dsw-alias-state-success-primary)}.zGbnIq_credentialDotMissing{background:var(--dsw-alias-state-error-primary)}.zGbnIq_rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}.zGbnIq_primaryButton,.zGbnIq_secondaryButton,.zGbnIq_addButton{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border:none;border-radius:18px;justify-content:center;align-items:center;gap:4px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.zGbnIq_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.zGbnIq_secondaryButton,.zGbnIq_addButton{border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);background:0 0}.zGbnIq_secondaryButton:hover:not(:disabled),.zGbnIq_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.zGbnIq_dangerButton{box-sizing:border-box;height:36px;color:var(--dsw-alias-state-error-primary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.zGbnIq_dangerButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_rowActions .zGbnIq_secondaryButton,.zGbnIq_rowActions .zGbnIq_dangerButton{border-radius:14px;height:28px;padding:0 10px;font-size:12px;line-height:18px}.zGbnIq_primaryButton:disabled,.zGbnIq_secondaryButton:disabled,.zGbnIq_dangerButton:disabled,.zGbnIq_addButton:disabled,.zGbnIq_linkButton:disabled,.zGbnIq_addModelButton:disabled{opacity:.4;cursor:default}.zGbnIq_primaryButton:focus-visible,.zGbnIq_secondaryButton:focus-visible,.zGbnIq_dangerButton:focus-visible,.zGbnIq_addButton:focus-visible,.zGbnIq_linkButton:focus-visible,.zGbnIq_addModelButton:focus-visible,.zGbnIq_iconButton:focus-visible,.zGbnIq_customizedSummary:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.zGbnIq_editor{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;display:flex}.zGbnIq_editorHeader{align-items:baseline;gap:8px;display:flex}.zGbnIq_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.zGbnIq_editorRoute{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_field{flex-direction:column;gap:6px;display:flex}.zGbnIq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.zGbnIq_linkButton{box-sizing:border-box;height:28px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:14px;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.zGbnIq_advancedHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_editorActions{justify-content:flex-end;gap:8px;display:flex}.zGbnIq_addBlock{flex-direction:column;gap:12px;display:flex}.zGbnIq_addActions{display:flex}.zGbnIq_addButton{border:1px dashed var(--dsw-alias-border-l3);border-radius:16px;flex:1 1 0;gap:6px;min-width:180px;height:44px}.zGbnIq_addModes{flex-direction:column;align-items:flex-start;gap:8px;display:flex}.zGbnIq_addPanel{flex-direction:column;gap:14px;display:flex}.zGbnIq_addPanel[hidden]{display:none}.zGbnIq_addCard,.zGbnIq_setupCard{background:var(--dsw-alias-bg-module-platform);border-radius:12px;flex-direction:column;gap:14px;padding:14px 16px;list-style:none;display:flex}.zGbnIq_addCard .zGbnIq_editor,.zGbnIq_setupCard .zGbnIq_editor{background:0 0;padding:0}.zGbnIq_customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px}.zGbnIq_customizedSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.zGbnIq_customizedSummary::-webkit-details-marker{display:none}.zGbnIq_customizedSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.zGbnIq_customized[open]>.zGbnIq_customizedSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.zGbnIq_customizedSummary:hover{color:var(--dsw-alias-label-primary)}.zGbnIq_customizedBody{flex-direction:column;gap:12px;padding-top:12px;display:flex}.zGbnIq_modelCatalog{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:12px;display:flex}.zGbnIq_modelCatalogHeading{flex-direction:column;gap:2px;display:flex}.zGbnIq_modelCatalogTitle{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.zGbnIq_modelCatalogMeta,.zGbnIq_modelEmpty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.zGbnIq_modelList{flex-direction:column;gap:8px;display:flex}.zGbnIq_modelListHead{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.zGbnIq_modelEntry{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;padding:6px}.zGbnIq_modelRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto auto;align-items:center;gap:6px;display:grid}.zGbnIq_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.zGbnIq_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.zGbnIq_iconButton:disabled{cursor:default;opacity:.4}.zGbnIq_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.zGbnIq_modelAdvanced{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px 4px 2px;display:grid}.zGbnIq_modelField{flex-direction:column;gap:4px;min-width:0;display:flex}.zGbnIq_modelInputTypes{border:none;grid-column:1/-1;min-width:0;margin:0;padding:0}.zGbnIq_modelInputTypes legend{margin-bottom:4px;padding:0}.zGbnIq_modelInputChoices{flex-wrap:wrap;align-items:center;gap:16px;min-height:32px;display:flex}.zGbnIq_modelFieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.zGbnIq_modelEmpty{border:1px dashed var(--dsw-alias-border-l3);text-align:center;border-radius:8px;padding:12px}.zGbnIq_addModelButton{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;background:0 0;border-radius:14px;align-self:flex-start;align-items:center;gap:4px;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.zGbnIq_addModelButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.zGbnIq_input{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.zGbnIq_input{cursor:pointer;max-width:240px}.zGbnIq_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.zGbnIq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.zGbnIq_input:disabled{opacity:.6;cursor:default}.zGbnIq_selectInput{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.zGbnIq_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.zGbnIq_deleteDialog{width:min(480px,100%)}.zGbnIq_deleteConfirm:not(:disabled){border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}.zGbnIq_deleteConfirm:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}.zGbnIq_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}@media (prefers-reduced-motion:reduce){.zGbnIq_customizedSummary:before,.zGbnIq_switchThumb{transition:none}}.zGbnIq_fetchDialog{--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);max-width:520px}.zGbnIq_candidateToolbar{align-items:center;gap:8px;margin-bottom:6px;display:flex}.zGbnIq_candidateSearch{flex:240px;min-width:0}.zGbnIq_candidateList{flex-direction:column;gap:2px;max-height:320px;margin:0;padding:0;list-style:none;display:flex;overflow-y:auto}.zGbnIq_candidate{border-radius:6px}.zGbnIq_candidateLabel{cursor:pointer;align-items:center;gap:8px;padding:6px 8px;display:flex}.zGbnIq_candidateName{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;font-size:13px;overflow:hidden}.zGbnIq_candidateEmpty{color:var(--dsw-alias-label-secondary);text-align:center;margin:24px 0;font-size:13px;line-height:20px}";
60
60
  const tagId$3 = "@mofeng2223/dsh-claude-provider/ModelsSection.module.css";
61
61
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
62
62
  const tag = document.createElement("style");
@@ -71,12 +71,14 @@ window.__ModuleLoader__.load({
71
71
  "addButton": "zGbnIq_addButton",
72
72
  "addCard": "zGbnIq_addCard",
73
73
  "addModelButton": "zGbnIq_addModelButton",
74
+ "addModes": "zGbnIq_addModes",
75
+ "addPanel": "zGbnIq_addPanel",
74
76
  "advancedHint": "zGbnIq_advancedHint",
75
77
  "candidate": "zGbnIq_candidate",
76
78
  "candidateEmpty": "zGbnIq_candidateEmpty",
77
- "candidateId": "zGbnIq_candidateId",
78
79
  "candidateLabel": "zGbnIq_candidateLabel",
79
80
  "candidateList": "zGbnIq_candidateList",
81
+ "candidateName": "zGbnIq_candidateName",
80
82
  "candidateSearch": "zGbnIq_candidateSearch",
81
83
  "candidateToolbar": "zGbnIq_candidateToolbar",
82
84
  "credentialDot": "zGbnIq_credentialDot",
@@ -232,7 +234,7 @@ window.__ModuleLoader__.load({
232
234
  "aria-expanded": props.expanded,
233
235
  title: t("modelAdvanced"),
234
236
  onClick: props.onToggle,
235
- children: props.expanded ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {}) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, {})
237
+ children: props.expanded ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {}) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutlineRegular, {})
236
238
  }),
237
239
  (0, react_jsx_runtime.jsx)("button", {
238
240
  type: "button",
@@ -241,7 +243,7 @@ window.__ModuleLoader__.load({
241
243
  title: t("removeModel"),
242
244
  disabled,
243
245
  onClick: props.onRemove,
244
- children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
246
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 14 })
245
247
  })
246
248
  ]
247
249
  }), props.expanded ? (0, react_jsx_runtime.jsxs)("div", {
@@ -275,7 +277,7 @@ window.__ModuleLoader__.load({
275
277
  }), props.thinkingPresets ? (0, react_jsx_runtime.jsxs)("label", {
276
278
  className: ModelsSection_module_css_default["modelField"],
277
279
  children: [t("thinkingPreset"), (0, react_jsx_runtime.jsxs)("select", {
278
- className: ModelsSection_module_css_default["input"],
280
+ className: ModelsSection_module_css_default["input"] + " " + ModelsSection_module_css_default["selectInput"],
279
281
  value: thinkingPresetOf(model),
280
282
  "aria-label": t("thinkingPreset") + " " + position,
281
283
  disabled,
@@ -522,7 +524,7 @@ window.__ModuleLoader__.load({
522
524
  onClick: () => {
523
525
  props.onChange([...props.models.map((model) => ({ ...model })), { id: "" }]);
524
526
  },
525
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), props.t("addModel")]
527
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), props.t("addModel")]
526
528
  })
527
529
  ]
528
530
  });
@@ -595,9 +597,12 @@ window.__ModuleLoader__.load({
595
597
  * @returns the model-list editor.
596
598
  */
597
599
  function ModelListEditor(props) {
598
- const { models, onChange, probe, operations, t, disabled } = props;
600
+ const { models, onChange, probe, operations, t, disabled, onBusyChange } = props;
599
601
  const { catalogProvider } = props;
600
602
  const [busy, setBusy] = (0, react.useState)(false);
603
+ (0, react.useEffect)(() => {
604
+ onBusyChange(busy);
605
+ }, [busy, onBusyChange]);
601
606
  const [failure, setFailure] = (0, react.useState)(void 0);
602
607
  const [inheritedCatalog, setInheritedCatalog] = (0, react.useState)(void 0);
603
608
  (0, react.useEffect)(() => {
@@ -822,7 +827,7 @@ window.__ModuleLoader__.load({
822
827
  onClick: () => {
823
828
  onChange([...models, props.thinkingPresets ? withDefaultClaudeReasoning({ id: "" }) : { id: "" }]);
824
829
  },
825
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("addModel")]
830
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), t("addModel")]
826
831
  }),
827
832
  failure !== void 0 ? (0, react_jsx_runtime.jsx)("p", {
828
833
  className: ModelsSection_module_css_default["error"],
@@ -879,8 +884,9 @@ window.__ModuleLoader__.load({
879
884
  toggle(candidate.id);
880
885
  }
881
886
  }), (0, react_jsx_runtime.jsx)("span", {
882
- className: ModelsSection_module_css_default["candidateId"],
883
- children: candidate.id
887
+ className: ModelsSection_module_css_default["candidateName"],
888
+ title: candidate.name ?? candidate.id,
889
+ children: candidate.name ?? candidate.id
884
890
  })]
885
891
  })
886
892
  }, candidate.id))
@@ -1132,18 +1138,46 @@ window.__ModuleLoader__.load({
1132
1138
  return { kind: "credential-missing" };
1133
1139
  }
1134
1140
  //#endregion
1141
+ //#region lib/types/client/protocol-label.js
1142
+ /**
1143
+ * Product names for the wire protocols a pi-ai route may speak. The pickers
1144
+ * show these instead of the schema identifiers (`openai-completions`), which
1145
+ * are what `settings.yaml` carries and what the option values stay.
1146
+ */
1147
+ /** The protocols this page names, keyed by their schema identifier. */
1148
+ const PROTOCOL_LABEL_KEYS = {
1149
+ "openai-completions": "protocolOpenAiCompletions",
1150
+ "openai-responses": "protocolOpenAiResponses",
1151
+ "anthropic-messages": "protocolAnthropicMessages"
1152
+ };
1153
+ /**
1154
+ * The picker text for one protocol identifier.
1155
+ * @param t - section copy.
1156
+ * @param protocol - the schema identifier of the protocol.
1157
+ * @returns the product name this page knows the protocol by; a protocol the
1158
+ * adapter adds before this page names it shows its identifier, the spelling
1159
+ * `settings.yaml` needs anyway.
1160
+ */
1161
+ function protocolLabel(t, protocol) {
1162
+ const key = PROTOCOL_LABEL_KEYS[protocol];
1163
+ return key === void 0 ? protocol : t(key);
1164
+ }
1165
+ //#endregion
1135
1166
  //#region lib/types/client/CustomProviderCard.js
1136
1167
  /**
1137
1168
  * The card that declares a provider pi-ai does not ship — an OpenAI-compatible
1138
1169
  * gateway, a self-hosted server, or a provider newer than the installed
1139
1170
  * catalog.
1140
1171
  *
1141
- * This is a create, not an edit, which is why it is its own card rather than
1172
+ * This is a create, not an edit, which is why it is its own form rather than
1142
1173
  * the provider editor with extra fields: the route id is being *chosen* here,
1143
- * and the settings address does not exist until it is. One `settings.mutate`
1144
- * sets the whole profile at `providers.<route>`; the key travels separately
1145
- * through `credentials/set` under the reference the profile records, exactly as
1146
- * an existing provider's key does.
1174
+ * and the settings address does not exist until it is. It renders as the
1175
+ * custom-API panel of the section's add card; the card's mode switch names it
1176
+ * when both modes are offered, and with the custom mode alone the card shows
1177
+ * this form directly. One `settings.mutate` sets the whole profile at
1178
+ * `providers.<route>`; the key travels separately through `credentials/set`
1179
+ * under the reference the profile records, exactly as an existing provider's
1180
+ * key does.
1147
1181
  *
1148
1182
  * The three fields a hand-declared route cannot default — endpoint, protocol,
1149
1183
  * and at least one model — are required here rather than at load, so the
@@ -1180,7 +1214,7 @@ window.__ModuleLoader__.load({
1180
1214
  * @returns the creation card.
1181
1215
  */
1182
1216
  function CustomProviderCard(props) {
1183
- const { taken, protocols, operations, t } = props;
1217
+ const { taken, protocols, operations, t, onBusyChange } = props;
1184
1218
  const [openedAt] = (0, react.useState)(() => props.revision);
1185
1219
  const [route, setRoute] = (0, react.useState)("");
1186
1220
  const [displayName, setDisplayName] = (0, react.useState)("");
@@ -1191,6 +1225,14 @@ window.__ModuleLoader__.load({
1191
1225
  const [keyDraft, setKeyDraft] = (0, react.useState)("");
1192
1226
  const [models, setModels] = (0, react.useState)([]);
1193
1227
  const [busy, setBusy] = (0, react.useState)(false);
1228
+ const [listBusy, setListBusy] = (0, react.useState)(false);
1229
+ (0, react.useEffect)(() => {
1230
+ onBusyChange?.(busy || listBusy);
1231
+ }, [
1232
+ busy,
1233
+ listBusy,
1234
+ onBusyChange
1235
+ ]);
1194
1236
  const [failure, setFailure] = (0, react.useState)(void 0);
1195
1237
  /**
1196
1238
  * The profile write landed. Only the key write can still be outstanding, so
@@ -1261,13 +1303,6 @@ window.__ModuleLoader__.load({
1261
1303
  return (0, react_jsx_runtime.jsxs)("div", {
1262
1304
  className: ModelsSection_module_css_default["editor"],
1263
1305
  children: [
1264
- (0, react_jsx_runtime.jsx)("div", {
1265
- className: ModelsSection_module_css_default["editorHeader"],
1266
- children: (0, react_jsx_runtime.jsx)("span", {
1267
- className: ModelsSection_module_css_default["editorTitle"],
1268
- children: t(isAdaptiveClaude ? "claudeCustomTitle" : "customTitle")
1269
- })
1270
- }),
1271
1306
  isAdaptiveClaude ? (0, react_jsx_runtime.jsxs)("p", {
1272
1307
  className: ModelsSection_module_css_default["savedNotice"],
1273
1308
  role: "status",
@@ -1323,7 +1358,7 @@ window.__ModuleLoader__.load({
1323
1358
  className: ModelsSection_module_css_default["input"],
1324
1359
  type: "text",
1325
1360
  value: baseURL,
1326
- placeholder: t("customBaseUrlPlaceholder"),
1361
+ placeholder: t(protocol === "anthropic-messages" ? "customAnthropicBaseUrlPlaceholder" : "customBaseUrlPlaceholder"),
1327
1362
  "aria-label": t("baseUrl"),
1328
1363
  "aria-invalid": baseUrlInvalid,
1329
1364
  disabled: profileDisabled,
@@ -1351,7 +1386,7 @@ window.__ModuleLoader__.load({
1351
1386
  },
1352
1387
  children: protocols.map((choice) => (0, react_jsx_runtime.jsx)("option", {
1353
1388
  value: choice,
1354
- children: choice
1389
+ children: protocolLabel(t, choice)
1355
1390
  }, choice))
1356
1391
  })]
1357
1392
  }),
@@ -1393,7 +1428,8 @@ window.__ModuleLoader__.load({
1393
1428
  thinkingPresets: isAdaptiveClaude,
1394
1429
  operations,
1395
1430
  t,
1396
- disabled: profileDisabled
1431
+ disabled: profileDisabled,
1432
+ onBusyChange: setListBusy
1397
1433
  }),
1398
1434
  failure !== void 0 ? (0, react_jsx_runtime.jsx)("p", {
1399
1435
  className: ModelsSection_module_css_default["error"],
@@ -1437,9 +1473,9 @@ window.__ModuleLoader__.load({
1437
1473
  * Reasoning effort is deliberately absent: it is a per-MODEL capability, and
1438
1474
  * the models under one provider disagree about it, so a provider-scoped
1439
1475
  * control can only be set to a value some of them reject. The composer's
1440
- * model picker offers each model its own levels; `settings.yaml` keeps the
1476
+ * model picker offers each model its own levels; `cordis.patch.yml` keeps the
1441
1477
  * profile field for a deployment that knows its route. Everything else stays
1442
- * owned by `settings.yaml`. Profile edits land as minimal `settings.mutate`
1478
+ * owned by `cordis.patch.yml`. Profile edits land as minimal `settings.mutate`
1443
1479
  * path ops against the stored section — the card names only the fields it can
1444
1480
  * see instead of rebuilding the whole subtree from a partial descriptor.
1445
1481
  */
@@ -1499,6 +1535,15 @@ window.__ModuleLoader__.load({
1499
1535
  const [keyDraft, setKeyDraft] = (0, react.useState)("");
1500
1536
  const [keyState, setKeyState] = (0, react.useState)(void 0);
1501
1537
  const [busy, setBusy] = (0, react.useState)(false);
1538
+ const [listBusy, setListBusy] = (0, react.useState)(false);
1539
+ const { onBusyChange } = props;
1540
+ (0, react.useEffect)(() => {
1541
+ onBusyChange?.(busy || listBusy);
1542
+ }, [
1543
+ busy,
1544
+ listBusy,
1545
+ onBusyChange
1546
+ ]);
1502
1547
  const [failure, setFailure] = (0, react.useState)(void 0);
1503
1548
  const [committedOriginal, setCommittedOriginal] = (0, react.useState)(() => schema.getPath(namespace.user, settingsPath));
1504
1549
  const [expectedRevision, setExpectedRevision] = (0, react.useState)(() => namespace.revision);
@@ -1711,7 +1756,7 @@ window.__ModuleLoader__.load({
1711
1756
  className: ModelsSection_module_css_default["input"],
1712
1757
  type: "text",
1713
1758
  value: stringAt(draft, "baseURL") ?? "",
1714
- placeholder: family === "deepseek" ? t(stringAt(fallback, "protocol") === "messages" ? "deepSeekMessagesBaseUrl" : "deepSeekChatBaseUrl") : stringAt(fallback, "baseURL") ?? t("baseUrlDefault"),
1759
+ placeholder: family === "deepseek" ? t("deepSeekBaseUrl") : stringAt(fallback, "baseURL") ?? t("baseUrlDefault"),
1715
1760
  "aria-describedby": family === "deepseek" ? `${props.provider}-endpoint-hint` : void 0,
1716
1761
  "aria-label": t("baseUrl"),
1717
1762
  disabled,
@@ -1744,7 +1789,7 @@ window.__ModuleLoader__.load({
1744
1789
  children: t("customApiUnset")
1745
1790
  }) : null, protocols.map((choice) => (0, react_jsx_runtime.jsx)("option", {
1746
1791
  value: choice,
1747
- children: choice
1792
+ children: protocolLabel(t, choice)
1748
1793
  }, choice))]
1749
1794
  })]
1750
1795
  }) : null,
@@ -1759,7 +1804,8 @@ window.__ModuleLoader__.load({
1759
1804
  probe,
1760
1805
  probeBlocked: keyFailure,
1761
1806
  thinkingPresets: props.thinkingPresets === true,
1762
- operations
1807
+ operations,
1808
+ onBusyChange: setListBusy
1763
1809
  })
1764
1810
  ]
1765
1811
  })]
@@ -1816,11 +1862,19 @@ window.__ModuleLoader__.load({
1816
1862
  * solid configured or missing dots. A whole-section provider without a
1817
1863
  * configured key renders as its open setup card instead of a row, but only in
1818
1864
  * the first-run posture — no provider on the page can serve requests yet — and
1819
- * only until the user closes that card; the add flow is a card carrying the
1820
- * dormant-provider select. Each card kind owns its own open state, so closing
1821
- * one never discards a draft in another. Every mutation writes through the
1822
- * wire, while a provider removal first requires confirmation; the page
1823
- * re-renders from pushed invalidations or the post-apply reload.
1865
+ * only until the user closes that card. The add flow is one card behind one
1866
+ * button: a mode switch chooses between adopting a dormant directory provider
1867
+ * (the catalog select over the provider editor) and declaring a custom model
1868
+ * API (the create form). A panel mounts the first time its mode is shown and
1869
+ * stays mounted, hidden, while the card is open and its mode stays offered,
1870
+ * so switching modes discards neither draft and an unvisited mode costs
1871
+ * nothing; the switch holds still while either panel has a write or an
1872
+ * endpoint interrogation in flight, since a switch underneath one would
1873
+ * orphan the answer. Each card kind owns its own open state, so closing one
1874
+ * never discards a draft in another. Every
1875
+ * mutation writes through the wire, while a provider removal first requires
1876
+ * confirmation; the page re-renders from pushed invalidations or the
1877
+ * post-apply reload.
1824
1878
  */
1825
1879
  /** Render an editor for either the setup posture or an expanded provider row. */
1826
1880
  function renderProviderEditor({ target, ...props }) {
@@ -2050,30 +2104,50 @@ window.__ModuleLoader__.load({
2050
2104
  const { controller, operations, schema, t } = injected;
2051
2105
  const state = injected.useSnapshot((snapshot) => snapshot);
2052
2106
  const [editing, setEditing] = (0, react.useState)(void 0);
2053
- const [adding, setAdding] = (0, react.useState)(false);
2107
+ const [addOpen, setAddOpen] = (0, react.useState)(false);
2108
+ const [addMode, setAddMode] = (0, react.useState)("catalog");
2109
+ /** The modes shown since the add card opened; each keeps its panel mounted. */
2110
+ const [visited, setVisited] = (0, react.useState)(() => /* @__PURE__ */ new Set());
2111
+ /** Whether each add panel has a write or an interrogation in flight. */
2112
+ const [catalogBusy, setCatalogBusy] = (0, react.useState)(false);
2113
+ const [customBusy, setCustomBusy] = (0, react.useState)(false);
2114
+ const [claudeBusy, setClaudeBusy] = (0, react.useState)(false);
2115
+ /** Base of the add card's tab and panel ids. */
2116
+ const addId = (0, react.useId)();
2054
2117
  const [deleteTarget, setDeleteTarget] = (0, react.useState)(void 0);
2055
2118
  const [deleting, setDeleting] = (0, react.useState)(false);
2056
2119
  const [deleteFailure, setDeleteFailure] = (0, react.useState)(void 0);
2057
2120
  const [savedTarget, setSavedTarget] = (0, react.useState)(void 0);
2058
- const [declaringType, setDeclaringType] = (0, react.useState)(void 0);
2059
2121
  const [dismissedSetup, setDismissedSetup] = (0, react.useState)(() => /* @__PURE__ */ new Set());
2060
2122
  const announceSaved = (target) => {
2061
2123
  controller.load().then(() => {
2062
2124
  setSavedTarget(target);
2063
2125
  });
2064
2126
  };
2065
- const closeEditor = (changed, target) => {
2127
+ /**
2128
+ * Close the add card whole. The catalog target is forgotten with it, since
2129
+ * `editing` doubles as the row editor's target once the card is closed and a
2130
+ * refresh could otherwise open the row of a provider the draft never saved.
2131
+ * The busy flags reset here because a panel that closes itself on success
2132
+ * unmounts before it can report idle.
2133
+ */
2134
+ const closeAdd = () => {
2066
2135
  setEditing(void 0);
2067
- setAdding(false);
2068
- setDeclaringType(void 0);
2136
+ setAddOpen(false);
2137
+ setCatalogBusy(false);
2138
+ setCustomBusy(false);
2139
+ setClaudeBusy(false);
2140
+ };
2141
+ const closeEditor = (changed, target) => {
2142
+ closeAdd();
2069
2143
  if (changed) announceSaved(target);
2070
2144
  };
2071
2145
  /**
2072
- * Close a setup card, which owns none of the state above: the row-editor,
2073
- * add, and declare cards each own one of those, so clearing them here would
2074
- * discard a draft the user opened beside this card. Dismissal is this card's
2075
- * own — the provider falls back to an ordinary row for the rest of the
2076
- * session, and reopens through Edit.
2146
+ * Close a setup card, which owns none of the state above: the row-editor
2147
+ * and add cards each own one of those, so clearing them here would discard
2148
+ * a draft the user opened beside this card. Dismissal is this card's own —
2149
+ * the provider falls back to an ordinary row for the rest of the session,
2150
+ * and reopens through Edit.
2077
2151
  */
2078
2152
  const closeSetup = (changed, target) => {
2079
2153
  setDismissedSetup((previous) => new Set([...previous, target.provider]));
@@ -2126,11 +2200,38 @@ window.__ModuleLoader__.load({
2126
2200
  const anyUsable = state.rows.some(providerUsable);
2127
2201
  const configured = state.rows.filter((row) => row.configured);
2128
2202
  const configurable = state.rows.filter((row) => state.namespaces.has(row.entry.settingsNs));
2129
- const addable = configurable.filter((row) => !row.configured);
2130
- const addTarget = adding ? editing : void 0;
2131
- const addNamespace = addTarget === void 0 ? void 0 : state.namespaces.get(addTarget.settingsNs);
2132
- const addRow = addTarget === void 0 ? void 0 : state.rows.find((row) => row.entry.provider === addTarget.provider);
2133
- const protocols = protocolChoices(state.namespaces.get("llm-pi-ai"), schema);
2203
+ const addable = state.rows.flatMap((row) => {
2204
+ const namespace = state.namespaces.get(row.entry.settingsNs);
2205
+ return namespace === void 0 || row.configured ? [] : [{
2206
+ row,
2207
+ namespace
2208
+ }];
2209
+ });
2210
+ const piAi = state.namespaces.get("llm-pi-ai");
2211
+ const protocols = protocolChoices(piAi, schema);
2212
+ const catalogOffered = configurable.length > 0;
2213
+ const catalogEnabled = addable.length > 0;
2214
+ const customOffered = piAi !== void 0;
2215
+ const customEnabled = protocols.length > 0;
2216
+ const claudeEnabled = customEnabled && state.namespaces.has(CLAUDE_PROVIDER_SETTINGS_NS);
2217
+ const bothOffered = customOffered;
2218
+ const mode = bothOffered ? addMode : customOffered ? "custom" : "catalog";
2219
+ const mounted = (candidate) => mode === candidate || visited.has(candidate);
2220
+ const switchLocked = catalogBusy || customBusy || claudeBusy;
2221
+ const draft = (() => {
2222
+ if (!addOpen || !catalogOffered) return void 0;
2223
+ const kept = editing === void 0 ? void 0 : state.namespaces.get(editing.settingsNs);
2224
+ if (editing !== void 0 && kept !== void 0) return {
2225
+ target: editing,
2226
+ namespace: kept
2227
+ };
2228
+ const first = addable[0];
2229
+ return first === void 0 ? void 0 : {
2230
+ target: targetOf(first.row),
2231
+ namespace: first.namespace
2232
+ };
2233
+ })();
2234
+ const addRow = draft === void 0 ? void 0 : state.rows.find((row) => row.entry.provider === draft.target.provider);
2134
2235
  return (0, react_jsx_runtime.jsxs)("div", {
2135
2236
  className: ModelsSection_module_css_default["section"],
2136
2237
  children: [
@@ -2187,7 +2288,7 @@ window.__ModuleLoader__.load({
2187
2288
  }, { entryKey: row.entry.settingsNs })
2188
2289
  ]
2189
2290
  }, row.entry.provider);
2190
- const open = !adding && editing?.provider === row.entry.provider;
2291
+ const open = !addOpen && editing?.provider === row.entry.provider;
2191
2292
  const adaptiveClaude = isClaudeProvider(row, state);
2192
2293
  const credentialConfigured = row.credential?.configured === true;
2193
2294
  const credentialMissing = !credentialConfigured && row.apiKeyEnv !== void 0 && row.credential?.configured === false;
@@ -2231,8 +2332,7 @@ window.__ModuleLoader__.load({
2231
2332
  "aria-label": providerCopy(t("editProvider"), target),
2232
2333
  onClick: () => {
2233
2334
  setSavedTarget(void 0);
2234
- setDeclaringType(void 0);
2235
- setAdding(false);
2335
+ setAddOpen(false);
2236
2336
  setEditing(open ? void 0 : target);
2237
2337
  },
2238
2338
  children: t("edit")
@@ -2274,106 +2374,160 @@ window.__ModuleLoader__.load({
2274
2374
  }),
2275
2375
  (0, react_jsx_runtime.jsx)("div", {
2276
2376
  className: ModelsSection_module_css_default["addBlock"],
2277
- children: addTarget !== void 0 && addNamespace !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
2377
+ children: addOpen ? (0, react_jsx_runtime.jsxs)("div", {
2278
2378
  className: ModelsSection_module_css_default["addCard"],
2279
2379
  children: [
2280
2380
  (0, react_jsx_runtime.jsxs)("div", {
2281
- className: ModelsSection_module_css_default["field"],
2282
- children: [(0, react_jsx_runtime.jsx)("span", {
2283
- className: ModelsSection_module_css_default["fieldLabel"],
2284
- children: t("provider")
2285
- }), (0, react_jsx_runtime.jsx)("select", {
2286
- className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
2287
- value: addTarget.provider,
2288
- "aria-label": t("provider"),
2289
- onChange: (event) => {
2290
- const row = addable.find((candidate) => candidate.entry.provider === event.target.value);
2291
- /* v8 ignore next -- the select only lists addable rows */
2292
- if (row === void 0) return;
2293
- setEditing(targetOf(row));
2294
- },
2295
- children: addable.map((row) => (0, react_jsx_runtime.jsx)("option", {
2296
- value: row.entry.provider,
2297
- children: row.entry.displayName
2298
- }, row.entry.provider))
2381
+ className: ModelsSection_module_css_default["addModes"],
2382
+ children: [bothOffered ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
2383
+ id: addId,
2384
+ label: t("addMode"),
2385
+ value: mode,
2386
+ disabled: switchLocked,
2387
+ options: [{
2388
+ value: "catalog",
2389
+ label: t("addCatalog"),
2390
+ disabled: !catalogEnabled,
2391
+ ...catalogEnabled ? {} : { title: t("addCatalogExhausted") }
2392
+ }, {
2393
+ value: "custom",
2394
+ label: t("addCustom"),
2395
+ disabled: !customEnabled,
2396
+ ...customEnabled ? {} : { title: t("addCustomUnavailable") }
2397
+ }, { value: "claude", label: t("claudeCustomTitle"), disabled: !claudeEnabled }],
2398
+ onChange: (next) => {
2399
+ setAddMode(next);
2400
+ setVisited((previous) => new Set([...previous, next]));
2401
+ }
2402
+ }) : (0, react_jsx_runtime.jsx)("div", {
2403
+ className: ModelsSection_module_css_default["editorHeader"],
2404
+ children: (0, react_jsx_runtime.jsx)("span", {
2405
+ className: ModelsSection_module_css_default["editorTitle"],
2406
+ children: t(mode === "catalog" ? "addCatalog" : "addCustom")
2407
+ })
2408
+ }), (0, react_jsx_runtime.jsx)("p", {
2409
+ hidden: mode === "claude",
2410
+ className: ModelsSection_module_css_default["advancedHint"],
2411
+ children: t(mode === "catalog" ? "addCatalogHint" : "addCustomHint")
2299
2412
  })]
2300
2413
  }),
2301
- (0, react_jsx_runtime.jsx)(ProviderEditor, {
2302
- provider: addTarget.provider,
2303
- displayName: addTarget.displayName,
2304
- hideTitle: true,
2305
- namespace: addNamespace,
2306
- schema,
2307
- settingsPath: addTarget.settingsPath,
2308
- operations,
2309
- t,
2310
- readOnly: !state.writable,
2311
- onClose: (changed) => {
2312
- closeEditor(changed, addTarget);
2313
- }
2314
- }, addTarget.provider),
2315
- addRow === void 0 ? null : renderSlot("settings.models.provider-card", {
2316
- provider: addRow.entry,
2317
- configured: addRow.configured,
2318
- keyConfigured: keyConfiguredOf(addRow)
2319
- }, { entryKey: addRow.entry.settingsNs })
2414
+ mounted("catalog") && draft !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
2415
+ id: `${addId}-catalog-panel`,
2416
+ ...bothOffered ? {
2417
+ role: "tabpanel",
2418
+ "aria-labelledby": `${addId}-catalog`
2419
+ } : {},
2420
+ hidden: mode !== "catalog",
2421
+ className: ModelsSection_module_css_default["addPanel"],
2422
+ children: [
2423
+ (0, react_jsx_runtime.jsxs)("div", {
2424
+ className: ModelsSection_module_css_default["field"],
2425
+ children: [(0, react_jsx_runtime.jsx)("span", {
2426
+ className: ModelsSection_module_css_default["fieldLabel"],
2427
+ children: t("provider")
2428
+ }), (0, react_jsx_runtime.jsx)("select", {
2429
+ className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
2430
+ value: draft.target.provider,
2431
+ "aria-label": t("provider"),
2432
+ disabled: catalogBusy,
2433
+ onChange: (event) => {
2434
+ const picked = addable.find((candidate) => candidate.row.entry.provider === event.target.value);
2435
+ /* v8 ignore next -- the select only lists addable rows */
2436
+ if (picked === void 0) return;
2437
+ setEditing(targetOf(picked.row));
2438
+ },
2439
+ children: addable.map(({ row }) => (0, react_jsx_runtime.jsx)("option", {
2440
+ value: row.entry.provider,
2441
+ children: row.entry.displayName
2442
+ }, row.entry.provider))
2443
+ })]
2444
+ }),
2445
+ (0, react_jsx_runtime.jsx)(ProviderEditor, {
2446
+ provider: draft.target.provider,
2447
+ displayName: draft.target.displayName,
2448
+ hideTitle: true,
2449
+ namespace: draft.namespace,
2450
+ schema,
2451
+ settingsPath: draft.target.settingsPath,
2452
+ operations,
2453
+ t,
2454
+ readOnly: !state.writable,
2455
+ onClose: (changed) => {
2456
+ closeEditor(changed, draft.target);
2457
+ },
2458
+ onBusyChange: setCatalogBusy
2459
+ }, draft.target.provider),
2460
+ addRow === void 0 ? null : renderSlot("settings.models.provider-card", {
2461
+ provider: addRow.entry,
2462
+ configured: addRow.configured,
2463
+ keyConfigured: keyConfiguredOf(addRow)
2464
+ }, { entryKey: addRow.entry.settingsNs })
2465
+ ]
2466
+ }) : null,
2467
+ mounted("custom") && piAi !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
2468
+ id: `${addId}-custom-panel`,
2469
+ ...bothOffered ? {
2470
+ role: "tabpanel",
2471
+ "aria-labelledby": `${addId}-custom`
2472
+ } : {},
2473
+ hidden: mode !== "custom",
2474
+ className: ModelsSection_module_css_default["addPanel"],
2475
+ children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2476
+ taken: state.rows.map((row) => row.entry.provider),
2477
+ protocols,
2478
+ revision: piAi.revision,
2479
+ operations,
2480
+ t,
2481
+ readOnly: !state.writable,
2482
+ onClose: (changed) => {
2483
+ closeAdd();
2484
+ if (changed) controller.load();
2485
+ },
2486
+ onBusyChange: setCustomBusy
2487
+ })
2488
+ }) : null,
2489
+ mounted("claude") && piAi !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
2490
+ id: `${addId}-claude-panel`,
2491
+ ...bothOffered ? {
2492
+ role: "tabpanel",
2493
+ "aria-labelledby": `${addId}-claude`
2494
+ } : {},
2495
+ hidden: mode !== "claude",
2496
+ className: ModelsSection_module_css_default["addPanel"],
2497
+ children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2498
+ providerType: CLAUDE_PROVIDER_TYPE,
2499
+ taken: state.rows.map((row) => row.entry.provider),
2500
+ protocols,
2501
+ revision: piAi.revision,
2502
+ operations,
2503
+ t,
2504
+ readOnly: !state.writable,
2505
+ onClose: (changed) => {
2506
+ closeAdd();
2507
+ if (changed) controller.load();
2508
+ },
2509
+ onBusyChange: setClaudeBusy
2510
+ })
2511
+ }) : null
2320
2512
  ]
2321
- }) : declaringType !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
2322
- className: ModelsSection_module_css_default["addCard"],
2323
- children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
2324
- taken: state.rows.map((row) => row.entry.provider),
2325
- protocols,
2326
- providerType: declaringType,
2327
- /* v8 ignore next -- the card only opens from a button disabled without this namespace */
2328
- revision: state.namespaces.get("llm-pi-ai")?.revision ?? 0,
2329
- operations,
2330
- t,
2331
- readOnly: !state.writable,
2332
- onClose: (changed) => {
2333
- setDeclaringType(void 0);
2334
- if (changed) controller.load();
2335
- }
2336
- })
2337
- }) : (0, react_jsx_runtime.jsxs)("div", {
2513
+ }) : catalogOffered || customOffered ? (0, react_jsx_runtime.jsx)("div", {
2338
2514
  className: ModelsSection_module_css_default["addActions"],
2339
- children: [configurable.length > 0 && (0, react_jsx_runtime.jsxs)("button", {
2515
+ children: (0, react_jsx_runtime.jsxs)("button", {
2340
2516
  type: "button",
2341
2517
  className: ModelsSection_module_css_default["addButton"],
2342
- disabled: addable.length === 0 || !state.writable,
2518
+ disabled: !state.writable || !catalogEnabled && !customEnabled,
2343
2519
  onClick: () => {
2344
2520
  const first = addable[0];
2345
- /* v8 ignore next -- the button is disabled while nothing is addable */
2346
- if (first === void 0) return;
2347
- setSavedTarget(void 0);
2348
- setDeclaringType(void 0);
2349
- setAdding(true);
2350
- setEditing(targetOf(first));
2351
- },
2352
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("add")]
2353
- }), state.namespaces.has("llm-pi-ai") && (0, react_jsx_runtime.jsxs)("button", {
2354
- type: "button",
2355
- className: ModelsSection_module_css_default["addButton"],
2356
- disabled: protocols.length === 0 || !state.writable,
2357
- onClick: () => {
2521
+ const initial = catalogEnabled ? "catalog" : "custom";
2358
2522
  setSavedTarget(void 0);
2359
- setAdding(false);
2360
- setEditing(void 0);
2361
- setDeclaringType("generic");
2523
+ setEditing(first === void 0 ? void 0 : targetOf(first.row));
2524
+ setAddMode(initial);
2525
+ setVisited(new Set([initial]));
2526
+ setAddOpen(true);
2362
2527
  },
2363
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("customAdd")]
2364
- }), (0, react_jsx_runtime.jsxs)("button", {
2365
- type: "button",
2366
- className: ModelsSection_module_css_default["addButton"],
2367
- disabled: !state.namespaces.has("llm-pi-ai") || !state.namespaces.has(CLAUDE_PROVIDER_SETTINGS_NS) || !state.writable,
2368
- onClick: () => {
2369
- setSavedTarget(void 0);
2370
- setAdding(false);
2371
- setEditing(void 0);
2372
- setDeclaringType(CLAUDE_PROVIDER_TYPE);
2373
- },
2374
- children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("claudeCustomAdd")]
2375
- })]
2376
- })
2528
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 14 }), t("add")]
2529
+ })
2530
+ }) : null
2377
2531
  }),
2378
2532
  renderSlot("settings.models.footer", {}),
2379
2533
  (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
@@ -2406,7 +2560,7 @@ window.__ModuleLoader__.load({
2406
2560
  }
2407
2561
  //#endregion
2408
2562
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/OnboardingModal.module.css.mjs
2409
- const css$2 = ".jLrgrW_dialog{width:min(600px,100%);padding:0}.jLrgrW_content{box-sizing:border-box;flex-direction:column;max-height:calc(100vh - 48px);padding:28px;display:flex;overflow-y:auto}.jLrgrW_title{color:var(--dsw-alias-label-primary);outline:none;margin:0;font-size:20px;font-weight:500;line-height:28px}.jLrgrW_body{margin-top:20px}@media (width<=560px){.jLrgrW_content{padding:24px}}";
2563
+ const css$2 = ".jLrgrW_dialog{width:min(600px,100%);max-height:100%;padding:0}.jLrgrW_content{box-sizing:border-box;flex-direction:column;min-height:0;padding:28px;display:flex;overflow-y:auto}.jLrgrW_title{color:var(--dsw-alias-label-primary);outline:none;margin:0;font-size:20px;font-weight:500;line-height:28px}.jLrgrW_body{margin-top:20px}@media (width<=560px){.jLrgrW_content{padding:24px}}";
2410
2564
  const tagId$2 = "@mofeng2223/dsh-claude-provider/OnboardingModal.module.css";
2411
2565
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
2412
2566
  const tag = document.createElement("style");
@@ -2501,20 +2655,34 @@ window.__ModuleLoader__.load({
2501
2655
  * @returns the onboarding modal or null when onboarding needs no intervention.
2502
2656
  */
2503
2657
  function DeepSeekOnboardingDialog(props) {
2504
- const { complete, controller, useModels, operations, schema, t } = props;
2658
+ const { complete, controller, useModels, operations, schema, t, renderSlot, automatic, explicit = false } = props;
2659
+ const [apiKey, setApiKey] = (0, react.useState)(explicit);
2505
2660
  const state = useModels((snapshot) => snapshot);
2506
2661
  const readiness = onboardingReadiness(state);
2507
2662
  (0, react.useEffect)(() => {
2508
- if (state.status === "idle") controller.load();
2509
- }, [controller, state.status]);
2663
+ if ((automatic || explicit) && state.status === "idle") controller.load();
2664
+ }, [
2665
+ controller,
2666
+ state.status,
2667
+ automatic,
2668
+ explicit
2669
+ ]);
2510
2670
  (0, react.useEffect)(() => {
2511
- if (readiness.kind === "adapter-absent" || readiness.kind === "provider-ready" || readiness.kind === "unavailable") complete();
2512
- }, [complete, readiness.kind]);
2671
+ if (!automatic && !explicit || readiness.kind === "adapter-absent" || !explicit && readiness.kind === "provider-ready" || readiness.kind === "unavailable") complete();
2672
+ }, [
2673
+ complete,
2674
+ readiness.kind,
2675
+ explicit,
2676
+ automatic
2677
+ ]);
2678
+ if (!automatic && !explicit) return null;
2513
2679
  switch (readiness.kind) {
2514
2680
  case "loading":
2515
2681
  case "adapter-absent":
2516
- case "provider-ready":
2517
2682
  case "unavailable": return null;
2683
+ case "provider-ready":
2684
+ if (!explicit) return null;
2685
+ break;
2518
2686
  case "credential-missing": break;
2519
2687
  /* v8 ignore next -- every current readiness variant is handled above */
2520
2688
  default: return assertNever$1(readiness);
@@ -2530,7 +2698,7 @@ window.__ModuleLoader__.load({
2530
2698
  }
2531
2699
  controller.load();
2532
2700
  };
2533
- return (0, react_jsx_runtime.jsxs)(OnboardingModal, {
2701
+ const editor = (0, react_jsx_runtime.jsxs)(OnboardingModal, {
2534
2702
  title: t("onboardingTitle"),
2535
2703
  children: [(0, react_jsx_runtime.jsx)("p", {
2536
2704
  className: DeepSeekOnboardingDialog_module_css_default.description,
@@ -2557,6 +2725,12 @@ window.__ModuleLoader__.load({
2557
2725
  })
2558
2726
  })]
2559
2727
  });
2728
+ return apiKey ? editor : renderSlot("settings.models.sign-in", {
2729
+ complete,
2730
+ useApiKey: () => {
2731
+ setApiKey(true);
2732
+ }
2733
+ }, { fallback: editor });
2560
2734
  }
2561
2735
  //#endregion
2562
2736
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-models/src/client/WelcomeNotice.module.css.mjs
@@ -2634,7 +2808,7 @@ window.__ModuleLoader__.load({
2634
2808
  //#endregion
2635
2809
  //#region lib/types/onboarding-copy.js
2636
2810
  /** Durable settings namespace for product-wide GUI onboarding facts. */
2637
- const WELCOME_NOTICE_SETTINGS_NAMESPACE = "ui-onboarding";
2811
+ const WELCOME_NOTICE_SETTINGS_NAMESPACE = "ui-settings-general";
2638
2812
  /** Field storing the last welcome notice version the user acknowledged. */
2639
2813
  const WELCOME_NOTICE_ACK_FIELD = "welcomeNoticeVersion";
2640
2814
  /**
@@ -2650,16 +2824,6 @@ window.__ModuleLoader__.load({
2650
2824
  * remote browser's memory-mode scope never answers and the acknowledgement
2651
2825
  * stays process-local here.
2652
2826
  */
2653
- /**
2654
- * Accept any object section verbatim; a malformed durable value reads as an
2655
- * empty section, so the notice treats it as unacknowledged instead of leaving
2656
- * the scope stuck on its previous value.
2657
- * @param section - the wire section value.
2658
- * @returns the section object, or an empty one for non-object values.
2659
- */
2660
- function decodeWelcomeSection(section) {
2661
- return typeof section === "object" && section !== null && !Array.isArray(section) ? section : {};
2662
- }
2663
2827
  /* v8 ignore next 3 -- closed-union default only defends future source widening */
2664
2828
  function assertNever(_value) {
2665
2829
  throw new Error("unexpected welcome settings status");
@@ -2858,7 +3022,14 @@ window.__ModuleLoader__.load({
2858
3022
  deleteDescriptionWithCredential: "Deleting {provider} removes its configuration and stored API key.",
2859
3023
  deleteConfirm: "Delete {provider}",
2860
3024
  deleting: "Deleting {provider}…",
2861
- add: "Add provider",
3025
+ add: "Add model provider",
3026
+ addMode: "How to add",
3027
+ addCatalog: "Third-party providers",
3028
+ addCustom: "Custom API",
3029
+ addCatalogHint: "Pick OpenAI, Anthropic, Kimi, or another provider from the built-in catalog and enter its API key.",
3030
+ addCustomHint: "Connect a relay, a self-hosted server, or any other OpenAI- or Anthropic-compatible endpoint by its base URL, protocol, and models.",
3031
+ addCatalogExhausted: "Every catalog provider is already configured.",
3032
+ addCustomUnavailable: "No API protocol is available to declare.",
2862
3033
  provider: "Provider",
2863
3034
  close: "Close",
2864
3035
  cancel: "Cancel",
@@ -2879,9 +3050,8 @@ window.__ModuleLoader__.load({
2879
3050
  customized: "Customized settings",
2880
3051
  baseUrl: "Base URL",
2881
3052
  baseUrlDefault: "Provider default",
2882
- deepSeekChatBaseUrl: "https://api.deepseek.com",
2883
- deepSeekMessagesBaseUrl: "https://api.deepseek.com/anthropic",
2884
- deepSeekEndpointHint: "Use an endpoint compatible with the configured connection.",
3053
+ deepSeekBaseUrl: "https://api.deepseek.com/anthropic",
3054
+ deepSeekEndpointHint: "Use an API endpoint compatible with Anthropic Messages.",
2885
3055
  models: "Models",
2886
3056
  modelsInherited: "Using the adapter defaults",
2887
3057
  modelsCustomized: "Customized model catalog",
@@ -2909,7 +3079,7 @@ window.__ModuleLoader__.load({
2909
3079
  modelNameInvalid: "Display name cannot be empty.",
2910
3080
  modelContextInvalid: "Context window must be a positive count, like 131072, 256K, or 1M.",
2911
3081
  modelMaxTokensInvalid: "Max output tokens must be a positive count, like 8192, 64K, or 1M.",
2912
- advancedHint: "Other fields live in settings.yaml; edit that section directly.",
3082
+ advancedHint: "Other fields live in cordis.patch.yml; edit that section directly.",
2913
3083
  modelCapacityInvalid: "A capacity must be a number, optionally suffixed K or M.",
2914
3084
  modelDuplicate: "Each model ID may appear once.",
2915
3085
  fetchModels: "Fetch available models",
@@ -2923,8 +3093,6 @@ window.__ModuleLoader__.load({
2923
3093
  fetchSelectAll: "Select all",
2924
3094
  fetchDeselectAll: "Deselect all",
2925
3095
  fetchAdopt: "Add selected",
2926
- customAdd: "Add a custom provider",
2927
- customTitle: "Custom provider",
2928
3096
  customTag: "Custom",
2929
3097
  claudeCustomAdd: "Add Claude provider",
2930
3098
  claudeCustomTitle: "Claude provider",
@@ -2946,10 +3114,14 @@ window.__ModuleLoader__.load({
2946
3114
  customDisplayName: "Display name",
2947
3115
  customApi: "API protocol",
2948
3116
  customApiUnset: "Not selected",
3117
+ protocolOpenAiCompletions: "OpenAI Chat Completions",
3118
+ protocolOpenAiResponses: "OpenAI Responses",
3119
+ protocolAnthropicMessages: "Anthropic Messages",
2949
3120
  customNeedsBaseUrl: "A custom provider needs a base URL.",
2950
3121
  customBaseUrlInvalid: "Enter a valid HTTP or HTTPS URL.",
2951
3122
  customNeedsModels: "A custom provider needs at least one model.",
2952
3123
  customBaseUrlPlaceholder: "https://gateway.example/v1",
3124
+ customAnthropicBaseUrlPlaceholder: "https://gateway.example",
2953
3125
  settingsPathUnresolvable: "unresolvable settings path",
2954
3126
  create: "Create provider",
2955
3127
  creating: "Creating…",
@@ -2968,7 +3140,7 @@ window.__ModuleLoader__.load({
2968
3140
  const zh = {
2969
3141
  nav: "模型",
2970
3142
  title: "模型",
2971
- intro: "填入各提供方的 API 密钥即可使用其模型。",
3143
+ intro: "填入各提供商的 API 密钥即可使用其模型。",
2972
3144
  edit: "编辑",
2973
3145
  editProvider: "编辑 {provider}",
2974
3146
  remove: "删除",
@@ -2978,8 +3150,15 @@ window.__ModuleLoader__.load({
2978
3150
  deleteDescriptionWithCredential: "删除 {provider} 会移除其配置和存储的 API 密钥。",
2979
3151
  deleteConfirm: "删除 {provider}",
2980
3152
  deleting: "正在删除 {provider}…",
2981
- add: "添加提供方",
2982
- provider: "提供方",
3153
+ add: "添加模型提供商",
3154
+ addMode: "添加方式",
3155
+ addCatalog: "第三方模型提供商",
3156
+ addCustom: "自定义模型 API",
3157
+ addCatalogHint: "从内置目录中选择 OpenAI、Anthropic、Kimi 等提供商,填入其 API 密钥即可使用。",
3158
+ addCustomHint: "连接中转站、自部署服务或其他兼容 OpenAI / Anthropic 协议的接口,需填写 API 地址、协议和模型。",
3159
+ addCatalogExhausted: "目录中的提供商都已添加。",
3160
+ addCustomUnavailable: "没有可用的 API 协议。",
3161
+ provider: "提供商",
2983
3162
  close: "关闭",
2984
3163
  cancel: "取消",
2985
3164
  apply: "保存",
@@ -2988,7 +3167,7 @@ window.__ModuleLoader__.load({
2988
3167
  credentialConfigured: "API 密钥已配置",
2989
3168
  credentialMissing: "API 密钥缺失",
2990
3169
  readOnly: "当前部署的设置文档为只读。",
2991
- loadFailed: "加载提供方目录失败",
3170
+ loadFailed: "加载提供商目录失败",
2992
3171
  conflict: "这张卡片打开期间,这些设置已被其他地方改动。请关闭后重新打开,在当前值上编辑。",
2993
3172
  retry: "重试",
2994
3173
  keyInput: "API 密钥",
@@ -2998,10 +3177,9 @@ window.__ModuleLoader__.load({
2998
3177
  keyEnvLocked: "由启动环境提供(只读)",
2999
3178
  customized: "自定义设置",
3000
3179
  baseUrl: "API 地址",
3001
- baseUrlDefault: "提供方默认",
3002
- deepSeekChatBaseUrl: "https://api.deepseek.com",
3003
- deepSeekMessagesBaseUrl: "https://api.deepseek.com/anthropic",
3004
- deepSeekEndpointHint: "请填写与当前连接配置兼容的 API 地址。",
3180
+ baseUrlDefault: "提供商默认",
3181
+ deepSeekBaseUrl: "https://api.deepseek.com/anthropic",
3182
+ deepSeekEndpointHint: "请填写兼容 Anthropic Messages 协议的 API 地址。",
3005
3183
  models: "模型目录",
3006
3184
  modelsInherited: "正在使用适配器默认模型",
3007
3185
  modelsCustomized: "已自定义模型目录",
@@ -3011,9 +3189,9 @@ window.__ModuleLoader__.load({
3011
3189
  modelName: "显示名称",
3012
3190
  modelNamePlaceholder: "留空时使用模型 ID",
3013
3191
  contextWindow: "上下文窗口",
3014
- contextWindowPlaceholder: "使用提供方默认值",
3192
+ contextWindowPlaceholder: "使用提供商默认值",
3015
3193
  maxTokens: "最大输出 token 数",
3016
- maxTokensPlaceholder: "使用提供方默认值",
3194
+ maxTokensPlaceholder: "使用提供商默认值",
3017
3195
  modelAdvanced: "模型选项",
3018
3196
  modelInputTypes: "输入类型",
3019
3197
  modelInputText: "文本",
@@ -3022,29 +3200,27 @@ window.__ModuleLoader__.load({
3022
3200
  removeModel: "删除模型",
3023
3201
  modelsEmpty: "模型选择器中将不显示任何模型;目录外 ID 仍可直接发送。",
3024
3202
  keyBlank: "请输入 API 密钥;留空则保持已存储的密钥。",
3025
- keyBlankNew: "请输入 API 密钥;若该提供方以其他方式鉴权,可以留空。",
3203
+ keyBlankNew: "请输入 API 密钥;若该提供商以其他方式鉴权,可以留空。",
3026
3204
  keyIllegalCharacters: "该 API 密钥格式错误,请检查。",
3027
3205
  modelIdRequired: "模型 ID 不能为空。",
3028
3206
  modelIdDuplicate: "模型 ID 不能重复。",
3029
3207
  modelNameInvalid: "显示名称不能为空。",
3030
3208
  modelContextInvalid: "上下文窗口必须是正数,例如 131072、256K 或 1M。",
3031
3209
  modelMaxTokensInvalid: "最大输出 token 数必须是正数,例如 8192、64K 或 1M。",
3032
- advancedHint: "其余字段在 settings.yaml 中,请直接编辑对应段。",
3210
+ advancedHint: "其余字段在 cordis.patch.yml 中,请直接编辑对应段。",
3033
3211
  modelCapacityInvalid: "容量需为数字,可加 K 或 M 后缀。",
3034
3212
  modelDuplicate: "每个模型 ID 只能出现一次。",
3035
3213
  fetchModels: "获取可用模型",
3036
- fetching: "正在询问提供方…",
3214
+ fetching: "正在询问提供商…",
3037
3215
  fetchNeedsBaseUrl: "请先填写 API 地址,再获取。",
3038
- fetchEmpty: "该提供方没有列出任何模型,请手动添加。",
3216
+ fetchEmpty: "该提供商没有列出任何模型,请手动添加。",
3039
3217
  fetchTitle: "选择要添加的模型",
3040
- fetchDescription: "以下是模型提供方的可用模型,勾选要添加的模型。",
3218
+ fetchDescription: "以下是模型提供商的可用模型,勾选要添加的模型。",
3041
3219
  fetchSearch: "搜索模型",
3042
3220
  fetchNoMatches: "没有匹配的模型。",
3043
3221
  fetchSelectAll: "全选",
3044
3222
  fetchDeselectAll: "取消全选",
3045
3223
  fetchAdopt: "添加所选",
3046
- customAdd: "添加自定义提供方",
3047
- customTitle: "自定义提供方",
3048
3224
  customTag: "自定义",
3049
3225
  claudeCustomAdd: "添加 Claude 提供方",
3050
3226
  claudeCustomTitle: "Claude 提供方",
@@ -3060,18 +3236,22 @@ window.__ModuleLoader__.load({
3060
3236
  thinkingPresetFour: "四档",
3061
3237
  thinkingPresetFive: "五档(默认)",
3062
3238
  customRoute: "Provider ID",
3063
- customRouteHint: "以小写字母开头的标识,在请求中唯一标识该提供方,并用于派生凭据名。",
3239
+ customRouteHint: "以小写字母开头的标识,在请求中唯一标识该提供商,并用于派生凭据名。",
3064
3240
  customRouteInvalid: "需以小写字母开头,之后可用小写字母、数字和短横线。",
3065
- customRouteTaken: "已有提供方使用了这个 ID。",
3241
+ customRouteTaken: "已有提供商使用了这个 ID。",
3066
3242
  customDisplayName: "显示名称",
3067
3243
  customApi: "API 协议",
3068
3244
  customApiUnset: "未选择",
3069
- customNeedsBaseUrl: "自定义提供方需要填写 API 地址。",
3245
+ protocolOpenAiCompletions: "OpenAI Chat Completions",
3246
+ protocolOpenAiResponses: "OpenAI Responses",
3247
+ protocolAnthropicMessages: "Anthropic Messages",
3248
+ customNeedsBaseUrl: "自定义模型 API 需要填写 API 地址。",
3070
3249
  customBaseUrlInvalid: "请输入有效的 HTTP 或 HTTPS 地址。",
3071
- customNeedsModels: "自定义提供方至少需要一个模型。",
3250
+ customNeedsModels: "自定义模型 API 至少需要一个模型。",
3072
3251
  customBaseUrlPlaceholder: "https://gateway.example/v1",
3252
+ customAnthropicBaseUrlPlaceholder: "https://gateway.example",
3073
3253
  settingsPathUnresolvable: "无法解析设置路径",
3074
- create: "创建提供方",
3254
+ create: "创建提供商",
3075
3255
  creating: "创建中…",
3076
3256
  welcomeTitle: "内测声明",
3077
3257
  welcomeBody: "DeepSeek Harness 目前的 0.1 版本仍处在面向 Harness 开发者进行测试的阶段,还有许多地方需要持续改进和打磨,希望听取广大开发者的反馈建议。预计 DeepSeek Harness 的核心插件以及基础 API 都会在接下来的一段时间内快速迭代、持续演化。\n\n我们期待与全球开发者一起,在开源、开放、可复用、可组合的基础设施之上,共同探索智能上限。欢迎全球 Harness 开发者加入 DSH 插件生态。",
@@ -3085,6 +3265,904 @@ window.__ModuleLoader__.load({
3085
3265
  keyRequired: "请输入 API 密钥后继续。"
3086
3266
  };
3087
3267
  //#endregion
3268
+ //#region ../../../vendor/cosmokit/src/misc.ts
3269
+ /** Return true when a value is `null` or `undefined`. */
3270
+ function isNullable(value) {
3271
+ return value === null || value === void 0;
3272
+ }
3273
+ /** Return true for non-array object values. */
3274
+ function isPlainObject(data) {
3275
+ return data && typeof data === "object" && !Array.isArray(data);
3276
+ }
3277
+ /** Filter object entries and return a new object. */
3278
+ function filterKeys(object, filter) {
3279
+ return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
3280
+ }
3281
+ /** Map object values while preserving the original key set. */
3282
+ function mapValues(object, transform) {
3283
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
3284
+ }
3285
+ /** Pick selected keys from an object, optionally including `undefined` values. */
3286
+ function pick(source, keys, forced) {
3287
+ if (!keys) return { ...source };
3288
+ const result = {};
3289
+ for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
3290
+ return result;
3291
+ }
3292
+ //#endregion
3293
+ //#region ../../../vendor/cosmokit/src/volatile.ts
3294
+ /** Shared config references used by schema validators and plugin runtimes. */
3295
+ const write = Symbol.for("cosmokit.volatile.write");
3296
+ function snapshot(value, ancestors = /* @__PURE__ */ new Set()) {
3297
+ if (typeof value === "function") throw new TypeError("volatile config cannot contain functions");
3298
+ if (value === null || typeof value !== "object") return value;
3299
+ if (ancestors.has(value)) throw new TypeError("volatile config cannot contain cycles");
3300
+ ancestors.add(value);
3301
+ try {
3302
+ if (Array.isArray(value)) return Object.freeze(value.map((item) => snapshot(item, ancestors)));
3303
+ if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("volatile config objects must be plain objects or arrays");
3304
+ return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])));
3305
+ } finally {
3306
+ ancestors.delete(value);
3307
+ }
3308
+ }
3309
+ /**
3310
+ * Create a detached reference containing an immutable copy of the supplied data.
3311
+ * @param value - validated config data; class instances and functions are unsupported.
3312
+ * @returns a reference whose value is updated only by its owning runtime.
3313
+ */
3314
+ function createVolatile(value) {
3315
+ let current = snapshot(value);
3316
+ return Object.freeze({
3317
+ get: () => current,
3318
+ [write]: (value) => {
3319
+ current = value;
3320
+ }
3321
+ });
3322
+ }
3323
+ /**
3324
+ * Identify references across ESM/CJS copies of the shared library.
3325
+ * @param value - a parsed config value.
3326
+ * @returns whether the value implements the shared reference protocol.
3327
+ */
3328
+ function isVolatile(value) {
3329
+ return typeof value === "object" && value !== null && write in value;
3330
+ }
3331
+ //#endregion
3332
+ //#region ../../../vendor/cosmokit/src/types.ts
3333
+ /** Test values using `instanceof` with a `toStringTag` fallback. */
3334
+ function is(type, value) {
3335
+ if (arguments.length === 1) return (value) => is(type, value);
3336
+ return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
3337
+ }
3338
+ function isArrayBufferLike(value) {
3339
+ return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
3340
+ }
3341
+ function isArrayBufferSource(value) {
3342
+ return isArrayBufferLike(value) || ArrayBuffer.isView(value);
3343
+ }
3344
+ let Binary;
3345
+ (function(_Binary) {
3346
+ _Binary.is = isArrayBufferLike;
3347
+ _Binary.isSource = isArrayBufferSource;
3348
+ function fromSource(source) {
3349
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
3350
+ else return source;
3351
+ }
3352
+ _Binary.fromSource = fromSource;
3353
+ function toBase64(source) {
3354
+ source = fromSource(source);
3355
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
3356
+ let binary = "";
3357
+ const bytes = new Uint8Array(source);
3358
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
3359
+ return btoa(binary);
3360
+ }
3361
+ _Binary.toBase64 = toBase64;
3362
+ function fromBase64(source) {
3363
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
3364
+ return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
3365
+ }
3366
+ _Binary.fromBase64 = fromBase64;
3367
+ function toHex(source) {
3368
+ source = fromSource(source);
3369
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
3370
+ return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
3371
+ }
3372
+ _Binary.toHex = toHex;
3373
+ function fromHex(source) {
3374
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
3375
+ const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
3376
+ const buffer = [];
3377
+ for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
3378
+ return Uint8Array.from(buffer).buffer;
3379
+ }
3380
+ _Binary.fromHex = fromHex;
3381
+ })(Binary || (Binary = {}));
3382
+ Binary.fromBase64;
3383
+ Binary.toBase64;
3384
+ Binary.fromHex;
3385
+ Binary.toHex;
3386
+ /** Deep-clone common JavaScript values while preserving prototypes and cycles. */
3387
+ function clone(source, refs = /* @__PURE__ */ new Map()) {
3388
+ if (!source || typeof source !== "object") return source;
3389
+ if (is("Date", source)) return new Date(source.valueOf());
3390
+ if (is("RegExp", source)) return new RegExp(source.source, source.flags);
3391
+ if (isArrayBufferLike(source)) return source.slice(0);
3392
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
3393
+ const cached = refs.get(source);
3394
+ if (cached) return cached;
3395
+ if (Array.isArray(source)) {
3396
+ const result = [];
3397
+ refs.set(source, result);
3398
+ source.forEach((value, index) => {
3399
+ result[index] = Reflect.apply(clone, null, [value, refs]);
3400
+ });
3401
+ return result;
3402
+ }
3403
+ const result = Object.create(Object.getPrototypeOf(source));
3404
+ refs.set(source, result);
3405
+ for (const key of Reflect.ownKeys(source)) {
3406
+ const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
3407
+ if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
3408
+ Reflect.defineProperty(result, key, descriptor);
3409
+ }
3410
+ return result;
3411
+ }
3412
+ /**
3413
+ * Compare values recursively, treating two volatile references as equal regardless of value.
3414
+ * Strict comparison distinguishes null/undefined, treats opaque objects by identity,
3415
+ * compares URLs by normalized href, treats array holes as undefined, and considers distinct cyclic structures unequal.
3416
+ * @param a - first value.
3417
+ * @param b - second value.
3418
+ * @param strict - whether to require strict data equality outside volatile references.
3419
+ * @returns whether the values compare equal.
3420
+ */
3421
+ function deepEqual(a, b, strict) {
3422
+ const ancestors = /* @__PURE__ */ new Set();
3423
+ function compare(a, b) {
3424
+ if (a === b) return true;
3425
+ if (isVolatile(a) || isVolatile(b)) return isVolatile(a) && isVolatile(b);
3426
+ if (!strict && isNullable(a) && isNullable(b)) return true;
3427
+ if (typeof a !== typeof b || typeof a !== "object" || !a || !b) return false;
3428
+ if (ancestors.has(a)) return false;
3429
+ function check(test, then) {
3430
+ return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
3431
+ }
3432
+ ancestors.add(a);
3433
+ try {
3434
+ return check(Array.isArray, (a, b) => {
3435
+ if (a.length !== b.length) return false;
3436
+ for (let index = 0; index < a.length; index++) if (!compare(a[index], b[index])) return false;
3437
+ return true;
3438
+ }) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("URL"), (a, b) => a.href === b.href) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
3439
+ if (a.byteLength !== b.byteLength) return false;
3440
+ const viewA = new Uint8Array(a);
3441
+ const viewB = new Uint8Array(b);
3442
+ for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
3443
+ return true;
3444
+ }) ?? ((!strict || [a, b].every((value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) && Object.keys({
3445
+ ...a,
3446
+ ...b
3447
+ }).every((key) => compare(a[key], b[key])));
3448
+ } finally {
3449
+ ancestors.delete(a);
3450
+ }
3451
+ }
3452
+ return compare(a, b);
3453
+ }
3454
+ //#endregion
3455
+ //#region ../../../vendor/cosmokit/src/time.ts
3456
+ let Time;
3457
+ (function(_Time) {
3458
+ _Time.millisecond = 1;
3459
+ const second = _Time.second = 1e3;
3460
+ const minute = _Time.minute = second * 60;
3461
+ const hour = _Time.hour = minute * 60;
3462
+ const day = _Time.day = hour * 24;
3463
+ const week = _Time.week = day * 7;
3464
+ let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
3465
+ function setTimezoneOffset(offset) {
3466
+ timezoneOffset = offset;
3467
+ }
3468
+ _Time.setTimezoneOffset = setTimezoneOffset;
3469
+ function getTimezoneOffset() {
3470
+ return timezoneOffset;
3471
+ }
3472
+ _Time.getTimezoneOffset = getTimezoneOffset;
3473
+ function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
3474
+ if (typeof date === "number") date = new Date(date);
3475
+ if (offset === void 0) offset = timezoneOffset;
3476
+ return Math.floor((date.valueOf() / minute - offset) / 1440);
3477
+ }
3478
+ _Time.getDateNumber = getDateNumber;
3479
+ function fromDateNumber(value, offset) {
3480
+ const date = new Date(value * day);
3481
+ if (offset === void 0) offset = timezoneOffset;
3482
+ return new Date(+date + offset * minute);
3483
+ }
3484
+ _Time.fromDateNumber = fromDateNumber;
3485
+ const numeric = /\d+(?:\.\d+)?/.source;
3486
+ const timeRegExp = new RegExp(`^${[
3487
+ "w(?:eek(?:s)?)?",
3488
+ "d(?:ay(?:s)?)?",
3489
+ "h(?:our(?:s)?)?",
3490
+ "m(?:in(?:ute)?(?:s)?)?",
3491
+ "s(?:ec(?:ond)?(?:s)?)?"
3492
+ ].map((unit) => `(${numeric}${unit})?`).join("")}$`);
3493
+ function parseTime(source) {
3494
+ const capture = timeRegExp.exec(source);
3495
+ if (!capture) return 0;
3496
+ return (parseFloat(capture[1]) * week || 0) + (parseFloat(capture[2]) * day || 0) + (parseFloat(capture[3]) * hour || 0) + (parseFloat(capture[4]) * minute || 0) + (parseFloat(capture[5]) * second || 0);
3497
+ }
3498
+ _Time.parseTime = parseTime;
3499
+ function parseDate(date) {
3500
+ const parsed = parseTime(date);
3501
+ if (parsed) date = Date.now() + parsed;
3502
+ else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
3503
+ else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
3504
+ return date ? new Date(date) : /* @__PURE__ */ new Date();
3505
+ }
3506
+ _Time.parseDate = parseDate;
3507
+ function format(ms) {
3508
+ const abs = Math.abs(ms);
3509
+ if (abs >= day - hour / 2) return Math.round(ms / day) + "d";
3510
+ else if (abs >= hour - minute / 2) return Math.round(ms / hour) + "h";
3511
+ else if (abs >= minute - second / 2) return Math.round(ms / minute) + "m";
3512
+ else if (abs >= second) return Math.round(ms / second) + "s";
3513
+ return ms + "ms";
3514
+ }
3515
+ _Time.format = format;
3516
+ function toDigits(source, length = 2) {
3517
+ return source.toString().padStart(length, "0");
3518
+ }
3519
+ _Time.toDigits = toDigits;
3520
+ function template(template, time = /* @__PURE__ */ new Date()) {
3521
+ return template.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
3522
+ }
3523
+ _Time.template = template;
3524
+ })(Time || (Time = {}));
3525
+ //#endregion
3526
+ //#region ../../../vendor/schemastery/src/index.ts
3527
+ const kSchema = Symbol.for("schemastery");
3528
+ const kValidationError = Symbol.for("ValidationError");
3529
+ globalThis.__schemastery_index__ ??= 0;
3530
+ globalThis.__schemastery_refs__ = void 0;
3531
+ var ValidationError = class extends TypeError {
3532
+ options;
3533
+ name = "ValidationError";
3534
+ constructor(message, options) {
3535
+ let prefix = "$";
3536
+ for (const segment of options.path || []) if (typeof segment === "string") prefix += "." + segment;
3537
+ else if (typeof segment === "number") prefix += "[" + segment + "]";
3538
+ else if (typeof segment === "symbol") prefix += `[Symbol(${segment.toString()})]`;
3539
+ if (prefix.startsWith(".")) prefix = prefix.slice(1);
3540
+ super((prefix === "$" ? "" : `${prefix} `) + message);
3541
+ this.options = options;
3542
+ }
3543
+ static is(error) {
3544
+ return !!error?.[kValidationError];
3545
+ }
3546
+ };
3547
+ Object.defineProperty(ValidationError.prototype, kValidationError, { value: true });
3548
+ const Schema = function(options) {
3549
+ const schema = function(data, options = {}) {
3550
+ return Schema.resolve(data, schema, options)[0];
3551
+ };
3552
+ if (options.refs) {
3553
+ const refs = mapValues(options.refs, (options) => new Schema(options));
3554
+ const getRef = (uid) => refs[uid];
3555
+ for (const key in refs) {
3556
+ const options = refs[key];
3557
+ options.sKey = getRef(options.sKey);
3558
+ options.inner = getRef(options.inner);
3559
+ options.list = options.list && options.list.map(getRef);
3560
+ options.dict = options.dict && mapValues(options.dict, getRef);
3561
+ }
3562
+ return refs[options.uid];
3563
+ }
3564
+ Object.assign(schema, options);
3565
+ if (typeof schema.callback === "string") try {
3566
+ schema.callback = new Function("return " + schema.callback)();
3567
+ } catch {}
3568
+ Object.defineProperty(schema, "uid", { value: globalThis.__schemastery_index__++ });
3569
+ Object.setPrototypeOf(schema, Schema.prototype);
3570
+ schema.meta ||= {};
3571
+ schema.toString = schema.toString.bind(schema);
3572
+ return schema;
3573
+ };
3574
+ Schema.prototype = Object.create(Function.prototype);
3575
+ Schema.prototype[kSchema] = true;
3576
+ Object.defineProperty(Schema.prototype, "~standard", { get() {
3577
+ return {
3578
+ version: 1,
3579
+ vendor: "schemastery",
3580
+ validate: (value) => {
3581
+ try {
3582
+ return { value: Schema.resolve(value, this, {})[0] };
3583
+ } catch (error) {
3584
+ if (ValidationError.is(error)) return { issues: [{
3585
+ message: error.message,
3586
+ path: error.options.path
3587
+ }] };
3588
+ throw error;
3589
+ }
3590
+ }
3591
+ };
3592
+ } });
3593
+ Schema.ValidationError = ValidationError;
3594
+ Schema.prototype.toJSON = function toJSON() {
3595
+ if (globalThis.__schemastery_refs__) {
3596
+ globalThis.__schemastery_refs__[this.uid] ??= JSON.parse(JSON.stringify({ ...this }));
3597
+ return this.uid;
3598
+ }
3599
+ globalThis.__schemastery_refs__ = { [this.uid]: { ...this } };
3600
+ globalThis.__schemastery_refs__[this.uid] = JSON.parse(JSON.stringify({ ...this }));
3601
+ const result = {
3602
+ uid: this.uid,
3603
+ refs: globalThis.__schemastery_refs__
3604
+ };
3605
+ globalThis.__schemastery_refs__ = void 0;
3606
+ return result;
3607
+ };
3608
+ Schema.prototype.set = function set(key, value) {
3609
+ this.dict[key] = value;
3610
+ return this;
3611
+ };
3612
+ Schema.prototype.push = function push(value) {
3613
+ this.list.push(value);
3614
+ return this;
3615
+ };
3616
+ function mergeDesc(original, messages) {
3617
+ const result = typeof original === "string" ? { "": original } : { ...original };
3618
+ for (const locale in messages) {
3619
+ const value = messages[locale];
3620
+ if (value?.$description || value?.$desc) result[locale] = value.$description || value.$desc;
3621
+ else if (typeof value === "string") result[locale] = value;
3622
+ }
3623
+ return result;
3624
+ }
3625
+ function getInner(value) {
3626
+ return value?.$value ?? value?.$inner;
3627
+ }
3628
+ function extractKeys(data) {
3629
+ return filterKeys(data ?? {}, (key) => !key.startsWith("$"));
3630
+ }
3631
+ Schema.prototype.i18n = function i18n(messages) {
3632
+ const schema = Schema(this);
3633
+ const desc = mergeDesc(schema.meta.description, messages);
3634
+ if (Object.keys(desc).length) schema.meta.description = desc;
3635
+ if (schema.dict) schema.dict = mapValues(schema.dict, (inner, key) => {
3636
+ return inner.i18n(mapValues(messages, (data) => getInner(data)?.[key] ?? data?.[key]));
3637
+ });
3638
+ if (schema.list) schema.list = schema.list.map((inner, index) => {
3639
+ return inner.i18n(mapValues(messages, (data = {}) => {
3640
+ if (Array.isArray(getInner(data))) return getInner(data)[index];
3641
+ if (Array.isArray(data)) return data[index];
3642
+ return extractKeys(data);
3643
+ }));
3644
+ });
3645
+ if (schema.inner) schema.inner = schema.inner.i18n(mapValues(messages, (data) => {
3646
+ if (getInner(data)) return getInner(data);
3647
+ return extractKeys(data);
3648
+ }));
3649
+ if (schema.sKey) schema.sKey = schema.sKey.i18n(mapValues(messages, (data) => data?.$key));
3650
+ return schema;
3651
+ };
3652
+ Schema.prototype.extra = function extra(key, value) {
3653
+ const schema = Schema(this);
3654
+ schema.meta = {
3655
+ ...schema.meta,
3656
+ [key]: value
3657
+ };
3658
+ return schema;
3659
+ };
3660
+ for (const key of [
3661
+ "required",
3662
+ "disabled",
3663
+ "collapse",
3664
+ "hidden",
3665
+ "loose"
3666
+ ]) Object.assign(Schema.prototype, { [key](value = true) {
3667
+ const schema = Schema(this);
3668
+ schema.meta = {
3669
+ ...schema.meta,
3670
+ [key]: value
3671
+ };
3672
+ return schema;
3673
+ } });
3674
+ Schema.prototype.deprecated = function deprecated() {
3675
+ const schema = Schema(this);
3676
+ schema.meta.badges ||= [];
3677
+ schema.meta.badges.push({
3678
+ text: "deprecated",
3679
+ type: "danger"
3680
+ });
3681
+ return schema;
3682
+ };
3683
+ Schema.prototype.experimental = function experimental() {
3684
+ const schema = Schema(this);
3685
+ schema.meta.badges ||= [];
3686
+ schema.meta.badges.push({
3687
+ text: "experimental",
3688
+ type: "warning"
3689
+ });
3690
+ return schema;
3691
+ };
3692
+ Schema.prototype.pattern = function pattern(regexp) {
3693
+ const schema = Schema(this);
3694
+ const pattern = pick(regexp, ["source", "flags"]);
3695
+ schema.meta = {
3696
+ ...schema.meta,
3697
+ pattern
3698
+ };
3699
+ return schema;
3700
+ };
3701
+ Schema.prototype.simplify = function simplify(value) {
3702
+ if (isVolatile(value)) value = value.get();
3703
+ if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
3704
+ if (isNullable(value)) return value;
3705
+ if (this.type === "object" || this.type === "dict") {
3706
+ const result = {};
3707
+ for (const key in value) {
3708
+ const item = (this.type === "object" ? this.dict[key] : this.inner)?.simplify(value[key]);
3709
+ if (this.type === "dict" || !isNullable(item)) result[key] = item;
3710
+ }
3711
+ if (deepEqual(result, this.meta.default, this.type === "dict")) return null;
3712
+ return result;
3713
+ } else if (this.type === "array" || this.type === "tuple") {
3714
+ const result = [];
3715
+ value.forEach((value, index) => {
3716
+ const schema = this.type === "array" ? this.inner : this.list[index];
3717
+ const item = schema ? schema.simplify(value) : value;
3718
+ result.push(item);
3719
+ });
3720
+ return result;
3721
+ } else if (this.type === "intersect") {
3722
+ const result = {};
3723
+ for (const item of this.list) Object.assign(result, item.simplify(value));
3724
+ return result;
3725
+ } else if (this.type === "union") for (const schema of this.list) try {
3726
+ Schema.resolve(value, schema, {});
3727
+ return schema.simplify(value);
3728
+ } catch {}
3729
+ return value;
3730
+ };
3731
+ Schema.prototype.toString = function toString(inline) {
3732
+ return formatters[this.type]?.(this, inline) ?? `Schema<${this.type}>`;
3733
+ };
3734
+ Schema.prototype.role = function role(role, extra) {
3735
+ const schema = Schema(this);
3736
+ schema.meta = {
3737
+ ...schema.meta,
3738
+ role,
3739
+ extra
3740
+ };
3741
+ return schema;
3742
+ };
3743
+ for (const key of [
3744
+ "default",
3745
+ "link",
3746
+ "comment",
3747
+ "description",
3748
+ "max",
3749
+ "min",
3750
+ "step"
3751
+ ]) Object.assign(Schema.prototype, { [key](value) {
3752
+ const schema = Schema(this);
3753
+ schema.meta = {
3754
+ ...schema.meta,
3755
+ [key]: value
3756
+ };
3757
+ return schema;
3758
+ } });
3759
+ Schema.prototype.volatile = function volatile() {
3760
+ if (this.meta.volatile) throw new TypeError("volatile schema is already wrapped");
3761
+ return this.extra("volatile", true);
3762
+ };
3763
+ const resolvers = {};
3764
+ const checkedVolatile = Symbol("checked-volatile-schema");
3765
+ function validateVolatileSchema(schema, path = [], blocked = false, seen = /* @__PURE__ */ new Map()) {
3766
+ const states = seen.get(schema) ?? /* @__PURE__ */ new Set();
3767
+ if (states.has(blocked)) return;
3768
+ states.add(blocked);
3769
+ seen.set(schema, states);
3770
+ if (schema.meta?.volatile && blocked) throw new ValidationError("volatile fields require a fixed object path without an enclosing volatile field", { path });
3771
+ const nested = blocked || !!schema.meta?.volatile;
3772
+ if (schema.dict) for (const [key, child] of Object.entries(schema.dict)) validateVolatileSchema(child, [...path, key], nested, seen);
3773
+ if (schema.sKey) validateVolatileSchema(schema.sKey, [...path, "<key>"], true, seen);
3774
+ if (schema.inner && (schema.type !== "lazy" || schema.inner[kSchema])) validateVolatileSchema(schema.inner, [...path, "*"], true, seen);
3775
+ if (schema.list) for (let index = 0; index < schema.list.length; index++) validateVolatileSchema(schema.list[index], [...path, String(index)], true, seen);
3776
+ }
3777
+ Schema.extend = function extend(type, resolve) {
3778
+ resolvers[type] = resolve;
3779
+ };
3780
+ Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
3781
+ if (!schema) return [data];
3782
+ if (!options[checkedVolatile]) {
3783
+ validateVolatileSchema(schema, options.path);
3784
+ options = {
3785
+ ...options,
3786
+ [checkedVolatile]: true
3787
+ };
3788
+ }
3789
+ if (schema.meta?.volatile) {
3790
+ const inner = Schema(schema);
3791
+ inner.meta = {
3792
+ ...schema.meta,
3793
+ volatile: false
3794
+ };
3795
+ const [value, adapted] = Schema.resolve(data, inner, options, strict);
3796
+ try {
3797
+ return [createVolatile(value), adapted];
3798
+ } catch (error) {
3799
+ throw new ValidationError(error instanceof Error ? error.message : String(error), options);
3800
+ }
3801
+ }
3802
+ if (options.ignore?.(data, schema)) return [data];
3803
+ if (isNullable(data) && schema.type !== "lazy") {
3804
+ if (schema.meta.required) throw new ValidationError(`missing required value`, options);
3805
+ let current = schema;
3806
+ let fallback = schema.meta.default;
3807
+ while (current?.type === "intersect" && isNullable(fallback)) {
3808
+ current = current.list[0];
3809
+ fallback = current?.meta.default;
3810
+ }
3811
+ if (isNullable(fallback)) return [data];
3812
+ data = clone(fallback);
3813
+ }
3814
+ const callback = resolvers[schema.type];
3815
+ if (!callback) throw new ValidationError(`unsupported type "${schema.type}"`, options);
3816
+ try {
3817
+ return callback(data, schema, options, strict);
3818
+ } catch (error) {
3819
+ if (!schema.meta.loose) throw error;
3820
+ return [schema.meta.default];
3821
+ }
3822
+ };
3823
+ Schema.from = function from(source) {
3824
+ if (isNullable(source)) return Schema.any();
3825
+ else if ([
3826
+ "string",
3827
+ "number",
3828
+ "boolean"
3829
+ ].includes(typeof source)) return Schema.const(source).required();
3830
+ else if (source[kSchema]) return source;
3831
+ else if (typeof source === "function") switch (source) {
3832
+ case String: return Schema.string().required();
3833
+ case Number: return Schema.number().required();
3834
+ case Boolean: return Schema.boolean().required();
3835
+ case Function: return Schema.function().required();
3836
+ default: return Schema.is(source).required();
3837
+ }
3838
+ else throw new TypeError(`cannot infer schema from ${source}`);
3839
+ };
3840
+ Schema.lazy = function lazy(builder) {
3841
+ const toJSON = () => {
3842
+ if (!schema.inner[kSchema]) {
3843
+ schema.inner = schema.builder();
3844
+ schema.inner.meta = {
3845
+ ...schema.meta,
3846
+ ...schema.inner.meta
3847
+ };
3848
+ }
3849
+ return schema.inner.toJSON();
3850
+ };
3851
+ const schema = new Schema({
3852
+ type: "lazy",
3853
+ builder,
3854
+ inner: { toJSON }
3855
+ });
3856
+ return schema;
3857
+ };
3858
+ Schema.natural = function natural() {
3859
+ return Schema.number().step(1).min(0);
3860
+ };
3861
+ Schema.percent = function percent() {
3862
+ return Schema.number().step(.01).min(0).max(1).role("slider");
3863
+ };
3864
+ Schema.date = function date() {
3865
+ return Schema.union([Schema.is(Date), Schema.transform(Schema.string().role("datetime"), (value, options) => {
3866
+ const date = new Date(value);
3867
+ if (isNaN(+date)) throw new ValidationError(`invalid date "${value}"`, options);
3868
+ return date;
3869
+ }, true)]);
3870
+ };
3871
+ Schema.regExp = function regExp(flag = "") {
3872
+ return Schema.union([Schema.is(RegExp), Schema.transform(Schema.string().role("regexp", { flag }), (value, options) => {
3873
+ try {
3874
+ return new RegExp(value, flag);
3875
+ } catch (e) {
3876
+ throw new ValidationError(e.message, options);
3877
+ }
3878
+ }, true)]);
3879
+ };
3880
+ Schema.arrayBuffer = function arrayBuffer(encoding) {
3881
+ return Schema.union([
3882
+ Schema.is(ArrayBuffer),
3883
+ Schema.is(SharedArrayBuffer),
3884
+ Schema.transform(Schema.any(), (value, options) => {
3885
+ if (Binary.isSource(value)) return Binary.fromSource(value);
3886
+ throw new ValidationError(`expected ArrayBufferSource but got ${value}`, options);
3887
+ }, true),
3888
+ ...encoding ? [Schema.transform(Schema.string(), (value, options) => {
3889
+ try {
3890
+ return encoding === "base64" ? Binary.fromBase64(value) : Binary.fromHex(value);
3891
+ } catch (e) {
3892
+ throw new ValidationError(e.message, options);
3893
+ }
3894
+ }, true)] : []
3895
+ ]);
3896
+ };
3897
+ Schema.extend("lazy", (data, schema, options, strict) => {
3898
+ if (!schema.inner[kSchema]) {
3899
+ schema.inner = schema.builder();
3900
+ schema.inner.meta = {
3901
+ ...schema.meta,
3902
+ ...schema.inner.meta
3903
+ };
3904
+ validateVolatileSchema(schema.inner, options.path, true);
3905
+ }
3906
+ return Schema.resolve(data, schema.inner, options, strict);
3907
+ });
3908
+ Schema.extend("any", (data) => {
3909
+ return [data];
3910
+ });
3911
+ Schema.extend("never", (data, _, options) => {
3912
+ throw new ValidationError(`expected nullable but got ${data}`, options);
3913
+ });
3914
+ Schema.extend("const", (data, { value }, options) => {
3915
+ if (deepEqual(data, value)) return [value];
3916
+ throw new ValidationError(`expected ${value} but got ${data}`, options);
3917
+ });
3918
+ function checkWithinRange(data, meta, description, options, skipMin = false) {
3919
+ const { max = Infinity, min = -Infinity } = meta;
3920
+ if (data > max) throw new ValidationError(`expected ${description} <= ${max} but got ${data}`, options);
3921
+ if (data < min && !skipMin) throw new ValidationError(`expected ${description} >= ${min} but got ${data}`, options);
3922
+ }
3923
+ Schema.extend("string", (data, { meta }, options) => {
3924
+ if (typeof data !== "string") throw new ValidationError(`expected string but got ${data}`, options);
3925
+ if (meta.pattern) {
3926
+ const regexp = new RegExp(meta.pattern.source, meta.pattern.flags);
3927
+ if (!regexp.test(data)) throw new ValidationError(`expect string to match regexp ${regexp}`, options);
3928
+ }
3929
+ checkWithinRange(data.length, meta, "string length", options);
3930
+ return [data];
3931
+ });
3932
+ function decimalShift(data, digits) {
3933
+ const str = data.toString();
3934
+ if (str.includes("e")) return data * Math.pow(10, digits);
3935
+ const index = str.indexOf(".");
3936
+ if (index === -1) return data * Math.pow(10, digits);
3937
+ const frac = str.slice(index + 1);
3938
+ const integer = str.slice(0, index);
3939
+ if (frac.length <= digits) return +(integer + frac.padEnd(digits, "0"));
3940
+ return +(integer + frac.slice(0, digits) + "." + frac.slice(digits));
3941
+ }
3942
+ function isMultipleOf(data, min, step) {
3943
+ step = Math.abs(step);
3944
+ if (!/^\d+\.\d+$/.test(step.toString())) return (data - min) % step === 0;
3945
+ const index = step.toString().indexOf(".");
3946
+ const digits = step.toString().slice(index + 1).length;
3947
+ return Math.abs(decimalShift(data, digits) - decimalShift(min, digits)) % decimalShift(step, digits) === 0;
3948
+ }
3949
+ Schema.extend("number", (data, { meta }, options) => {
3950
+ if (typeof data !== "number") throw new ValidationError(`expected number but got ${data}`, options);
3951
+ checkWithinRange(data, meta, "number", options);
3952
+ const { step } = meta;
3953
+ if (step && !isMultipleOf(data, meta.min ?? 0, step)) throw new ValidationError(`expected number multiple of ${step} but got ${data}`, options);
3954
+ return [data];
3955
+ });
3956
+ Schema.extend("boolean", (data, _, options) => {
3957
+ if (typeof data === "boolean") return [data];
3958
+ throw new ValidationError(`expected boolean but got ${data}`, options);
3959
+ });
3960
+ Schema.extend("bitset", (data, { bits, meta }, options) => {
3961
+ let value = 0, keys = [];
3962
+ if (typeof data === "number") {
3963
+ value = data;
3964
+ for (const key in bits) if (data & bits[key]) keys.push(key);
3965
+ } else if (Array.isArray(data)) {
3966
+ keys = data;
3967
+ for (const key of keys) {
3968
+ if (typeof key !== "string") throw new ValidationError(`expected string but got ${key}`, options);
3969
+ if (key in bits) value |= bits[key];
3970
+ }
3971
+ } else throw new ValidationError(`expected number or array but got ${data}`, options);
3972
+ if (value === meta.default) return [value];
3973
+ return [value, keys];
3974
+ });
3975
+ Schema.extend("function", (data, _, options) => {
3976
+ if (typeof data === "function") return [data];
3977
+ throw new ValidationError(`expected function but got ${data}`, options);
3978
+ });
3979
+ Schema.extend("is", (data, { constructor }, options) => {
3980
+ if (typeof constructor === "function") {
3981
+ if (data instanceof constructor) return [data];
3982
+ throw new ValidationError(`expected ${constructor.name} but got ${data}`, options);
3983
+ } else {
3984
+ if (isNullable(data)) throw new ValidationError(`expected ${constructor} but got ${data}`, options);
3985
+ let prototype = Object.getPrototypeOf(data);
3986
+ while (prototype) {
3987
+ if (prototype.constructor?.name === constructor) return [data];
3988
+ prototype = Object.getPrototypeOf(prototype);
3989
+ }
3990
+ throw new ValidationError(`expected ${constructor} but got ${data}`, options);
3991
+ }
3992
+ });
3993
+ function property(data, key, schema, options) {
3994
+ try {
3995
+ const [value, adapted] = Schema.resolve(data[key], schema, {
3996
+ ...options,
3997
+ path: [...options.path || [], key]
3998
+ });
3999
+ if (adapted !== void 0) data[key] = adapted;
4000
+ return value;
4001
+ } catch (e) {
4002
+ if (!options?.autofix) throw e;
4003
+ delete data[key];
4004
+ return schema.meta.volatile ? createVolatile(schema.meta.default) : schema.meta.default;
4005
+ }
4006
+ }
4007
+ Schema.extend("array", (data, { inner, meta }, options) => {
4008
+ if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
4009
+ checkWithinRange(data.length, meta, "array length", options, !isNullable(inner.meta.default));
4010
+ return [data.map((_, index) => property(data, index, inner, options))];
4011
+ });
4012
+ Schema.extend("dict", (data, { inner, sKey }, options, strict) => {
4013
+ if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
4014
+ const result = {};
4015
+ for (const key in data) {
4016
+ let rKey;
4017
+ try {
4018
+ rKey = Schema.resolve(key, sKey, options)[0];
4019
+ } catch (error) {
4020
+ if (strict) continue;
4021
+ throw error;
4022
+ }
4023
+ result[rKey] = property(data, key, inner, options);
4024
+ data[rKey] = data[key];
4025
+ if (key !== rKey) delete data[key];
4026
+ }
4027
+ return [result];
4028
+ });
4029
+ Schema.extend("tuple", (data, { list }, options, strict) => {
4030
+ if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
4031
+ const result = list.map((inner, index) => property(data, index, inner, options));
4032
+ if (strict) return [result];
4033
+ result.push(...data.slice(list.length));
4034
+ return [result];
4035
+ });
4036
+ function merge(result, data) {
4037
+ for (const key in data) {
4038
+ if (key in result) continue;
4039
+ result[key] = data[key];
4040
+ }
4041
+ }
4042
+ Schema.extend("object", (data, { dict }, options, strict) => {
4043
+ if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
4044
+ const result = {};
4045
+ for (const key in dict) {
4046
+ const value = property(data, key, dict[key], options);
4047
+ if (!isNullable(value) || key in data) result[key] = value;
4048
+ }
4049
+ if (!strict) merge(result, data);
4050
+ return [result];
4051
+ });
4052
+ Schema.extend("union", (data, { list, toString }, options, strict) => {
4053
+ const messages = [];
4054
+ for (const inner of list) try {
4055
+ return Schema.resolve(data, inner, options, strict);
4056
+ } catch (error) {
4057
+ messages.push(error);
4058
+ }
4059
+ throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
4060
+ });
4061
+ Schema.extend("intersect", (data, { list, toString }, options, strict) => {
4062
+ if (!list.length) return [data];
4063
+ let result;
4064
+ for (const inner of list) {
4065
+ const value = Schema.resolve(data, inner, options, true)[0];
4066
+ if (isNullable(value)) continue;
4067
+ if (isNullable(result)) result = value;
4068
+ else if (typeof result !== typeof value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
4069
+ else if (typeof value === "object") merge(result ??= {}, value);
4070
+ else if (result !== value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
4071
+ }
4072
+ if (!strict && isPlainObject(data)) merge(result, data);
4073
+ return [result];
4074
+ });
4075
+ Schema.extend("transform", (data, { inner, callback, preserve }, options) => {
4076
+ const [result, adapted = data] = Schema.resolve(data, inner, options, true);
4077
+ if (preserve) return [callback(result)];
4078
+ else return [callback(result), callback(adapted)];
4079
+ });
4080
+ const formatters = {};
4081
+ function defineMethod(name, keys, format) {
4082
+ formatters[name] = format;
4083
+ Object.assign(Schema, { [name](...args) {
4084
+ const schema = new Schema({ type: name });
4085
+ keys.forEach((key, index) => {
4086
+ switch (key) {
4087
+ case "sKey":
4088
+ schema.sKey = args[index] ?? Schema.string();
4089
+ break;
4090
+ case "inner":
4091
+ schema.inner = Schema.from(args[index]);
4092
+ break;
4093
+ case "list":
4094
+ schema.list = args[index].map(Schema.from);
4095
+ break;
4096
+ case "dict":
4097
+ schema.dict = mapValues(args[index], Schema.from);
4098
+ break;
4099
+ case "bits":
4100
+ schema.bits = {};
4101
+ for (const key in args[index]) {
4102
+ if (typeof args[index][key] !== "number") continue;
4103
+ schema.bits[key] = args[index][key];
4104
+ }
4105
+ break;
4106
+ case "callback": {
4107
+ const callback = schema.callback = args[index];
4108
+ callback["toJSON"] ||= () => callback.toString();
4109
+ break;
4110
+ }
4111
+ case "constructor": {
4112
+ const constructor = schema.constructor = args[index];
4113
+ if (typeof constructor === "function") constructor["toJSON"] ||= () => constructor["name"];
4114
+ break;
4115
+ }
4116
+ default: schema[key] = args[index];
4117
+ }
4118
+ });
4119
+ if (name === "object" || name === "dict") schema.meta.default = {};
4120
+ else if (name === "array" || name === "tuple") schema.meta.default = [];
4121
+ else if (name === "bitset") schema.meta.default = 0;
4122
+ return schema;
4123
+ } });
4124
+ }
4125
+ defineMethod("is", ["constructor"], ({ constructor }) => {
4126
+ if (typeof constructor === "function") return constructor.name;
4127
+ else return constructor;
4128
+ });
4129
+ defineMethod("any", [], () => "any");
4130
+ defineMethod("never", [], () => "never");
4131
+ defineMethod("const", ["value"], ({ value }) => typeof value === "string" ? JSON.stringify(value) : value);
4132
+ defineMethod("string", [], () => "string");
4133
+ defineMethod("number", [], () => "number");
4134
+ defineMethod("boolean", [], () => "boolean");
4135
+ defineMethod("bitset", ["bits"], () => "bitset");
4136
+ defineMethod("function", [], () => "function");
4137
+ defineMethod("array", ["inner"], ({ inner }) => `${inner.toString(true)}[]`);
4138
+ defineMethod("dict", ["inner", "sKey"], ({ inner, sKey }) => `{ [key: ${sKey.toString()}]: ${inner.toString()} }`);
4139
+ defineMethod("tuple", ["list"], ({ list }) => `[${list.map((inner) => inner.toString()).join(", ")}]`);
4140
+ defineMethod("object", ["dict"], ({ dict }) => {
4141
+ if (Object.keys(dict).length === 0) return "{}";
4142
+ return `{ ${Object.entries(dict).map(([key, inner]) => {
4143
+ return `${key}${inner.meta.required ? "" : "?"}: ${inner.toString()}`;
4144
+ }).join(", ")} }`;
4145
+ });
4146
+ defineMethod("union", ["list"], ({ list }, inline) => {
4147
+ const result = list.map(({ toString: format }) => format()).join(" | ");
4148
+ return inline ? `(${result})` : result;
4149
+ });
4150
+ defineMethod("intersect", ["list"], ({ list }) => {
4151
+ return `${list.map((inner) => inner.toString(true)).join(" & ")}`;
4152
+ });
4153
+ defineMethod("transform", [
4154
+ "inner",
4155
+ "callback",
4156
+ "preserve"
4157
+ ], ({ inner }, isInner) => inner.toString(isInner));
4158
+ //#endregion
4159
+ //#region lib/types/onboarding-config.js
4160
+ /** Public page-bootstrap options shared by the Host and Client halves. */
4161
+ /** Validate Host configuration and its public page-bootstrap payload. */
4162
+ const Config = Schema.object({ credentialOnboarding: Schema.boolean().default(true) });
4163
+ /** Page-global key carrying only the public onboarding options. */
4164
+ const ONBOARDING_CONFIG_GLOBAL = "__DSH_MODELS_ONBOARDING__";
4165
+ //#endregion
3088
4166
  //#region lib/types/client/index.js
3089
4167
  /** Dictionary namespace owned by this plugin. */
3090
4168
  const NS = "settings.models";
@@ -3109,7 +4187,7 @@ window.__ModuleLoader__.load({
3109
4187
  "remote.credentials",
3110
4188
  "remote.llm",
3111
4189
  "remote.settings",
3112
- "settingsScope",
4190
+ "configForms",
3113
4191
  "settingsSchema"
3114
4192
  ];
3115
4193
  /**
@@ -3119,13 +4197,15 @@ window.__ModuleLoader__.load({
3119
4197
  * @param ctx - client root context.
3120
4198
  */
3121
4199
  function apply(ctx) {
4200
+ const payload = globalThis[ONBOARDING_CONFIG_GLOBAL];
4201
+ const credentialOnboarding = Config(payload === void 0 ? {} : payload).credentialOnboarding && !("dshDesktop" in globalThis);
3122
4202
  ctx.effect(() => ctx.locale.register(NS, {
3123
4203
  zh,
3124
4204
  en
3125
4205
  }), "ui-settings-models: copy dictionaries");
3126
4206
  const schema = createSettingsSchemaOperations(ctx.settingsSchema);
3127
4207
  const operations = createModelsOperations(ctx);
3128
- const controller = new ModelsSettingsStore(ctx, schema, ctx.settingsScope.describe());
4208
+ const controller = new ModelsSettingsStore(ctx, schema, ctx.configForms.describe());
3129
4209
  const t = ctx.locale.bind(NS);
3130
4210
  const injected = () => ({
3131
4211
  controller,
@@ -3135,16 +4215,14 @@ window.__ModuleLoader__.load({
3135
4215
  t
3136
4216
  });
3137
4217
  const deepSeekOnboardingInjected = () => ({
4218
+ automatic: credentialOnboarding,
3138
4219
  controller,
3139
4220
  hooks: { models: controller.store },
3140
4221
  operations,
3141
4222
  schema,
3142
4223
  t
3143
4224
  });
3144
- const welcomeController = new WelcomeNoticeStore(ctx.settingsScope.bind({
3145
- namespace: WELCOME_NOTICE_SETTINGS_NAMESPACE,
3146
- decode: decodeWelcomeSection
3147
- }));
4225
+ const welcomeController = new WelcomeNoticeStore(ctx.configForms.get(WELCOME_NOTICE_SETTINGS_NAMESPACE));
3148
4226
  const welcomeInjected = () => ({
3149
4227
  controller: welcomeController,
3150
4228
  hooks: { welcome: welcomeController.store },
@@ -3194,6 +4272,10 @@ window.__ModuleLoader__.load({
3194
4272
  ctx.slots.inject("settings.onboarding", () => ctx.slots.register({
3195
4273
  name: "settings.onboarding",
3196
4274
  id: "deepseek-official",
4275
+ children: { "settings.models.sign-in": {
4276
+ kind: "single",
4277
+ scope: "root"
4278
+ } },
3197
4279
  order: 0,
3198
4280
  inject: deepSeekOnboardingInjected
3199
4281
  }, DeepSeekOnboardingDialog));