tagword-codeagent 1.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. codeagent/__init__.py +10 -0
  2. codeagent/__main__.py +13 -0
  3. codeagent/cli/__init__.py +13 -0
  4. codeagent/cli/entrypoint.py +161 -0
  5. codeagent/cli/main.py +979 -0
  6. codeagent/core/__init__.py +2 -0
  7. codeagent/core/attachments.py +342 -0
  8. codeagent/core/audio_models.py +50 -0
  9. codeagent/core/bootstrap.py +178 -0
  10. codeagent/core/default_presets.py +159 -0
  11. codeagent/core/env.py +106 -0
  12. codeagent/core/folder_picker.py +151 -0
  13. codeagent/core/image_understanding.py +90 -0
  14. codeagent/core/models.py +6 -0
  15. codeagent/core/paths.py +238 -0
  16. codeagent/core/pricing.py +107 -0
  17. codeagent/core/process_ports.py +258 -0
  18. codeagent/core/seed_bridge.py +21 -0
  19. codeagent/core/settings.py +98 -0
  20. codeagent/core/speech_synth.py +330 -0
  21. codeagent/core/team_config.py +247 -0
  22. codeagent/core/team_manager.py +245 -0
  23. codeagent/core/token_counter.py +41 -0
  24. codeagent/core/tts_voices.py +103 -0
  25. codeagent/core/usage_billing.py +72 -0
  26. codeagent/core/vision_models.py +93 -0
  27. codeagent/memory/diary.py +144 -0
  28. codeagent/persona_defaults/README.md +12 -0
  29. codeagent/persona_defaults/agent.md +352 -0
  30. codeagent/persona_defaults/identity.md +29 -0
  31. codeagent/persona_defaults/memory.md +4 -0
  32. codeagent/persona_defaults/skills.md +325 -0
  33. codeagent/persona_defaults/soul.md +38 -0
  34. codeagent/persona_defaults/tools.md +41 -0
  35. codeagent/persona_defaults/user.md +18 -0
  36. codeagent/runtime/__init__.py +11 -0
  37. codeagent/runtime/agent_context.py +6 -0
  38. codeagent/runtime/agent_runtime.py +21 -0
  39. codeagent/runtime/compact_prompt.py +9 -0
  40. codeagent/runtime/compact_state.py +40 -0
  41. codeagent/runtime/llm_worker.py +132 -0
  42. codeagent/runtime/orchestrator.py +161 -0
  43. codeagent/runtime/prompt_enrichment.py +324 -0
  44. codeagent/runtime/session_manager.py +30 -0
  45. codeagent/runtime/task_split.py +38 -0
  46. codeagent/runtime/turn_loop.py +13 -0
  47. codeagent/runtime/worker.py +48 -0
  48. codeagent/server/__init__.py +505 -0
  49. codeagent/server/app_factory.py +1292 -0
  50. codeagent/server/attachment_api.py +261 -0
  51. codeagent/server/hub.py +175 -0
  52. codeagent/server/icon.png +0 -0
  53. codeagent/server/self_healing.py +143 -0
  54. codeagent/server/team_engine.py +243 -0
  55. codeagent/server/tts_api.py +51 -0
  56. codeagent/server/webui_api_app.py +3180 -0
  57. codeagent/skills/__init__.py +2 -0
  58. codeagent/skills/select.py +144 -0
  59. codeagent/tools/__init__.py +6 -0
  60. codeagent/tools/agent_tools.py +10 -0
  61. codeagent/web/__init__.py +1 -0
  62. codeagent/web/__pycache__/__init__.cpython-313.pyc +0 -0
  63. codeagent/web/__pycache__/app.cpython-313.pyc +0 -0
  64. codeagent/web/__pycache__/auth_impl.cpython-313.pyc +0 -0
  65. codeagent/web/auth_impl.py +326 -0
  66. codeagent/web/static/00-marked.min.js +6 -0
  67. codeagent/web/static/00-mermaid.min.js +2314 -0
  68. codeagent/web/static/00-purify.min.js +3 -0
  69. codeagent/web/static/00-storage.js +83 -0
  70. codeagent/web/static/00-utils.js +51 -0
  71. codeagent/web/static/01-theme.css +96 -0
  72. codeagent/web/static/01a-scroll.js +86 -0
  73. codeagent/web/static/01b-think.js +62 -0
  74. codeagent/web/static/01c-session-identity.js +81 -0
  75. codeagent/web/static/01c-session-thinking.js +61 -0
  76. codeagent/web/static/01c-session-tree-menus.js +349 -0
  77. codeagent/web/static/01c-session-tree.js +531 -0
  78. codeagent/web/static/01c-session-utilities.js +83 -0
  79. codeagent/web/static/01c-vision-model.js +132 -0
  80. codeagent/web/static/01d-image-gen-model.js +84 -0
  81. codeagent/web/static/01e-audio-model.js +97 -0
  82. codeagent/web/static/01f-compose-ui.js +88 -0
  83. codeagent/web/static/01g-tts-settings.js +128 -0
  84. codeagent/web/static/01h-music-model.js +85 -0
  85. codeagent/web/static/01i-model-stack.js +291 -0
  86. codeagent/web/static/01j-video-gen-model.js +85 -0
  87. codeagent/web/static/01k-model-stack-bootstrap.js +66 -0
  88. codeagent/web/static/01k-model-stack-state.js +209 -0
  89. codeagent/web/static/01q-inflight.js +195 -0
  90. codeagent/web/static/02-reset.css +22 -0
  91. codeagent/web/static/02-session-ui.js +201 -0
  92. codeagent/web/static/03-layout-p1.css +2 -0
  93. codeagent/web/static/03-layout-p2.css +2 -0
  94. codeagent/web/static/03a-links-stream.js +88 -0
  95. codeagent/web/static/03b-stream-text.js +119 -0
  96. codeagent/web/static/03c-render-bubble.js +520 -0
  97. codeagent/web/static/03d-agent-bubble.js +126 -0
  98. codeagent/web/static/03e-tool-trace.js +182 -0
  99. codeagent/web/static/03f-timeline.js +54 -0
  100. codeagent/web/static/03g-bubble-tts.js +292 -0
  101. codeagent/web/static/04-components-p1.css +1 -0
  102. codeagent/web/static/04-components-p2b.css +1 -0
  103. codeagent/web/static/04-components-p3.css +2 -0
  104. codeagent/web/static/04-components-p4.css +1 -0
  105. codeagent/web/static/04-components-p5.css +1 -0
  106. codeagent/web/static/04-components-p6.css +58 -0
  107. codeagent/web/static/04-components-p7.css +4 -0
  108. codeagent/web/static/04-components-p8.css +1 -0
  109. codeagent/web/static/04-components-p9.css +1 -0
  110. codeagent/web/static/04-ws-connect.js +351 -0
  111. codeagent/web/static/04-ws-helpers.js +116 -0
  112. codeagent/web/static/05-chat-p1.css +1058 -0
  113. codeagent/web/static/05-chat-p2.css +1 -0
  114. codeagent/web/static/05-chat-p3.css +295 -0
  115. codeagent/web/static/05a-session-history.js +176 -0
  116. codeagent/web/static/05b-session-list.js +114 -0
  117. codeagent/web/static/05c-dialog.js +123 -0
  118. codeagent/web/static/05d-project-create.js +172 -0
  119. codeagent/web/static/05e-project-remote.js +143 -0
  120. codeagent/web/static/06-chat.js +552 -0
  121. codeagent/web/static/06-utilities.css +181 -0
  122. codeagent/web/static/06b-attachments.js +304 -0
  123. codeagent/web/static/06c-camera.js +488 -0
  124. codeagent/web/static/07-activity-bar.css +127 -0
  125. codeagent/web/static/09-git-p1.css +1 -0
  126. codeagent/web/static/09-git-p2.css +1 -0
  127. codeagent/web/static/09a-init-bind.js +110 -0
  128. codeagent/web/static/09b-panel-toggle.js +80 -0
  129. codeagent/web/static/09c-api-refresh.js +100 -0
  130. codeagent/web/static/09d-status-log.js +80 -0
  131. codeagent/web/static/09e-commit.js +80 -0
  132. codeagent/web/static/09f-remote.js +93 -0
  133. codeagent/web/static/11a-cron-parse.js +117 -0
  134. codeagent/web/static/11b-cron-panel.js +100 -0
  135. codeagent/web/static/11c-cron-card.js +133 -0
  136. codeagent/web/static/11d-cron-form.js +105 -0
  137. codeagent/web/static/11e-env-config.js +168 -0
  138. codeagent/web/static/11f-env-llm-a-state.js +136 -0
  139. codeagent/web/static/11f-env-llm-b-form.js +409 -0
  140. codeagent/web/static/11f-env-llm-c-board.js +322 -0
  141. codeagent/web/static/11g-env-paths-git.js +973 -0
  142. codeagent/web/static/11h-env-mcp-a-state.js +161 -0
  143. codeagent/web/static/11h-env-mcp-b-form.js +229 -0
  144. codeagent/web/static/11h-env-mcp-c-board.js +565 -0
  145. codeagent/web/static/12-plugins.js +127 -0
  146. codeagent/web/static/13a-skills.js +156 -0
  147. codeagent/web/static/13a-todo-utils.js +140 -0
  148. codeagent/web/static/13b-todo-fetch.js +106 -0
  149. codeagent/web/static/13b-tools.js +127 -0
  150. codeagent/web/static/13c-todo-crud.js +92 -0
  151. codeagent/web/static/14-activity-bar.js +177 -0
  152. codeagent/web/static/15-plans.css +194 -0
  153. codeagent/web/static/15-plans.js +219 -0
  154. codeagent/web/static/16-mobile.css +612 -0
  155. codeagent/web/static/16-mobile.js +178 -0
  156. codeagent/web/static/17-preset-ux.css +287 -0
  157. codeagent/web/static/18-agent-mgr.css +616 -0
  158. codeagent/web/static/18-agent-mgr.js +204 -0
  159. codeagent/web/static/24-health-dashboard.js +68 -0
  160. codeagent/web/static/99-brand-polish.css +152 -0
  161. codeagent/web/static/body.html +824 -0
  162. codeagent/web/static/setup/setup_form.html +171 -0
  163. codeagent/web/static/setup/setup_head.html +100 -0
  164. codeagent/web/static/setup/setup_tail.html +264 -0
  165. codeagent/web/static/setup/setup_test.html +38 -0
  166. codeagent/web/web_login.html +265 -0
  167. codeagent/web/webui.html +35 -0
  168. tagword_codeagent-1.1.6.dist-info/METADATA +273 -0
  169. tagword_codeagent-1.1.6.dist-info/RECORD +173 -0
  170. tagword_codeagent-1.1.6.dist-info/WHEEL +5 -0
  171. tagword_codeagent-1.1.6.dist-info/entry_points.txt +3 -0
  172. tagword_codeagent-1.1.6.dist-info/licenses/LICENSE +21 -0
  173. tagword_codeagent-1.1.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,161 @@
