@uniqueli/openwork 0.3.0 → 0.4.1

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,
@@ -77075,6 +77103,639 @@ function SkillsPanel(_props) {
77075
77103
  )
77076
77104
  ] });
77077
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
+ }
77078
77739
  const HEADER_HEIGHT = 40;
77079
77740
  const HANDLE_HEIGHT = 6;
77080
77741
  const MIN_CONTENT_HEIGHT = 60;
@@ -77153,31 +77814,34 @@ function RightPanel() {
77153
77814
  const [filesOpen, setFilesOpen] = reactExports.useState(true);
77154
77815
  const [agentsOpen, setAgentsOpen] = reactExports.useState(true);
77155
77816
  const [skillsOpen, setSkillsOpen] = reactExports.useState(false);
77817
+ const [mcpOpen, setMcpOpen] = reactExports.useState(false);
77156
77818
  const [tasksHeight, setTasksHeight] = reactExports.useState(null);
77157
77819
  const [filesHeight, setFilesHeight] = reactExports.useState(null);
77158
77820
  const [agentsHeight, setAgentsHeight] = reactExports.useState(null);
77159
77821
  const [skillsHeight, setSkillsHeight] = reactExports.useState(null);
77822
+ const [mcpHeight, setMcpHeight] = reactExports.useState(null);
77160
77823
  const dragStartHeights = reactExports.useRef(null);
77161
77824
  const getAvailableContentHeight = reactExports.useCallback(() => {
77162
77825
  if (!containerRef.current) return 0;
77163
77826
  const totalHeight = containerRef.current.clientHeight;
77164
- let used = HEADER_HEIGHT * 4;
77165
- 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;
77166
77829
  used += HANDLE_HEIGHT * (openPanels - 1);
77167
77830
  return Math.max(0, totalHeight - used);
77168
- }, [tasksOpen, filesOpen, agentsOpen, skillsOpen]);
77831
+ }, [tasksOpen, filesOpen, agentsOpen, skillsOpen, mcpOpen]);
77169
77832
  const getContentHeights = reactExports.useCallback(() => {
77170
77833
  const available = getAvailableContentHeight();
77171
- const openCount = [tasksOpen, filesOpen, agentsOpen, skillsOpen].filter(Boolean).length;
77834
+ const openCount = [tasksOpen, filesOpen, agentsOpen, skillsOpen, mcpOpen].filter(Boolean).length;
77172
77835
  if (openCount === 0) {
77173
- return { tasks: 0, files: 0, agents: 0, skills: 0 };
77836
+ return { tasks: 0, files: 0, agents: 0, skills: 0, mcp: 0 };
77174
77837
  }
77175
77838
  const defaultHeight = available / openCount;
77176
77839
  return {
77177
77840
  tasks: tasksOpen ? tasksHeight ?? defaultHeight : 0,
77178
77841
  files: filesOpen ? filesHeight ?? defaultHeight : 0,
77179
77842
  agents: agentsOpen ? agentsHeight ?? defaultHeight : 0,
77180
- skills: skillsOpen ? skillsHeight ?? defaultHeight : 0
77843
+ skills: skillsOpen ? skillsHeight ?? defaultHeight : 0,
77844
+ mcp: mcpOpen ? mcpHeight ?? defaultHeight : 0
77181
77845
  };
77182
77846
  }, [
77183
77847
  getAvailableContentHeight,
@@ -77185,10 +77849,12 @@ function RightPanel() {
77185
77849
  filesOpen,
77186
77850
  agentsOpen,
77187
77851
  skillsOpen,
77852
+ mcpOpen,
77188
77853
  tasksHeight,
77189
77854
  filesHeight,
77190
77855
  agentsHeight,
77191
- skillsHeight
77856
+ skillsHeight,
77857
+ mcpHeight
77192
77858
  ]);
77193
77859
  const handleTasksResize = reactExports.useCallback(
77194
77860
  (totalDelta) => {
@@ -77300,38 +77966,86 @@ function RightPanel() {
77300
77966
  }
77301
77967
  const start = dragStartHeights.current;
77302
77968
  const available = getAvailableContentHeight();
77303
- const tasksH = tasksOpen ? tasksHeight ?? available / 4 : 0;
77304
- const filesH = filesOpen ? filesHeight ?? available / 4 : 0;
77969
+ const tasksH = tasksOpen ? tasksHeight ?? available / 5 : 0;
77970
+ const filesH = filesOpen ? filesHeight ?? available / 5 : 0;
77305
77971
  const aboveHeight = tasksH + filesH;
77306
- 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;
77307
77977
  let newAgentsHeight = start.agents + totalDelta;
77308
- let newSkillsHeight = start.skills - totalDelta;
77978
+ let newNextHeight = start[nextPanel] - totalDelta;
77309
77979
  if (newAgentsHeight < MIN_CONTENT_HEIGHT) {
77310
77980
  newAgentsHeight = MIN_CONTENT_HEIGHT;
77311
- newSkillsHeight = start.skills + (start.agents - MIN_CONTENT_HEIGHT);
77981
+ newNextHeight = start[nextPanel] + (start.agents - MIN_CONTENT_HEIGHT);
77312
77982
  }
77313
- if (newSkillsHeight < MIN_CONTENT_HEIGHT) {
77314
- newSkillsHeight = MIN_CONTENT_HEIGHT;
77315
- 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);
77316
77986
  }
77317
- if (newAgentsHeight + newSkillsHeight > maxForAgentsAndSkills) {
77318
- const excess = newAgentsHeight + newSkillsHeight - maxForAgentsAndSkills;
77987
+ if (newAgentsHeight + newNextHeight > maxForAgentsAndNext) {
77988
+ const excess = newAgentsHeight + newNextHeight - maxForAgentsAndNext;
77319
77989
  if (totalDelta > 0) {
77320
- newSkillsHeight = Math.max(MIN_CONTENT_HEIGHT, newSkillsHeight - excess);
77990
+ newNextHeight = Math.max(MIN_CONTENT_HEIGHT, newNextHeight - excess);
77321
77991
  } else {
77322
77992
  newAgentsHeight = Math.max(MIN_CONTENT_HEIGHT, newAgentsHeight - excess);
77323
77993
  }
77324
77994
  }
77325
77995
  setAgentsHeight(newAgentsHeight);
77326
- setSkillsHeight(newSkillsHeight);
77996
+ if (nextPanel === "skills") setSkillsHeight(newNextHeight);
77997
+ else if (nextPanel === "mcp") setMcpHeight(newNextHeight);
77327
77998
  if (newAgentsHeight < COLLAPSE_THRESHOLD) {
77328
77999
  setAgentsOpen(false);
77329
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);
77330
78041
  if (newSkillsHeight < COLLAPSE_THRESHOLD) {
77331
78042
  setSkillsOpen(false);
77332
78043
  }
78044
+ if (newMcpHeight < COLLAPSE_THRESHOLD) {
78045
+ setMcpOpen(false);
78046
+ }
77333
78047
  },
77334
- [getContentHeights, getAvailableContentHeight, tasksOpen, filesOpen, tasksHeight, filesHeight]
78048
+ [getContentHeights, getAvailableContentHeight, tasksOpen, filesOpen, agentsOpen, tasksHeight, filesHeight, agentsHeight]
77335
78049
  );
77336
78050
  reactExports.useEffect(() => {
77337
78051
  const handleMouseUp = () => {
@@ -77345,8 +78059,9 @@ function RightPanel() {
77345
78059
  setFilesHeight(null);
77346
78060
  setAgentsHeight(null);
77347
78061
  setSkillsHeight(null);
77348
- }, [tasksOpen, filesOpen, agentsOpen, skillsOpen]);
77349
- 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 });
77350
78065
  reactExports.useEffect(() => {
77351
78066
  setHeights(getContentHeights());
77352
78067
  }, [getContentHeights]);
@@ -77397,8 +78112,8 @@ function RightPanel() {
77397
78112
  ),
77398
78113
  agentsOpen && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-auto", style: { height: heights.agents }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(AgentsContent, {}) })
77399
78114
  ] }),
