@uniqueli/openwork 0.2.4 → 0.4.0

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.
@@ -12813,10 +12813,20 @@ const Pencil = createLucideIcon("Pencil", [
12813
12813
  ],
12814
12814
  ["path", { d: "m15 5 4 4", key: "1mk7zo" }]
12815
12815
  ]);
12816
+ const Plug = createLucideIcon("Plug", [
12817
+ ["path", { d: "M12 22v-5", key: "1ega77" }],
12818
+ ["path", { d: "M9 8V2", key: "14iosj" }],
12819
+ ["path", { d: "M15 8V2", key: "18g5xt" }],
12820
+ ["path", { d: "M18 8v5a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V8Z", key: "osxo6l" }]
12821
+ ]);
12816
12822
  const Plus = createLucideIcon("Plus", [
12817
12823
  ["path", { d: "M5 12h14", key: "1ays0h" }],
12818
12824
  ["path", { d: "M12 5v14", key: "s699le" }]
12819
12825
  ]);
12826
+ const Power = createLucideIcon("Power", [
12827
+ ["path", { d: "M12 2v10", key: "mnfbl" }],
12828
+ ["path", { d: "M18.4 6.6a9 9 0 1 1-12.77.04", key: "obofu9" }]
12829
+ ]);
12820
12830
  const RotateCw = createLucideIcon("RotateCw", [
12821
12831
  ["path", { d: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8", key: "1p45f6" }],
12822
12832
  ["path", { d: "M21 3v5h-5", key: "1q7to0" }]
@@ -17995,6 +18005,13 @@ function convertToV1FromResponses(message) {
17995
18005
  };
17996
18006
  continue;
17997
18007
  } else if (_isContentBlock(toolOutput, "image_generation_call")) {
18008
+ if (_isString(toolOutput.result)) yield {
18009
+ type: "image",
18010
+ mimeType: "image/png",
18011
+ data: toolOutput.result,
18012
+ id: _isString(toolOutput.id) ? toolOutput.id : void 0,
18013
+ metadata: { status: _isString(toolOutput.status) ? toolOutput.status : void 0 }
18014
+ };
17998
18015
  yield {
17999
18016
  type: "non_standard",
18000
18017
  value: toolOutput
@@ -18238,8 +18255,8 @@ const DEFAULT_MERGE_IGNORE_KEYS = [
18238
18255
  ];
18239
18256
  function _mergeDicts(left, right, options) {
18240
18257
  const ignoreKeys = options?.ignoreKeys ?? DEFAULT_MERGE_IGNORE_KEYS;
18241
- if (left === void 0 && right === void 0) return void 0;
18242
- if (left === void 0 || right === void 0) return left ?? right;
18258
+ if (left == null && right == null) return void 0;
18259
+ if (left == null || right == null) return left ?? right;
18243
18260
  const merged = { ...left };
18244
18261
  for (const [key2, value] of Object.entries(right)) if (merged[key2] == null) merged[key2] = value;
18245
18262
  else if (value == null) continue;
@@ -18264,8 +18281,8 @@ function _mergeDicts(left, right, options) {
18264
18281
  return merged;
18265
18282
  }
18266
18283
  function _mergeLists(left, right, options) {
18267
- if (left === void 0 && right === void 0) return void 0;
18268
- else if (left === void 0 || right === void 0) return left || right;
18284
+ if (left == null && right == null) return void 0;
18285
+ else if (left == null || right == null) return left || right;
18269
18286
  else {
18270
18287
  const merged = [...left];
18271
18288
  for (const item of right) if (typeof item === "object" && item !== null && "index" in item && typeof item.index === "number") {
@@ -18284,8 +18301,8 @@ function _mergeLists(left, right, options) {
18284
18301
  }
18285
18302
  }
18286
18303
  function _mergeObj(left, right, options) {
18287
- if (left === void 0 && right === void 0) return void 0;
18288
- if (left === void 0 || right === void 0) return left ?? right;
18304
+ if (left == null && right == null) return void 0;
18305
+ if (left == null || right == null) return left ?? right;
18289
18306
  else if (typeof left !== typeof right) throw new Error(`Cannot merge objects of different types.
18290
18307
  Left ${typeof left}
18291
18308
  Right ${typeof right}`);
@@ -30375,7 +30392,7 @@ var CallbackManager = class CallbackManager2 extends BaseCallbackManager {
30375
30392
  callbackManager = callbackManager.copy(Array.isArray(localHandlers) ? localHandlers.map(ensureHandler) : localHandlers?.handlers, false);
30376
30393
  }
30377
30394
  const verboseEnabled = getEnvironmentVariable$2("LANGCHAIN_VERBOSE") === "true" || options?.verbose;
30378
- const tracingV2Enabled = LangChainTracer.getTraceableRunTree()?.tracingEnabled || isTracingEnabled();
30395
+ const tracingV2Enabled = LangChainTracer.getTraceableRunTree()?.tracingEnabled ?? isTracingEnabled();
30379
30396
  const tracingEnabled = tracingV2Enabled || (getEnvironmentVariable$2("LANGCHAIN_TRACING") ?? false);
30380
30397
  if (verboseEnabled || tracingEnabled) {
30381
30398
  if (!callbackManager) callbackManager = new CallbackManager2();
@@ -31950,7 +31967,9 @@ const defaultFailedAttemptHandler = (error) => {
31950
31967
  if (typeof error !== "object" || error === null) return;
31951
31968
  if ("message" in error && typeof error.message === "string" && (error.message.startsWith("Cancel") || error.message.startsWith("AbortError")) || "name" in error && typeof error.name === "string" && error.name === "AbortError") throw error;
31952
31969
  if ("code" in error && typeof error.code === "string" && error.code === "ECONNABORTED") throw error;
31953
- const status = "response" in error && typeof error.response === "object" && error.response !== null && "status" in error.response && typeof error.response.status === "number" ? error.response.status : void 0;
31970
+ const responseStatus = "response" in error && typeof error.response === "object" && error.response !== null && "status" in error.response && typeof error.response.status === "number" ? error.response.status : void 0;
31971
+ const directStatus = "status" in error && typeof error.status === "number" ? error.status : void 0;
31972
+ const status = responseStatus ?? directStatus;
31954
31973
  if (status && STATUS_NO_RETRY$1.includes(+status)) throw error;
31955
31974
  const code2 = "error" in error && typeof error.error === "object" && error.error !== null && "code" in error.error && typeof error.error.code === "string" ? error.error.code : void 0;
31956
31975
  if (code2 === "insufficient_quota") {
@@ -41351,14 +41370,15 @@ var StreamManager = class {
41351
41370
  };
41352
41371
  getMutateFn = (kind, historyValues) => {
41353
41372
  return (update) => {
41373
+ const stateValues = (this.state.values ?? [null, "stream"])[0];
41354
41374
  const prev = {
41355
41375
  ...historyValues,
41356
- ...(this.state.values ?? [null, "stream"])[0]
41376
+ ...stateValues ?? {}
41357
41377
  };
41358
41378
  const next = typeof update === "function" ? update(prev) : update;
41359
41379
  this.setStreamValues({
41360
41380
  ...prev,
41361
- ...next
41381
+ ...next ?? {}
41362
41382
  }, kind);
41363
41383
  };
41364
41384
  };
@@ -41395,8 +41415,8 @@ var StreamManager = class {
41395
41415
  if (this.matchEventType("checkpoints", event, data)) options.callbacks.onCheckpointEvent?.(data, { namespace });
41396
41416
  if (this.matchEventType("tasks", event, data)) options.callbacks.onTaskEvent?.(data, { namespace });
41397
41417
  if (this.matchEventType("debug", event, data)) options.callbacks.onDebugEvent?.(data, { namespace });
41398
- if (event === "values") if ("__interrupt__" in data) this.setStreamValues((prev) => ({
41399
- ...prev,
41418
+ if (event === "values") if (data != null && typeof data === "object" && "__interrupt__" in data) this.setStreamValues((prev) => ({
41419
+ ...prev ?? {},
41400
41420
  ...data
41401
41421
  }));
41402
41422
  else this.setStreamValues(data);
@@ -41410,7 +41430,7 @@ var StreamManager = class {
41410
41430
  this.setStreamValues((streamValues) => {
41411
41431
  const values$1 = {
41412
41432
  ...options.initialValues,
41413
- ...streamValues
41433
+ ...streamValues ?? {}
41414
41434
  };
41415
41435
  let messages = options.getMessages(values$1).slice();
41416
41436
  const { chunk, index: index2 } = this.messages.get(messageId, messages.length) ?? {};
@@ -42976,7 +42996,7 @@ var BaseClient = class {
42976
42996
  const [url, init] = this.prepareFetchOptions(path2, options);
42977
42997
  let finalInit = init;
42978
42998
  if (this.onRequest) finalInit = await this.onRequest(url, init);
42979
- const response = await this.asyncCaller.fetch(url, finalInit);
42999
+ const response = await this.asyncCaller.fetch(url.toString(), finalInit);
42980
43000
  const body2 = (() => {
42981
43001
  if (response.status === 202 || response.status === 204) return;
42982
43002
  return response.json();
@@ -43006,7 +43026,7 @@ var BaseClient = class {
43006
43026
  json: isReconnect ? void 0 : config2.json
43007
43027
  });
43008
43028
  if (this.onRequest != null) init = await this.onRequest(url, init);
43009
- const response = await this.asyncCaller.fetch(url, init);
43029
+ const response = await this.asyncCaller.fetch(url.toString(), init);
43010
43030
  if (!isReconnect && config2.onInitialResponse) await config2.onInitialResponse(response);
43011
43031
  return {
43012
43032
  response,
@@ -43973,7 +43993,7 @@ var UiClient = class UiClient2 extends BaseClient {
43973
43993
  json: { name: agentName }
43974
43994
  });
43975
43995
  if (this.onRequest != null) init = await this.onRequest(url, init);
43976
- return (await this.asyncCaller.fetch(url, init)).text();
43996
+ return (await this.asyncCaller.fetch(url.toString(), init)).text();
43977
43997
  });
43978
43998
  }
43979
43999
  };
@@ -44038,6 +44058,13 @@ function unique(array) {
44038
44058
  function findLast(array, predicate) {
44039
44059
  for (let i2 = array.length - 1; i2 >= 0; i2 -= 1) if (predicate(array[i2])) return array[i2];
44040
44060
  }
44061
+ async function* filterStream(stream, filter) {
44062
+ while (true) {
44063
+ const { value, done } = await stream.next();
44064
+ if (done) return value;
44065
+ if (filter(value)) yield value;
44066
+ }
44067
+ }
44041
44068
  function getBranchSequence(history) {
44042
44069
  const nodeIds = /* @__PURE__ */ new Set();
44043
44070
  const childrenMap = {};
@@ -44497,11 +44524,12 @@ function useStreamLGP(options) {
44497
44524
  };
44498
44525
  await stream.start(async (signal) => {
44499
44526
  threadIdStreamingRef.current = threadId;
44500
- return client2.runs.joinStream(threadId, runId, {
44527
+ const stream$1 = client2.runs.joinStream(threadId, runId, {
44501
44528
  signal,
44502
44529
  lastEventId,
44503
44530
  streamMode: joinOptions?.streamMode
44504
44531
  });
44532
+ return joinOptions?.filter != null ? filterStream(stream$1, joinOptions.filter) : stream$1;
44505
44533
  }, {
44506
44534
  getMessages,
44507
44535
  setMessages,
@@ -76278,7 +76306,14 @@ function TabbedPanel({ threadId, showTabBar = true }) {
76278
76306
  ) })
76279
76307
  ] });
76280
76308
  }
76281
- function Switch({ checked = false, onCheckedChange, disabled = false, className, onClick, ...props }) {
76309
+ function Switch({
76310
+ checked = false,
76311
+ onCheckedChange,
76312
+ disabled = false,
76313
+ className,
76314
+ onClick,
76315
+ ...props
76316
+ }) {
76282
76317
  const handleClick = (e) => {
76283
76318
  onClick?.(e);
76284
76319
  if (!disabled) {
@@ -76611,19 +76646,20 @@ function CreateSkillDialog({ open, onClose, onCreate }) {
76611
76646
  };
76612
76647
  const currentTemplate = SKILL_TEMPLATES.find((t) => t.id === selectedTemplate);
76613
76648
  if (!open) return null;
76614
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-3xl mx-4 bg-[#1A1A1D] rounded-lg shadow-xl border border-white/10", children: [
76615
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-white/5", children: [
76649
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-2xl bg-[#1A1A1D] rounded-lg shadow-xl border border-white/10 flex flex-col max-h-[90vh]", children: [
76650
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-white/5 flex-shrink-0", children: [
76616
76651
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-semibold text-white", children: "Create Custom Skill" }),
76617
- /* @__PURE__ */ jsxRuntimeExports.jsx(
76618
- "button",
76652
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "text-gray-400 hover:text-white transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
76653
+ "path",
76619
76654
  {
76620
- onClick: onClose,
76621
- className: "text-gray-400 hover:text-white transition-colors",
76622
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
76655
+ strokeLinecap: "round",
76656
+ strokeLinejoin: "round",
76657
+ strokeWidth: 2,
76658
+ d: "M6 18L18 6M6 6l12 12"
76623
76659
  }
76624
- )
76660
+ ) }) })
76625
76661
  ] }),
76626
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-6 py-3 border-b border-white/5", children: [
76662
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-6 py-3 border-b border-white/5 flex-shrink-0", children: [
76627
76663
  /* @__PURE__ */ jsxRuntimeExports.jsx(
76628
76664
  "button",
76629
76665
  {
@@ -76643,7 +76679,7 @@ function CreateSkillDialog({ open, onClose, onCreate }) {
76643
76679
  }
76644
76680
  )
76645
76681
  ] }),
76646
- /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, className: "p-6 space-y-4", children: [
76682
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, className: "p-6 space-y-4 overflow-y-auto flex-1", children: [
76647
76683
  mode === "template" && /* Template Selection */
76648
76684
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
76649
76685
  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-300 mb-2", children: "Choose Template" }),
@@ -76749,7 +76785,7 @@ function CreateSkillDialog({ open, onClose, onCreate }) {
76749
76785
  ),
76750
76786
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1 text-xs text-gray-500", children: "This prompt will be loaded when the agent activates this skill, providing specialized knowledge and instructions." })
76751
76787
  ] }),
76752
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-3 pt-4 border-t border-white/5", children: [
76788
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-3 pt-4 border-t border-white/5 flex-shrink-0", children: [
76753
76789
  /* @__PURE__ */ jsxRuntimeExports.jsx(
76754
76790
  "button",
76755
76791
  {
@@ -76780,7 +76816,13 @@ const categoryColors = {
76780
76816
  system: "bg-orange-500/10 text-orange-500 border-orange-500/20",
76781
76817
  custom: "bg-cyan-500/10 text-cyan-500 border-cyan-500/20"
76782
76818
  };
76783
- function SkillDetailDialog({ skill, open, onClose, onToggle, onDelete }) {
76819
+ function SkillDetailDialog({
76820
+ skill,
76821
+ open,
76822
+ onClose,
76823
+ onToggle,
76824
+ onDelete
76825
+ }) {
76784
76826
  if (!open) return null;
76785
76827
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-3xl mx-4 max-h-[80vh] bg-[#1A1A1D] rounded-lg shadow-xl border border-white/10 flex flex-col", children: [
76786
76828
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between px-6 py-4 border-b border-white/5", children: [
@@ -76803,7 +76845,15 @@ function SkillDetailDialog({ skill, open, onClose, onToggle, onDelete }) {
76803
76845
  {
76804
76846
  onClick: onClose,
76805
76847
  className: "ml-4 text-gray-400 hover:text-white transition-colors",
76806
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
76848
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
76849
+ "path",
76850
+ {
76851
+ strokeLinecap: "round",
76852
+ strokeLinejoin: "round",
76853
+ strokeWidth: 2,
76854
+ d: "M6 18L18 6M6 6l12 12"
76855
+ }
76856
+ ) })
76807
76857
  }
76808
76858
  )
76809
76859
  ] }),
@@ -77053,6 +77103,639 @@ function SkillsPanel(_props) {
77053
77103
  )
77054
77104
  ] });
77055
77105
  }
77106
+ const TRANSPORT_TYPES = [
77107
+ { value: "stdio", label: "STDIO", description: "标准输入/输出 (本地进程)" },
77108
+ { value: "sse", label: "SSE", description: "服务器发送事件 (HTTP端点)" }
77109
+ ];
77110
+ const CATEGORIES = [
77111
+ { value: "filesystem", label: "文件系统", description: "文件操作工具" },
77112
+ { value: "database", label: "数据库", description: "数据库访问工具" },
77113
+ { value: "api", label: "API", description: "外部API集成" },
77114
+ { value: "development", label: "开发", description: "开发工具" },
77115
+ { value: "productivity", label: "生产力", description: "生产力工具" },
77116
+ { value: "custom", label: "自定义", description: "其他工具" }
77117
+ ];
77118
+ function CreateMCPServerDialog({ open, onClose, onCreated }) {
77119
+ const [formData, setFormData] = reactExports.useState({
77120
+ id: "",
77121
+ name: "",
77122
+ type: "stdio",
77123
+ command: "",
77124
+ args: "",
77125
+ url: "",
77126
+ description: "",
77127
+ category: "custom",
77128
+ enabled: true,
77129
+ env: {}
77130
+ });
77131
+ const [envVars, setEnvVars] = reactExports.useState([{ key: "", value: "" }]);
77132
+ const [errors, setErrors] = reactExports.useState({});
77133
+ const [testing, setTesting] = reactExports.useState(false);
77134
+ const [testResult, setTestResult] = reactExports.useState(null);
77135
+ const handleEnvVarChange = (index2, field, value) => {
77136
+ const newEnvVars = [...envVars];
77137
+ newEnvVars[index2][field] = value;
77138
+ setEnvVars(newEnvVars);
77139
+ const env2 = {};
77140
+ newEnvVars.forEach((envVar) => {
77141
+ if (envVar.key.trim()) {
77142
+ env2[envVar.key.trim()] = envVar.value;
77143
+ }
77144
+ });
77145
+ setFormData({ ...formData, env: env2 });
77146
+ };
77147
+ const addEnvVar = () => {
77148
+ setEnvVars([...envVars, { key: "", value: "" }]);
77149
+ };
77150
+ const removeEnvVar = (index2) => {
77151
+ const newEnvVars = envVars.filter((_2, i2) => i2 !== index2);
77152
+ setEnvVars(newEnvVars);
77153
+ const env2 = {};
77154
+ newEnvVars.forEach((envVar) => {
77155
+ if (envVar.key.trim()) {
77156
+ env2[envVar.key.trim()] = envVar.value;
77157
+ }
77158
+ });
77159
+ setFormData({ ...formData, env: env2 });
77160
+ };
77161
+ if (!open) return null;
77162
+ const validateForm = () => {
77163
+ const newErrors = {};
77164
+ if (!formData.id.trim()) {
77165
+ newErrors.id = "ID不能为空";
77166
+ } else if (!/^[a-z0-9-]+$/.test(formData.id)) {
77167
+ newErrors.id = "ID只能包含小写字母、数字和连字符";
77168
+ }
77169
+ if (!formData.name.trim()) {
77170
+ newErrors.name = "名称不能为空";
77171
+ }
77172
+ if (formData.type === "stdio" && !formData.command.trim()) {
77173
+ newErrors.command = "STDIO类型需要命令";
77174
+ }
77175
+ if (formData.type === "sse") {
77176
+ try {
77177
+ new URL(formData.url);
77178
+ } catch {
77179
+ newErrors.url = "请输入有效的URL";
77180
+ }
77181
+ }
77182
+ setErrors(newErrors);
77183
+ return Object.keys(newErrors).length === 0;
77184
+ };
77185
+ const handleSubmit = async (e) => {
77186
+ e.preventDefault();
77187
+ if (!validateForm()) return;
77188
+ try {
77189
+ const argsArray = formData.args.split(" ").map((arg) => arg.trim()).filter(Boolean);
77190
+ const result = await window.api.mcp.create({
77191
+ id: formData.id.trim(),
77192
+ name: formData.name.trim(),
77193
+ type: formData.type,
77194
+ command: formData.type === "stdio" ? formData.command.trim() : void 0,
77195
+ args: formData.type === "stdio" && argsArray.length > 0 ? argsArray : void 0,
77196
+ url: formData.type === "sse" ? formData.url.trim() : void 0,
77197
+ description: formData.description.trim() || void 0,
77198
+ category: formData.category,
77199
+ enabled: formData.enabled,
77200
+ env: Object.keys(formData.env).length > 0 ? formData.env : void 0
77201
+ });
77202
+ if (result.success) {
77203
+ onCreated();
77204
+ onClose();
77205
+ } else {
77206
+ setErrors({ id: result.error || "创建失败" });
77207
+ }
77208
+ } catch (error) {
77209
+ console.error("[CreateMCPServerDialog] Create error:", error);
77210
+ setErrors({ id: "创建失败,请重试" });
77211
+ }
77212
+ };
77213
+ const handleTest = async () => {
77214
+ if (formData.type === "stdio" && !formData.command.trim()) {
77215
+ setErrors({ command: "请先输入命令" });
77216
+ return;
77217
+ }
77218
+ if (formData.type === "sse" && !formData.url.trim()) {
77219
+ setErrors({ url: "请先输入URL" });
77220
+ return;
77221
+ }
77222
+ setTesting(true);
77223
+ setTestResult(null);
77224
+ try {
77225
+ const argsArray = formData.args.split(" ").map((arg) => arg.trim()).filter(Boolean);
77226
+ const result = await window.api.mcp.test({
77227
+ type: formData.type,
77228
+ command: formData.type === "stdio" ? formData.command.trim() : void 0,
77229
+ args: formData.type === "stdio" && argsArray.length > 0 ? argsArray : void 0,
77230
+ url: formData.type === "sse" ? formData.url.trim() : void 0,
77231
+ env: Object.keys(formData.env).length > 0 ? formData.env : void 0
77232
+ });
77233
+ setTestResult({
77234
+ success: result.success,
77235
+ message: result.success ? `连接成功!发现 ${result.tools || 0} 个工具` : result.error || "连接失败",
77236
+ tools: result.tools
77237
+ });
77238
+ } catch (error) {
77239
+ setTestResult({
77240
+ success: false,
77241
+ message: error instanceof Error ? error.message : "测试失败"
77242
+ });
77243
+ } finally {
77244
+ setTesting(false);
77245
+ }
77246
+ };
77247
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-2xl bg-[#1A1A1D] rounded-lg shadow-xl border border-white/10 flex flex-col max-h-[90vh]", children: [
77248
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-white/5 flex-shrink-0", children: [
77249
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-semibold text-white", children: "添加MCP服务器" }),
77250
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: onClose, className: "text-gray-400 hover:text-white transition-colors", children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
77251
+ "path",
77252
+ {
77253
+ strokeLinecap: "round",
77254
+ strokeLinejoin: "round",
77255
+ strokeWidth: 2,
77256
+ d: "M6 18L18 6M6 6l12 12"
77257
+ }
77258
+ ) }) })
77259
+ ] }),
77260
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, className: "p-6 space-y-4 overflow-y-auto flex-1", children: [
77261
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77262
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium text-gray-300 mb-2", children: [
77263
+ "传输类型 ",
77264
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-500", children: "*" })
77265
+ ] }),
77266
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-2 gap-2", children: TRANSPORT_TYPES.map((type) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
77267
+ "button",
77268
+ {
77269
+ type: "button",
77270
+ onClick: () => setFormData({ ...formData, type: type.value }),
77271
+ className: `px-3 py-2 text-left text-sm rounded-md transition-colors ${formData.type === type.value ? "bg-blue-500/20 text-blue-400 border border-blue-500/50" : "bg-white/5 text-gray-400 border border-white/10 hover:bg-white/10"}`,
77272
+ children: [
77273
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium", children: type.label }),
77274
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs opacity-75", children: type.description })
77275
+ ]
77276
+ },
77277
+ type.value
77278
+ )) })
77279
+ ] }),
77280
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77281
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium text-gray-300 mb-1", children: [
77282
+ "服务器ID ",
77283
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-500", children: "*" })
77284
+ ] }),
77285
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77286
+ "input",
77287
+ {
77288
+ type: "text",
77289
+ value: formData.id,
77290
+ onChange: (e) => setFormData({ ...formData, id: e.target.value }),
77291
+ placeholder: "例如: filesystem-server",
77292
+ className: "w-full px-3 py-2 text-sm text-white placeholder-gray-500 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-blue-500",
77293
+ required: true
77294
+ }
77295
+ ),
77296
+ errors.id && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-status-critical mt-1", children: errors.id }),
77297
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 mt-1", children: "唯一标识符,只能包含小写字母、数字和连字符" })
77298
+ ] }),
77299
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77300
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium text-gray-300 mb-1", children: [
77301
+ "显示名称 ",
77302
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-500", children: "*" })
77303
+ ] }),
77304
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77305
+ "input",
77306
+ {
77307
+ type: "text",
77308
+ value: formData.name,
77309
+ onChange: (e) => setFormData({ ...formData, name: e.target.value }),
77310
+ placeholder: "例如: 文件系统服务器",
77311
+ className: "w-full px-3 py-2 text-sm text-white placeholder-gray-500 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-blue-500",
77312
+ required: true
77313
+ }
77314
+ ),
77315
+ errors.name && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-status-critical mt-1", children: errors.name })
77316
+ ] }),
77317
+ formData.type === "stdio" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77318
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium text-gray-300 mb-1", children: [
77319
+ "命令 ",
77320
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-500", children: "*" })
77321
+ ] }),
77322
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77323
+ "input",
77324
+ {
77325
+ type: "text",
77326
+ value: formData.command,
77327
+ onChange: (e) => setFormData({ ...formData, command: e.target.value }),
77328
+ placeholder: "例如: npx",
77329
+ className: "w-full px-3 py-2 text-sm text-white placeholder-gray-500 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-blue-500",
77330
+ required: formData.type === "stdio"
77331
+ }
77332
+ ),
77333
+ errors.command && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-status-critical mt-1", children: errors.command })
77334
+ ] }),
77335
+ formData.type === "stdio" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77336
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-300 mb-1", children: "命令参数" }),
77337
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77338
+ "input",
77339
+ {
77340
+ type: "text",
77341
+ value: formData.args,
77342
+ onChange: (e) => setFormData({ ...formData, args: e.target.value }),
77343
+ placeholder: "例如: @modelcontextprotocol/server-filesystem /path/to/dir",
77344
+ className: "w-full px-3 py-2 text-sm text-white placeholder-gray-500 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-blue-500"
77345
+ }
77346
+ ),
77347
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 mt-1", children: "多个参数用空格分隔" })
77348
+ ] }),
77349
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77350
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-300 mb-2", children: "环境变量" }),
77351
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: envVars.map((envVar, index2) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
77352
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77353
+ "input",
77354
+ {
77355
+ type: "text",
77356
+ value: envVar.key,
77357
+ onChange: (e) => handleEnvVarChange(index2, "key", e.target.value),
77358
+ placeholder: "变量名 (如: YAPI_BASE_URL)",
77359
+ className: "flex-1 px-3 py-2 text-sm text-white placeholder-gray-500 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-blue-500"
77360
+ }
77361
+ ),
77362
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77363
+ "input",
77364
+ {
77365
+ type: "text",
77366
+ value: envVar.value,
77367
+ onChange: (e) => handleEnvVarChange(index2, "value", e.target.value),
77368
+ placeholder: "值 (不要包含引号)",
77369
+ className: "flex-1 px-3 py-2 text-sm text-white placeholder-gray-500 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-blue-500"
77370
+ }
77371
+ ),
77372
+ envVars.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
77373
+ "button",
77374
+ {
77375
+ type: "button",
77376
+ onClick: () => removeEnvVar(index2),
77377
+ className: "px-2 py-1 text-status-critical hover:bg-status-critical/20 rounded-md transition-colors",
77378
+ title: "删除",
77379
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
77380
+ }
77381
+ )
77382
+ ] }, index2)) }),
77383
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77384
+ "button",
77385
+ {
77386
+ type: "button",
77387
+ onClick: addEnvVar,
77388
+ className: "mt-2 text-xs text-blue-400 hover:text-blue-300 transition-colors",
77389
+ children: "+ 添加环境变量"
77390
+ }
77391
+ ),
77392
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-gray-500 mt-1", children: "可选,用于传递敏感信息或配置参数。直接输入值即可,不要包含引号或JSON格式" })
77393
+ ] }),
77394
+ formData.type === "sse" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77395
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium text-gray-300 mb-1", children: [
77396
+ "服务器URL ",
77397
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-red-500", children: "*" })
77398
+ ] }),
77399
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77400
+ "input",
77401
+ {
77402
+ type: "text",
77403
+ value: formData.url,
77404
+ onChange: (e) => setFormData({ ...formData, url: e.target.value }),
77405
+ placeholder: "例如: http://localhost:3000/sse",
77406
+ className: "w-full px-3 py-2 text-sm text-white placeholder-gray-500 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-blue-500",
77407
+ required: formData.type === "sse"
77408
+ }
77409
+ ),
77410
+ errors.url && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-status-critical mt-1", children: errors.url })
77411
+ ] }),
77412
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77413
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-300 mb-1", children: "描述" }),
77414
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77415
+ "input",
77416
+ {
77417
+ type: "text",
77418
+ value: formData.description,
77419
+ onChange: (e) => setFormData({ ...formData, description: e.target.value }),
77420
+ placeholder: "可选的服务器描述",
77421
+ className: "w-full px-3 py-2 text-sm text-white placeholder-gray-500 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-blue-500"
77422
+ }
77423
+ )
77424
+ ] }),
77425
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
77426
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-gray-300 mb-2", children: "分类" }),
77427
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-3 gap-2", children: CATEGORIES.map((cat) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
77428
+ "button",
77429
+ {
77430
+ type: "button",
77431
+ onClick: () => setFormData({ ...formData, category: cat.value }),
77432
+ className: `px-3 py-2 text-left text-sm rounded-md transition-colors ${formData.category === cat.value ? "bg-blue-500/20 text-blue-400 border border-blue-500/50" : "bg-white/5 text-gray-400 border border-white/10 hover:bg-white/10"}`,
77433
+ children: [
77434
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium", children: cat.label }),
77435
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs opacity-75", children: cat.description })
77436
+ ]
77437
+ },
77438
+ cat.value
77439
+ )) })
77440
+ ] }),
77441
+ testResult && /* @__PURE__ */ jsxRuntimeExports.jsx(
77442
+ "div",
77443
+ {
77444
+ className: `p-3 rounded-md text-sm ${testResult.success ? "bg-status-nominal/20 text-status-nominal border border-status-nominal/50" : "bg-status-critical/20 text-status-critical border border-status-critical/50"}`,
77445
+ children: testResult.message
77446
+ }
77447
+ )
77448
+ ] }),
77449
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-3 px-6 py-4 border-t border-white/5 flex-shrink-0", children: [
77450
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77451
+ Button,
77452
+ {
77453
+ type: "button",
77454
+ variant: "ghost",
77455
+ onClick: handleTest,
77456
+ disabled: testing,
77457
+ className: "text-xs",
77458
+ children: testing ? "测试中..." : "测试连接"
77459
+ }
77460
+ ),
77461
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
77462
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", variant: "ghost", onClick: onClose, children: "取消" }),
77463
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", onClick: handleSubmit, children: "创建服务器" })
77464
+ ] })
77465
+ ] }) });
77466
+ }
77467
+ function MCPPanel() {
77468
+ const [servers, setServers] = reactExports.useState([]);
77469
+ const [loading, setLoading] = reactExports.useState(true);
77470
+ const [showCreateDialog, setShowCreateDialog] = reactExports.useState(false);
77471
+ reactExports.useEffect(() => {
77472
+ loadServers();
77473
+ }, []);
77474
+ reactExports.useEffect(() => {
77475
+ const interval = setInterval(async () => {
77476
+ const result = await window.api.mcp.getAllStates();
77477
+ if (result.success && result.states) {
77478
+ const stateMap = /* @__PURE__ */ new Map();
77479
+ result.states.forEach((state) => {
77480
+ stateMap.set(state.serverId, state);
77481
+ });
77482
+ setServers(
77483
+ (prev) => prev.map((server) => {
77484
+ const state = stateMap.get(server.id);
77485
+ return {
77486
+ ...server,
77487
+ connectionStatus: state?.status,
77488
+ toolCount: state?.tools?.length || 0
77489
+ };
77490
+ })
77491
+ );
77492
+ }
77493
+ }, 2e3);
77494
+ return () => clearInterval(interval);
77495
+ }, []);
77496
+ async function loadServers() {
77497
+ setLoading(true);
77498
+ try {
77499
+ const result = await window.api.mcp.list();
77500
+ if (result.success && result.servers) {
77501
+ const statesResult = await window.api.mcp.getAllStates();
77502
+ const stateMap = /* @__PURE__ */ new Map();
77503
+ if (statesResult.success && statesResult.states) {
77504
+ statesResult.states.forEach((state) => {
77505
+ stateMap.set(state.serverId, state);
77506
+ });
77507
+ }
77508
+ setServers(
77509
+ result.servers.map((server) => ({
77510
+ ...server,
77511
+ connectionStatus: stateMap.get(server.id)?.status,
77512
+ toolCount: stateMap.get(server.id)?.tools?.length || 0
77513
+ }))
77514
+ );
77515
+ }
77516
+ } catch (error) {
77517
+ console.error("[MCPPanel] Failed to load servers:", error);
77518
+ } finally {
77519
+ setLoading(false);
77520
+ }
77521
+ }
77522
+ async function handleToggleServer(serverId, currentEnabled) {
77523
+ console.log("[MCPPanel] Toggling server:", serverId, "from", currentEnabled, "to", !currentEnabled);
77524
+ try {
77525
+ const result = await window.api.mcp.toggle(serverId, !currentEnabled);
77526
+ console.log("[MCPPanel] Toggle result:", result);
77527
+ if (result.success) {
77528
+ setServers(
77529
+ (prev) => prev.map(
77530
+ (s2) => s2.id === serverId ? { ...s2, enabled: !currentEnabled } : s2
77531
+ )
77532
+ );
77533
+ await loadServers();
77534
+ } else {
77535
+ alert(`切换失败: ${result.error}`);
77536
+ }
77537
+ } catch (error) {
77538
+ console.error("[MCPPanel] Failed to toggle server:", error);
77539
+ alert(`切换失败: ${error instanceof Error ? error.message : "未知错误"}`);
77540
+ }
77541
+ }
77542
+ async function handleDeleteServer(serverId) {
77543
+ if (!confirm("确定要删除这个MCP服务器吗?")) return;
77544
+ console.log("[MCPPanel] Deleting server:", serverId);
77545
+ try {
77546
+ const result = await window.api.mcp.delete(serverId);
77547
+ console.log("[MCPPanel] Delete result:", result);
77548
+ if (result.success) {
77549
+ setServers((prev) => prev.filter((s2) => s2.id !== serverId));
77550
+ await loadServers();
77551
+ } else {
77552
+ alert(`删除失败: ${result.error}`);
77553
+ }
77554
+ } catch (error) {
77555
+ console.error("[MCPPanel] Failed to delete server:", error);
77556
+ alert(`删除失败: ${error instanceof Error ? error.message : "未知错误"}`);
77557
+ }
77558
+ }
77559
+ async function handleConnect(serverId) {
77560
+ console.log("[MCPPanel] Connecting to server:", serverId, "type:", typeof serverId);
77561
+ try {
77562
+ setServers(
77563
+ (prev) => prev.map(
77564
+ (s2) => s2.id === serverId ? { ...s2, connectionStatus: "connecting" } : s2
77565
+ )
77566
+ );
77567
+ console.log("[MCPPanel] Calling window.api.mcp.connect with:", serverId);
77568
+ const result = await window.api.mcp.connect(serverId);
77569
+ console.log("[MCPPanel] Connect result:", result);
77570
+ if (result.success) {
77571
+ await loadServers();
77572
+ } else {
77573
+ alert(`连接失败: ${result.error}`);
77574
+ await loadServers();
77575
+ }
77576
+ } catch (error) {
77577
+ console.error("[MCPPanel] Failed to connect:", error);
77578
+ alert(`连接失败: ${error instanceof Error ? error.message : "未知错误"}`);
77579
+ await loadServers();
77580
+ }
77581
+ }
77582
+ async function handleDisconnect(serverId) {
77583
+ console.log("[MCPPanel] Disconnecting from server:", serverId);
77584
+ try {
77585
+ const result = await window.api.mcp.disconnect(serverId);
77586
+ console.log("[MCPPanel] Disconnect result:", result);
77587
+ if (result.success) {
77588
+ setServers(
77589
+ (prev) => prev.map(
77590
+ (s2) => s2.id === serverId ? { ...s2, connectionStatus: "disconnected", toolCount: 0 } : s2
77591
+ )
77592
+ );
77593
+ await loadServers();
77594
+ } else {
77595
+ alert(`断开连接失败: ${result.error}`);
77596
+ }
77597
+ } catch (error) {
77598
+ console.error("[MCPPanel] Failed to disconnect:", error);
77599
+ alert(`断开连接失败: ${error instanceof Error ? error.message : "未知错误"}`);
77600
+ }
77601
+ }
77602
+ if (loading) {
77603
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center h-40", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-5 animate-spin text-muted-foreground" }) });
77604
+ }
77605
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full", children: [
77606
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between px-3 py-2 border-b border-border/50 bg-background/30", children: [
77607
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
77608
+ servers.filter((s2) => s2.enabled).length,
77609
+ " / ",
77610
+ servers.length,
77611
+ " 启用"
77612
+ ] }),
77613
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
77614
+ Button,
77615
+ {
77616
+ variant: "ghost",
77617
+ size: "sm",
77618
+ onClick: () => setShowCreateDialog(true),
77619
+ className: "h-5 px-1.5 text-[10px]",
77620
+ children: [
77621
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-3" }),
77622
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-1", children: "添加" })
77623
+ ]
77624
+ }
77625
+ )
77626
+ ] }),
77627
+ servers.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center text-center text-sm text-muted-foreground py-8 px-4 flex-1", children: [
77628
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plug, { className: "size-8 mb-2 opacity-50" }),
77629
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "没有MCP服务器" }),
77630
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs mt-1", children: '点击"添加"配置服务器' })
77631
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-2 overflow-auto flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 space-y-2", children: servers.map((server) => /* @__PURE__ */ jsxRuntimeExports.jsx(
77632
+ ServerCard,
77633
+ {
77634
+ server,
77635
+ onToggle: handleToggleServer,
77636
+ onDelete: handleDeleteServer,
77637
+ onConnect: handleConnect,
77638
+ onDisconnect: handleDisconnect
77639
+ },
77640
+ server.id
77641
+ )) }) }),
77642
+ showCreateDialog && /* @__PURE__ */ jsxRuntimeExports.jsx(
77643
+ CreateMCPServerDialog,
77644
+ {
77645
+ open: showCreateDialog,
77646
+ onClose: () => setShowCreateDialog(false),
77647
+ onCreated: loadServers
77648
+ }
77649
+ )
77650
+ ] });
77651
+ }
77652
+ function ServerCard({ server, onToggle, onDelete, onConnect, onDisconnect }) {
77653
+ const getStatusIcon = () => {
77654
+ if (!server.enabled) {
77655
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Circle, { className: "size-3 text-muted-foreground" });
77656
+ }
77657
+ switch (server.connectionStatus) {
77658
+ case "connected":
77659
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3 text-status-nominal" });
77660
+ case "connecting":
77661
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin text-status-info" });
77662
+ case "error":
77663
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3 text-status-critical" });
77664
+ default:
77665
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Circle, { className: "size-3 text-muted-foreground" });
77666
+ }
77667
+ };
77668
+ const getStatusText = () => {
77669
+ if (!server.enabled) return "已禁用";
77670
+ switch (server.connectionStatus) {
77671
+ case "connected":
77672
+ return "已连接";
77673
+ case "connecting":
77674
+ return "连接中";
77675
+ case "error":
77676
+ return "错误";
77677
+ default:
77678
+ return "未连接";
77679
+ }
77680
+ };
77681
+ const getTransportTypeLabel = () => {
77682
+ return server.type === "stdio" ? "STDIO" : "SSE";
77683
+ };
77684
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3 rounded-sm border border-border bg-background/50 hover:bg-background transition-colors", children: [
77685
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
77686
+ getStatusIcon(),
77687
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 text-sm font-medium truncate", children: server.name }),
77688
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "text-[9px] px-1 py-0 h-4", children: getTransportTypeLabel() })
77689
+ ] }),
77690
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground mb-2 space-y-0.5", children: [
77691
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
77692
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "状态:" }),
77693
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: server.connectionStatus === "connected" ? "text-status-nominal" : "", children: getStatusText() })
77694
+ ] }),
77695
+ server.connectionStatus === "connected" && server.toolCount !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
77696
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "工具:" }),
77697
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: server.toolCount })
77698
+ ] }),
77699
+ server.description && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] truncate", title: server.description, children: server.description })
77700
+ ] }),
77701
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 mt-2", children: [
77702
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77703
+ Button,
77704
+ {
77705
+ variant: "ghost",
77706
+ size: "sm",
77707
+ onClick: () => onToggle(server.id, server.enabled),
77708
+ className: `h-6 px-2 text-[10px] flex-1 ${server.enabled ? "text-status-nominal hover:text-status-nominal" : ""}`,
77709
+ title: server.enabled ? "禁用" : "启用",
77710
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Power, { className: "size-3" })
77711
+ }
77712
+ ),
77713
+ server.enabled && /* @__PURE__ */ jsxRuntimeExports.jsx(
77714
+ Button,
77715
+ {
77716
+ variant: "ghost",
77717
+ size: "sm",
77718
+ onClick: () => server.connectionStatus === "connected" ? onDisconnect(server.id) : onConnect(server.id),
77719
+ className: "h-6 px-2 text-[10px] flex-1",
77720
+ disabled: server.connectionStatus === "connecting",
77721
+ title: server.connectionStatus === "connected" ? "断开" : "连接",
77722
+ children: server.connectionStatus === "connecting" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }) : server.connectionStatus === "connected" ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Plug, { className: "size-3" })
77723
+ }
77724
+ ),
77725
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
77726
+ Button,
77727
+ {
77728
+ variant: "ghost",
77729
+ size: "sm",
77730
+ onClick: () => onDelete(server.id),
77731
+ className: "h-6 px-2 text-[10px] text-status-critical hover:text-status-critical",
77732
+ title: "删除",
77733
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3" })
77734
+ }
77735
+ )
77736
+ ] })
77737
+ ] });
77738
+ }
77056
77739
  const HEADER_HEIGHT = 40;