1
+ /* ================================================================
2
+ * 11h-env-mcp-a-state.js
3
+ * MCP server 状态层:常量、缓存、arg/env 解析、状态行展示。
4
+ * 不依赖任何其他 11h-env-mcp-* 文件(function declaration 全部 hoisted)。
5
+ * 上游依赖:00-utils.js(escapeHtml / escAttr)、00-storage.js。
6
+ * ================================================================ */
7
+
8
+ var MINIMAX_MCP_ID = 'MiniMax';
9
+
10
+ var MCP_TEMPLATES = [
11
+ {
12
+ id: 'minimax',
13
+ label: 'MiniMax Token Plan',
14
+ serverId: MINIMAX_MCP_ID,
15
+ kind: 'minimax',
16
+ hint: 'understand_image、web_search;需 uvx 与 Token Plan Key',
17
+ },
18
+ {
19
+ id: 'uvx',
20
+ label: 'uvx 包',
21
+ serverId: '',
22
+ kind: 'generic',
23
+ config: {
24
+ enabled: true,
25
+ transport: 'stdio',
26
+ command: 'uvx',
27
+ args: ['your-mcp-package', '-y'],
28
+ env: {},
29
+ },
30
+ hint: '将 your-mcp-package 换成 PyPI 包名',
31
+ },
32
+ {
33
+ id: 'npx',
34
+ label: 'npx 包',
35
+ serverId: '',
36
+ kind: 'generic',
37
+ config: {
38
+ enabled: true,
39
+ transport: 'stdio',
40
+ command: 'npx',
41
+ args: ['-y', '@modelcontextprotocol/server-filesystem', '/path/to/dir'],
42
+ env: {},
43
+ },
44
+ hint: 'Node MCP 示例;请修改 args 中的路径或包名',
45
+ },
46
+ {
47
+ id: 'custom',
48
+ label: '自定义 MCP',
49
+ serverId: '',
50
+ kind: 'generic',
51
+ config: {
52
+ enabled: true,
53
+ transport: 'stdio',
54
+ command: '',
55
+ args: [],
56
+ env: {},
57
+ },
58
+ hint: '选择传输协议;stdio 可填写 uvx / npx / 本地二进制,HTTP / SSE 填写远程 URL。',
59
+ },
60
+ {
61
+ id: 'sse',
62
+ label: 'SSE 远程服务器',
63
+ serverId: '',
64
+ kind: 'generic',
65
+ config: {
66
+ enabled: true,
67
+ transport: 'sse',
68
+ url: '',
69
+ env: {},
70
+ },
71
+ hint: '填写 SSE endpoint URL(如 http://host:port/sse)',
72
+ },
73
+ {
74
+ id: 'streamable-http',
75
+ label: 'Streamable HTTP',
76
+ serverId: '',
77
+ kind: 'generic',
78
+ config: {
79
+ enabled: true,
80
+ transport: 'streamable-http',
81
+ url: '',
82
+ headers: {},
83
+ env: {},
84
+ },
85
+ hint: 'Streamable HTTP MCP 服务(MCP Spec 2025-06-18 标准);填写 URL 及可选请求头',
86
+ },
87
+ ];
88
+
89
+ window._mcpConfigCache = { servers: {} };
90
+ window._mcpStatusCache = [];
91
+
92
+ function _mcpEnvVal(j, key, fallback) {
93
+ if (j[key] !== undefined && j[key] !== null && String(j[key]) !== '') return j[key];
94
+ return fallback;
95
+ }
96
+
97
+ function isMinimaxMcpServer(id, cfg) {
98
+ if (id === MINIMAX_MCP_ID) return true;
99
+ const args = (cfg && cfg.args) || [];
100
+ return args.indexOf('minimax-coding-plan-mcp') >= 0;
101
+ }
102
+
103
+ function mcpStatusForId(serverId) {
104
+ return (window._mcpStatusCache || []).find(function(r) { return r.id === serverId; }) || null;
105
+ }
106
+
107
+ function parseArgsText(raw) {
108
+ const s = String(raw || '').trim();
109
+ if (!s) return [];
110
+ if (s.startsWith('[')) {
111
+ try {
112
+ const arr = JSON.parse(s);
113
+ if (Array.isArray(arr)) return arr.map(String);
114
+ } catch (_) {}
115
+ }
116
+ return s.split(/\s+/).filter(Boolean);
117
+ }
118
+
119
+ function formatArgsList(args) {
120
+ if (!Array.isArray(args) || !args.length) return '';
121
+ return args.join(' ');
122
+ }
123
+
124
+ function parseEnvLines(raw) {
125
+ const out = {};
126
+ String(raw || '').split('\n').forEach(function(line) {
127
+ const t = line.trim();
128
+ if (!t || t.startsWith('#')) return;
129
+ const i = t.indexOf('=');
130
+ if (i <= 0) return;
131
+ out[t.slice(0, i).trim()] = t.slice(i + 1).trim();
132
+ });
133
+ return out;
134
+ }
135
+
136
+ function formatEnvLines(env) {
137
+ if (!env || typeof env !== 'object') return '';
138
+ return Object.keys(env).sort().map(function(k) { return k + '=' + env[k]; }).join('\n');
139
+ }
140
+
141
+ function validateMcpServerId(id) {
142
+ return /^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(String(id || '').trim());
143
+ }
144
+
145
+ /** 生成简短的状态行文字(用于卡片 badge)。 */
146
+ function mcpStatusLine(status) {
147
+ if (!status) return '未探测';
148
+ if (status.connected) {
149
+ var parts = [];
150
+ parts.push('已连接');
151
+ if (status.tools && status.tools.length) {
152
+ parts.push(status.tools.length + ' 工具');
153
+ }
154
+ if (status.skills && status.skills.length) {
155
+ parts.push(status.skills.length + ' skills');
156
+ }
157
+ return parts.join(' · ');
158
+ }
159
+ if (!status.enabled) return '已禁用';
160
+ return '未连接';
161
+ }
@@ -0,0 +1,229 @@
1
+ /* ================================================================
2
+ * 11h-env-mcp-b-form.js
3
+ * MCP server 表单 HTML 构造 + 收集器。
4
+ * 依赖:a-state.js 的 isMinimaxMcpServer / MINIMAX_MCP_ID / validateMcpServerId / parseArgsText。
5
+ * 上游依赖:00-utils.js(escAttr)。
6
+ * ================================================================ */
7
+
8
+ /* ---- 表单构造 ---- */
9
+
10
+ function mcpGenericFormHtml(cfg, opts) {
11
+ opts = opts || {};
12
+ cfg = cfg || {};
13
+ const idReadonly = opts.idReadonly ? ' readonly' : '';
14
+ const transport = cfg.transport || 'stdio';
15
+ const stdioSel = transport === 'stdio' ? ' selected' : '';
16
+ const sseSel = transport === 'sse' ? ' selected' : '';
17
+ const streamableSel = transport === 'streamable-http' ? ' selected' : '';
18
+ const stdioVis = transport === 'stdio' ? '' : ' style="display:none;"';
19
+ const sseVis = transport === 'sse' ? '' : ' style="display:none;"';
20
+ const streamableVis = transport === 'streamable-http' ? '' : ' style="display:none;"';
21
+ const remoteVis = (transport === 'sse' || transport === 'streamable-http') ? '' : ' style="display:none;"';
22
+
23
+ const c = function(v) { return escAttr(v || ''); };
24
+ const isStreamable = transport === 'streamable-http';
25
+ const isSse = transport === 'sse';
26
+
27
+ return (
28
+ // ---- 基础信息 ----
29
+ '<div class="mcp-form-section">' +
30
+ '<label class="form-label">服务 ID</label>' +
31
+ '<input class="mcp-fld-id form-input-sm" type="text" placeholder="MyMCP" value="' + c(opts.serverId || '') + '"' + idReadonly + '/>' +
32
+
33
+ '<label class="checkbox-row" style="margin:var(--sp-1) 0 var(--sp-2);">' +
34
+ '<input type="checkbox" class="mcp-fld-enabled"' + (cfg.enabled !== false ? ' checked' : '') + '/>' +
35
+ '<span>启用</span></label>' +
36
+
37
+ // ---- 传输协议选择 ----
38
+ '<div class="form-row">' +
39
+ '<label class="form-label">传输协议</label>' +
40
+ '<select class="mcp-fld-transport form-input-sm mcp-transport-sel" onchange="mcpToggleTransport(this)">' +
41
+ '<option value="stdio"' + stdioSel + '>stdio(uvx / npx / 本地进程)</option>' +
42
+ '<option value="streamable-http"' + streamableSel + '>HTTP(Streamable HTTP,推荐远程)</option>' +
43
+ '<option value="sse"' + sseSel + '>SSE(旧版远程)</option>' +
44
+ '</select></div>' +
45
+
46
+ // ---- stdio 字段组 ----
47
+ '<div class="mcp-stdio-fields mcp-field-group"' + stdioVis + '>' +
48
+ '<div class="mcp-field-group__label">子进程配置</div>' +
49
+ '<label class="form-label">Command</label>' +
50
+ '<input class="mcp-fld-command form-input-sm" type="text" placeholder="uvx / npx / /path/to/bin" value="' + c(cfg.command || '') + '"/>' +
51
+ '<label class="form-label">Args</label>' +
52
+ '<input class="mcp-fld-args form-input-sm" type="text" placeholder="例如:your-mcp-package -y,或 JSON 数组" value="' + c(formatArgsList(cfg.args)) + '"/>' +
53
+ '<label class="form-label">工作目录 cwd(可选)</label>' +
54
+ '<input class="mcp-fld-cwd form-input-sm" type="text" value="' + c(cfg.cwd || '') + '"/>' +
55
+ '</div>' +
56
+
57
+ // ---- 远程连接字段组(SSE + Streamable HTTP 共用 URL 与 headers) ----
58
+ '<div class="mcp-remote-fields mcp-field-group"' + remoteVis + '>' +
59
+ '<div class="mcp-field-group__label">远程连接配置</div>' +
60
+ '<label class="form-label">Endpoint URL</label>' +
61
+ '<input class="mcp-fld-url form-input-sm" type="text" placeholder="http://host:port/mcp" value="' + c(cfg.url || '') + '"/>' +
62
+ '<label class="form-label">Headers <span class="form-hint">(每行 KEY=value,用于 Authorization 等认证)</span></label>' +
63
+ '<textarea class="mcp-fld-headers form-input-sm" rows="3" placeholder="Authorization=Bearer xxx&#10;X-API-Key=yyy" style="font-family:var(--font-mono);font-size:var(--fs-sm);">' + c(formatEnvLines(cfg.headers)) + '</textarea>' +
64
+ '<div class="form-hint" style="margin-top:var(--sp-1);">' +
65
+ (isSse
66
+ ? '⚠ SSE 协议已废弃(Spec 2024-11-05),建议改用 Streamable HTTP(Spec 2025-06-18)。'
67
+ : (isStreamable
68
+ ? 'Streamable HTTP 自动携带 <code>MCP-Protocol-Version: 2025-11-25</code> 头,无需手动填写。'
69
+ : '')) +
70
+ '</div>' +
71
+ '</div>' +
72
+
73
+ // ---- 环境变量 ----
74
+ '<div class="mcp-field-group">' +
75
+ '<div class="mcp-field-group__label">环境变量</div>' +
76
+ '<textarea class="mcp-fld-env form-input-sm" rows="4" placeholder="KEY=value&#10;每行一个" style="font-family:var(--font-mono);font-size:var(--fs-sm);">' + c(formatEnvLines(cfg.env)) + '</textarea>' +
77
+ '</div>' +
78
+ '</div>'
79
+ );
80
+ }
81
+
82
+ function mcpMinimaxFormHtml(cfg, meta) {
83
+ meta = meta || {};
84
+ cfg = cfg || {};
85
+ const env = cfg.env || {};
86
+ const c = function(v) { return escAttr(v || ''); };
87
+ return (
88
+ '<input type="hidden" class="mcp-fld-kind" value="minimax"/>' +
89
+
90
+ '<div class="mcp-form-section">' +
91
+ '<label class="checkbox-row" style="margin:0 0 var(--sp-2);">' +
92
+ '<input type="checkbox" class="mcp-fld-enabled"' + (cfg.enabled !== false ? ' checked' : '') + '/>' +
93
+ '<span>启用 MiniMax MCP</span></label>' +
94
+
95
+ '<div class="mcp-field-group">' +
96
+ '<div class="mcp-field-group__label">密钥</div>' +
97
+ '<div class="form-row sub-row">' +
98
+ ' <label class="form-label">Token Plan API Key</label>' +
99
+ ' <input class="mcp-fld-minimax-key form-input-sm" type="password" value="' + c(env.MINIMAX_API_KEY || '') + '" autocomplete="off"/>' +
100
+ '</div>' +
101
+ '<div class="form-row sub-row">' +
102
+ ' <label class="form-label">朗读 API Key(可选)</label>' +
103
+ ' <input class="mcp-fld-minimax-tts-key form-input-sm" type="password" value="' + c(env.MINIMAX_TTS_API_KEY || '') + '" autocomplete="off"/>' +
104
+ '</div>' +
105
+ '</div>' +
106
+
107
+ '<div class="mcp-field-group">' +
108
+ '<div class="mcp-field-group__label">高级选项</div>' +
109
+ '<div class="form-row sub-row">' +
110
+ ' <label class="form-label">API Host</label>' +
111
+ ' <input class="mcp-fld-minimax-host form-input-sm" type="text" value="' + c(env.MINIMAX_API_HOST || 'https://api.minimaxi.com') + '"/>' +
112
+ '</div>' +
113
+ '<div class="form-row sub-row">' +
114
+ ' <label class="form-label">uvx 路径</label>' +
115
+ ' <input class="mcp-fld-minimax-uvx form-input-sm" type="text" value="' + c(cfg.command || meta.uvx_path || 'uvx') + '"/>' +
116
+ '</div>' +
117
+ '<div class="form-row sub-row">' +
118
+ ' <label class="form-label">本地输出目录</label>' +
119
+ ' <input class="mcp-fld-minimax-base form-input-sm" type="text" value="' + c(env.MINIMAX_MCP_BASE_PATH || meta.minimax_output_dir || '') + '"/>' +
120
+ '</div>' +
121
+ '<div class="form-row sub-row">' +
122
+ ' <label class="form-label">资源模式</label>' +
123
+ ' <select class="mcp-fld-minimax-mode md-select" style="max-width:200px;">' +
124
+ ' <option value=""' + (!env.MINIMAX_API_RESOURCE_MODE ? ' selected' : '') + '>默认</option>' +
125
+ ' <option value="url"' + (env.MINIMAX_API_RESOURCE_MODE === 'url' ? ' selected' : '') + '>url</option>' +
126
+ ' <option value="local"' + (env.MINIMAX_API_RESOURCE_MODE === 'local' ? ' selected' : '') + '>local</option>' +
127
+ ' </select>' +
128
+ '</div>' +
129
+ '</div>' +
130
+ '</div>'
131
+ );
132
+ }
133
+
134
+ /* ---- Transport 切换 ---- */
135
+
136
+ /** Toggle stdio/sse/streamable-http field visibility + update hint. */
137
+ function mcpToggleTransport(sel) {
138
+ var wrap = sel.closest('.mcp-card-wrap') || sel.closest('.mcp-edit-wrap');
139
+ if (!wrap) return;
140
+ var stdioFields = wrap.querySelector('.mcp-stdio-fields');
141
+ var remoteFields = wrap.querySelector('.mcp-remote-fields');
142
+ if (!stdioFields || !remoteFields) return;
143
+ if (sel.value === 'stdio') {
144
+ stdioFields.style.display = '';
145
+ remoteFields.style.display = 'none';
146
+ } else {
147
+ // 'sse' or 'streamable-http'
148
+ stdioFields.style.display = 'none';
149
+ remoteFields.style.display = '';
150
+ }
151
+ }
152
+
153
+ /* ---- 采集器 ---- */
154
+
155
+ function collectGenericServerFromWrap(wrap) {
156
+ const id = (wrap.querySelector('.mcp-fld-id') || {}).value.trim();
157
+ if (!validateMcpServerId(id)) throw new Error('服务 ID 无效(字母开头,仅含字母数字 _ -)');
158
+ const enabled = !!(wrap.querySelector('.mcp-fld-enabled') || {}).checked;
159
+ const transport = (wrap.querySelector('.mcp-fld-transport') || {}).value || 'stdio';
160
+ const env = parseEnvLines((wrap.querySelector('.mcp-fld-env') || {}).value);
161
+
162
+ if (transport === 'sse' || transport === 'streamable-http') {
163
+ const url = (wrap.querySelector('.mcp-fld-url') || {}).value.trim();
164
+ const headers = parseEnvLines((wrap.querySelector('.mcp-fld-headers') || {}).value);
165
+ if (enabled && !url) throw new Error('服务「' + id + '」:' + transport + ' 模式请填写 URL');
166
+ if (!enabled && !url) return null;
167
+ const row = { enabled: enabled, transport: transport, url: url, env: env };
168
+ if (Object.keys(headers).length > 0) row.headers = headers;
169
+ return { id: id, config: row };
170
+ }
171
+
172
+ // stdio
173
+ const command = (wrap.querySelector('.mcp-fld-command') || {}).value.trim();
174
+ const args = parseArgsText((wrap.querySelector('.mcp-fld-args') || {}).value);
175
+ const cwd = (wrap.querySelector('.mcp-fld-cwd') || {}).value.trim();
176
+ if (!enabled && !command) return null;
177
+ if (enabled && !command) throw new Error('服务「' + id + '」:请填写 command');
178
+ const row = { enabled: enabled, transport: 'stdio', command: command, args: args, env: env };
179
+ if (cwd) row.cwd = cwd;
180
+ return { id: id, config: row };
181
+ }
182
+
183
+ function collectMinimaxServerFromWrap(wrap, meta) {
184
+ meta = meta || {};
185
+ const enabled = !!(wrap.querySelector('.mcp-fld-enabled') || {}).checked;
186
+ const key = (wrap.querySelector('.mcp-fld-minimax-key') || {}).value.trim();
187
+ const ttsKey = (wrap.querySelector('.mcp-fld-minimax-tts-key') || {}).value.trim();
188
+ const host = (wrap.querySelector('.mcp-fld-minimax-host') || {}).value.trim() || 'https://api.minimaxi.com';
189
+ const uvx = (wrap.querySelector('.mcp-fld-minimax-uvx') || {}).value.trim() || 'uvx';
190
+ const basePath = (wrap.querySelector('.mcp-fld-minimax-base') || {}).value.trim()
191
+ || meta.minimax_output_dir || '';
192
+ const mode = (wrap.querySelector('.mcp-fld-minimax-mode') || {}).value.trim();
193
+ if (!enabled && !key) return null;
194
+ const env = {
195
+ MINIMAX_API_KEY: key,
196
+ MINIMAX_API_HOST: host.replace(/\/+$/, ''),
197
+ MINIMAX_MCP_BASE_PATH: basePath,
198
+ };
199
+ if (ttsKey) env.MINIMAX_TTS_API_KEY = ttsKey;
200
+ if (mode === 'url' || mode === 'local') env.MINIMAX_API_RESOURCE_MODE = mode;
201
+ return {
202
+ id: MINIMAX_MCP_ID,
203
+ config: { enabled: enabled, transport: 'stdio', command: uvx, args: ['minimax-coding-plan-mcp', '-y'], env: env },
204
+ };
205
+ }
206
+
207
+ function collectServerFromWrap(wrap, meta) {
208
+ const kind = (wrap.querySelector('.mcp-fld-kind') || {}).value;
209
+ const sid = wrap.getAttribute('data-server-id') || '';
210
+ if (kind === 'minimax' || sid === MINIMAX_MCP_ID || isMinimaxMcpServer(sid, {})) {
211
+ return collectMinimaxServerFromWrap(wrap, meta);
212
+ }
213
+ return collectGenericServerFromWrap(wrap);
214
+ }
215
+
216
+ function collectAllMcpServersFromBoard(meta) {
217
+ const servers = {};
218
+ const board = document.getElementById('mcpServerBoard');
219
+ if (!board) return servers;
220
+ board.querySelectorAll('.mcp-card-wrap').forEach(function(wrap) {
221
+ try {
222
+ const row = collectServerFromWrap(wrap, meta);
223
+ if (row && row.id) servers[row.id] = row.config;
224
+ } catch (e) {
225
+ throw e;
226
+ }
227
+ });
228
+ return servers;
229
+ }