77400
- agentsOpen && skillsOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(ResizeHandle$1, { onDrag: handleAgentsResize }),
77401
- /* @__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: [
77402
78117
  /* @__PURE__ */ jsxRuntimeExports.jsx(
77403
78118
  SectionHeader,
77404
78119
  {
@@ -77409,6 +78124,19 @@ function RightPanel() {
77409
78124
  }
77410
78125
  ),
77411
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, {}) })
77412
78140
  ] })
77413
78141
  ]
77414
78142
  }
@@ -77833,6 +78561,9 @@ function AgentsContent() {
77833
78561
  function SkillsContent() {
77834
78562
  return /* @__PURE__ */ jsxRuntimeExports.jsx(SkillsPanel, {});
77835
78563
  }
78564
+ function MCPContent() {
78565
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(MCPPanel, {});
78566
+ }
77836
78567
  function formatSize(bytes) {
77837
78568
  if (bytes < 1024) return `${bytes}B`;
77838
78569
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
@@ -78231,7 +78962,7 @@ function App() {
78231
78962
  },
78232
78963
  children: [
78233
78964
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-name", children: "OPENWORK" }),
78234
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-version", children: "0.3.0" })
78965
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "app-badge-version", children: "0.4.1" })
78235
78966
  ]
78236
78967
  }
78237
78968
  ),