77057
77740
  const HANDLE_HEIGHT = 6;
77058
77741
  const MIN_CONTENT_HEIGHT = 60;
@@ -77131,31 +77814,34 @@ function RightPanel() {
77131
77814
  const [filesOpen, setFilesOpen] = reactExports.useState(true);
77132
77815
  const [agentsOpen, setAgentsOpen] = reactExports.useState(true);
77133
77816
  const [skillsOpen, setSkillsOpen] = reactExports.useState(false);
77817
+ const [mcpOpen, setMcpOpen] = reactExports.useState(false);
77134
77818
  const [tasksHeight, setTasksHeight] = reactExports.useState(null);
77135
77819
  const [filesHeight, setFilesHeight] = reactExports.useState(null);
77136
77820
  const [agentsHeight, setAgentsHeight] = reactExports.useState(null);
77137
77821
  const [skillsHeight, setSkillsHeight] = reactExports.useState(null);
77822
+ const [mcpHeight, setMcpHeight] = reactExports.useState(null);
77138
77823
  const dragStartHeights = reactExports.useRef(null);
77139
77824
  const getAvailableContentHeight = reactExports.useCallback(() => {
77140
77825
  if (!containerRef.current) return 0;
77141
77826
  const totalHeight = containerRef.current.clientHeight;
77142
- let used = HEADER_HEIGHT * 4;
77143
- const openPanels = [tasksOpen, filesOpen, agentsOpen, skillsOpen].filter(Boolean).length;
77827
+ let used = HEADER_HEIGHT * 5;
77828
+ const openPanels = [tasksOpen, filesOpen, agentsOpen, skillsOpen, mcpOpen].filter(Boolean).length;
77144
77829
  used += HANDLE_HEIGHT * (openPanels - 1);
77145
77830
  return Math.max(0, totalHeight - used);
77146
- }, [tasksOpen, filesOpen, agentsOpen, skillsOpen]);
77831
+ }, [tasksOpen, filesOpen, agentsOpen, skillsOpen, mcpOpen]);
77147
77832
  const getContentHeights = reactExports.useCallback(() => {
77148
77833
  const available = getAvailableContentHeight();
77149
- const openCount = [tasksOpen, filesOpen, agentsOpen, skillsOpen].filter(Boolean).length;
77834
+ const openCount = [tasksOpen, filesOpen, agentsOpen, skillsOpen, mcpOpen].filter(Boolean).length;
77150
77835
  if (openCount === 0) {
77151
- return { tasks: 0, files: 0, agents: 0, skills: 0 };
77836
+ return { tasks: 0, files: 0, agents: 0, skills: 0, mcp: 0 };
77152
77837
  }
77153
77838
  const defaultHeight = available / openCount;
77154
77839
  return {
77155
77840
  tasks: tasksOpen ? tasksHeight ?? defaultHeight : 0,
77156
77841
  files: filesOpen ? filesHeight ?? defaultHeight : 0,
77157
77842
  agents: agentsOpen ? agentsHeight ?? defaultHeight : 0,
77158
- skills: skillsOpen ? skillsHeight ?? defaultHeight : 0
77843
+ skills: skillsOpen ? skillsHeight ?? defaultHeight : 0,
77844
+ mcp: mcpOpen ? mcpHeight ?? defaultHeight : 0
77159
77845
  };
77160
77846
  }, [
77161
77847
  getAvailableContentHeight,
@@ -77163,10 +77849,12 @@ function RightPanel() {
77163
77849
  filesOpen,
77164
77850
  agentsOpen,
77165
77851
  skillsOpen,
77852
+ mcpOpen,
77166
77853
  tasksHeight,
77167
77854
  filesHeight,
77168
77855
  agentsHeight,
77169
- skillsHeight
77856
+ skillsHeight,
77857
+ mcpHeight
77170
77858
  ]);
77171
77859
  const handleTasksResize = reactExports.useCallback(
77172
77860
  (totalDelta) => {
@@ -77215,7 +77903,15 @@ function RightPanel() {
77215
77903
  else if (nextPanel === "skills") setSkillsOpen(false);
77216
77904
  }
77217
77905
  },
77218
- [getContentHeights, getAvailableContentHeight, filesOpen, agentsOpen, skillsOpen, agentsHeight, skillsHeight]
77906
+ [
77907
+ getContentHeights,
77908
+ getAvailableContentHeight,
77909
+ filesOpen,
77910
+ agentsOpen,
77911
+ skillsOpen,
77912
+ agentsHeight,
77913
+ skillsHeight
77914
+ ]
77219
77915
  );
77220
77916
  const handleFilesResize = reactExports.useCallback(
77221
77917
  (totalDelta) => {
@@ -77270,38 +77966,86 @@ function RightPanel() {
77270
77966
  }
77271
77967
  const start = dragStartHeights.current;
77272
77968
  const available = getAvailableContentHeight();
77273
- const tasksH = tasksOpen ? tasksHeight ?? available / 4 : 0;
77274
- const filesH = filesOpen ? filesHeight ?? available / 4 : 0;
77969
+ const tasksH = tasksOpen ? tasksHeight ?? available / 5 : 0;
77970
+ const filesH = filesOpen ? filesHeight ?? available / 5 : 0;
77275
77971
  const aboveHeight = tasksH + filesH;
77276
- const maxForAgentsAndSkills = available - aboveHeight;
77972
+ let nextPanel = null;
77973
+ if (skillsOpen) nextPanel = "skills";
77974
+ else if (mcpOpen) nextPanel = "mcp";
77975
+ if (!nextPanel) return;
77976
+ const maxForAgentsAndNext = available - aboveHeight;
77277
77977
  let newAgentsHeight = start.agents + totalDelta;
77278
- let newSkillsHeight = start.skills - totalDelta;
77978
+ let newNextHeight = start[nextPanel] - totalDelta;
77279
77979
  if (newAgentsHeight < MIN_CONTENT_HEIGHT) {
77280
77980
  newAgentsHeight = MIN_CONTENT_HEIGHT;
77281
- newSkillsHeight = start.skills + (start.agents - MIN_CONTENT_HEIGHT);
77981
+ newNextHeight = start[nextPanel] + (start.agents - MIN_CONTENT_HEIGHT);
77282
77982
  }
77283
- if (newSkillsHeight < MIN_CONTENT_HEIGHT) {
77284
- newSkillsHeight = MIN_CONTENT_HEIGHT;
77285
- newAgentsHeight = start.agents + (start.skills - MIN_CONTENT_HEIGHT);
77983
+ if (newNextHeight < MIN_CONTENT_HEIGHT) {
77984
+ newNextHeight = MIN_CONTENT_HEIGHT;
77985
+ newAgentsHeight = start.agents + (start[nextPanel] - MIN_CONTENT_HEIGHT);
77286
77986
  }
77287
- if (newAgentsHeight + newSkillsHeight > maxForAgentsAndSkills) {
77288
- const excess = newAgentsHeight + newSkillsHeight - maxForAgentsAndSkills;
77987
+ if (newAgentsHeight + newNextHeight > maxForAgentsAndNext) {
77988
+ const excess = newAgentsHeight + newNextHeight - maxForAgentsAndNext;
77289
77989
  if (totalDelta > 0) {
77290
- newSkillsHeight = Math.max(MIN_CONTENT_HEIGHT, newSkillsHeight - excess);
77990
+ newNextHeight = Math.max(MIN_CONTENT_HEIGHT, newNextHeight - excess);
77291
77991
  } else {
77292
77992
  newAgentsHeight = Math.max(MIN_CONTENT_HEIGHT, newAgentsHeight - excess);
77293
77993
  }
77294
77994
  }
77295
77995
  setAgentsHeight(newAgentsHeight);
77296
- setSkillsHeight(newSkillsHeight);
77996
+ if (nextPanel === "skills") setSkillsHeight(newNextHeight);
77997
+ else if (nextPanel === "mcp") setMcpHeight(newNextHeight);
77297
77998
  if (newAgentsHeight < COLLAPSE_THRESHOLD) {
77298
77999
  setAgentsOpen(false);
77299
78000
  }
78001
+ if (newNextHeight < COLLAPSE_THRESHOLD) {
78002
+ if (nextPanel === "skills") setSkillsOpen(false);
78003
+ else if (nextPanel === "mcp") setMcpOpen(false);
78004
+ }
78005
+ },
78006
+ [getContentHeights, getAvailableContentHeight, tasksOpen, filesOpen, tasksHeight, filesHeight, skillsOpen, mcpOpen]
78007
+ );
78008
+ const handleSkillsResize = reactExports.useCallback(
78009
+ (totalDelta) => {
78010
+ if (!dragStartHeights.current) {
78011
+ const heights2 = getContentHeights();
78012
+ dragStartHeights.current = { ...heights2 };
78013
+ }
78014
+ const start = dragStartHeights.current;
78015
+ const available = getAvailableContentHeight();
78016
+ const tasksH = tasksOpen ? tasksHeight ?? available / 5 : 0;
78017
+ const filesH = filesOpen ? filesHeight ?? available / 5 : 0;
78018
+ const agentsH = agentsOpen ? agentsHeight ?? available / 5 : 0;
78019
+ const aboveHeight = tasksH + filesH + agentsH;
78020
+ const maxForSkillsAndMcp = available - aboveHeight;
78021
+ let newSkillsHeight = start.skills + totalDelta;
78022
+ let newMcpHeight = start.mcp - totalDelta;
78023
+ if (newSkillsHeight < MIN_CONTENT_HEIGHT) {
78024
+ newSkillsHeight = MIN_CONTENT_HEIGHT;
78025
+ newMcpHeight = start.mcp + (start.skills - MIN_CONTENT_HEIGHT);
78026
+ }
78027
+ if (newMcpHeight < MIN_CONTENT_HEIGHT) {
78028
+ newMcpHeight = MIN_CONTENT_HEIGHT;
78029
+ newSkillsHeight = start.skills + (start.mcp - MIN_CONTENT_HEIGHT);
78030
+ }
78031
+ if (newSkillsHeight + newMcpHeight > maxForSkillsAndMcp) {
78032
+ const excess = newSkillsHeight + newMcpHeight - maxForSkillsAndMcp;
78033
+ if (totalDelta > 0) {
78034
+ newMcpHeight = Math.max(MIN_CONTENT_HEIGHT, newMcpHeight - excess);
78035
+ } else {
78036
+ newSkillsHeight = Math.max(MIN_CONTENT_HEIGHT, newSkillsHeight - excess);
78037
+ }
78038
+ }
78039
+ setSkillsHeight(newSkillsHeight);
78040
+ setMcpHeight(newMcpHeight);
77300
78041
  if (newSkillsHeight < COLLAPSE_THRESHOLD) {
77301
78042
  setSkillsOpen(false);
77302
78043
  }
78044
+ if (newMcpHeight < COLLAPSE_THRESHOLD) {
78045
+ setMcpOpen(false);
78046
+ }
77303
78047
  },
77304
- [getContentHeights, getAvailableContentHeight, tasksOpen, filesOpen, tasksHeight, filesHeight]
78048
+ [getContentHeights, getAvailableContentHeight, tasksOpen, filesOpen, agentsOpen, tasksHeight, filesHeight, agentsHeight]
77305
78049
  );
77306
78050
  reactExports.useEffect(() => {
77307
78051
  const handleMouseUp = () => {
@@ -77315,8 +78059,9 @@ function RightPanel() {
77315
78059
  setFilesHeight(null);
77316
78060
  setAgentsHeight(null);
77317
78061
  setSkillsHeight(null);
77318
- }, [tasksOpen, filesOpen, agentsOpen, skillsOpen]);
77319
- const [heights, setHeights] = reactExports.useState({ tasks: 0, files: 0, agents: 0, skills: 0 });
78062
+ setMcpHeight(null);
78063
+ }, [tasksOpen, filesOpen, agentsOpen, skillsOpen, mcpOpen]);
78064
+ const [heights, setHeights] = reactExports.useState({ tasks: 0, files: 0, agents: 0, skills: 0, mcp: 0 });
77320
78065
  reactExports.useEffect(() => {
77321
78066
  setHeights(getContentHeights());
77322
78067
  }, [getContentHeights]);
@@ -77367,8 +78112,8 @@ function RightPanel() {
77367
78112
  ),
77368
78113
  agentsOpen && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-auto", style: { height: heights.agents }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(AgentsContent, {}) })
77369
78114
  ] }),
