aitoearn-dsh-plugin-test 0.0.4 → 0.0.5

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/cordis.patch.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  - insert:
2
2
  - id: aitoearn
3
- name: '@aitoearn/dsh-plugin'
3
+ name: 'aitoearn-dsh-plugin-test'
package/lib/client.js CHANGED
@@ -1,4 +1,4 @@
1
- window.__ModuleLoader__.load({ id: "@aitoearn/dsh-plugin", factory: (require) => {
1
+ window.__ModuleLoader__.load({ id: "aitoearn-dsh-plugin-test", factory: (require) => {
2
2
  var module = { exports: {} }; var exports = module.exports;
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -384,7 +384,7 @@ var aitoearnCardLocales = {
384
384
  envCn: "\u4E2D\u56FD\u7AD9 aitoearn.cn",
385
385
  envCustom: "\u81EA\u5B9A\u4E49",
386
386
  baseUrl: "\u63A5\u53E3\u5730\u5740",
387
- baseUrlHint: "\u7559\u7A7A\u5219\u4F7F\u7528\u6240\u9009\u73AF\u5883\u9ED8\u8BA4\u5730\u5740\u3002",
387
+ baseUrlHint: "\u7559\u7A7A\u5219\u4F7F\u7528\u6240\u9009\u73AF\u5883\u9ED8\u8BA4\u5730\u5740\uFF1B\u586B\u5199\u5B8C\u6574 MCP \u7AEF\u70B9\uFF08\u542B\u8DEF\u5F84\uFF09\u65F6\u6309\u539F\u6837\u4F7F\u7528\u3002",
388
388
  apiKey: "API Key",
389
389
  apiKeySet: "\u5DF2\u914D\u7F6E\u5BC6\u94A5\u3002",
390
390
  apiKeyUnset: "\u672A\u914D\u7F6E\u5BC6\u94A5\uFF1B\u914D\u7F6E\u4E4B\u524D Aitoearn \u5DE5\u5177\u4E0D\u53EF\u7528\u3002",
@@ -406,7 +406,7 @@ var aitoearnCardLocales = {
406
406
  envCn: "China (aitoearn.cn)",
407
407
  envCustom: "Custom",
408
408
  baseUrl: "Base URL",
409
- baseUrlHint: "Leave blank to use the selected environment default.",
409
+ baseUrlHint: "Leave blank for the selected environment default; a full MCP endpoint (with path) is used as-is.",
410
410
  apiKey: "API key",
411
411
  apiKeySet: "A key is configured.",
412
412
  apiKeyUnset: "No key is configured; Aitoearn tools are unavailable until one is.",
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../tools/agent-plugin/src/dsh/client/index.ts", "../../../../../tools/agent-plugin/src/dsh/auth.ts", "../../../../../tools/agent-plugin/src/dsh/client/AitoearnCard.tsx", "../../../../../tools/agent-plugin/src/dsh/client/card.css.ts", "../../../../../tools/agent-plugin/src/dsh/client/card-store.ts", "../../../../../tools/agent-plugin/src/dsh/client/i18n.ts"],
4
- "sourcesContent": ["import { Context } from '@deepseek-ai/cordis'\nimport { AitoearnPluginConfig, SETTINGS_NAMESPACE } from '../auth.js'\nimport { AitoearnCard } from './AitoearnCard.js'\nimport { createAitoearnCardFace } from './card-store.js'\nimport { aitoearnCardLocales, aitoearnCardNS } from './i18n.js'\n\nexport const name = 'aitoearn-settings-card'\nexport const inject = ['slots', 'locale', 'configForms']\n\nexport function apply(ctx: Context): void {\n ctx.effect(\n () => ctx.locale.register(aitoearnCardNS, aitoearnCardLocales),\n 'aitoearn-settings-card: dictionaries',\n )\n const t = ctx.locale.bind(aitoearnCardNS)\n ctx.effect(() => ctx.configForms.whileServed([SETTINGS_NAMESPACE], () => {\n const form = ctx.configForms.get<AitoearnPluginConfig>(SETTINGS_NAMESPACE)\n const face = createAitoearnCardFace(form, ctx.configForms.describe())\n return ctx.slots.inject('settings.plugins.tab', () =>\n ctx.slots.register(\n {\n name: 'settings.plugins.tab',\n id: 'aitoearn',\n order: 10,\n label: () => t('title'),\n locale: aitoearnCardNS,\n inject: () => face,\n },\n AitoearnCard,\n ))\n }), 'aitoearn-settings-card: tab')\n}\n", "export const MCP_PATH = '/api/ai/agent/mcp'\nexport const API_KEY_ENV = 'AITOEARN_API_KEY'\nexport const DEFAULT_ENVIRONMENT = 'global'\nexport const SETTINGS_NAMESPACE = 'aitoearn'\n\nexport const ENVIRONMENT_ORIGINS = {\n global: 'https://aitoearn.ai',\n cn: 'https://aitoearn.cn',\n} as const\n\nexport const DEFAULT_MCP_URL = `${ENVIRONMENT_ORIGINS.global}${MCP_PATH}`\n\nexport type AitoearnEnvironment = 'global' | 'cn' | 'custom'\n\nexport interface AitoearnAuth {\n mcpUrl: string\n apiKey: string\n}\n\nexport interface AitoearnPluginConfig {\n environment?: string\n baseUrl?: string\n apiKey?: string\n}\n\nexport function pluginConfigFromSettings(\n section: unknown,\n fallback: AitoearnPluginConfig = {},\n): AitoearnPluginConfig {\n if (section === null || typeof section !== 'object' || Array.isArray(section))\n return fallback\n const record = section as Record<string, unknown>\n return {\n ...fallback,\n ...(typeof record['environment'] === 'string' ? { environment: record['environment'] } : {}),\n ...(typeof record['baseUrl'] === 'string' ? { baseUrl: record['baseUrl'] } : {}),\n ...(typeof record['apiKey'] === 'string' ? { apiKey: record['apiKey'] } : {}),\n }\n}\n\nexport function resolveEnvironment(value: string | undefined): AitoearnEnvironment {\n if (value === 'cn' || value === 'custom' || value === 'global')\n return value\n return DEFAULT_ENVIRONMENT\n}\n\nexport function resolveOrigin(environment: AitoearnEnvironment, baseUrl?: string): string {\n if (environment !== 'custom')\n return ENVIRONMENT_ORIGINS[environment]\n const trimmed = baseUrl?.trim() ?? ''\n if (trimmed.length === 0)\n return ''\n try {\n const url = new URL(trimmed)\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n return ''\n return url.origin\n }\n catch {\n return ''\n }\n}\n\nexport function resolveMcpUrl(config: AitoearnPluginConfig): string {\n const environment = resolveEnvironment(config.environment)\n const origin = resolveOrigin(environment, config.baseUrl)\n if (origin.length === 0)\n return ''\n return `${origin}${MCP_PATH}`\n}\n\nexport function resolveAuth(\n config: AitoearnPluginConfig,\n env: NodeJS.ProcessEnv = process.env,\n): AitoearnAuth {\n return {\n mcpUrl: resolveMcpUrl(config),\n apiKey: config.apiKey?.trim() || env[API_KEY_ENV]?.trim() || '',\n }\n}\n\nexport function authorizationHeader(apiKey: string): Record<string, string> {\n if (apiKey.length === 0)\n return {}\n return { Authorization: `Bearer ${apiKey}` }\n}\n", "import { IconChevronDownOutlineRegular, Tag } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { SnapshotSelectorHook, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport { useEffect, useRef, useState } from 'react'\nimport { AitoearnEnvironment } from '../auth.js'\nimport { AitoearnCardState } from './card-store.js'\nimport * as css from './card.css.js'\nimport { AitoearnCardLocaleKey } from './i18n.js'\n\nexport interface AitoearnCardProps {\n t: TranslateNS<'aitoearn-card'>\n useAitoearnCard: SnapshotSelectorHook<AitoearnCardState>\n edit: (field: 'environment' | 'baseUrl' | 'apiKey', value: string) => void\n save: () => void\n discard: () => void\n}\n\nconst cx = (names: Array<string | false | undefined>): string => names.filter(Boolean).join(' ')\n\nconst ENVIRONMENT_OPTIONS: Array<{ value: AitoearnEnvironment, labelKey: AitoearnCardLocaleKey }> = [\n { value: 'global', labelKey: 'envGlobal' },\n { value: 'cn', labelKey: 'envCn' },\n { value: 'custom', labelKey: 'envCustom' },\n]\n\n/**\n * The Aitoearn plugin settings card, laid out after the official plugin cards:\n * a header naming the plugin, its fields in place, and the footer that writes\n * the staged edits.\n */\nexport function AitoearnCard(props: AitoearnCardProps) {\n const { t } = props\n const state = props.useAitoearnCard(snapshot => snapshot)\n const [open, setOpen] = useState(false)\n const saveStarted = useRef(false)\n useEffect(() => {\n if (state.saving) {\n saveStarted.current = true\n return\n }\n if (!saveStarted.current)\n return\n saveStarted.current = false\n if (!state.dirty && !state.failed)\n setOpen(false)\n }, [state.dirty, state.failed, state.saving])\n if (!state.available)\n return null\n const disabled = !state.writable\n const blocked = !state.dirty || state.saving\n return (\n <li className={cx([css.card, open && css.cardOpen])}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${t(open ? 'collapse' : 'expand')}: ${t('title')}`}\n onClick={() => {\n setOpen(!open)\n }}\n >\n <span className={css.headText}>\n <span className={css.name}>{t('title')}</span>\n <span className={css.description}>{t('description')}</span>\n </span>\n {state.dirty ? <Tag tone=\"neutral\" className={css.pending}>{t('unsaved')}</Tag> : null}\n <IconChevronDownOutlineRegular className={cx([css.chevron, open && css.chevronOpen])} />\n </button>\n {open\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{t('readOnly')}</p> : null}\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label} id=\"aitoearn-environment-label\">{t('environment')}</span>\n </div>\n <div className={css.envGroup} role=\"radiogroup\" aria-labelledby=\"aitoearn-environment-label\">\n {ENVIRONMENT_OPTIONS.map(option => (\n <button\n key={option.value}\n type=\"button\"\n role=\"radio\"\n aria-checked={state.environment === option.value}\n className={cx([css.envPill, state.environment === option.value && css.envPillActive])}\n disabled={disabled}\n onClick={() => {\n props.edit('environment', option.value)\n }}\n >\n {t(option.labelKey)}\n </button>\n ))}\n </div>\n </div>\n {state.environment === 'custom'\n ? (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor=\"aitoearn-base-url\">{t('baseUrl')}</label>\n </div>\n <input\n id=\"aitoearn-base-url\"\n className={css.input}\n type=\"text\"\n value={state.baseUrl}\n placeholder=\"https://example.com\"\n disabled={disabled}\n onChange={(event) => {\n props.edit('baseUrl', event.currentTarget.value)\n }}\n />\n <p className={css.hint}>{t('baseUrlHint')}</p>\n </div>\n )\n : null}\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor=\"aitoearn-api-key\">{t('apiKey')}</label>\n <span className={css.badges}>\n <Tag tone={state.apiKeyConfigured ? 'neutral' : 'quiet'}>\n {state.apiKeyConfigured ? t('apiKeySet') : t('apiKeyUnset')}\n </Tag>\n </span>\n </div>\n <input\n id=\"aitoearn-api-key\"\n className={css.input}\n type=\"password\"\n autoComplete=\"off\"\n value={state.apiKey}\n disabled={disabled}\n onChange={(event) => {\n props.edit('apiKey', event.currentTarget.value)\n }}\n />\n <p className={css.hint}>{t('apiKeyHint')}</p>\n </div>\n <div className={css.footer}>\n {state.failed ? <p className={css.failed} role=\"status\">{t('saveFailed')}</p> : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving}\n onClick={props.discard}\n >\n {t('discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={props.save}\n >\n {t(state.saving ? 'saving' : 'save')}\n </button>\n </div>\n </div>\n )\n : null}\n </li>\n )\n}\n", "/**\n * Card styles for the Aitoearn plugin settings card. Values mirror the\n * official `PluginCard` / field styling in `@deepseek-ai/dsh-client-ui-settings-plugins`\n * so the card reads like the Bash / Agent loop / Web search cards. Everything is\n * token-styled through `--dsw-alias-*`; the sheet self-injects once, the same\n * `style[data-plugin-css]` way the official client packages do.\n */\n\nconst CARD_CSS = `\n.Ata_card{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);border-radius:16px;list-style:none;transition:border-color .16s,background .16s}\n.Ata_card:hover{border-color:var(--dsw-alias-label-dimmed)}\n.Ata_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}\n.Ata_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}\n.Ata_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}\n.Ata_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}\n.Ata_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}\n.Ata_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}\n.Ata_pending{flex:none}\n.Ata_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}\n.Ata_chevronOpen{transform:rotate(180deg)}\n.Ata_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}\n.Ata_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}\n.Ata_footer{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}\n.Ata_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}\n.Ata_discard,.Ata_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5;transition:background .16s,border-color .16s,color .16s}\n.Ata_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}\n.Ata_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}\n.Ata_discard:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}\n.Ata_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}\n.Ata_discard:disabled,.Ata_save:disabled{opacity:.4;cursor:default}\n.Ata_discard:focus-visible,.Ata_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}\n.Ata_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}\n.Ata_field+.Ata_field{border-top:.5px solid var(--dsw-alias-border-l2)}\n.Ata_head{align-items:center;gap:8px;display:flex}\n.Ata_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}\n.Ata_badges{align-items:center;gap:8px;display:inline-flex}\n.Ata_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}\n.Ata_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}\n.Ata_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}\n.Ata_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}\n.Ata_envGroup{flex-wrap:wrap;gap:8px;display:flex}\n.Ata_envPill{appearance:none;font:inherit;cursor:pointer;border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:5px 14px;font-size:13px;line-height:1.5;transition:background .16s,border-color .16s,color .16s}\n.Ata_envPill:hover:not(:disabled):not(.Ata_envPillActive){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}\n.Ata_envPill:active:not(:disabled){background:var(--dsw-alias-bg-layer-4)}\n.Ata_envPill:disabled{opacity:.4;cursor:not-allowed}\n.Ata_envPill:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}\n.Ata_envPillActive{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary)}\n`\n\nconst STYLE_TAG_ID = '@aitoearn/dsh-plugin/card.css'\n\nif (typeof document !== 'undefined' && document.querySelector(`style[data-plugin-css=\"${STYLE_TAG_ID}\"]`) === null) {\n const tag = document.createElement('style')\n tag.dataset['plugin'] = '@aitoearn/dsh-plugin'\n tag.dataset['pluginCss'] = STYLE_TAG_ID\n tag.textContent = CARD_CSS\n document.head.appendChild(tag)\n}\n\nexport const card = 'Ata_card'\nexport const cardOpen = 'Ata_cardOpen'\nexport const header = 'Ata_header'\nexport const headText = 'Ata_headText'\nexport const name = 'Ata_name'\nexport const description = 'Ata_description'\nexport const pending = 'Ata_pending'\nexport const chevron = 'Ata_chevron'\nexport const chevronOpen = 'Ata_chevronOpen'\nexport const body = 'Ata_body'\nexport const readOnly = 'Ata_readOnly'\nexport const footer = 'Ata_footer'\nexport const failed = 'Ata_failed'\nexport const discard = 'Ata_discard'\nexport const save = 'Ata_save'\nexport const field = 'Ata_field'\nexport const head = 'Ata_head'\nexport const label = 'Ata_label'\nexport const badges = 'Ata_badges'\nexport const input = 'Ata_input'\nexport const hint = 'Ata_hint'\nexport const envGroup = 'Ata_envGroup'\nexport const envPill = 'Ata_envPill'\nexport const envPillActive = 'Ata_envPillActive'\n", "import { createSnapshotStore, SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport { ConfigForm, SettingsDescribeFace } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { AitoearnEnvironment, AitoearnPluginConfig, resolveEnvironment, SETTINGS_NAMESPACE } from '../auth.js'\n\nexport type AitoearnCardField = 'environment' | 'baseUrl' | 'apiKey'\n\nexport interface AitoearnCardState {\n available: boolean\n writable: boolean\n dirty: boolean\n saving: boolean\n failed: boolean\n environment: AitoearnEnvironment\n baseUrl: string\n apiKey: string\n apiKeyConfigured: boolean\n}\n\nexport interface AitoearnCardFace {\n hooks: { aitoearnCard: SnapshotStore<AitoearnCardState> }\n edit: (field: AitoearnCardField, value: string) => void\n save: () => void\n discard: () => void\n}\n\n/** Staged edits over the card's fields; an absent field inherits the section. */\ninterface CardDraft {\n environment?: AitoearnEnvironment\n baseUrl?: string\n apiKey?: string\n}\n\ntype PendingWrite = 'environment' | 'apiKey' | 'baseUrl'\n\n/** One schema-declared secret slot in the namespace view (`SettingsSecretView`). */\ninterface SecretSlot {\n path: string[]\n set: boolean\n}\n\n/**\n * Bridges the `aitoearn` settings scope onto the card's staged form. Edits stay\n * local drafts until `save` writes them in one gesture; a save that did not land\n * keeps its drafts so the user can correct them instead of retyping.\n *\n * The API key is a schema-declared secret: the Host redacts it from every view\n * it serves, so the key can never be read back and the input starts blank. A\n * blank draft keeps the stored key, and whether one is configured comes from\n * the namespace view's secret record.\n */\nexport function createAitoearnCardFace(form: ConfigForm<AitoearnPluginConfig>, describe: SettingsDescribeFace): AitoearnCardFace {\n let draft: CardDraft = {}\n let saving = false\n let failed = false\n const store = createSnapshotStore<AitoearnCardState>(project())\n\n function stored(): AitoearnPluginConfig {\n return form.getSnapshot().value ?? {}\n }\n\n function apiKeyConfigured(): boolean {\n const view = describe.getSnapshot().view\n const section = view?.namespaces.find(candidate => candidate.ns === SETTINGS_NAMESPACE)\n return section?.secrets.some((secret: SecretSlot) => secret.path.length === 1 && secret.path[0] === 'apiKey' && secret.set) ?? false\n }\n\n function pendingWrites(): PendingWrite[] {\n const value = stored()\n const writes: PendingWrite[] = []\n const environment = draft.environment ?? resolveEnvironment(value.environment)\n if (draft.environment !== undefined && environment !== resolveEnvironment(value.environment))\n writes.push('environment')\n if (draft.apiKey !== undefined && draft.apiKey.trim() !== '')\n writes.push('apiKey')\n if (draft.baseUrl !== undefined && environment === 'custom' && draft.baseUrl !== (value.baseUrl ?? ''))\n writes.push('baseUrl')\n if (environment !== 'custom' && value.baseUrl !== undefined)\n writes.push('baseUrl')\n return writes\n }\n\n function project(): AitoearnCardState {\n const snapshot = form.getSnapshot()\n const value = stored()\n return {\n available: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: pendingWrites().length > 0,\n saving,\n failed,\n environment: draft.environment ?? resolveEnvironment(value.environment),\n baseUrl: draft.baseUrl ?? value.baseUrl ?? '',\n apiKey: draft.apiKey ?? '',\n apiKeyConfigured: apiKeyConfigured(),\n }\n }\n\n const publish = () => {\n store.set(project())\n }\n\n async function commit(): Promise<void> {\n const snapshot = form.getSnapshot()\n const writes = pendingWrites()\n if (saving || snapshot.status !== 'ready' || !snapshot.writable || writes.length === 0)\n return\n const value = stored()\n const environment = draft.environment ?? resolveEnvironment(value.environment)\n const baseUrl = draft.baseUrl ?? value.baseUrl ?? ''\n const apiKey = draft.apiKey?.trim() ?? ''\n saving = true\n failed = false\n publish()\n // A `false` answer means the Host refused or skipped the write (transport\n // failures reject instead): keep the drafts so the user can correct them.\n let accepted = true\n try {\n if (writes.includes('environment'))\n accepted = await form.set('environment', environment) && accepted\n if (writes.includes('apiKey'))\n accepted = await form.set('apiKey', apiKey) && accepted\n if (writes.includes('baseUrl')) {\n if (environment === 'custom')\n accepted = await form.set('baseUrl', baseUrl) && accepted\n else\n accepted = await form.unset('baseUrl') && accepted\n }\n }\n catch {\n // A rejected write keeps the drafts on screen (with the failure flag) so\n // the user can correct them instead of retyping.\n saving = false\n failed = true\n publish()\n return\n }\n if (!accepted) {\n saving = false\n failed = true\n publish()\n return\n }\n const landed = (writes.includes('environment') ? resolveEnvironment(stored().environment) === environment : true)\n && (writes.includes('apiKey') ? apiKeyConfigured() : true)\n && (writes.includes('baseUrl')\n ? (environment === 'custom' ? (stored().baseUrl ?? '') === baseUrl : stored().baseUrl === undefined)\n : true)\n saving = false\n if (landed)\n draft = {}\n else\n failed = true\n publish()\n }\n\n form.subscribe(publish)\n describe.subscribe(publish)\n void describe.ensure()\n\n return {\n hooks: { aitoearnCard: store },\n edit: (field, value) => {\n if (saving)\n return\n failed = false\n if (field === 'environment') {\n const environment = resolveEnvironment(value)\n draft = environment === 'custom'\n ? { ...draft, environment }\n : { ...draft, environment, baseUrl: undefined }\n }\n else {\n draft = { ...draft, [field]: value }\n }\n publish()\n },\n save: () => commit(),\n discard: () => {\n draft = {}\n failed = false\n publish()\n },\n }\n}\n", "/** Locale dictionary for the Aitoearn plugin settings card. */\n\nexport const aitoearnCardNS = 'aitoearn-card'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** The Aitoearn plugin card's copy. */\n 'aitoearn-card': keyof typeof aitoearnCardLocales.zh\n }\n}\n\nexport type AitoearnCardLocaleKey = keyof typeof aitoearnCardLocales.zh\n\nexport const aitoearnCardLocales = {\n zh: {\n title: 'Aitoearn',\n description: 'Aitoearn \u5DE5\u5177\u7684\u8FD0\u884C\u73AF\u5883\u4E0E API Key\u3002',\n environment: '\u8FD0\u884C\u73AF\u5883',\n envGlobal: '\u56FD\u9645\u7AD9 aitoearn.ai',\n envCn: '\u4E2D\u56FD\u7AD9 aitoearn.cn',\n envCustom: '\u81EA\u5B9A\u4E49',\n baseUrl: '\u63A5\u53E3\u5730\u5740',\n baseUrlHint: '\u7559\u7A7A\u5219\u4F7F\u7528\u6240\u9009\u73AF\u5883\u9ED8\u8BA4\u5730\u5740\u3002',\n apiKey: 'API Key',\n apiKeySet: '\u5DF2\u914D\u7F6E\u5BC6\u94A5\u3002',\n apiKeyUnset: '\u672A\u914D\u7F6E\u5BC6\u94A5\uFF1B\u914D\u7F6E\u4E4B\u524D Aitoearn \u5DE5\u5177\u4E0D\u53EF\u7528\u3002',\n apiKeyHint: '\u51FA\u4E8E\u5B89\u5168\u8003\u8651\u5BC6\u94A5\u4E0D\u4F1A\u56DE\u663E\uFF1B\u7559\u7A7A\u8868\u793A\u4FDD\u6301\u5F53\u524D\u5BC6\u94A5\uFF0C\u8F93\u5165\u65B0\u503C\u5C06\u8986\u76D6\u3002',\n expand: '\u5C55\u5F00\u8BBE\u7F6E',\n collapse: '\u6536\u8D77\u8BBE\u7F6E',\n save: '\u4FDD\u5B58',\n saving: '\u4FDD\u5B58\u4E2D\u2026',\n discard: '\u653E\u5F03\u4FEE\u6539',\n unsaved: '\u672A\u4FDD\u5B58',\n saveFailed: '\u672C\u90E8\u7F72\u6CA1\u6709\u63A5\u53D7\u8FD9\u4E9B\u503C\uFF0C\u5DF2\u4FDD\u7559\u4F9B\u4F60\u4FEE\u6539\u3002',\n readOnly: '\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002',\n },\n en: {\n title: 'Aitoearn',\n description: 'Environment and API key for Aitoearn tools.',\n environment: 'Environment',\n envGlobal: 'Global (aitoearn.ai)',\n envCn: 'China (aitoearn.cn)',\n envCustom: 'Custom',\n baseUrl: 'Base URL',\n baseUrlHint: 'Leave blank to use the selected environment default.',\n apiKey: 'API key',\n apiKeySet: 'A key is configured.',\n apiKeyUnset: 'No key is configured; Aitoearn tools are unavailable until one is.',\n apiKeyHint: 'The key is never displayed back; leave blank to keep the current key, or type a new one to overwrite it.',\n expand: 'Show settings',\n collapse: 'Hide settings',\n save: 'Save',\n saving: 'Saving\u2026',\n discard: 'Discard',\n unsaved: 'Unsaved',\n saveFailed: 'The deployment did not accept these values; they were left for you to correct.',\n readOnly: 'This deployment stores settings read-only.',\n },\n}\n"],
4
+ "sourcesContent": ["import { Context } from '@deepseek-ai/cordis'\nimport { AitoearnPluginConfig, SETTINGS_NAMESPACE } from '../auth.js'\nimport { AitoearnCard } from './AitoearnCard.js'\nimport { createAitoearnCardFace } from './card-store.js'\nimport { aitoearnCardLocales, aitoearnCardNS } from './i18n.js'\n\nexport const name = 'aitoearn-settings-card'\nexport const inject = ['slots', 'locale', 'configForms']\n\nexport function apply(ctx: Context): void {\n ctx.effect(\n () => ctx.locale.register(aitoearnCardNS, aitoearnCardLocales),\n 'aitoearn-settings-card: dictionaries',\n )\n const t = ctx.locale.bind(aitoearnCardNS)\n ctx.effect(() => ctx.configForms.whileServed([SETTINGS_NAMESPACE], () => {\n const form = ctx.configForms.get<AitoearnPluginConfig>(SETTINGS_NAMESPACE)\n const face = createAitoearnCardFace(form, ctx.configForms.describe())\n return ctx.slots.inject('settings.plugins.tab', () =>\n ctx.slots.register(\n {\n name: 'settings.plugins.tab',\n id: 'aitoearn',\n order: 10,\n label: () => t('title'),\n locale: aitoearnCardNS,\n inject: () => face,\n },\n AitoearnCard,\n ))\n }), 'aitoearn-settings-card: tab')\n}\n", "export const MCP_PATH = '/api/ai/agent/mcp'\nexport const API_KEY_ENV = 'AITOEARN_API_KEY'\nexport const DEFAULT_ENVIRONMENT = 'global'\nexport const SETTINGS_NAMESPACE = 'aitoearn'\n\nexport const ENVIRONMENT_ORIGINS = {\n global: 'https://aitoearn.ai',\n cn: 'https://aitoearn.cn',\n} as const\n\nexport const DEFAULT_MCP_URL = `${ENVIRONMENT_ORIGINS.global}${MCP_PATH}`\n\nexport type AitoearnEnvironment = 'global' | 'cn' | 'custom'\n\nexport interface AitoearnAuth {\n mcpUrl: string\n apiKey: string\n}\n\nexport interface AitoearnPluginConfig {\n environment?: string\n baseUrl?: string\n apiKey?: string\n}\n\nexport function pluginConfigFromSettings(\n section: unknown,\n fallback: AitoearnPluginConfig = {},\n): AitoearnPluginConfig {\n if (section === null || typeof section !== 'object' || Array.isArray(section))\n return fallback\n const record = section as Record<string, unknown>\n return {\n ...fallback,\n ...(typeof record['environment'] === 'string' ? { environment: record['environment'] } : {}),\n ...(typeof record['baseUrl'] === 'string' ? { baseUrl: record['baseUrl'] } : {}),\n ...(typeof record['apiKey'] === 'string' ? { apiKey: record['apiKey'] } : {}),\n }\n}\n\nexport function resolveEnvironment(value: string | undefined): AitoearnEnvironment {\n if (value === 'cn' || value === 'custom' || value === 'global')\n return value\n return DEFAULT_ENVIRONMENT\n}\n\n/**\n * Normalize a custom endpoint. A non-empty path names the MCP endpoint itself:\n * a deployment may mount the same controller under another prefix, and that\n * full URL cannot be rebuilt from an origin plus {@link MCP_PATH}. A bare\n * origin still falls back to the default path.\n */\nexport function resolveCustomMcpUrl(baseUrl?: string): string {\n const trimmed = baseUrl?.trim() ?? ''\n if (trimmed.length === 0)\n return ''\n let url: URL\n try {\n url = new URL(trimmed)\n }\n catch {\n return ''\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n return ''\n const path = url.pathname.replace(/\\/+$/, '')\n return path.length === 0 ? `${url.origin}${MCP_PATH}` : `${url.origin}${path}`\n}\n\n/** Resolve the origin, or the full custom endpoint, for one environment. */\nexport function resolveOrigin(environment: AitoearnEnvironment, baseUrl?: string): string {\n if (environment === 'custom')\n return resolveCustomMcpUrl(baseUrl)\n return ENVIRONMENT_ORIGINS[environment]\n}\n\nexport function resolveMcpUrl(config: AitoearnPluginConfig): string {\n const environment = resolveEnvironment(config.environment)\n if (environment === 'custom')\n return resolveCustomMcpUrl(config.baseUrl)\n return `${ENVIRONMENT_ORIGINS[environment]}${MCP_PATH}`\n}\n\nexport function resolveAuth(\n config: AitoearnPluginConfig,\n env: NodeJS.ProcessEnv = process.env,\n): AitoearnAuth {\n return {\n mcpUrl: resolveMcpUrl(config),\n apiKey: config.apiKey?.trim() || env[API_KEY_ENV]?.trim() || '',\n }\n}\n\nexport function authorizationHeader(apiKey: string): Record<string, string> {\n if (apiKey.length === 0)\n return {}\n return { Authorization: `Bearer ${apiKey}` }\n}\n", "import { IconChevronDownOutlineRegular, Tag } from '@deepseek-ai/dsh-client-ui-primitives'\nimport { SnapshotSelectorHook, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport { useEffect, useRef, useState } from 'react'\nimport { AitoearnEnvironment } from '../auth.js'\nimport { AitoearnCardState } from './card-store.js'\nimport * as css from './card.css.js'\nimport { AitoearnCardLocaleKey } from './i18n.js'\n\nexport interface AitoearnCardProps {\n t: TranslateNS<'aitoearn-card'>\n useAitoearnCard: SnapshotSelectorHook<AitoearnCardState>\n edit: (field: 'environment' | 'baseUrl' | 'apiKey', value: string) => void\n save: () => void\n discard: () => void\n}\n\nconst cx = (names: Array<string | false | undefined>): string => names.filter(Boolean).join(' ')\n\nconst ENVIRONMENT_OPTIONS: Array<{ value: AitoearnEnvironment, labelKey: AitoearnCardLocaleKey }> = [\n { value: 'global', labelKey: 'envGlobal' },\n { value: 'cn', labelKey: 'envCn' },\n { value: 'custom', labelKey: 'envCustom' },\n]\n\n/**\n * The Aitoearn plugin settings card, laid out after the official plugin cards:\n * a header naming the plugin, its fields in place, and the footer that writes\n * the staged edits.\n */\nexport function AitoearnCard(props: AitoearnCardProps) {\n const { t } = props\n const state = props.useAitoearnCard(snapshot => snapshot)\n const [open, setOpen] = useState(false)\n const saveStarted = useRef(false)\n useEffect(() => {\n if (state.saving) {\n saveStarted.current = true\n return\n }\n if (!saveStarted.current)\n return\n saveStarted.current = false\n if (!state.dirty && !state.failed)\n setOpen(false)\n }, [state.dirty, state.failed, state.saving])\n if (!state.available)\n return null\n const disabled = !state.writable\n const blocked = !state.dirty || state.saving\n return (\n <li className={cx([css.card, open && css.cardOpen])}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n aria-label={`${t(open ? 'collapse' : 'expand')}: ${t('title')}`}\n onClick={() => {\n setOpen(!open)\n }}\n >\n <span className={css.headText}>\n <span className={css.name}>{t('title')}</span>\n <span className={css.description}>{t('description')}</span>\n </span>\n {state.dirty ? <Tag tone=\"neutral\" className={css.pending}>{t('unsaved')}</Tag> : null}\n <IconChevronDownOutlineRegular className={cx([css.chevron, open && css.chevronOpen])} />\n </button>\n {open\n ? (\n <div className={css.body}>\n {!state.writable ? <p className={css.readOnly} role=\"status\">{t('readOnly')}</p> : null}\n <div className={css.field}>\n <div className={css.head}>\n <span className={css.label} id=\"aitoearn-environment-label\">{t('environment')}</span>\n </div>\n <div className={css.envGroup} role=\"radiogroup\" aria-labelledby=\"aitoearn-environment-label\">\n {ENVIRONMENT_OPTIONS.map(option => (\n <button\n key={option.value}\n type=\"button\"\n role=\"radio\"\n aria-checked={state.environment === option.value}\n className={cx([css.envPill, state.environment === option.value && css.envPillActive])}\n disabled={disabled}\n onClick={() => {\n props.edit('environment', option.value)\n }}\n >\n {t(option.labelKey)}\n </button>\n ))}\n </div>\n </div>\n {state.environment === 'custom'\n ? (\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor=\"aitoearn-base-url\">{t('baseUrl')}</label>\n </div>\n <input\n id=\"aitoearn-base-url\"\n className={css.input}\n type=\"text\"\n value={state.baseUrl}\n placeholder=\"https://example.com\"\n disabled={disabled}\n onChange={(event) => {\n props.edit('baseUrl', event.currentTarget.value)\n }}\n />\n <p className={css.hint}>{t('baseUrlHint')}</p>\n </div>\n )\n : null}\n <div className={css.field}>\n <div className={css.head}>\n <label className={css.label} htmlFor=\"aitoearn-api-key\">{t('apiKey')}</label>\n <span className={css.badges}>\n <Tag tone={state.apiKeyConfigured ? 'neutral' : 'quiet'}>\n {state.apiKeyConfigured ? t('apiKeySet') : t('apiKeyUnset')}\n </Tag>\n </span>\n </div>\n <input\n id=\"aitoearn-api-key\"\n className={css.input}\n type=\"password\"\n autoComplete=\"off\"\n value={state.apiKey}\n disabled={disabled}\n onChange={(event) => {\n props.edit('apiKey', event.currentTarget.value)\n }}\n />\n <p className={css.hint}>{t('apiKeyHint')}</p>\n </div>\n <div className={css.footer}>\n {state.failed ? <p className={css.failed} role=\"status\">{t('saveFailed')}</p> : null}\n <button\n type=\"button\"\n className={css.discard}\n disabled={!state.dirty || state.saving}\n onClick={props.discard}\n >\n {t('discard')}\n </button>\n <button\n type=\"button\"\n className={css.save}\n disabled={blocked}\n onClick={props.save}\n >\n {t(state.saving ? 'saving' : 'save')}\n </button>\n </div>\n </div>\n )\n : null}\n </li>\n )\n}\n", "/**\n * Card styles for the Aitoearn plugin settings card. Values mirror the\n * official `PluginCard` / field styling in `@deepseek-ai/dsh-client-ui-settings-plugins`\n * so the card reads like the Bash / Agent loop / Web search cards. Everything is\n * token-styled through `--dsw-alias-*`; the sheet self-injects once, the same\n * `style[data-plugin-css]` way the official client packages do.\n */\n\nconst CARD_CSS = `\n.Ata_card{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);border-radius:16px;list-style:none;transition:border-color .16s,background .16s}\n.Ata_card:hover{border-color:var(--dsw-alias-label-dimmed)}\n.Ata_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}\n.Ata_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}\n.Ata_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}\n.Ata_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}\n.Ata_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}\n.Ata_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}\n.Ata_pending{flex:none}\n.Ata_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}\n.Ata_chevronOpen{transform:rotate(180deg)}\n.Ata_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}\n.Ata_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}\n.Ata_footer{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}\n.Ata_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}\n.Ata_discard,.Ata_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5;transition:background .16s,border-color .16s,color .16s}\n.Ata_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}\n.Ata_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}\n.Ata_discard:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}\n.Ata_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}\n.Ata_discard:disabled,.Ata_save:disabled{opacity:.4;cursor:default}\n.Ata_discard:focus-visible,.Ata_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}\n.Ata_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}\n.Ata_field+.Ata_field{border-top:.5px solid var(--dsw-alias-border-l2)}\n.Ata_head{align-items:center;gap:8px;display:flex}\n.Ata_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}\n.Ata_badges{align-items:center;gap:8px;display:inline-flex}\n.Ata_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}\n.Ata_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}\n.Ata_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}\n.Ata_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}\n.Ata_envGroup{flex-wrap:wrap;gap:8px;display:flex}\n.Ata_envPill{appearance:none;font:inherit;cursor:pointer;border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:5px 14px;font-size:13px;line-height:1.5;transition:background .16s,border-color .16s,color .16s}\n.Ata_envPill:hover:not(:disabled):not(.Ata_envPillActive){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}\n.Ata_envPill:active:not(:disabled){background:var(--dsw-alias-bg-layer-4)}\n.Ata_envPill:disabled{opacity:.4;cursor:not-allowed}\n.Ata_envPill:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}\n.Ata_envPillActive{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary)}\n`\n\nconst STYLE_TAG_ID = '@aitoearn/dsh-plugin/card.css'\n\nif (typeof document !== 'undefined' && document.querySelector(`style[data-plugin-css=\"${STYLE_TAG_ID}\"]`) === null) {\n const tag = document.createElement('style')\n tag.dataset['plugin'] = '@aitoearn/dsh-plugin'\n tag.dataset['pluginCss'] = STYLE_TAG_ID\n tag.textContent = CARD_CSS\n document.head.appendChild(tag)\n}\n\nexport const card = 'Ata_card'\nexport const cardOpen = 'Ata_cardOpen'\nexport const header = 'Ata_header'\nexport const headText = 'Ata_headText'\nexport const name = 'Ata_name'\nexport const description = 'Ata_description'\nexport const pending = 'Ata_pending'\nexport const chevron = 'Ata_chevron'\nexport const chevronOpen = 'Ata_chevronOpen'\nexport const body = 'Ata_body'\nexport const readOnly = 'Ata_readOnly'\nexport const footer = 'Ata_footer'\nexport const failed = 'Ata_failed'\nexport const discard = 'Ata_discard'\nexport const save = 'Ata_save'\nexport const field = 'Ata_field'\nexport const head = 'Ata_head'\nexport const label = 'Ata_label'\nexport const badges = 'Ata_badges'\nexport const input = 'Ata_input'\nexport const hint = 'Ata_hint'\nexport const envGroup = 'Ata_envGroup'\nexport const envPill = 'Ata_envPill'\nexport const envPillActive = 'Ata_envPillActive'\n", "import { createSnapshotStore, SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport { ConfigForm, SettingsDescribeFace } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { AitoearnEnvironment, AitoearnPluginConfig, resolveEnvironment, SETTINGS_NAMESPACE } from '../auth.js'\n\nexport type AitoearnCardField = 'environment' | 'baseUrl' | 'apiKey'\n\nexport interface AitoearnCardState {\n available: boolean\n writable: boolean\n dirty: boolean\n saving: boolean\n failed: boolean\n environment: AitoearnEnvironment\n baseUrl: string\n apiKey: string\n apiKeyConfigured: boolean\n}\n\nexport interface AitoearnCardFace {\n hooks: { aitoearnCard: SnapshotStore<AitoearnCardState> }\n edit: (field: AitoearnCardField, value: string) => void\n save: () => void\n discard: () => void\n}\n\n/** Staged edits over the card's fields; an absent field inherits the section. */\ninterface CardDraft {\n environment?: AitoearnEnvironment\n baseUrl?: string\n apiKey?: string\n}\n\ntype PendingWrite = 'environment' | 'apiKey' | 'baseUrl'\n\n/** One schema-declared secret slot in the namespace view (`SettingsSecretView`). */\ninterface SecretSlot {\n path: string[]\n set: boolean\n}\n\n/**\n * Bridges the `aitoearn` settings scope onto the card's staged form. Edits stay\n * local drafts until `save` writes them in one gesture; a save that did not land\n * keeps its drafts so the user can correct them instead of retyping.\n *\n * The API key is a schema-declared secret: the Host redacts it from every view\n * it serves, so the key can never be read back and the input starts blank. A\n * blank draft keeps the stored key, and whether one is configured comes from\n * the namespace view's secret record.\n */\nexport function createAitoearnCardFace(form: ConfigForm<AitoearnPluginConfig>, describe: SettingsDescribeFace): AitoearnCardFace {\n let draft: CardDraft = {}\n let saving = false\n let failed = false\n const store = createSnapshotStore<AitoearnCardState>(project())\n\n function stored(): AitoearnPluginConfig {\n return form.getSnapshot().value ?? {}\n }\n\n function apiKeyConfigured(): boolean {\n const view = describe.getSnapshot().view\n const section = view?.namespaces.find(candidate => candidate.ns === SETTINGS_NAMESPACE)\n return section?.secrets.some((secret: SecretSlot) => secret.path.length === 1 && secret.path[0] === 'apiKey' && secret.set) ?? false\n }\n\n function pendingWrites(): PendingWrite[] {\n const value = stored()\n const writes: PendingWrite[] = []\n const environment = draft.environment ?? resolveEnvironment(value.environment)\n if (draft.environment !== undefined && environment !== resolveEnvironment(value.environment))\n writes.push('environment')\n if (draft.apiKey !== undefined && draft.apiKey.trim() !== '')\n writes.push('apiKey')\n if (draft.baseUrl !== undefined && environment === 'custom' && draft.baseUrl !== (value.baseUrl ?? ''))\n writes.push('baseUrl')\n if (environment !== 'custom' && value.baseUrl !== undefined)\n writes.push('baseUrl')\n return writes\n }\n\n function project(): AitoearnCardState {\n const snapshot = form.getSnapshot()\n const value = stored()\n return {\n available: snapshot.status === 'ready',\n writable: snapshot.writable,\n dirty: pendingWrites().length > 0,\n saving,\n failed,\n environment: draft.environment ?? resolveEnvironment(value.environment),\n baseUrl: draft.baseUrl ?? value.baseUrl ?? '',\n apiKey: draft.apiKey ?? '',\n apiKeyConfigured: apiKeyConfigured(),\n }\n }\n\n const publish = () => {\n store.set(project())\n }\n\n async function commit(): Promise<void> {\n const snapshot = form.getSnapshot()\n const writes = pendingWrites()\n if (saving || snapshot.status !== 'ready' || !snapshot.writable || writes.length === 0)\n return\n const value = stored()\n const environment = draft.environment ?? resolveEnvironment(value.environment)\n const baseUrl = draft.baseUrl ?? value.baseUrl ?? ''\n const apiKey = draft.apiKey?.trim() ?? ''\n saving = true\n failed = false\n publish()\n // A `false` answer means the Host refused or skipped the write (transport\n // failures reject instead): keep the drafts so the user can correct them.\n let accepted = true\n try {\n if (writes.includes('environment'))\n accepted = await form.set('environment', environment) && accepted\n if (writes.includes('apiKey'))\n accepted = await form.set('apiKey', apiKey) && accepted\n if (writes.includes('baseUrl')) {\n if (environment === 'custom')\n accepted = await form.set('baseUrl', baseUrl) && accepted\n else\n accepted = await form.unset('baseUrl') && accepted\n }\n }\n catch {\n // A rejected write keeps the drafts on screen (with the failure flag) so\n // the user can correct them instead of retyping.\n saving = false\n failed = true\n publish()\n return\n }\n if (!accepted) {\n saving = false\n failed = true\n publish()\n return\n }\n const landed = (writes.includes('environment') ? resolveEnvironment(stored().environment) === environment : true)\n && (writes.includes('apiKey') ? apiKeyConfigured() : true)\n && (writes.includes('baseUrl')\n ? (environment === 'custom' ? (stored().baseUrl ?? '') === baseUrl : stored().baseUrl === undefined)\n : true)\n saving = false\n if (landed)\n draft = {}\n else\n failed = true\n publish()\n }\n\n form.subscribe(publish)\n describe.subscribe(publish)\n void describe.ensure()\n\n return {\n hooks: { aitoearnCard: store },\n edit: (field, value) => {\n if (saving)\n return\n failed = false\n if (field === 'environment') {\n const environment = resolveEnvironment(value)\n draft = environment === 'custom'\n ? { ...draft, environment }\n : { ...draft, environment, baseUrl: undefined }\n }\n else {\n draft = { ...draft, [field]: value }\n }\n publish()\n },\n save: () => commit(),\n discard: () => {\n draft = {}\n failed = false\n publish()\n },\n }\n}\n", "/** Locale dictionary for the Aitoearn plugin settings card. */\n\nexport const aitoearnCardNS = 'aitoearn-card'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** The Aitoearn plugin card's copy. */\n 'aitoearn-card': keyof typeof aitoearnCardLocales.zh\n }\n}\n\nexport type AitoearnCardLocaleKey = keyof typeof aitoearnCardLocales.zh\n\nexport const aitoearnCardLocales = {\n zh: {\n title: 'Aitoearn',\n description: 'Aitoearn \u5DE5\u5177\u7684\u8FD0\u884C\u73AF\u5883\u4E0E API Key\u3002',\n environment: '\u8FD0\u884C\u73AF\u5883',\n envGlobal: '\u56FD\u9645\u7AD9 aitoearn.ai',\n envCn: '\u4E2D\u56FD\u7AD9 aitoearn.cn',\n envCustom: '\u81EA\u5B9A\u4E49',\n baseUrl: '\u63A5\u53E3\u5730\u5740',\n baseUrlHint: '\u7559\u7A7A\u5219\u4F7F\u7528\u6240\u9009\u73AF\u5883\u9ED8\u8BA4\u5730\u5740\uFF1B\u586B\u5199\u5B8C\u6574 MCP \u7AEF\u70B9\uFF08\u542B\u8DEF\u5F84\uFF09\u65F6\u6309\u539F\u6837\u4F7F\u7528\u3002',\n apiKey: 'API Key',\n apiKeySet: '\u5DF2\u914D\u7F6E\u5BC6\u94A5\u3002',\n apiKeyUnset: '\u672A\u914D\u7F6E\u5BC6\u94A5\uFF1B\u914D\u7F6E\u4E4B\u524D Aitoearn \u5DE5\u5177\u4E0D\u53EF\u7528\u3002',\n apiKeyHint: '\u51FA\u4E8E\u5B89\u5168\u8003\u8651\u5BC6\u94A5\u4E0D\u4F1A\u56DE\u663E\uFF1B\u7559\u7A7A\u8868\u793A\u4FDD\u6301\u5F53\u524D\u5BC6\u94A5\uFF0C\u8F93\u5165\u65B0\u503C\u5C06\u8986\u76D6\u3002',\n expand: '\u5C55\u5F00\u8BBE\u7F6E',\n collapse: '\u6536\u8D77\u8BBE\u7F6E',\n save: '\u4FDD\u5B58',\n saving: '\u4FDD\u5B58\u4E2D\u2026',\n discard: '\u653E\u5F03\u4FEE\u6539',\n unsaved: '\u672A\u4FDD\u5B58',\n saveFailed: '\u672C\u90E8\u7F72\u6CA1\u6709\u63A5\u53D7\u8FD9\u4E9B\u503C\uFF0C\u5DF2\u4FDD\u7559\u4F9B\u4F60\u4FEE\u6539\u3002',\n readOnly: '\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002',\n },\n en: {\n title: 'Aitoearn',\n description: 'Environment and API key for Aitoearn tools.',\n environment: 'Environment',\n envGlobal: 'Global (aitoearn.ai)',\n envCn: 'China (aitoearn.cn)',\n envCustom: 'Custom',\n baseUrl: 'Base URL',\n baseUrlHint: 'Leave blank for the selected environment default; a full MCP endpoint (with path) is used as-is.',\n apiKey: 'API key',\n apiKeySet: 'A key is configured.',\n apiKeyUnset: 'No key is configured; Aitoearn tools are unavailable until one is.',\n apiKeyHint: 'The key is never displayed back; leave blank to keep the current key, or type a new one to overwrite it.',\n expand: 'Show settings',\n collapse: 'Hide settings',\n save: 'Save',\n saving: 'Saving\u2026',\n discard: 'Discard',\n unsaved: 'Unsaved',\n saveFailed: 'The deployment did not accept these values; they were left for you to correct.',\n readOnly: 'This deployment stores settings read-only.',\n },\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["name", "failed", "field", "name"]
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitoearn-dsh-plugin-test",
3
3
  "type": "module",
4
- "version": "0.0.4",
4
+ "version": "0.0.5",
5
5
  "description": "Aitoearn agent preset, skills, and MCP tools for DeepSeek Harness",
6
6
  "exports": {
7
7
  ".": {
@@ -151,5 +151,5 @@
151
151
  "react": "^19.1.1",
152
152
  "zustand": "~4.4.7"
153
153
  },
154
- "module": "./tools/agent-plugin/src/dsh/index.js"
155
- }
154
+ "module": "./src/dsh/index.js"
155
+ }
@@ -27,27 +27,26 @@ function resolveEnvironment(value) {
27
27
  return value;
28
28
  return DEFAULT_ENVIRONMENT;
29
29
  }
30
- function resolveOrigin(environment, baseUrl) {
31
- if (environment !== "custom")
32
- return ENVIRONMENT_ORIGINS[environment];
30
+ function resolveCustomMcpUrl(baseUrl) {
33
31
  const trimmed = baseUrl?.trim() ?? "";
34
32
  if (trimmed.length === 0)
35
33
  return "";
34
+ let url;
36
35
  try {
37
- const url = new URL(trimmed);
38
- if (url.protocol !== "http:" && url.protocol !== "https:")
39
- return "";
40
- return url.origin;
36
+ url = new URL(trimmed);
41
37
  } catch {
42
38
  return "";
43
39
  }
40
+ if (url.protocol !== "http:" && url.protocol !== "https:")
41
+ return "";
42
+ const path = url.pathname.replace(/\/+$/, "");
43
+ return path.length === 0 ? `${url.origin}${MCP_PATH}` : `${url.origin}${path}`;
44
44
  }
45
45
  function resolveMcpUrl(config) {
46
46
  const environment = resolveEnvironment(config.environment);
47
- const origin = resolveOrigin(environment, config.baseUrl);
48
- if (origin.length === 0)
49
- return "";
50
- return `${origin}${MCP_PATH}`;
47
+ if (environment === "custom")
48
+ return resolveCustomMcpUrl(config.baseUrl);
49
+ return `${ENVIRONMENT_ORIGINS[environment]}${MCP_PATH}`;
51
50
  }
52
51
  function resolveAuth(config, env = process.env) {
53
52
  return {
@@ -78,19 +77,29 @@ function toRawPublicName(publicName, serverName = MCP_SERVER_NAME) {
78
77
  return { modelName: publicName, stripped: false };
79
78
  return { modelName: raw, stripped: true };
80
79
  }
80
+ function registerOwner(tools) {
81
+ let current = tools;
82
+ while (current !== null && !Object.hasOwn(current, "register"))
83
+ current = Object.getPrototypeOf(current);
84
+ return current ?? void 0;
85
+ }
81
86
  function patchRawToolNames(tools, serverName = MCP_SERVER_NAME, onKeepQualified) {
82
- const original = tools.register.bind(tools);
83
- tools.register = (definition) => {
87
+ const owner = registerOwner(tools);
88
+ if (owner === void 0)
89
+ return () => {
90
+ };
91
+ const original = owner.register;
92
+ owner.register = function(definition) {
84
93
  const next = toRawPublicName(definition.name, serverName);
85
94
  if (!next.stripped) {
86
95
  if (definition.name.startsWith(qualifiedPrefix(serverName)))
87
96
  onKeepQualified?.(definition.name);
88
- return original(definition);
97
+ return original.call(this, definition);
89
98
  }
90
- return original({ ...definition, name: next.modelName });
99
+ return original.call(this, { ...definition, name: next.modelName });
91
100
  };
92
101
  return () => {
93
- tools.register = original;
102
+ owner.register = original;
94
103
  };
95
104
  }
96
105
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../tools/agent-plugin/src/dsh/mcp.ts", "../../../../../../../tools/agent-plugin/src/dsh/auth.ts", "../../../../../../../tools/agent-plugin/src/dsh/tool-name.ts"],
4
- "sourcesContent": ["import { createRequire } from 'node:module'\nimport { Context, Fiber } from '@deepseek-ai/cordis'\nimport { StreamableHttpConfig } from '@deepseek-ai/dsh-mcp-client'\nimport {} from '@deepseek-ai/dsh-settings'\nimport {} from '@deepseek-ai/dsh-settings/types'\nimport {} from '@deepseek-ai/dsh-tools'\nimport { AitoearnPluginConfig, authorizationHeader, pluginConfigFromSettings, resolveAuth, SETTINGS_NAMESPACE } from './auth.js'\nimport { MCP_SERVER_NAME, patchRawToolNames } from './tool-name.js'\n\nconst runtimeRequire = createRequire(import.meta.url)\n\nexport const name = 'aitoearn-mcp'\nexport const inject = ['tools']\n\nexport async function apply(ctx: Context, config: AitoearnPluginConfig = {}): Promise<void> {\n let current = () => config\n let restoreNames: (() => void) | undefined\n let fiber: Fiber | undefined\n let generation = 0\n let mountChain: Promise<void> = Promise.resolve()\n let remountTimer: ReturnType<typeof setTimeout> | undefined\n let mountedFingerprint: string | undefined\n\n const unmountAsync = async () => {\n restoreNames?.()\n restoreNames = undefined\n const previous = fiber\n fiber = undefined\n if (previous) {\n try {\n await previous.dispose()\n }\n catch (error) {\n ctx.logger.warn(error, 'aitoearn-mcp: failed to dispose previous MCP client')\n }\n }\n }\n\n const doMount = async (gen: number) => {\n const auth = resolveAuth(current())\n if (auth.apiKey.length === 0 || auth.mcpUrl.length === 0) {\n await unmountAsync()\n mountedFingerprint = undefined\n ctx.logger.warn('aitoearn-mcp: skip mounting; API key or MCP URL is not configured yet')\n return\n }\n const fingerprint = `${auth.mcpUrl}\\n${auth.apiKey}`\n if (fingerprint === mountedFingerprint && fiber) {\n return\n }\n await unmountAsync()\n if (gen !== generation) {\n return\n }\n restoreNames = patchRawToolNames(ctx.tools, MCP_SERVER_NAME, (publicName) => {\n ctx.logger.info(`kept qualified MCP tool name ${publicName}`)\n })\n try {\n const mcpClient: typeof import('@deepseek-ai/dsh-mcp-client') = runtimeRequire('@deepseek-ai/dsh-mcp-client')\n const mcpConfig: StreamableHttpConfig = {\n serverName: MCP_SERVER_NAME,\n transport: 'streamable-http',\n url: auth.mcpUrl,\n headers: authorizationHeader(auth.apiKey),\n failOnStartupError: false,\n toolCallTimeoutMs: 120000,\n }\n const next = ctx.plugin(mcpClient, mcpConfig)\n fiber = next\n await next\n }\n catch (error) {\n restoreNames?.()\n restoreNames = undefined\n fiber = undefined\n ctx.logger.warn(error, 'aitoearn-mcp: mount failed; will retry on next settings change')\n return\n }\n if (gen !== generation) {\n await unmountAsync()\n mountedFingerprint = undefined\n return\n }\n mountedFingerprint = fingerprint\n ctx.logger.info(`aitoearn-mcp: mounted MCP tools from ${auth.mcpUrl}`)\n }\n\n const scheduleMount = () => {\n generation += 1\n const gen = generation\n mountChain = mountChain\n .then(() => doMount(gen))\n .catch((error: Error) => {\n ctx.logger.warn(error, 'aitoearn-mcp: mount failed')\n })\n return mountChain\n }\n\n const scheduleRemountSoon = () => {\n if (remountTimer) {\n clearTimeout(remountTimer)\n }\n remountTimer = setTimeout(() => {\n remountTimer = undefined\n void scheduleMount()\n }, 50)\n }\n\n ctx.inject(['settings'], (scoped) => {\n // Host `describe()` serves the live value unredacted by default, so the\n // preset can read the stored key; remote callers get the redacted view.\n const readSection = (): AitoearnPluginConfig => {\n const descriptor = scoped.settings.describe().find(candidate => String(candidate.ns) === SETTINGS_NAMESPACE)\n return pluginConfigFromSettings(descriptor?.value, config)\n }\n current = readSection\n ctx.on('settings/document-updated', (namespace) => {\n if (String(namespace) === SETTINGS_NAMESPACE) {\n scheduleRemountSoon()\n }\n })\n // The initial apply-time mount may run before the settings section is\n // readable; mount again once it is, so a stored key takes effect without\n // requiring a manual settings save.\n void scheduleMount()\n })\n\n ctx.effect(() => () => {\n if (remountTimer) {\n clearTimeout(remountTimer)\n remountTimer = undefined\n }\n void unmountAsync()\n }, 'aitoearn-mcp.client')\n await scheduleMount()\n}\n", "export const MCP_PATH = '/api/ai/agent/mcp'\nexport const API_KEY_ENV = 'AITOEARN_API_KEY'\nexport const DEFAULT_ENVIRONMENT = 'global'\nexport const SETTINGS_NAMESPACE = 'aitoearn'\n\nexport const ENVIRONMENT_ORIGINS = {\n global: 'https://aitoearn.ai',\n cn: 'https://aitoearn.cn',\n} as const\n\nexport const DEFAULT_MCP_URL = `${ENVIRONMENT_ORIGINS.global}${MCP_PATH}`\n\nexport type AitoearnEnvironment = 'global' | 'cn' | 'custom'\n\nexport interface AitoearnAuth {\n mcpUrl: string\n apiKey: string\n}\n\nexport interface AitoearnPluginConfig {\n environment?: string\n baseUrl?: string\n apiKey?: string\n}\n\nexport function pluginConfigFromSettings(\n section: unknown,\n fallback: AitoearnPluginConfig = {},\n): AitoearnPluginConfig {\n if (section === null || typeof section !== 'object' || Array.isArray(section))\n return fallback\n const record = section as Record<string, unknown>\n return {\n ...fallback,\n ...(typeof record['environment'] === 'string' ? { environment: record['environment'] } : {}),\n ...(typeof record['baseUrl'] === 'string' ? { baseUrl: record['baseUrl'] } : {}),\n ...(typeof record['apiKey'] === 'string' ? { apiKey: record['apiKey'] } : {}),\n }\n}\n\nexport function resolveEnvironment(value: string | undefined): AitoearnEnvironment {\n if (value === 'cn' || value === 'custom' || value === 'global')\n return value\n return DEFAULT_ENVIRONMENT\n}\n\nexport function resolveOrigin(environment: AitoearnEnvironment, baseUrl?: string): string {\n if (environment !== 'custom')\n return ENVIRONMENT_ORIGINS[environment]\n const trimmed = baseUrl?.trim() ?? ''\n if (trimmed.length === 0)\n return ''\n try {\n const url = new URL(trimmed)\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n return ''\n return url.origin\n }\n catch {\n return ''\n }\n}\n\nexport function resolveMcpUrl(config: AitoearnPluginConfig): string {\n const environment = resolveEnvironment(config.environment)\n const origin = resolveOrigin(environment, config.baseUrl)\n if (origin.length === 0)\n return ''\n return `${origin}${MCP_PATH}`\n}\n\nexport function resolveAuth(\n config: AitoearnPluginConfig,\n env: NodeJS.ProcessEnv = process.env,\n): AitoearnAuth {\n return {\n mcpUrl: resolveMcpUrl(config),\n apiKey: config.apiKey?.trim() || env[API_KEY_ENV]?.trim() || '',\n }\n}\n\nexport function authorizationHeader(apiKey: string): Record<string, string> {\n if (apiKey.length === 0)\n return {}\n return { Authorization: `Bearer ${apiKey}` }\n}\n", "import { ToolRuntime } from '@deepseek-ai/dsh-tools'\n\nexport const MCP_SERVER_NAME = 'aitoearn'\n\nconst HASHED_PUBLIC_NAME = /_[0-9a-f]{12}$/\n\nexport interface RawPublicName {\n modelName: string\n stripped: boolean\n}\n\nexport function qualifiedPrefix(serverName = MCP_SERVER_NAME): string {\n return `mcp__${serverName}__`\n}\n\nexport function toRawPublicName(publicName: string, serverName = MCP_SERVER_NAME): RawPublicName {\n const prefix = qualifiedPrefix(serverName)\n if (!publicName.startsWith(prefix))\n return { modelName: publicName, stripped: false }\n if (publicName.length === 64 && HASHED_PUBLIC_NAME.test(publicName))\n return { modelName: publicName, stripped: false }\n const raw = publicName.slice(prefix.length)\n if (raw.length === 0)\n return { modelName: publicName, stripped: false }\n return { modelName: raw, stripped: true }\n}\n\nexport function patchRawToolNames(\n tools: Pick<ToolRuntime, 'register'>,\n serverName = MCP_SERVER_NAME,\n onKeepQualified?: (publicName: string) => void,\n): () => void {\n const original = tools.register.bind(tools)\n tools.register = (definition) => {\n const next = toRawPublicName(definition.name, serverName)\n if (!next.stripped) {\n if (definition.name.startsWith(qualifiedPrefix(serverName)))\n onKeepQualified?.(definition.name)\n return original(definition)\n }\n return original({ ...definition, name: next.modelName })\n }\n return () => {\n tools.register = original\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { createRequire } from 'node:module'\nimport { Context, Fiber } from '@deepseek-ai/cordis'\nimport { StreamableHttpConfig } from '@deepseek-ai/dsh-mcp-client'\nimport {} from '@deepseek-ai/dsh-settings'\nimport {} from '@deepseek-ai/dsh-settings/types'\nimport {} from '@deepseek-ai/dsh-tools'\nimport { AitoearnPluginConfig, authorizationHeader, pluginConfigFromSettings, resolveAuth, SETTINGS_NAMESPACE } from './auth.js'\nimport { MCP_SERVER_NAME, patchRawToolNames } from './tool-name.js'\n\nconst runtimeRequire = createRequire(import.meta.url)\n\nexport const name = 'aitoearn-mcp'\nexport const inject = ['tools']\n\nexport async function apply(ctx: Context, config: AitoearnPluginConfig = {}): Promise<void> {\n let current = () => config\n let restoreNames: (() => void) | undefined\n let fiber: Fiber | undefined\n let generation = 0\n let mountChain: Promise<void> = Promise.resolve()\n let remountTimer: ReturnType<typeof setTimeout> | undefined\n let mountedFingerprint: string | undefined\n\n const unmountAsync = async () => {\n restoreNames?.()\n restoreNames = undefined\n const previous = fiber\n fiber = undefined\n if (previous) {\n try {\n await previous.dispose()\n }\n catch (error) {\n ctx.logger.warn(error, 'aitoearn-mcp: failed to dispose previous MCP client')\n }\n }\n }\n\n const doMount = async (gen: number) => {\n const auth = resolveAuth(current())\n if (auth.apiKey.length === 0 || auth.mcpUrl.length === 0) {\n await unmountAsync()\n mountedFingerprint = undefined\n ctx.logger.warn('aitoearn-mcp: skip mounting; API key or MCP URL is not configured yet')\n return\n }\n const fingerprint = `${auth.mcpUrl}\\n${auth.apiKey}`\n if (fingerprint === mountedFingerprint && fiber) {\n return\n }\n await unmountAsync()\n if (gen !== generation) {\n return\n }\n restoreNames = patchRawToolNames(ctx.tools, MCP_SERVER_NAME, (publicName) => {\n ctx.logger.info(`kept qualified MCP tool name ${publicName}`)\n })\n try {\n const mcpClient: typeof import('@deepseek-ai/dsh-mcp-client') = runtimeRequire('@deepseek-ai/dsh-mcp-client')\n const mcpConfig: StreamableHttpConfig = {\n serverName: MCP_SERVER_NAME,\n transport: 'streamable-http',\n url: auth.mcpUrl,\n headers: authorizationHeader(auth.apiKey),\n failOnStartupError: false,\n toolCallTimeoutMs: 120000,\n }\n const next = ctx.plugin(mcpClient, mcpConfig)\n fiber = next\n await next\n }\n catch (error) {\n restoreNames?.()\n restoreNames = undefined\n fiber = undefined\n ctx.logger.warn(error, 'aitoearn-mcp: mount failed; will retry on next settings change')\n return\n }\n if (gen !== generation) {\n await unmountAsync()\n mountedFingerprint = undefined\n return\n }\n mountedFingerprint = fingerprint\n ctx.logger.info(`aitoearn-mcp: mounted MCP tools from ${auth.mcpUrl}`)\n }\n\n const scheduleMount = () => {\n generation += 1\n const gen = generation\n mountChain = mountChain\n .then(() => doMount(gen))\n .catch((error: Error) => {\n ctx.logger.warn(error, 'aitoearn-mcp: mount failed')\n })\n return mountChain\n }\n\n const scheduleRemountSoon = () => {\n if (remountTimer) {\n clearTimeout(remountTimer)\n }\n remountTimer = setTimeout(() => {\n remountTimer = undefined\n void scheduleMount()\n }, 50)\n }\n\n ctx.inject(['settings'], (scoped) => {\n // Host `describe()` serves the live value unredacted by default, so the\n // preset can read the stored key; remote callers get the redacted view.\n const readSection = (): AitoearnPluginConfig => {\n const descriptor = scoped.settings.describe().find(candidate => String(candidate.ns) === SETTINGS_NAMESPACE)\n return pluginConfigFromSettings(descriptor?.value, config)\n }\n current = readSection\n ctx.on('settings/document-updated', (namespace) => {\n if (String(namespace) === SETTINGS_NAMESPACE) {\n scheduleRemountSoon()\n }\n })\n // The initial apply-time mount may run before the settings section is\n // readable; mount again once it is, so a stored key takes effect without\n // requiring a manual settings save.\n void scheduleMount()\n })\n\n ctx.effect(() => () => {\n if (remountTimer) {\n clearTimeout(remountTimer)\n remountTimer = undefined\n }\n void unmountAsync()\n }, 'aitoearn-mcp.client')\n await scheduleMount()\n}\n", "export const MCP_PATH = '/api/ai/agent/mcp'\nexport const API_KEY_ENV = 'AITOEARN_API_KEY'\nexport const DEFAULT_ENVIRONMENT = 'global'\nexport const SETTINGS_NAMESPACE = 'aitoearn'\n\nexport const ENVIRONMENT_ORIGINS = {\n global: 'https://aitoearn.ai',\n cn: 'https://aitoearn.cn',\n} as const\n\nexport const DEFAULT_MCP_URL = `${ENVIRONMENT_ORIGINS.global}${MCP_PATH}`\n\nexport type AitoearnEnvironment = 'global' | 'cn' | 'custom'\n\nexport interface AitoearnAuth {\n mcpUrl: string\n apiKey: string\n}\n\nexport interface AitoearnPluginConfig {\n environment?: string\n baseUrl?: string\n apiKey?: string\n}\n\nexport function pluginConfigFromSettings(\n section: unknown,\n fallback: AitoearnPluginConfig = {},\n): AitoearnPluginConfig {\n if (section === null || typeof section !== 'object' || Array.isArray(section))\n return fallback\n const record = section as Record<string, unknown>\n return {\n ...fallback,\n ...(typeof record['environment'] === 'string' ? { environment: record['environment'] } : {}),\n ...(typeof record['baseUrl'] === 'string' ? { baseUrl: record['baseUrl'] } : {}),\n ...(typeof record['apiKey'] === 'string' ? { apiKey: record['apiKey'] } : {}),\n }\n}\n\nexport function resolveEnvironment(value: string | undefined): AitoearnEnvironment {\n if (value === 'cn' || value === 'custom' || value === 'global')\n return value\n return DEFAULT_ENVIRONMENT\n}\n\n/**\n * Normalize a custom endpoint. A non-empty path names the MCP endpoint itself:\n * a deployment may mount the same controller under another prefix, and that\n * full URL cannot be rebuilt from an origin plus {@link MCP_PATH}. A bare\n * origin still falls back to the default path.\n */\nexport function resolveCustomMcpUrl(baseUrl?: string): string {\n const trimmed = baseUrl?.trim() ?? ''\n if (trimmed.length === 0)\n return ''\n let url: URL\n try {\n url = new URL(trimmed)\n }\n catch {\n return ''\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n return ''\n const path = url.pathname.replace(/\\/+$/, '')\n return path.length === 0 ? `${url.origin}${MCP_PATH}` : `${url.origin}${path}`\n}\n\n/** Resolve the origin, or the full custom endpoint, for one environment. */\nexport function resolveOrigin(environment: AitoearnEnvironment, baseUrl?: string): string {\n if (environment === 'custom')\n return resolveCustomMcpUrl(baseUrl)\n return ENVIRONMENT_ORIGINS[environment]\n}\n\nexport function resolveMcpUrl(config: AitoearnPluginConfig): string {\n const environment = resolveEnvironment(config.environment)\n if (environment === 'custom')\n return resolveCustomMcpUrl(config.baseUrl)\n return `${ENVIRONMENT_ORIGINS[environment]}${MCP_PATH}`\n}\n\nexport function resolveAuth(\n config: AitoearnPluginConfig,\n env: NodeJS.ProcessEnv = process.env,\n): AitoearnAuth {\n return {\n mcpUrl: resolveMcpUrl(config),\n apiKey: config.apiKey?.trim() || env[API_KEY_ENV]?.trim() || '',\n }\n}\n\nexport function authorizationHeader(apiKey: string): Record<string, string> {\n if (apiKey.length === 0)\n return {}\n return { Authorization: `Bearer ${apiKey}` }\n}\n", "import { ToolRuntime } from '@deepseek-ai/dsh-tools'\n\nexport const MCP_SERVER_NAME = 'aitoearn'\n\nconst HASHED_PUBLIC_NAME = /_[0-9a-f]{12}$/\n\nexport interface RawPublicName {\n modelName: string\n stripped: boolean\n}\n\nexport function qualifiedPrefix(serverName = MCP_SERVER_NAME): string {\n return `mcp__${serverName}__`\n}\n\nexport function toRawPublicName(publicName: string, serverName = MCP_SERVER_NAME): RawPublicName {\n const prefix = qualifiedPrefix(serverName)\n if (!publicName.startsWith(prefix))\n return { modelName: publicName, stripped: false }\n if (publicName.length === 64 && HASHED_PUBLIC_NAME.test(publicName))\n return { modelName: publicName, stripped: false }\n const raw = publicName.slice(prefix.length)\n if (raw.length === 0)\n return { modelName: publicName, stripped: false }\n return { modelName: raw, stripped: true }\n}\n\n/** One tool definition as the registry receives it; only `name` is rewritten. */\ntype ToolDefinitionLike = { name: string } & Record<string, unknown>\n\n/** The registrar's calling convention: the receiver carries the caller's scope. */\ntype ToolRegistrar = (this: unknown, definition: ToolDefinitionLike) => () => void\n\ninterface RegisterOwner {\n register: ToolRegistrar\n}\n\n/** The object that actually declares `register` along `tools`' prototype chain. */\nfunction registerOwner(tools: object): RegisterOwner | undefined {\n let current: object | null = tools\n while (current !== null && !Object.hasOwn(current, 'register'))\n current = Object.getPrototypeOf(current) as object | null\n return current as RegisterOwner | null ?? undefined\n}\n\n/**\n * Register MCP tools under their bare names instead of `mcp__<server>__<name>`.\n *\n * `ctx.tools` is a scope-tracked service facade: the receiver of `register`\n * carries the calling context, which is how the registry attributes a tool to\n * an agent or preset scope. Reading `register` and binding it captures the\n * accessing context, so a wrapper built that way routes every later\n * registration \u2014 from any scope \u2014 through the scope that installed it, and\n * unrelated presets then collide with `already registered in this scope`.\n * Patching the owner that declares `register` and forwarding `this` keeps each\n * caller's scope intact.\n *\n * @param tools - the `ctx.tools` service face.\n * @param serverName - MCP namespace whose prefix is stripped.\n * @param onKeepQualified - called for names the strip keeps qualified.\n * @returns the disposer that restores the original registrar.\n */\nexport function patchRawToolNames(\n tools: Pick<ToolRuntime, 'register'>,\n serverName = MCP_SERVER_NAME,\n onKeepQualified?: (publicName: string) => void,\n): () => void {\n const owner = registerOwner(tools)\n if (owner === undefined)\n return () => {}\n const original = owner.register\n owner.register = function (this: unknown, definition: ToolDefinitionLike) {\n const next = toRawPublicName(definition.name, serverName)\n if (!next.stripped) {\n if (definition.name.startsWith(qualifiedPrefix(serverName)))\n onKeepQualified?.(definition.name)\n return original.call(this, definition)\n }\n return original.call(this, { ...definition, name: next.modelName })\n }\n return () => {\n owner.register = original\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,13 @@
1
1
  // src/dsh/persona.ts
2
- import { readFileSync } from "node:fs";
2
+ import { existsSync, readFileSync } from "node:fs";
3
3
  var name = "aitoearn-persona";
4
4
  var inject = ["systemPrompt"];
5
5
  function agentPath() {
6
- return new URL("../AGENT.md", import.meta.url);
6
+ const candidates = [
7
+ new URL("../AGENT.md", import.meta.url),
8
+ new URL("../../AGENT.md", import.meta.url)
9
+ ];
10
+ return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
7
11
  }
8
12
  function apply(ctx) {
9
13
  const text = readFileSync(agentPath(), "utf8");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../tools/agent-plugin/src/dsh/persona.ts"],
4
- "sourcesContent": ["import { readFileSync } from 'node:fs'\nimport { Context } from '@deepseek-ai/cordis'\nimport {} from '@deepseek-ai/dsh-system-prompt'\n\nexport const name = 'aitoearn-persona'\nexport const inject = ['systemPrompt']\n\n/**\n * Bundled as `<preset dir>/lib/persona.js`; the persona text lives one level\n * up beside `preset.yml`, so the preset directory stays self-contained.\n */\nfunction agentPath(): URL {\n return new URL('../AGENT.md', import.meta.url)\n}\n\nexport function apply(ctx: Context): void {\n const text = readFileSync(agentPath(), 'utf8')\n ctx.systemPrompt.section({\n name: 'aitoearn:persona',\n order: 0,\n text,\n })\n}\n"],
5
- "mappings": ";AAAA,SAAS,oBAAoB;AAItB,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,cAAc;AAMrC,SAAS,YAAiB;AACxB,SAAO,IAAI,IAAI,eAAe,YAAY,GAAG;AAC/C;AAEO,SAAS,MAAM,KAAoB;AACxC,QAAM,OAAO,aAAa,UAAU,GAAG,MAAM;AAC7C,MAAI,aAAa,QAAQ;AAAA,IACvB,MAAM;AAAA,IACN,OAAO;AAAA,IACP;AAAA,EACF,CAAC;AACH;",
4
+ "sourcesContent": ["import { existsSync, readFileSync } from 'node:fs'\nimport { Context } from '@deepseek-ai/cordis'\nimport {} from '@deepseek-ai/dsh-system-prompt'\n\nexport const name = 'aitoearn-persona'\nexport const inject = ['systemPrompt']\n\n/**\n * The persona text lives at the package root: one level above `src/dsh/` in the\n * installed package, and two above the legacy bundled `presets/aitoearn/lib/`.\n * Prefer the nearest existing candidate so both layouts resolve the same file.\n */\nfunction agentPath(): URL {\n const candidates = [\n new URL('../AGENT.md', import.meta.url),\n new URL('../../AGENT.md', import.meta.url),\n ]\n return candidates.find(candidate => existsSync(candidate)) ?? candidates[0]\n}\n\nexport function apply(ctx: Context): void {\n const text = readFileSync(agentPath(), 'utf8')\n ctx.systemPrompt.section({\n name: 'aitoearn:persona',\n order: 0,\n text,\n })\n}\n"],
5
+ "mappings": ";AAAA,SAAS,YAAY,oBAAoB;AAIlC,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,cAAc;AAOrC,SAAS,YAAiB;AACxB,QAAM,aAAa;AAAA,IACjB,IAAI,IAAI,eAAe,YAAY,GAAG;AAAA,IACtC,IAAI,IAAI,kBAAkB,YAAY,GAAG;AAAA,EAC3C;AACA,SAAO,WAAW,KAAK,eAAa,WAAW,SAAS,CAAC,KAAK,WAAW,CAAC;AAC5E;AAEO,SAAS,MAAM,KAAoB;AACxC,QAAM,OAAO,aAAa,UAAU,GAAG,MAAM;AAC7C,MAAI,aAAa,QAAQ;AAAA,IACvB,MAAM;AAAA,IACN,OAAO;AAAA,IACP;AAAA,EACF,CAAC;AACH;",
6
6
  "names": []
7
7
  }
package/src/dsh/auth.d.ts CHANGED
@@ -19,6 +19,14 @@ export interface AitoearnPluginConfig {
19
19
  }
20
20
  export declare function pluginConfigFromSettings(section: unknown, fallback?: AitoearnPluginConfig): AitoearnPluginConfig;
21
21
  export declare function resolveEnvironment(value: string | undefined): AitoearnEnvironment;
22
+ /**
23
+ * Normalize a custom endpoint. A non-empty path names the MCP endpoint itself:
24
+ * a deployment may mount the same controller under another prefix, and that
25
+ * full URL cannot be rebuilt from an origin plus {@link MCP_PATH}. A bare
26
+ * origin still falls back to the default path.
27
+ */
28
+ export declare function resolveCustomMcpUrl(baseUrl?: string): string;
29
+ /** Resolve the origin, or the full custom endpoint, for one environment. */
22
30
  export declare function resolveOrigin(environment: AitoearnEnvironment, baseUrl?: string): string;
23
31
  export declare function resolveMcpUrl(config: AitoearnPluginConfig): string;
24
32
  export declare function resolveAuth(config: AitoearnPluginConfig, env?: NodeJS.ProcessEnv): AitoearnAuth;
package/src/dsh/auth.js CHANGED
@@ -23,28 +23,39 @@ export function resolveEnvironment(value) {
23
23
  return value;
24
24
  return DEFAULT_ENVIRONMENT;
25
25
  }
26
- export function resolveOrigin(environment, baseUrl) {
27
- if (environment !== 'custom')
28
- return ENVIRONMENT_ORIGINS[environment];
26
+ /**
27
+ * Normalize a custom endpoint. A non-empty path names the MCP endpoint itself:
28
+ * a deployment may mount the same controller under another prefix, and that
29
+ * full URL cannot be rebuilt from an origin plus {@link MCP_PATH}. A bare
30
+ * origin still falls back to the default path.
31
+ */
32
+ export function resolveCustomMcpUrl(baseUrl) {
29
33
  const trimmed = baseUrl?.trim() ?? '';
30
34
  if (trimmed.length === 0)
31
35
  return '';
36
+ let url;
32
37
  try {
33
- const url = new URL(trimmed);
34
- if (url.protocol !== 'http:' && url.protocol !== 'https:')
35
- return '';
36
- return url.origin;
38
+ url = new URL(trimmed);
37
39
  }
38
40
  catch {
39
41
  return '';
40
42
  }
43
+ if (url.protocol !== 'http:' && url.protocol !== 'https:')
44
+ return '';
45
+ const path = url.pathname.replace(/\/+$/, '');
46
+ return path.length === 0 ? `${url.origin}${MCP_PATH}` : `${url.origin}${path}`;
47
+ }
48
+ /** Resolve the origin, or the full custom endpoint, for one environment. */
49
+ export function resolveOrigin(environment, baseUrl) {
50
+ if (environment === 'custom')
51
+ return resolveCustomMcpUrl(baseUrl);
52
+ return ENVIRONMENT_ORIGINS[environment];
41
53
  }
42
54
  export function resolveMcpUrl(config) {
43
55
  const environment = resolveEnvironment(config.environment);
44
- const origin = resolveOrigin(environment, config.baseUrl);
45
- if (origin.length === 0)
46
- return '';
47
- return `${origin}${MCP_PATH}`;
56
+ if (environment === 'custom')
57
+ return resolveCustomMcpUrl(config.baseUrl);
58
+ return `${ENVIRONMENT_ORIGINS[environment]}${MCP_PATH}`;
48
59
  }
49
60
  export function resolveAuth(config, env = process.env) {
50
61
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/auth.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ export const aitoearnCardLocales = {
9
9
  envCn: '中国站 aitoearn.cn',
10
10
  envCustom: '自定义',
11
11
  baseUrl: '接口地址',
12
- baseUrlHint: '留空则使用所选环境默认地址。',
12
+ baseUrlHint: '留空则使用所选环境默认地址;填写完整 MCP 端点(含路径)时按原样使用。',
13
13
  apiKey: 'API Key',
14
14
  apiKeySet: '已配置密钥。',
15
15
  apiKeyUnset: '未配置密钥;配置之前 Aitoearn 工具不可用。',
@@ -31,7 +31,7 @@ export const aitoearnCardLocales = {
31
31
  envCn: 'China (aitoearn.cn)',
32
32
  envCustom: 'Custom',
33
33
  baseUrl: 'Base URL',
34
- baseUrlHint: 'Leave blank to use the selected environment default.',
34
+ baseUrlHint: 'Leave blank for the selected environment default; a full MCP endpoint (with path) is used as-is.',
35
35
  apiKey: 'API key',
36
36
  apiKeySet: 'A key is configured.',
37
37
  apiKeyUnset: 'No key is configured; Aitoearn tools are unavailable until one is.',
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../../../../../../tools/agent-plugin/src/dsh/client/i18n.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAE/D,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAA;AAW7C,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,EAAE,EAAE;QACF,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,4BAA4B;QACzC,WAAW,EAAE,MAAM;QACnB,SAAS,EAAE,iBAAiB;QAC5B,KAAK,EAAE,iBAAiB;QACxB,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,MAAM;QACf,WAAW,EAAE,gBAAgB;QAC7B,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,QAAQ;QACnB,WAAW,EAAE,4BAA4B;QACzC,UAAU,EAAE,kCAAkC;QAC9C,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,MAAM;QACf,OAAO,EAAE,KAAK;QACd,UAAU,EAAE,qBAAqB;QACjC,QAAQ,EAAE,YAAY;KACvB;IACD,EAAE,EAAE;QACF,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,6CAA6C;QAC1D,WAAW,EAAE,aAAa;QAC1B,SAAS,EAAE,sBAAsB;QACjC,KAAK,EAAE,qBAAqB;QAC5B,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,UAAU;QACnB,WAAW,EAAE,sDAAsD;QACnE,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,sBAAsB;QACjC,WAAW,EAAE,oEAAoE;QACjF,UAAU,EAAE,0GAA0G;QACtH,MAAM,EAAE,eAAe;QACvB,QAAQ,EAAE,eAAe;QACzB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,SAAS;QAClB,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,gFAAgF;QAC5F,QAAQ,EAAE,4CAA4C;KACvD;CACF,CAAA"}
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../../../../../../tools/agent-plugin/src/dsh/client/i18n.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAE/D,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAA;AAW7C,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,EAAE,EAAE;QACF,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,4BAA4B;QACzC,WAAW,EAAE,MAAM;QACnB,SAAS,EAAE,iBAAiB;QAC5B,KAAK,EAAE,iBAAiB;QACxB,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,MAAM;QACf,WAAW,EAAE,uCAAuC;QACpD,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,QAAQ;QACnB,WAAW,EAAE,4BAA4B;QACzC,UAAU,EAAE,kCAAkC;QAC9C,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,MAAM;QACf,OAAO,EAAE,KAAK;QACd,UAAU,EAAE,qBAAqB;QACjC,QAAQ,EAAE,YAAY;KACvB;IACD,EAAE,EAAE;QACF,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,6CAA6C;QAC1D,WAAW,EAAE,aAAa;QAC1B,SAAS,EAAE,sBAAsB;QACjC,KAAK,EAAE,qBAAqB;QAC5B,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,UAAU;QACnB,WAAW,EAAE,kGAAkG;QAC/G,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,sBAAsB;QACjC,WAAW,EAAE,oEAAoE;QACjF,UAAU,EAAE,0GAA0G;QACtH,MAAM,EAAE,eAAe;QACvB,QAAQ,EAAE,eAAe;QACzB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,SAAS;QAClB,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,gFAAgF;QAC5F,QAAQ,EAAE,4CAA4C;KACvD;CACF,CAAA"}
@@ -3,7 +3,28 @@ import { AitoearnPluginConfig, SETTINGS_NAMESPACE } from './auth.js';
3
3
  import { Config } from './settings-schema.js';
4
4
  export declare const name = "aitoearn";
5
5
  export { Config, SETTINGS_NAMESPACE };
6
- export declare function apply(ctx: Context, config?: AitoearnPluginConfig, paths?: {
6
+ /** One preset declaration, structurally compatible with `@deepseek-ai/dsh-agent-preset`. */
7
+ interface PresetDefinition {
8
+ id: string;
9
+ name?: string;
10
+ description?: string;
11
+ order?: number;
12
+ plugins: Array<Record<string, unknown>>;
13
+ }
14
+ /** The registry face this plugin consumes when the host composes one. */
15
+ interface PresetRegistry {
16
+ register: (definition: PresetDefinition) => Promise<() => Promise<void>>;
17
+ }
18
+ declare module '@deepseek-ai/cordis' {
19
+ interface Context {
20
+ /** Provided by `@deepseek-ai/dsh-agent-preset-registry` when it is composed. */
21
+ agentPresets: PresetRegistry;
22
+ }
23
+ }
24
+ /** Test seam for the bundled layout; production callers omit every field. */
25
+ export interface AitoearnPaths {
7
26
  sourceDir?: string;
8
27
  targetDir?: string;
9
- }): void;
28
+ dshHome?: string;
29
+ }
30
+ export declare function apply(ctx: Context, config?: AitoearnPluginConfig, paths?: AitoearnPaths): void;
package/src/dsh/index.js CHANGED
@@ -1,15 +1,74 @@
1
+ import { fileURLToPath } from 'node:url';
1
2
  import { SETTINGS_NAMESPACE } from './auth.js';
2
3
  import { syncAitoearnPreset } from './preset-sync.js';
3
4
  import { Config } from './settings-schema.js';
4
5
  export const name = 'aitoearn';
5
6
  export { Config, SETTINGS_NAMESPACE };
7
+ /** Agent preset identity this plugin contributes to the host preset registry. */
8
+ const PRESET_ID = 'aitoearn';
9
+ /** Absolute file URL of one module shipped inside this package. */
10
+ function bundledModule(relative) {
11
+ return new URL(relative, import.meta.url).href;
12
+ }
13
+ /**
14
+ * The preset's plugin rows. Every bundled path is absolute: preset rows resolve
15
+ * relative names against the declaring tree (the profile directory), never
16
+ * against this package, and the loaded modules resolve their own bundled
17
+ * assets beside themselves.
18
+ */
19
+ function presetDefinition() {
20
+ return {
21
+ id: PRESET_ID,
22
+ name: 'Aitoearn',
23
+ description: '社交媒体内容生成助手,用于文案、图片、视频和发布。',
24
+ order: 20,
25
+ plugins: [
26
+ { id: 'persona', name: bundledModule('./persona.js') },
27
+ {
28
+ id: 'skill-filesystem',
29
+ name: '@deepseek-ai/dsh-skill-filesystem',
30
+ config: {
31
+ providerName: PRESET_ID,
32
+ includeDefaultRoots: false,
33
+ customSkillDirs: [fileURLToPath(new URL('../skills/', import.meta.url))],
34
+ },
35
+ },
36
+ { id: 'tool-skill', name: '@deepseek-ai/dsh-tool-skill' },
37
+ { id: 'aitoearn-mcp', name: bundledModule('./mcp.js') },
38
+ ],
39
+ };
40
+ }
6
41
  export function apply(ctx, config = {}, paths) {
7
42
  void config;
8
43
  ctx.inject(['settings'], (scoped) => {
9
44
  scoped.effect(() => scoped.settings.configure({ auto: false }, ctx.fiber), 'aitoearn:settings-page-policy');
10
45
  });
46
+ // Current dsh reads presets only from the agent-preset registry, so declare
47
+ // this one at runtime; the directory sync below cannot register anything on
48
+ // its own. Registration is async, so the disposer is claimed once it lands.
49
+ ctx.inject(['agentPresets'], (scoped) => {
50
+ let unregister;
51
+ let disposed = false;
52
+ scoped.effect(() => {
53
+ scoped.agentPresets.register(presetDefinition()).then((off) => {
54
+ if (disposed)
55
+ void off();
56
+ else
57
+ unregister = off;
58
+ }, (error) => {
59
+ ctx.logger.error(error, 'Aitoearn preset registration failed');
60
+ });
61
+ return () => {
62
+ disposed = true;
63
+ if (unregister !== undefined)
64
+ void unregister();
65
+ };
66
+ }, 'aitoearn:preset');
67
+ });
68
+ // Legacy layout for dsh versions that read `$DSH_HOME/.agent-presets/<id>/`
69
+ // as the user preset root; harmless on a registry-backed runtime.
11
70
  try {
12
- syncAitoearnPreset(paths?.sourceDir, paths?.targetDir);
71
+ syncAitoearnPreset(paths?.sourceDir, paths?.targetDir, paths?.dshHome);
13
72
  }
14
73
  catch (error) {
15
74
  ctx.logger.error(error, 'Aitoearn preset sync failed');
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAE7C,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAA;AAC9B,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAA;AAErC,MAAM,UAAU,KAAK,CACnB,GAAY,EACZ,SAA+B,EAAE,EACjC,KAAkD;IAElD,KAAK,MAAM,CAAA;IACX,GAAG,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE;QAClC,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,EAC3D,+BAA+B,CAChC,CAAA;IACH,CAAC,CAAC,CAAA;IACF,IAAI,CAAC;QACH,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,KAAK,EAAE,CAAC;QACb,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,6BAA6B,CAAC,CAAA;IACxD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/index.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,8 @@
1
+ /**
2
+ * The installed package root. Modules live at `<root>/src/dsh/`, so the root is
3
+ * three directories above this file — walking up two reached `<root>/src`,
4
+ * where neither `presets/` nor `AGENT.md` exists.
5
+ */
1
6
  export declare function packageRoot(): string;
2
7
  export declare function bundledPresetDir(root?: string): string;
3
8
  export declare function bundledSkillsDir(root?: string): string;
package/src/dsh/paths.js CHANGED
@@ -1,8 +1,13 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
+ /**
5
+ * The installed package root. Modules live at `<root>/src/dsh/`, so the root is
6
+ * three directories above this file — walking up two reached `<root>/src`,
7
+ * where neither `presets/` nor `AGENT.md` exists.
8
+ */
4
9
  export function packageRoot() {
5
- return dirname(dirname(fileURLToPath(import.meta.url)));
10
+ return dirname(dirname(dirname(fileURLToPath(import.meta.url))));
6
11
  }
7
12
  export function bundledPresetDir(root = packageRoot()) {
8
13
  return join(root, 'presets', 'aitoearn');
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AACzD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe,WAAW,EAAE;IAC3D,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAI,GAAG,WAAW,EAAE;IACnD,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAI,GAAG,WAAW,EAAE;IACnD,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;IACrF,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAkB,UAAU,EAAE;IAC1D,OAAO,IAAI,CAAC,OAAO,EAAE,gBAAgB,EAAE,UAAU,CAAC,CAAA;AACpD,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC;;;;GAIG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe,WAAW,EAAE;IAC3D,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAI,GAAG,WAAW,EAAE;IACnD,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAI,GAAG,WAAW,EAAE;IACnD,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;IACrF,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,UAAkB,UAAU,EAAE;IAC1D,OAAO,IAAI,CAAC,OAAO,EAAE,gBAAgB,EAAE,UAAU,CAAC,CAAA;AACpD,CAAC"}
@@ -1,12 +1,17 @@
1
- import { readFileSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from 'node:fs';
2
2
  export const name = 'aitoearn-persona';
3
3
  export const inject = ['systemPrompt'];
4
4
  /**
5
- * Bundled as `<preset dir>/lib/persona.js`; the persona text lives one level
6
- * up beside `preset.yml`, so the preset directory stays self-contained.
5
+ * The persona text lives at the package root: one level above `src/dsh/` in the
6
+ * installed package, and two above the legacy bundled `presets/aitoearn/lib/`.
7
+ * Prefer the nearest existing candidate so both layouts resolve the same file.
7
8
  */
8
9
  function agentPath() {
9
- return new URL('../AGENT.md', import.meta.url);
10
+ const candidates = [
11
+ new URL('../AGENT.md', import.meta.url),
12
+ new URL('../../AGENT.md', import.meta.url),
13
+ ];
14
+ return candidates.find(candidate => existsSync(candidate)) ?? candidates[0];
10
15
  }
11
16
  export function apply(ctx) {
12
17
  const text = readFileSync(agentPath(), 'utf8');
@@ -1 +1 @@
1
- {"version":3,"file":"persona.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/persona.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAItC,MAAM,CAAC,MAAM,IAAI,GAAG,kBAAkB,CAAA;AACtC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,cAAc,CAAC,CAAA;AAEtC;;;GAGG;AACH,SAAS,SAAS;IAChB,OAAO,IAAI,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAY;IAChC,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAA;IAC9C,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC;QACvB,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE,CAAC;QACR,IAAI;KACL,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"persona.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/persona.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAIlD,MAAM,CAAC,MAAM,IAAI,GAAG,kBAAkB,CAAA;AACtC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,cAAc,CAAC,CAAA;AAEtC;;;;GAIG;AACH,SAAS,SAAS;IAChB,MAAM,UAAU,GAAG;QACjB,IAAI,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACvC,IAAI,GAAG,CAAC,gBAAgB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;KAC3C,CAAA;IACD,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;AAC7E,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,GAAY;IAChC,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAA;IAC9C,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC;QACvB,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE,CAAC;QACR,IAAI;KACL,CAAC,CAAA;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import { cpSync, existsSync, mkdirSync, readlinkSync, rmSync, symlinkSync } from 'node:fs';
1
+ import { cpSync, existsSync, mkdirSync, readlinkSync, rmSync, symlinkSync, unlinkSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { bundledAgentPath, bundledPresetDir, bundledSkillsDir, dshHomeDir, packageRoot, userPresetDir } from './paths.js';
@@ -27,7 +27,16 @@ function linkRuntimeDependency(packageName, dshHome) {
27
27
  return;
28
28
  if (existing !== undefined || !existsSync(link)) {
29
29
  mkdirSync(dirname(link), { recursive: true });
30
- rmSync(link, { force: true });
30
+ // `rmSync` follows a symlink to a directory and fails with EISDIR, so a
31
+ // stale link could never be replaced; `unlinkSync` removes the link only.
32
+ try {
33
+ unlinkSync(link);
34
+ }
35
+ catch (error) {
36
+ // An absent link (fresh dsh home) is already the desired state.
37
+ if (error.code !== 'ENOENT')
38
+ throw error;
39
+ }
31
40
  symlinkSync(target, link, process.platform === 'win32' ? 'junction' : 'dir');
32
41
  }
33
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"preset-sync.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/preset-sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"preset-sync.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/preset-sync.ts"],"names":[],"mappings":"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"}
@@ -6,4 +6,21 @@ export interface RawPublicName {
6
6
  }
7
7
  export declare function qualifiedPrefix(serverName?: string): string;
8
8
  export declare function toRawPublicName(publicName: string, serverName?: string): RawPublicName;
9
+ /**
10
+ * Register MCP tools under their bare names instead of `mcp__<server>__<name>`.
11
+ *
12
+ * `ctx.tools` is a scope-tracked service facade: the receiver of `register`
13
+ * carries the calling context, which is how the registry attributes a tool to
14
+ * an agent or preset scope. Reading `register` and binding it captures the
15
+ * accessing context, so a wrapper built that way routes every later
16
+ * registration — from any scope — through the scope that installed it, and
17
+ * unrelated presets then collide with `already registered in this scope`.
18
+ * Patching the owner that declares `register` and forwarding `this` keeps each
19
+ * caller's scope intact.
20
+ *
21
+ * @param tools - the `ctx.tools` service face.
22
+ * @param serverName - MCP namespace whose prefix is stripped.
23
+ * @param onKeepQualified - called for names the strip keeps qualified.
24
+ * @returns the disposer that restores the original registrar.
25
+ */
9
26
  export declare function patchRawToolNames(tools: Pick<ToolRuntime, 'register'>, serverName?: string, onKeepQualified?: (publicName: string) => void): () => void;
@@ -14,19 +14,46 @@ export function toRawPublicName(publicName, serverName = MCP_SERVER_NAME) {
14
14
  return { modelName: publicName, stripped: false };
15
15
  return { modelName: raw, stripped: true };
16
16
  }
17
+ /** The object that actually declares `register` along `tools`' prototype chain. */
18
+ function registerOwner(tools) {
19
+ let current = tools;
20
+ while (current !== null && !Object.hasOwn(current, 'register'))
21
+ current = Object.getPrototypeOf(current);
22
+ return current ?? undefined;
23
+ }
24
+ /**
25
+ * Register MCP tools under their bare names instead of `mcp__<server>__<name>`.
26
+ *
27
+ * `ctx.tools` is a scope-tracked service facade: the receiver of `register`
28
+ * carries the calling context, which is how the registry attributes a tool to
29
+ * an agent or preset scope. Reading `register` and binding it captures the
30
+ * accessing context, so a wrapper built that way routes every later
31
+ * registration — from any scope — through the scope that installed it, and
32
+ * unrelated presets then collide with `already registered in this scope`.
33
+ * Patching the owner that declares `register` and forwarding `this` keeps each
34
+ * caller's scope intact.
35
+ *
36
+ * @param tools - the `ctx.tools` service face.
37
+ * @param serverName - MCP namespace whose prefix is stripped.
38
+ * @param onKeepQualified - called for names the strip keeps qualified.
39
+ * @returns the disposer that restores the original registrar.
40
+ */
17
41
  export function patchRawToolNames(tools, serverName = MCP_SERVER_NAME, onKeepQualified) {
18
- const original = tools.register.bind(tools);
19
- tools.register = (definition) => {
42
+ const owner = registerOwner(tools);
43
+ if (owner === undefined)
44
+ return () => { };
45
+ const original = owner.register;
46
+ owner.register = function (definition) {
20
47
  const next = toRawPublicName(definition.name, serverName);
21
48
  if (!next.stripped) {
22
49
  if (definition.name.startsWith(qualifiedPrefix(serverName)))
23
50
  onKeepQualified?.(definition.name);
24
- return original(definition);
51
+ return original.call(this, definition);
25
52
  }
26
- return original({ ...definition, name: next.modelName });
53
+ return original.call(this, { ...definition, name: next.modelName });
27
54
  };
28
55
  return () => {
29
- tools.register = original;
56
+ owner.register = original;
30
57
  };
31
58
  }
32
59
  //# sourceMappingURL=tool-name.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-name.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/tool-name.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAA;AAEzC,MAAM,kBAAkB,GAAG,gBAAgB,CAAA;AAO3C,MAAM,UAAU,eAAe,CAAC,UAAU,GAAG,eAAe;IAC1D,OAAO,QAAQ,UAAU,IAAI,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAkB,EAAE,UAAU,GAAG,eAAe;IAC9E,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAA;IAC1C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC;QAChC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACnD,IAAI,UAAU,CAAC,MAAM,KAAK,EAAE,IAAI,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;QACjE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACnD,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAClB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACnD,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;AAC3C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAoC,EACpC,UAAU,GAAG,eAAe,EAC5B,eAA8C;IAE9C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC3C,KAAK,CAAC,QAAQ,GAAG,CAAC,UAAU,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,eAAe,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;QACzD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;gBACzD,eAAe,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;YACpC,OAAO,QAAQ,CAAC,UAAU,CAAC,CAAA;QAC7B,CAAC;QACD,OAAO,QAAQ,CAAC,EAAE,GAAG,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;IAC1D,CAAC,CAAA;IACD,OAAO,GAAG,EAAE;QACV,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC3B,CAAC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"tool-name.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/tool-name.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAA;AAEzC,MAAM,kBAAkB,GAAG,gBAAgB,CAAA;AAO3C,MAAM,UAAU,eAAe,CAAC,UAAU,GAAG,eAAe;IAC1D,OAAO,QAAQ,UAAU,IAAI,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAkB,EAAE,UAAU,GAAG,eAAe;IAC9E,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAA;IAC1C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC;QAChC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACnD,IAAI,UAAU,CAAC,MAAM,KAAK,EAAE,IAAI,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC;QACjE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACnD,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAClB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACnD,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;AAC3C,CAAC;AAYD,mFAAmF;AACnF,SAAS,aAAa,CAAC,KAAa;IAClC,IAAI,OAAO,GAAkB,KAAK,CAAA;IAClC,OAAO,OAAO,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC;QAC5D,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAkB,CAAA;IAC3D,OAAO,OAA+B,IAAI,SAAS,CAAA;AACrD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAoC,EACpC,UAAU,GAAG,eAAe,EAC5B,eAA8C;IAE9C,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAA;IAClC,IAAI,KAAK,KAAK,SAAS;QACrB,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IACjB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAA;IAC/B,KAAK,CAAC,QAAQ,GAAG,UAAyB,UAA8B;QACtE,MAAM,IAAI,GAAG,eAAe,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;QACzD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;gBACzD,eAAe,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;YACpC,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;QACxC,CAAC;QACD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;IACrE,CAAC,CAAA;IACD,OAAO,GAAG,EAAE;QACV,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC3B,CAAC,CAAA;AACH,CAAC"}