This commit is contained in:
锦恢 2025-05-15 23:10:08 +08:00
parent 535ab54128
commit 4e8e5680ed
22 changed files with 132 additions and 37 deletions

95
.vscode/tabs.my-browser.json vendored Normal file
View File

@ -0,0 +1,95 @@
{
"currentIndex": 0,
"tabs": [
{
"name": "交互测试",
"icon": "icon-robot",
"type": "blank",
"componentIndex": 3,
"storage": {
"messages": [
{
"role": "user",
"content": "websocket 如何进行转发",
"extraInfo": {
"created": 1747321092503,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "assistant",
"content": "WebSocket 转发通常涉及在服务器端接收来自客户端的 WebSocket 连接,并将消息转发到另一个 WebSocket 服务器或客户端。以下是一个简单的步骤来实现 WebSocket 转发:\n\n### 1. 建立 WebSocket 服务器\n首先你需要创建一个 WebSocket 服务器来接收客户端的连接。\n\n```javascript\nconst WebSocket = require('ws');\nconst wss = new WebSocket.Server({ port: 8080 });\n\nwss.on('connection', function connection(ws) {\n console.log('Client connected');\n\n ws.on('message', function incoming(message) {\n console.log('Received: %s', message);\n // 在这里处理消息转发\n });\n\n ws.on('close', function close() {\n console.log('Client disconnected');\n });\n});\n```\n\n### 2. 连接到目标 WebSocket 服务器\n在接收到客户端的消息后你需要将这些消息转发到目标 WebSocket 服务器。\n\n```javascript\nconst targetWs = new WebSocket('ws://target-websocket-server');\n\ntargetWs.on('open', function open() {\n console.log('Connected to target WebSocket server');\n});\n\ntargetWs.on('message', function incoming(message) {\n console.log('Received from target: %s', message);\n // 将消息转发回客户端\n ws.send(message);\n});\n\ntargetWs.on('close', function close() {\n console.log('Disconnected from target WebSocket server');\n});\n```\n\n### 3. 转发消息\n在接收到客户端的消息后将其转发到目标 WebSocket 服务器。\n\n```javascript\nws.on('message', function incoming(message) {\n console.log('Received: %s', message);\n targetWs.send(message);\n});\n```\n\n### 4. 处理目标服务器的响应\n将目标服务器的响应转发回客户端。\n\n```javascript\ntargetWs.on('message', function incoming(message) {\n console.log('Received from target: %s', message);\n ws.send(message);\n});\n```\n\n### 5. 处理错误和关闭事件\n确保处理错误和关闭事件以保持连接的稳定性。\n\n```javascript\nws.on('error', function error(err) {\n console.error('WebSocket error:', err);\n});\n\nws.on('close', function close() {\n console.log('WebSocket closed');\n targetWs.close();\n});\n\ntargetWs.on('error', function error(err) {\n console.error('Target WebSocket error:', err);\n});\n\ntargetWs.on('close', function close() {\n console.log('Target WebSocket closed');\n});\n```\n\n### 完整示例\n以下是一个完整的示例代码\n\n```javascript\nconst WebSocket = require('ws');\nconst wss = new WebSocket.Server({ port: 8080 });\n\nwss.on('connection', function connection(ws) {\n console.log('Client connected');\n\n const targetWs = new WebSocket('ws://target-websocket-server');\n\n targetWs.on('open', function open() {\n console.log('Connected to target WebSocket server');\n });\n\n targetWs.on('message', function incoming(message) {\n console.log('Received from target: %s', message);\n ws.send(message);\n });\n\n targetWs.on('close', function close() {\n console.log('Disconnected from target WebSocket server');\n });\n\n ws.on('message', function incoming(message) {\n console.log('Received: %s', message);\n targetWs.send(message);\n });\n\n ws.on('error', function error(err) {\n console.error('WebSocket error:', err);\n });\n\n ws.on('close', function close() {\n console.log('WebSocket closed');\n targetWs.close();\n });\n\n targetWs.on('error', function error(err) {\n console.error('Target WebSocket error:', err);\n });\n\n targetWs.on('close', function close() {\n console.log('Target WebSocket closed');\n });\n});\n```\n\n### 总结\n通过上述步骤你可以实现一个简单的 WebSocket 转发服务器。这个服务器将接收客户端的 WebSocket 连接,并将消息转发到目标 WebSocket 服务器,同时将目标服务器的响应转发回客户端。",
"extraInfo": {
"created": 1747321106609,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "user",
"content": "我现在有一个内网的 8282 上的 ws想要通过一台公网服务器 kirigaya.cn 转发到公网的 8282 上",
"extraInfo": {
"created": 1747321168696,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "assistant",
"content": "要通过一台公网服务器 `kirigaya.cn` 将内网的 WebSocket 服务(运行在 `8282` 端口)转发到公网的 `8282` 端口上,你可以使用以下步骤:\n\n### 1. 在公网服务器上设置 WebSocket 转发\n在公网服务器 `kirigaya.cn` 上,你需要设置一个 WebSocket 服务器来接收公网客户端的连接,并将消息转发到内网的 WebSocket 服务。\n\n#### 使用 Node.js 实现 WebSocket 转发\n以下是一个使用 Node.js 和 `ws` 库实现的 WebSocket 转发示例:\n\n```javascript\nconst WebSocket = require('ws');\n\n// 公网 WebSocket 服务器\nconst wss = new WebSocket.Server({ port: 8282 });\n\nwss.on('connection', function connection(ws) {\n console.log('Public client connected');\n\n // 连接到内网 WebSocket 服务\n const targetWs = new WebSocket('ws://内网服务器IP:8282');\n\n targetWs.on('open', function open() {\n console.log('Connected to internal WebSocket server');\n });\n\n targetWs.on('message', function incoming(message) {\n console.log('Received from internal: %s', message);\n // 将消息转发回公网客户端\n ws.send(message);\n });\n\n targetWs.on('close', function close() {\n console.log('Disconnected from internal WebSocket server');\n });\n\n ws.on('message', function incoming(message) {\n console.log('Received from public: %s', message);\n // 将消息转发到内网 WebSocket 服务\n targetWs.send(message);\n });\n\n ws.on('error', function error(err) {\n console.error('Public WebSocket error:', err);\n });\n\n ws.on('close', function close() {\n console.log('Public WebSocket closed');\n targetWs.close();\n });\n\n targetWs.on('error', function error(err) {\n console.error('Internal WebSocket error:', err);\n });\n\n targetWs.on('close', function close() {\n console.log('Internal WebSocket closed');\n });\n});\n```\n\n### 2. 运行 WebSocket 转发服务\n将上述代码保存为 `websocket-proxy.js`,然后在公网服务器上运行:\n\n```bash\nnode websocket-proxy.js\n```\n\n### 3. 配置防火墙和端口\n确保公网服务器的防火墙允许 `8282` 端口的流量。你可以使用以下命令开放端口(假设使用 `ufw`\n\n```bash\nsudo ufw allow 8282\n```\n\n### 4. 客户端连接\n公网客户端现在可以通过 `ws://kirigaya.cn:8282` 连接到 WebSocket 服务。消息将通过公网服务器转发到内网的 WebSocket 服务。\n\n### 5. 保持服务运行\n为了确保 WebSocket 转发服务持续运行,你可以使用 `pm2` 或 `systemd` 来管理 Node.js 进程。\n\n#### 使用 `pm2` 管理进程\n安装 `pm2`\n\n```bash\nnpm install -g pm2\n```\n\n启动服务\n\n```bash\npm2 start websocket-proxy.js\n```\n\n保存进程列表\n\n```bash\npm2 save\n```\n\n### 总结\n通过上述步骤你可以在公网服务器 `kirigaya.cn` 上设置一个 WebSocket 转发服务,将公网客户端的连接转发到内网的 WebSocket 服务。这样可以实现内网服务的公网访问。",
"extraInfo": {
"created": 1747321181151,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
}
],
"settings": {
"modelIndex": 7,
"enableTools": [
{
"name": "k_navigate",
"description": "Navigate to a URL",
"enabled": true
},
{
"name": "k_screenshot",
"description": "Take a screenshot of the current page or a specific element",
"enabled": true
},
{
"name": "k_click",
"description": "Click an element on the page",
"enabled": true
},
{
"name": "k_fill",
"description": "Fill out an input field",
"enabled": true
},
{
"name": "k_select",
"description": "Select an element on the page with Select tag",
"enabled": true
},
{
"name": "k_hover",
"description": "Hover an element on the page",
"enabled": true
},
{
"name": "k_evaluate",
"description": "Execute JavaScript in the browser console",
"enabled": true
}
],
"enableWebSearch": false,
"temperature": 0.7,
"contextLength": 20,
"systemPrompt": ""
}
}
}
]
}

View File

@ -9,7 +9,7 @@
<link rel="stylesheet" href="/iconfont.css">
<link rel="stylesheet" href="/animation.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
<title>OpenMCP 客户端</title>
</head>
<body>
<div id="app"></div>

View File

@ -6,7 +6,7 @@
</template>
<script setup lang="ts">
import { computed, defineProps, PropType } from 'vue';
import { computed, defineProps, type PropType } from 'vue';
import { renderJson } from '../main-panel/chat/markdown/markdown';
const props = defineProps({

View File

@ -12,7 +12,7 @@
</template>
<script setup lang="ts">
import { defineProps, defineEmits, provide, PropType, computed } from 'vue';
import { defineProps, defineEmits, provide, computed } from 'vue';
import { llmManager } from '@/views/setting/llm';
import { tabs } from '@/components/main-panel/panel';
import type { ChatSetting, ChatStorage } from '../chat';

View File

@ -62,7 +62,7 @@
<script setup lang="ts">
import { ref, computed, inject, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { ChatStorage } from '../chat';
import type { ChatStorage } from '../chat';
import { systemPrompts, setSystemPrompt, loadSystemPrompts, deleteSystemPrompt } from './system-prompt';
import { ElMessage } from 'element-plus';
import { debounce } from 'lodash';

View File

@ -25,7 +25,7 @@
<script setup lang="ts">
import { inject, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { ChatStorage } from '../chat';
import type { ChatStorage } from '../chat';
const { t } = useI18n();
const tabStorage = inject('tabStorage') as ChatStorage;

View File

@ -37,7 +37,7 @@
<script setup lang="ts">
import { ref, computed, inject, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { allTools, ChatStorage, getToolSchema } from '../chat';
import { allTools, type ChatStorage, getToolSchema } from '../chat';
import { markdownToHtml } from '@/components/main-panel/chat/markdown/markdown';
import { useMessageBridge } from '@/api/message-bridge';

View File

@ -10,7 +10,7 @@
<script setup lang="ts">
import { inject } from 'vue';
import { useI18n } from 'vue-i18n';
import { ChatStorage } from '../chat';
import type { ChatStorage } from '../chat';
const { t } = useI18n();

View File

@ -8,8 +8,8 @@
</template>
<script setup lang="ts">
import { PromptsGetResponse } from '@/hook/type';
import { defineProps, PropType } from 'vue';
import type { PromptsGetResponse } from '@/hook/type';
import { defineProps, type PropType } from 'vue';
const props = defineProps({
messages: {

View File

@ -30,9 +30,9 @@
<script setup lang="ts">
import { useMessageBridge } from '@/api/message-bridge';
import { ResourcesReadResponse } from '@/hook/type';
import type { ResourcesReadResponse } from '@/hook/type';
import { getImageBlobUrlByBase64 } from '@/hook/util';
import { computed, defineProps, PropType, reactive, ref, watch } from 'vue';
import { computed, defineProps, type PropType, ref } from 'vue';
const props = defineProps({
contents: {

View File

@ -20,8 +20,7 @@
</template>
<script setup lang="ts">
import { ref, PropType, defineProps, defineEmits, computed, provide } from 'vue';
import type { RichTextItem } from './chat';
import { ref, defineProps, defineEmits, computed, provide } from 'vue';
import Setting from './options/setting.vue';

View File

@ -39,9 +39,9 @@
<script setup lang="ts">
import { useMessageBridge } from '@/api/message-bridge';
import { ToolCallContent } from '@/hook/type';
import type { ToolCallContent } from '@/hook/type';
import { getBlobUrlByFilename } from '@/hook/util';
import { defineComponent, PropType, defineProps, ref, defineEmits } from 'vue';
import { defineComponent, type PropType, defineProps, ref, defineEmits } from 'vue';
defineComponent({ name: 'toolcall-result-item' });
const emits = defineEmits(['update:item', 'update:ocr-done']);

View File

@ -121,14 +121,14 @@
</template>
<script setup lang="ts">
import { defineProps, ref, watch, PropType, computed, defineEmits } from 'vue';
import { defineProps, ref, watch, type PropType, computed, defineEmits } from 'vue';
import { useI18n } from 'vue-i18n';
import MessageMeta from './message-meta.vue';
import { markdownToHtml } from '@/components/main-panel/chat/markdown/markdown';
import { createTest } from '@/views/setting/llm';
import { IToolRenderMessage, MessageState } from '../chat-box/chat';
import { ToolCallContent } from '@/hook/type';
import type { ToolCallContent } from '@/hook/type';
import ToolcallResultItem from './toolcall-result-item.vue';
import JsonRender from '@/components/json-render/index.vue';

View File

@ -35,7 +35,7 @@
import { defineComponent, defineProps, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { tabs } from '../panel';
import { PromptStorage } from './prompts';
import type { PromptStorage } from './prompts';
import JsonRender from '@/components/json-render/index.vue';
defineComponent({ name: 'prompt-logger' });

View File

@ -36,8 +36,8 @@ import { defineComponent, defineProps, defineEmits, watch, ref, computed, reacti
import { useI18n } from 'vue-i18n';
import type { FormInstance, FormRules } from 'element-plus';
import { tabs } from '../panel';
import { promptsManager, PromptStorage } from './prompts';
import { PromptsGetResponse } from '@/hook/type';
import { promptsManager, type PromptStorage } from './prompts';
import type { PromptsGetResponse } from '@/hook/type';
import { useMessageBridge } from '@/api/message-bridge';
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';

View File

@ -39,7 +39,7 @@ import { useI18n } from 'vue-i18n';
import type { FormInstance, FormRules } from 'element-plus';
import { tabs } from '../panel';
import { parseResourceTemplate, resourcesManager, type ResourceStorage } from './resources';
import type{ CasualRestAPI, ResourcesReadResponse } from '@/hook/type';
import type{ ResourcesReadResponse } from '@/hook/type';
import { useMessageBridge } from '@/api/message-bridge';
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';

View File

@ -60,7 +60,7 @@ import { defineComponent, defineProps, watch, ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import type { FormInstance, FormRules } from 'element-plus';
import { tabs } from '../panel';
import { callTool, toolsManager, ToolStorage } from './tools';
import { callTool, toolsManager, type ToolStorage } from './tools';
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';
import KInputObject from '@/components/k-input-object/index.vue';

View File

@ -39,7 +39,7 @@
import { defineComponent, defineProps, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { tabs } from '../panel';
import { ToolStorage } from './tools';
import type { ToolStorage } from './tools';
import JsonRender from '@/components/json-render/index.vue';
defineComponent({ name: 'tool-logger' });

View File

@ -44,7 +44,7 @@
<script setup lang="ts">
import { defineComponent, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { connectionEnv, EnvItem, handleEnvSwitch } from './connection';
import { connectionEnv, type EnvItem, handleEnvSwitch } from './connection';
defineComponent({ name: 'env-var' });

View File

@ -5,14 +5,15 @@ import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})
export default defineConfig(({ mode }) => ({
plugins: [
vue(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
base: mode === 'website' ? '/mcp/' : '/',
}))

View File

@ -3,7 +3,7 @@ import { OpenAI } from "openai";
import { MyMessageType, MyToolMessageType } from "./llm.dto";
import { RestfulResponse } from "../common/index.dto";
import { ocrDB } from "../hook/db";
import { ToolCallContent } from "../mcp/client.dto";
import type { ToolCallContent } from "../mcp/client.dto";
import { ocrWorkerStorage } from "../mcp/ocr.service";
export let currentStream: AsyncIterable<any> | null = null;

View File

@ -2,7 +2,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { McpOptions, McpTransport, IServerVersion, ToolCallResponse, ToolCallContent } from './client.dto';
import type { McpOptions, McpTransport, IServerVersion, ToolCallResponse, ToolCallContent } from './client.dto';
import { PostMessageble } from "../hook/adapter";
import { createOcrWorker, saveBase64ImageData } from "./ocr.service";