77370
- agentsOpen && skillsOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(ResizeHandle$1, { onDrag: handleAgentsResize }),
77371
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col shrink-0", children: [
78115
+ agentsOpen && (skillsOpen || mcpOpen) && /* @__PURE__ */ jsxRuntimeExports.jsx(ResizeHandle$1, { onDrag: handleAgentsResize }),
78116
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col shrink-0 border-b border-border", children: [
77372
78117
  /* @__PURE__ */ jsxRuntimeExports.jsx(
77373
78118
  SectionHeader,
77374
78119
  {
@@ -77379,6 +78124,19 @@ function RightPanel() {
77379
78124
  }
77380
78125
  ),
77381
78126
  skillsOpen && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-auto", style: { height: heights.skills }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(SkillsContent, {}) })
78127
+ ] }),
78128
+ skillsOpen && mcpOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(ResizeHandle$1, { onDrag: handleSkillsResize }),
78129
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col shrink-0", children: [
78130
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
78131
+ SectionHeader,
78132
+ {
78133
+ title: "MCP",
78134
+ icon: Plug,
78135
+ isOpen: mcpOpen,
78136
+ onToggle: () => setMcpOpen((prev) => !prev)
78137
+ }
78138
+ ),
78139
+ mcpOpen && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-auto", style: { height: heights.mcp }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(MCPContent, {}) })
77382
78140
  ] })
77383
78141
  ]
77384
78142
  }
@@ -77803,6 +78561,9 @@ function AgentsContent() {
77803
78561
  function SkillsContent() {
77804
78562
  return /* @__PURE__ */ jsxRuntimeExports.jsx(SkillsPanel, {});
77805
78563
  }
78564
+ function MCPContent() {
78565
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(MCPPanel, {});
78566
+ }
77806
78567
  function formatSize(bytes) {
77807
78568
  if (bytes < 1024) return `${bytes}B`;
77808
78569
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
@@ -78201,7 +78962,7 @@ function App() {
78201
78962
  },
78202
78963
  children: [
78203
78964
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-name", children: "OPENWORK" }),
78204
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-version", children: "0.2.4" })
78965
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-version", children: "0.4.0" })
78205
78966
  ]
78206
78967
  }
78207
78968
  ),