使用 vite 重构 renderer

This commit is contained in:
锦恢 2025-05-15 22:30:02 +08:00
parent 19872c25b3
commit 535ab54128
59 changed files with 2386 additions and 12435 deletions

View File

@ -1,4 +0,0 @@
> 1%
last 2 versions
not dead
not ie 11

View File

0
renderer/.env.production Normal file
View File

View File

@ -1,19 +0,0 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended"
],
parserOptions: {
ecmaVersion: 2020,
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
'vue/multi-word-component-names': 'off'
},
};

30
renderer/.gitignore vendored
View File

@ -1,23 +1,31 @@
.DS_Store # Logs
node_modules logs
/dist *.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files # Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea .idea
.vscode
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
*.tsbuildinfo
update.sh

View File

@ -1,24 +1,33 @@
# app # test-vite
## Project setup This template should help get you started developing with Vue 3 in Vite.
```
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vite.dev/config/).
## Project Setup
```sh
npm install npm install
``` ```
### Compiles and hot-reloads for development ### Compile and Hot-Reload for Development
```
npm run serve ```sh
npm run dev
``` ```
### Compiles and minifies for production ### Type-Check, Compile and Minify for Production
```
```sh
npm run build npm run build
``` ```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@ -1,3 +0,0 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
};

1
renderer/env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

18
renderer/index.html Normal file
View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.png">
<link rel="stylesheet" href="/default-dark.css">
<link rel="stylesheet" href="/vscode.css">
<link rel="stylesheet" href="/mcp.css">
<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>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

13647
renderer/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,16 @@
{ {
"name": "renderer", "name": "test-vite",
"version": "0.1.0", "version": "0.0.0",
"private": true, "private": true,
"type": "module",
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vite",
"build": "vue-cli-service build", "serve:website": "vite --mode website",
"lint": "vue-cli-service lint" "build": "run-p type-check \"build-only {@}\" --",
"build:website": "vite build --mode website",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.8.3", "core-js": "^3.8.3",
@ -15,29 +20,22 @@
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"markdown-it-katex": "^2.0.3", "markdown-it-katex": "^2.0.3",
"openai": "^4.93.0", "openai": "^4.93.0",
"prismjs": "^1.30.0",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vue": "^3.2.13", "vue": "^3.5.13",
"vue-i18n": "^11.1.0", "vue-i18n": "^11.1.0",
"vue-router": "^4.0.3" "vue-router": "^4.5.0"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.17.16", "@tsconfig/node22": "^22.0.1",
"@types/markdown-it": "^14.1.2", "@types/node": "^22.14.0",
"@typescript-eslint/eslint-plugin": "^5.4.0", "@types/prismjs": "^1.26.5",
"@typescript-eslint/parser": "^5.4.0", "@vitejs/plugin-vue": "^5.2.3",
"@vue/cli-plugin-babel": "~5.0.0", "@vue/tsconfig": "^0.7.0",
"@vue/cli-plugin-eslint": "~5.0.0", "npm-run-all2": "^7.0.2",
"@vue/cli-plugin-router": "~5.0.0", "typescript": "~5.8.0",
"@vue/cli-plugin-typescript": "~5.0.0", "vite": "^6.2.4",
"@vue/cli-service": "~5.0.0", "vite-plugin-vue-devtools": "^7.7.2",
"@vue/eslint-config-typescript": "^9.1.0", "vue-tsc": "^2.2.8"
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.0.3",
"prettier": "^2.4.1",
"typescript": "^4.4.3",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.26.0"
} }
} }

BIN
renderer/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 824 834" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="path_1" d="M300 0C465.708 0 600 134.292 600 300L600 300C600 465.708 465.708 600 300 600L300 600C134.292 600 0 465.708 0 300L0 300C0 134.292 134.292 0 300 0Z" />
<linearGradient id="gradient_2" gradientUnits="userSpaceOnUse" x1="300" y1="0" x2="300" y2="600">
<stop offset="0" stop-color="#A1A7F6" />
<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.2" />
</linearGradient>
</defs>
<g>
<g transform="translate(186 116)">
<use p4:href="#path_1" fill="#5A00FF" xmlns:p4="http://www.w3.org/1999/xlink" />
<use p4:href="#path_1" fill="url(#gradient_2)" xmlns:p4="http://www.w3.org/1999/xlink" />
</g>
<path d="M0 110.5C0 49.4725 49.4725 0 110.5 0C171.527 0 221 49.4725 221 110.5C221 171.527 171.527 221 110.5 221C49.4725 221 0 171.527 0 110.5Z" fill="#FFFFFF" fill-rule="evenodd" fill-opacity="0.431" transform="translate(445 458)" />
<path d="M0 55.5C0 24.8482 24.8482 0 55.5 0C86.1518 0 111 24.8482 111 55.5C111 86.1518 86.1518 111 55.5 111C24.8482 111 0 86.1518 0 55.5Z" fill="#FFFFFF" fill-rule="evenodd" fill-opacity="0.431" transform="translate(199 386)" />
<path d="M0 182.5C0 81.708 81.708 0 182.5 0C283.292 0 365 81.708 365 182.5C365 283.292 283.292 365 182.5 365C81.708 365 0 283.292 0 182.5Z" fill="#FFFFFF" fill-rule="evenodd" fill-opacity="0.424" transform="translate(339 156)" />
<path d="M0 57C0 25.5198 25.5198 0 57 0C88.4802 0 114 25.5198 114 57C114 88.4802 88.4802 114 57 114C25.5198 114 0 88.4802 0 57Z" fill="#FFFFFF" fill-rule="evenodd" fill-opacity="0.431" transform="translate(521 188)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="referrer" content="no-referrer">
<link rel="icon" href="<%= BASE_URL %>favicon.svg">
<link rel="stylesheet" href="default-dark.css">
<link rel="stylesheet" href="vscode.css">
<link rel="stylesheet" href="mcp.css">
<link rel="stylesheet" href="iconfont.css">
<link rel="stylesheet" href="animation.css">
<title>OpenMCP Client</title>
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@ -4,6 +4,7 @@
<MainPanel></MainPanel> <MainPanel></MainPanel>
<Tour v-if="!userHasReadGuide"/> <Tour v-if="!userHasReadGuide"/>
<PasswordDialog v-if="password"/>
</div> </div>
</template> </template>
@ -17,14 +18,13 @@ import MainPanel from '@/components/main-panel/index.vue';
import { setDefaultCss } from './hook/css'; import { setDefaultCss } from './hook/css';
import { greenLog, pinkLog } from './views/setting/util'; import { greenLog, pinkLog } from './views/setting/util';
import { useMessageBridge } from './api/message-bridge'; import { useMessageBridge } from './api/message-bridge';
import { doConnect, loadEnvVar } from './views/connect/connection'; import { initialise } from './views/connect/connection';
import { getTour, loadSetting } from './hook/setting';
import { loadPanels } from './hook/panel';
import { getPlatform } from './api/platform'; import { getPlatform } from './api/platform';
import Tour from '@/components/guide/tour.vue'; import Tour from '@/components/guide/tour.vue';
import { userHasReadGuide } from './components/guide/tour'; import { userHasReadGuide } from './components/guide/tour';
import { ElLoading } from 'element-plus'; import PasswordDialog from '@/components/password-dialog/index.vue';
import { privilegeStatus } from './components/password-dialog/status';
const bridge = useMessageBridge(); const bridge = useMessageBridge();
@ -37,23 +37,14 @@ bridge.addCommandListener('hello', data => {
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
onMounted(async () => { const password = Boolean(import.meta.env.VITE_USE_PASSWORD);
const loading = ElLoading.service({ privilegeStatus.allow = !Boolean(password);
fullscreen: true,
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)'
});
onMounted(async () => {
// css // css
setDefaultCss(); setDefaultCss();
document.addEventListener('click', () => {
Connection.showPanel = false;
});
pinkLog('OpenMCP Client 启动'); pinkLog('OpenMCP Client 启动');
const platform = getPlatform(); const platform = getPlatform();
// //
@ -65,29 +56,18 @@ onMounted(async () => {
} }
// //
await bridge.awaitForWebsockt(); await bridge.awaitForWebsocket();
pinkLog('准备请求设置'); //
if (!privilegeStatus.allow) {
return;
}
// document.addEventListener('click', () => {
loadSetting(); Connection.showPanel = false;
//
loadEnvVar();
//
getTour();
//
await doConnect({
namespace: platform,
updateCommandString: true
}); });
// loading panels await initialise();
await loadPanels();
loading.close();
}); });
</script> </script>

0
renderer/src/api/api.ts Normal file
View File

View File

@ -1,10 +1,12 @@
import { pinkLog, redLog } from '@/views/setting/util'; import { pinkLog, redLog } from '@/views/setting/util';
import { acquireVsCodeApi, electronApi, getPlatform } from './platform'; import { acquireVsCodeApi, electronApi, getPlatform } from './platform';
import { privilegeStatus } from '@/components/password-dialog/status';
export interface VSCodeMessage { export interface VSCodeMessage {
command: string; command: string;
data?: unknown; data?: unknown;
callbackId?: string; callbackId?: string;
password?: string;
} }
export interface RestFulResponse { export interface RestFulResponse {
@ -92,6 +94,7 @@ export class MessageBridge {
this.postMessage = (message) => { this.postMessage = (message) => {
if (this.ws?.readyState === WebSocket.OPEN) { if (this.ws?.readyState === WebSocket.OPEN) {
console.log('send', message); console.log('send', message);
message.password = privilegeStatus.password;
this.ws.send(JSON.stringify(message)); this.ws.send(JSON.stringify(message));
} }
}; };
@ -105,7 +108,7 @@ export class MessageBridge {
}); });
} }
public async awaitForWebsockt() { public async awaitForWebsocket() {
if (this.isConnected) { if (this.isConnected) {
await this.isConnected; await this.isConnected;
} }

View File

@ -1,5 +1,5 @@
import type { ToolCallContent, ToolItem } from "@/hook/type"; import type { ToolCallContent, ToolItem } from "@/hook/type";
import { Ref, ref } from "vue"; import { type Ref, ref } from "vue";
import type { OpenAI } from 'openai'; import type { OpenAI } from 'openai';
type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk; type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;

View File

@ -21,12 +21,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { provide, onMounted, onUnmounted, ref, defineEmits, defineProps, PropType, inject, Ref } from 'vue'; import { provide, onMounted, onUnmounted, ref, defineEmits, defineProps, type PropType, inject, type Ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import KRichTextarea from './rich-textarea.vue'; import KRichTextarea from './rich-textarea.vue';
import { tabs } from '../../panel'; import { tabs } from '../../panel';
import { ChatMessage, ChatStorage, MessageState, ToolCall, RichTextItem } from './chat'; import type { ChatMessage, ChatStorage, ToolCall, RichTextItem } from './chat';
import { MessageState } from './chat';
import { TaskLoop } from '../core/task-loop'; import { TaskLoop } from '../core/task-loop';
import { llmManager, llms } from '@/views/setting/llm'; import { llmManager, llms } from '@/views/setting/llm';

View File

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

View File

@ -26,8 +26,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { createApp, inject, ref } from 'vue'; import { createApp, inject, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ChatStorage, EditorContext } from '../chat'; import type { ChatStorage, EditorContext } from '../chat';
import { PromptsGetResponse, PromptTemplate } from '@/hook/type'; import type { PromptsGetResponse, PromptTemplate } from '@/hook/type';
import PromptTemplates from '@/components/main-panel/prompt/prompt-templates.vue'; import PromptTemplates from '@/components/main-panel/prompt/prompt-templates.vue';
import PromptReader from '@/components/main-panel/prompt/prompt-reader.vue'; import PromptReader from '@/components/main-panel/prompt/prompt-reader.vue';

View File

@ -24,8 +24,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { createApp, inject, ref } from 'vue'; import { createApp, inject, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ChatStorage, EditorContext } from '../chat'; import type { ChatStorage, EditorContext } from '../chat';
import { Resources, ResourcesReadResponse, ResourceTemplate } from '@/hook/type'; import type { Resources, ResourcesReadResponse, ResourceTemplate } from '@/hook/type';
import ResourceList from '@/components/main-panel/resource/resource-list.vue'; import ResourceList from '@/components/main-panel/resource/resource-list.vue';
import ResourceReader from '@/components/main-panel/resource/resouce-reader.vue'; import ResourceReader from '@/components/main-panel/resource/resouce-reader.vue';

View File

@ -1,6 +1,6 @@
import { ToolCallContent, ToolCallResponse } from "@/hook/type"; import type { ToolCallContent, ToolCallResponse } from "@/hook/type";
import { callTool } from "../../tool/tools"; import { callTool } from "../../tool/tools";
import { MessageState, ToolCall } from "../chat-box/chat"; import { MessageState, type ToolCall } from "../chat-box/chat";
export interface ToolCallResult { export interface ToolCallResult {
state: MessageState; state: MessageState;

View File

@ -1,12 +1,12 @@
/* eslint-disable */ /* eslint-disable */
import { ref, type Ref } from "vue"; import { ref, type Ref } from "vue";
import { ToolCall, ChatStorage, getToolSchema, MessageState } from "../chat-box/chat"; import { type ToolCall, type ChatStorage, getToolSchema, MessageState } from "../chat-box/chat";
import { useMessageBridge, MessageBridge, createMessageBridge } from "@/api/message-bridge"; import { useMessageBridge, MessageBridge, createMessageBridge } from "@/api/message-bridge";
import type { OpenAI } from 'openai'; import type { OpenAI } from 'openai';
import { llmManager, llms } from "@/views/setting/llm"; import { llmManager, llms } from "@/views/setting/llm";
import { pinkLog, redLog } from "@/views/setting/util"; import { pinkLog, redLog } from "@/views/setting/util";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { handleToolCalls, ToolCallResult } from "./handle-tool-calls"; import { handleToolCalls, type ToolCallResult } from "./handle-tool-calls";
import { getPlatform } from "@/api/platform"; import { getPlatform } from "@/api/platform";
export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk; export type ChatCompletionChunk = OpenAI.Chat.Completions.ChatCompletionChunk;

View File

@ -1,4 +1,4 @@
import { IExtraInfo } from "../chat-box/chat"; import type { IExtraInfo } from "../chat-box/chat";
export interface UsageStatistic { export interface UsageStatistic {
input: number; input: number;

View File

@ -57,9 +57,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, defineComponent, defineProps, onUnmounted, computed, nextTick, watch, provide } from 'vue'; import { ref, onMounted, defineComponent, defineProps, onUnmounted, computed, nextTick, watch, provide } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ElMessage, ScrollbarInstance } from 'element-plus'; import { ElMessage, type ScrollbarInstance } from 'element-plus';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { ChatMessage, ChatStorage, IRenderMessage, MessageState, ToolCall } from './chat-box/chat'; import type { ChatMessage, ChatStorage, IRenderMessage, ToolCall } from './chat-box/chat';
import { MessageState } from './chat-box/chat';
import * as Message from './message'; import * as Message from './message';
import ChatBox from './chat-box/index.vue'; import ChatBox from './chat-box/index.vue';

View File

@ -1,4 +1,4 @@
import Prism from "./prism"; import Prism from 'prismjs';
// 定义 escapeHtml 函数 // 定义 escapeHtml 函数
function escapeHtml(unsafe: string) { function escapeHtml(unsafe: string) {

View File

@ -1,4 +1,4 @@
const Prism: { export const Prism: {
highlight: (code: string, language: any, lang: string) => string; highlight: (code: string, language: any, lang: string) => string;
languages: Record<string, any>; languages: Record<string, any>;
// 添加其他需要的类型定义 // 添加其他需要的类型定义

View File

@ -32,7 +32,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, ref, PropType, inject } from 'vue'; import { defineProps, ref, type PropType, inject } from 'vue';
import { tabs } from '../../panel'; import { tabs } from '../../panel';
import type { ChatStorage, IRenderMessage } from '../chat-box/chat'; import type { ChatStorage, IRenderMessage } from '../chat-box/chat';

View File

@ -27,10 +27,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { CasualRestAPI, PromptTemplate, PromptsListResponse } from '@/hook/type'; import type { CasualRestAPI, PromptTemplate, PromptsListResponse } from '@/hook/type';
import { onMounted, onUnmounted, defineProps, defineEmits, reactive } from 'vue'; import { onMounted, onUnmounted, defineProps, defineEmits, reactive } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { promptsManager, PromptStorage } from './prompts'; import { promptsManager, type PromptStorage } from './prompts';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';

View File

@ -1,4 +1,4 @@
import { PromptsGetResponse, PromptTemplate } from '@/hook/type'; import type { PromptsGetResponse, PromptTemplate } from '@/hook/type';
import { reactive } from 'vue'; import { reactive } from 'vue';
export const promptsManager = reactive<{ export const promptsManager = reactive<{

View File

@ -38,8 +38,8 @@ import { defineComponent, defineProps, watch, ref, computed, reactive, defineEmi
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import type { FormInstance, FormRules } from 'element-plus'; import type { FormInstance, FormRules } from 'element-plus';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { parseResourceTemplate, resourcesManager, ResourceStorage } from './resources'; import { parseResourceTemplate, resourcesManager, type ResourceStorage } from './resources';
import { CasualRestAPI, ResourcesReadResponse } from '@/hook/type'; import type{ CasualRestAPI, ResourcesReadResponse } from '@/hook/type';
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp'; import { getDefaultValue, normaliseJavascriptType } from '@/hook/mcp';

View File

@ -30,10 +30,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { CasualRestAPI, ResourceTemplate, ResourceTemplatesListResponse } from '@/hook/type'; import type { CasualRestAPI, ResourceTemplate, ResourceTemplatesListResponse } from '@/hook/type';
import { onMounted, onUnmounted, defineProps, ref, reactive } from 'vue'; import { onMounted, onUnmounted, defineProps, ref, reactive } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { resourcesManager, ResourceStorage } from './resources'; import { resourcesManager, type ResourceStorage } from './resources';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';

View File

@ -27,10 +27,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { CasualRestAPI, Resources, ResourcesListResponse } from '@/hook/type'; import type { CasualRestAPI, Resources, ResourcesListResponse } from '@/hook/type';
import { onMounted, onUnmounted, defineProps, defineEmits, reactive } from 'vue'; import { onMounted, onUnmounted, defineProps, defineEmits, reactive } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { resourcesManager, ResourceStorage } from './resources'; import { resourcesManager, type ResourceStorage } from './resources';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';

View File

@ -1,4 +1,4 @@
import { ResourcesReadResponse, ResourceTemplate, Resources } from '@/hook/type'; import type { ResourcesReadResponse, ResourceTemplate, Resources } from '@/hook/type';
import { reactive } from 'vue'; import { reactive } from 'vue';

View File

@ -31,10 +31,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { CasualRestAPI, ToolsListResponse } from '@/hook/type'; import type { CasualRestAPI, ToolsListResponse } from '@/hook/type';
import { onMounted, onUnmounted, defineProps } from 'vue'; import { onMounted, onUnmounted, defineProps } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { toolsManager, ToolStorage } from './tools'; import { toolsManager, type ToolStorage } from './tools';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';

View File

@ -1,6 +1,6 @@
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { mcpSetting } from '@/hook/mcp'; import { mcpSetting } from '@/hook/mcp';
import { ToolsListResponse, ToolCallResponse, CasualRestAPI } from '@/hook/type'; import type { ToolsListResponse, ToolCallResponse, CasualRestAPI } from '@/hook/type';
import { pinkLog } from '@/views/setting/util'; import { pinkLog } from '@/views/setting/util';
import { reactive } from 'vue'; import { reactive } from 'vue';

View File

@ -0,0 +1,55 @@
<template>
<el-dialog v-model="dialogVisible" title="请输入密码" :close-on-click-modal="false" :close-on-press-escape="false"
:show-close="false" width="30%" top="20vh">
<br>
<el-input v-model="privilegeStatus.password"
type="password"
placeholder="请输入密码"
@keyup.enter.prevent="handleSubmit"
/>
<template #footer>
<el-button type="primary" @click="handleSubmit">确认</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { privilegeStatus } from './status';
import { useMessageBridge } from '@/api/message-bridge';
import { initialise } from '@/views/connect/connection';
const dialogVisible = ref(true);
const handleSubmit = async () => {
const bridge = useMessageBridge();
const res = await bridge.commandRequest('ciallo', { password: privilegeStatus.password });
if (res.code === 200) {
ElMessage.success('密码验证成功,欢迎回来锦恢');
dialogVisible.value = false;
initialise();
} else {
ElMessage.error('密码验证失败,请重新输入');
}
};
onMounted(() => {
dialogVisible.value = true;
});
</script>
<style scoped>
.el-dialog {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
z-index: 9999;
}
</style>

View File

@ -0,0 +1,6 @@
import { reactive } from "vue";
export const privilegeStatus = reactive({
allow: true,
password: ''
});

View File

@ -1,4 +1,4 @@
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"; import { createRouter, createWebHistory, type RouteRecordRaw } from "vue-router";
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
@ -33,7 +33,7 @@ const routes: Array<RouteRecordRaw> = [
]; ];
const router = createRouter({ const router = createRouter({
history: createWebHistory(process.env.BASE_URL), history: createWebHistory('/'),
routes, routes,
}); });

View File

@ -1,8 +1,10 @@
import { useMessageBridge } from '@/api/message-bridge'; import { useMessageBridge } from '@/api/message-bridge';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { pinkLog } from '../setting/util'; import { pinkLog } from '../setting/util';
import { ElMessage } from 'element-plus'; import { ElLoading, ElMessage } from 'element-plus';
import { OpenMcpSupportPlatform } from '@/api/platform'; import { getPlatform, type OpenMcpSupportPlatform } from '@/api/platform';
import { getTour, loadSetting } from '@/hook/setting';
import { loadPanels } from '@/hook/panel';
export const connectionMethods = reactive({ export const connectionMethods = reactive({
current: 'STDIO', current: 'STDIO',
@ -98,7 +100,7 @@ export async function doConnect(
} }
} else { } else {
connectionMethods.current = 'SSE'; connectionMethods.current = 'SSE';
connectionArgs.urlString = connectionItem.url; connectionArgs.urlString = connectionItem.url || '';
if (connectionArgs.urlString.length === 0) { if (connectionArgs.urlString.length === 0) {
return; return;
@ -346,3 +348,36 @@ export async function handleEnvSwitch(enabled: boolean) {
export async function loadEnvVar() { export async function loadEnvVar() {
return await handleEnvSwitch(true); return await handleEnvSwitch(true);
} }
export async function initialise() {
pinkLog('准备请求设置');
const loading = ElLoading.service({
fullscreen: true,
lock: true,
text: 'Loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const platform = getPlatform();
// 加载全局设置
loadSetting();
// 设置环境变量
await loadEnvVar();
// 获取引导状态
await getTour();
// 尝试进行初始化连接
await doConnect({
namespace: platform,
updateCommandString: true
});
// loading panels
await loadPanels();
loading.close();
}

View File

@ -1,4 +1,4 @@
import { ChatStorage } from '@/components/main-panel/chat/chat-box/chat'; import type { ChatStorage } from '@/components/main-panel/chat/chat-box/chat';
import { TaskLoop } from '@/components/main-panel/chat/core/task-loop'; import { TaskLoop } from '@/components/main-panel/chat/core/task-loop';
import { llmManager } from './llm'; import { llmManager } from './llm';
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
@ -60,7 +60,7 @@ export async function makeSimpleTalk() {
await loop.start(chatStorage, testMessage); await loop.start(chatStorage, testMessage);
const costTime = (performance.now() - startTime!) / 1000; const costTime = (performance.now() - startTime!) / 1000;
const message = chatStorage.messages.at(-1); const message = chatStorage.messages[chatStorage.messages.length - 1];
console.log(chatStorage.messages); console.log(chatStorage.messages);
if (message?.extraInfo) { if (message?.extraInfo) {
@ -70,5 +70,4 @@ export async function makeSimpleTalk() {
simpleTestResult.tps = (total / costTime).toFixed(2); simpleTestResult.tps = (total / costTime).toFixed(2);
} }
} }
} }

View File

@ -1,7 +1,7 @@
import { markRaw, reactive } from 'vue'; import { markRaw, reactive } from 'vue';
import { createTab, debugModes, tabs } from '@/components/main-panel/panel'; import { createTab, debugModes, tabs } from '@/components/main-panel/panel';
import { ToolStorage } from '@/components/main-panel/tool/tools'; import type { ToolStorage } from '@/components/main-panel/tool/tools';
import { ToolCall } from '@/components/main-panel/chat/chat-box/chat'; import type { ToolCall } from '@/components/main-panel/chat/chat-box/chat';
import I18n from '@/i18n'; import I18n from '@/i18n';
const { t } = I18n.global; const { t } = I18n.global;

View File

@ -0,0 +1,19 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": [
"env.d.ts",
"src/**/*",
"src/**/*.vue"
],
"exclude": [
"src/**/__tests__/*"
],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": [
"./src/*"
]
}
}
}

View File

@ -1,41 +1,17 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "esnext",
"module": "nodenext",
"strict": true,
"jsx": "preserve",
"moduleResolution": "nodenext",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"useDefineForClassFields": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [ "lib": [
"esnext", "es2022",
"dom", "dom"
"dom.iterable",
"scripthost"
] ]
}, },
"include": [ "files": [],
"src/**/*.ts", "references": [
"src/**/*.tsx", {
"src/**/*.vue", "path": "./tsconfig.node.json"
"tests/**/*.ts", },
"tests/**/*.tsx" {
], "path": "./tsconfig.app.json"
"exclude": [ }
"node_modules"
] ]
} }

View File

@ -0,0 +1,20 @@
{
"extends": "@tsconfig/node22/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": [
"node"
]
}
}

18
renderer/vite.config.ts Normal file
View File

@ -0,0 +1,18 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
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))
},
},
})

View File

@ -1,54 +0,0 @@
const { defineConfig } = require("@vue/cli-service");
function getPublicPath() {
const env = process.env.NODE_ENV;
if (env === 'production') {
return '';
} else if (env === 'kirigaya') {
return '/mcp';
} else {
return '/';
}
}
module.exports = defineConfig({
transpileDependencies: true,
publicPath: process.env.NODE_ENV === 'production' ? '' : '/',
configureWebpack: {
optimization: {
splitChunks: false
}
},
chainWebpack: config => {
// 删除所有预设的代码分割规则
config.optimization.delete('splitChunks');
// 确保路由组件同步加载
config.plugins.delete('prefetch');
config.plugins.delete('preload');
// 删除 public 下指定的 css 文件
config.plugin('copy').tap(args => {
args[0].patterns = args[0].patterns.map((pattern) => {
if (pattern.from === "public") {
// 忽略指定的 CSS 文件
pattern.globOptions = {
ignore: [
"vscode.css",
"default-light.css",
"default-dark.css",
],
};
}
return pattern;
});
return args;
});
},
css: {
extract: false
},
devServer: {
port: 8081
}
});

View File

@ -1,22 +0,0 @@
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports = {
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
new webpack.DefinePlugin({
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
})
],
resolve: {
fallback: {
"stream": false
}
},
};

3
service/.gitignore vendored
View File

@ -26,4 +26,5 @@ setting.json
*.traineddata *.traineddata
.env .env
tabs.example-servers_puppeteer.json tabs.*
update.sh

View File

@ -1,77 +0,0 @@
/**
* source: https://gist.github.com/Laci21/9dd074f3a5a461ab04adb7db678534d3
* issue: https://github.com/modelcontextprotocol/typescript-sdk/issues/217
*
* This script fixes the MCP SDK issue with pkce-challenge ES Module
* It replaces the static require with a dynamic import in the auth.js file
*/
const fs = require('fs');
const path = require('path');
// Path to the file that needs patching
const authFilePath = path.resolve(
__dirname,
'node_modules',
'@modelcontextprotocol',
'sdk',
'dist',
'cjs',
'client',
'auth.js'
);
console.log('Checking if MCP SDK patch is needed...');
// Check if the file exists
if (!fs.existsSync(authFilePath)) {
console.error(`Error: File not found at ${authFilePath}`);
console.log('Make sure you have installed @modelcontextprotocol/sdk package');
process.exit(1);
}
// Read the file content
const fileContent = fs.readFileSync(authFilePath, 'utf8');
// Check if the file already contains our patch
if (fileContent.includes('loadPkceChallenge')) {
console.log('MCP SDK is already patched!');
process.exit(0);
}
// Check if the file contains the problematic require
if (!fileContent.includes("require(\"pkce-challenge\")")) {
console.log('The MCP SDK file does not contain the expected require statement.');
console.log('This patch may not be needed or the SDK has been updated.');
process.exit(0);
}
console.log('Applying patch to MCP SDK...');
// The code to replace the problematic require
const requireLine = "const pkce_challenge_1 = __importDefault(require(\"pkce-challenge\"));";
const replacementCode = `let pkce_challenge_1 = { default: null };
async function loadPkceChallenge() {
if (!pkce_challenge_1.default) {
const mod = await import("pkce-challenge");
pkce_challenge_1.default = mod.default;
}
}`;
// Replace the require line
let patchedContent = fileContent.replace(requireLine, replacementCode);
// Replace the function call to add the loading step
const challengeCall = "const challenge = await (0, pkce_challenge_1.default)();";
const replacementCall = "await loadPkceChallenge();\n const challenge = await pkce_challenge_1.default();";
patchedContent = patchedContent.replace(challengeCall, replacementCall);
// Write the patched content back to the file
fs.writeFileSync(authFilePath, patchedContent, 'utf8');
console.log('✅ MCP SDK patched successfully!');
console.log('The patch changes:');
console.log('1. Replaced static require with dynamic import for pkce-challenge');
console.log('2. Added async loading function to handle the import');
console.log('\nYou should now be able to run the application without ESM errors.');

View File

@ -98,7 +98,7 @@ function updateConnectionOption(data: any) {
const devHome = path.join(__dirname, '..', '..'); const devHome = path.join(__dirname, '..', '..');
setRunningCWD(devHome); setRunningCWD(devHome);
wss.on('connection', ws => { wss.on('connection', (ws: any) => {
// 仿造 webview 进行统一接口访问 // 仿造 webview 进行统一接口访问
const webview = new VSCodeWebViewLike(ws); const webview = new VSCodeWebViewLike(ws);

View File

@ -57,6 +57,10 @@ export async function connectService(
return connectResult; return connectResult;
} catch (error) { } catch (error) {
console.log('meet error');
console.log(error);
// TODO: 这边获取到的 error 不够精致,如何才能获取到更加精准的错误 // TODO: 这边获取到的 error 不够精致,如何才能获取到更加精准的错误
// 比如 error: Failed to spawn: `server.py` // 比如 error: Failed to spawn: `server.py`
// Caused by: No such file or directory (os error 2) // Caused by: No such file or directory (os error 2)

200
service/src/server.ts Normal file
View File

@ -0,0 +1,200 @@
import WebSocket from 'ws';
import pino from 'pino';
import { routeMessage } from './common/router';
import { VSCodeWebViewLike } from './hook/adapter';
import path from 'node:path';
import * as fs from 'node:fs';
import { setRunningCWD } from './hook/setting';
import { exit } from 'node:process';
export interface VSCodeMessage {
command: string;
data?: unknown;
callbackId?: string;
}
const logger = pino({
transport: {
target: 'pino-pretty', // 启用 pino-pretty
options: {
colorize: true, // 开启颜色
levelFirst: true, // 先打印日志级别
translateTime: 'SYS:yyyy-mm-dd HH:MM:ss', // 格式化时间
ignore: 'pid,hostname', // 忽略部分字段
}
}
});
export type MessageHandler = (message: VSCodeMessage) => void;
const wss = new (WebSocket as any).Server({ port: 8282 });
interface IStdioLaunchSignature {
type: 'stdio';
commandString: string;
cwd: string;
}
interface ISSELaunchSignature {
type:'sse';
url: string;
oauth: string;
}
export type ILaunchSigature = IStdioLaunchSignature | ISSELaunchSignature;
function refreshConnectionOption(envPath: string) {
const defaultOption = {
type:'stdio',
command: 'mcp',
args: ['run', 'main.py'],
cwd: '../server'
};
fs.writeFileSync(envPath, JSON.stringify(defaultOption, null, 4));
return defaultOption;
}
function getInitConnectionOption() {
const envPath = path.join(__dirname, '..', '.env');
if (!fs.existsSync(envPath)) {
return refreshConnectionOption(envPath);
}
try {
const option = JSON.parse(fs.readFileSync(envPath, 'utf-8'));
return option;
} catch (error) {
logger.error('读取 .env 配置文件');
return refreshConnectionOption(envPath);
}
}
if (!fs.existsSync(path.join(__dirname, '..', '.env.website.local'))) {
console.log('.env.website.local 不存在!');
exit(0);
}
const authPassword = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '.env.website.local'), 'utf-8')).password;
function updateConnectionOption(data: any) {
const envPath = path.join(__dirname, '..', '.env');
if (data.connectionType === 'STDIO') {
const connectionItem = {
type: 'stdio',
command: data.command,
args: data.args,
cwd: data.cwd.replace(/\\/g, '/')
};
fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4));
} else {
const connectionItem = {
type: 'sse',
url: data.url,
oauth: data.oauth
};
fs.writeFileSync(envPath, JSON.stringify(connectionItem, null, 4));
}
}
const devHome = path.join(__dirname, '..', '..');
setRunningCWD(devHome);
wss.on('connection', (ws: any) => {
// 仿造 webview 进行统一接口访问
const webview = new VSCodeWebViewLike(ws);
// 先发送成功建立的消息
webview.postMessage({
command: 'hello',
data: {
version: '0.0.1',
name: '消息桥连接完成'
}
});
const option = getInitConnectionOption();
// 注册消息接受的管线
webview.onDidReceiveMessage(message => {
logger.info(`command: [${message.command || 'No Command'}]`);
const { command, data, password } = message;
console.log(command, data);
if (command === 'ciallo') {
if (data.password === authPassword) {
webview.postMessage({
command,
data: {
code: 200,
msg: 'ciallo'
}
});
} else {
webview.postMessage({
command,
data: {
code: 403,
msg: '没有权限'
}
});
}
return;
}
if (password !== authPassword) {
webview.postMessage({
command,
data: {
code: 403,
msg: '没有权限'
}
});
return;
}
switch (command) {
case 'web/launch-signature':
const launchResultMessage: ILaunchSigature = option.type === 'stdio' ?
{
type: 'stdio',
commandString: option.command + ' ' + option.args.join(' '),
cwd: option.cwd || ''
} :
{
type: 'sse',
url: option.url,
oauth: option.oauth || ''
};
const launchResult = {
code: 200,
msg: launchResultMessage
};
webview.postMessage({
command: 'web/launch-signature',
data: launchResult
});
break;
case 'web/update-connection-sigature':
updateConnectionOption(data);
break;
default:
routeMessage(command, data, webview);
break;
}
});
});

View File

@ -17,7 +17,6 @@ export class SettingController {
@Controller('setting/load') @Controller('setting/load')
async loadSetting(client: RequestClientType, data: any, webview: PostMessageble) { async loadSetting(client: RequestClientType, data: any, webview: PostMessageble) {
console.log('enter loading');
const config = loadSetting(); const config = loadSetting();
return { return {
@ -41,7 +40,6 @@ export class SettingController {
@Controller('setting/get-tour') @Controller('setting/get-tour')
async getTourController(client: RequestClientType, data: any, webview: PostMessageble) { async getTourController(client: RequestClientType, data: any, webview: PostMessageble) {
console.log('enter');
const { userHasReadGuide } = getTour(); const { userHasReadGuide } = getTour();

View File

@ -1,175 +0,0 @@
{
"currentIndex": 2,
"tabs": [
{
"name": "资源",
"icon": "icon-file",
"type": "blank",
"componentIndex": 0,
"storage": {
"formData": {}
}
},
{
"name": "工具",
"icon": "icon-tool",
"type": "blank",
"componentIndex": 2,
"storage": {
"currentToolName": "image_crawler",
"formData": {
"key_word": "",
"image_num": 0
}
}
},
{
"name": "交互测试",
"icon": "icon-robot",
"type": "blank",
"componentIndex": 3,
"storage": {
"messages": [
{
"role": "user",
"content": "帮我找几张 明日方舟 m3 的图片,渲染出来",
"extraInfo": {
"created": 1745730609368,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_660r5ehyhh86cjtqnako0mnt",
"index": 0,
"type": "function",
"function": {
"name": "image_crawler",
"arguments": "{\"key_word\":\"明日方舟 m3\",\"image_num\":5}"
}
}
],
"extraInfo": {
"created": 1745730610263,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "tool",
"tool_call_id": "call_660r5ehyhh86cjtqnako0mnt",
"content": [
{
"type": "text",
"text": "[ImageResult(url='https://gd-hbimg.huaban.com/0c97d53a94f333fad66ca70f32601d8c34c2d4db8f53e0-netl2f_fw658', title='', source='bing'), ImageResult(url='https://ts1.tc.mm.bing.net/th/id/R-C.db1a4364345d8b082d4de48cae32ae05?rik=rhE9iKxzhvwzDg', title='', source='bing'), ImageResult(url='https://gd-hbimg.huaban.com/a4b669d3e68da3ee91a12afe7a0bd689038bdc262aece-yasxcI_fw658', title='', source='bing'), ImageResult(url='https://ts1.tc.mm.bing.net/th/id/R-C.d9dc5fcd4b628e805d4b6a0edb0f9a48?rik=RiUbfVRpViaT%2fw', title='', source='bing'), ImageResult(url='https://gd-hbimg.huaban.com/205d12dbf22aec83ff34bd533616575f0939b561136c6-jwF7XH_fw658', title='', source='bing')]"
}
],
"extraInfo": {
"created": 1745730610855,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "assistant",
"content": "以下是根据关键词“明日方舟 m3”找到的图片\n\n1. ![Image 1](https://gd-hbimg.huaban.com/0c97d53a94f333fad66ca70f32601d8c34c2d4db8f53e0-netl2f_fw658)\n2. ![Image 2](https://ts1.tc.mm.bing.net/th/id/R-C.db1a4364345d8b082d4de48cae32ae05?rik=rhE9iKxzhvwzDg)\n3. ![Image 3](https://gd-hbimg.huaban.com/a4b669d3e68da3ee91a12afe7a0bd689038bdc262aece-yasxcI_fw658)\n4. ![Image 4](https://ts1.tc.mm.bing.net/th/id/R-C.d9dc5fcd4b628e805d4b6a0edb0f9a48?rik=RiUbfVRpViaT%2fw)\n5. ![Image 5](https://gd-hbimg.huaban.com/205d12dbf22aec83ff34bd533616575f0939b561136c6-jwF7XH_fw658)\n\n你可以点击链接查看这些图片。",
"extraInfo": {
"created": 1745730615968,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "user",
"content": "图源是哪里",
"extraInfo": {
"created": 1745730806510,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "assistant",
"content": "这些图片的来源是Bing搜索引擎和Huaban网站。具体来源如下\n\n1. **Bing**:\n - [Image 2](https://ts1.tc.mm.bing.net/th/id/R-C.db1a4364345d8b082d4de48cae32ae05?rik=rhE9iKxzhvwzDg)\n - [Image 4](https://ts1.tc.mm.bing.net/th/id/R-C.d9dc5fcd4b628e805d4b6a0edb0f9a48?rik=RiUbfVRpViaT%2fw)\n\n2. **Huaban**:\n - [Image 1](https://gd-hbimg.huaban.com/0c97d53a94f333fad66ca70f32601d8c34c2d4db8f53e0-netl2f_fw658)\n - [Image 3](https://gd-hbimg.huaban.com/a4b669d3e68da3ee91a12afe7a0bd689038bdc262aece-yasxcI_fw658)\n - [Image 5](https://gd-hbimg.huaban.com/205d12dbf22aec83ff34bd533616575f0939b561136c6-jwF7XH_fw658)\n\n你可以通过点击链接查看图片的详细信息。",
"extraInfo": {
"created": 1745730812395,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "user",
"content": "帮我找几张 明日方舟 白面鸮 的图片,渲染出来",
"extraInfo": {
"created": 1745730909927,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_ho74cop478w5gbfc0jnuo12f",
"index": 0,
"type": "function",
"function": {
"name": "image_crawler",
"arguments": "{\"key_word\":\"明日方舟 白面鸮\",\"image_num\":5}"
}
}
],
"extraInfo": {
"created": 1745730910906,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "tool",
"tool_call_id": "call_ho74cop478w5gbfc0jnuo12f",
"content": [
{
"type": "text",
"text": "[ImageResult(url='https://c-ssl.duitang.com/uploads/item/201910/22/20191022190241_dixsc.thumb.1000_0.jpg', title='', source='bing'), ImageResult(url='https://img-baofun.zhhainiao.com/pcwallpaper_ugc_mobile/live/e06866505212ddcce0dabf9afebce225.mp4.jpg', title='', source='bing'), ImageResult(url='https://c-ssl.dtstatic.com/uploads/item/202007/15/20200715183732_iomsj.thumb.1000_0.jpg', title='', source='bing'), ImageResult(url='https://img-baofun.zhhainiao.com/pcwallpaper_ugc_mobile/live/710b67dfa125248f816d63617f551452_preview.jpg', title='', source='bing'), ImageResult(url='https://n.sinaimg.cn/sinacn10122/670/w400h270/20200104/db75-imrkkfx7540303.jpg', title='', source='bing')]"
}
],
"extraInfo": {
"created": 1745730911376,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
},
{
"role": "assistant",
"content": "以下是根据关键词“明日方舟 白面鸮”找到的图片:\n\n1. ![Image 1](https://c-ssl.duitang.com/uploads/item/201910/22/20191022190241_dixsc.thumb.1000_0.jpg)\n2. ![Image 2](https://img-baofun.zhhainiao.com/pcwallpaper_ugc_mobile/live/e06866505212ddcce0dabf9afebce225.mp4.jpg)\n3. ![Image 3](https://c-ssl.dtstatic.com/uploads/item/202007/15/20200715183732_iomsj.thumb.1000_0.jpg)\n4. ![Image 4](https://img-baofun.zhhainiao.com/pcwallpaper_ugc_mobile/live/710b67dfa125248f816d63617f551452_preview.jpg)\n5. ![Image 5](https://n.sinaimg.cn/sinacn10122/670/w400h270/20200104/db75-imrkkfx7540303.jpg)\n\n你可以点击链接查看这些图片。",
"extraInfo": {
"created": 1745730916072,
"state": "success",
"serverName": "Huoshan DeepSeek"
}
}
],
"settings": {
"modelIndex": 8,
"enableTools": [
{
"name": "image_crawler",
"description": "根据关键词从指定搜索引擎爬取图片",
"enabled": true
}
],
"enableWebSearch": false,
"temperature": 0.7,
"contextLength": 10,
"systemPrompt": ""
}
}
}
]
}

View File

@ -1,12 +0,0 @@
{
"currentIndex": 0,
"tabs": [
{
"name": "Blank test 1",
"icon": "icon-blank",
"type": "blank",
"componentIndex": -1,
"storage": {}
}
]
}

View File

@ -1,38 +0,0 @@
{
"currentIndex": 0,
"tabs": [
{
"name": "资源",
"icon": "icon-file",
"type": "blank",
"componentIndex": 0,
"storage": {
"formData": {},
"currentType": "resource",
"currentResourceName": "network_log",
"lastResourceReadResponse": {
"contents": [
{
"uri": "network://log",
"mimeType": "text/plain",
"text": "Response from ..."
}
]
}
}
},
{
"name": "工具",
"icon": "icon-tool",
"type": "blank",
"componentIndex": 2,
"storage": {
"formData": {
"a": 0,
"b": 0
},
"currentToolName": "add"
}
}
]
}

View File

@ -1,31 +0,0 @@
const path = require('path');
module.exports = {
entry: './src/index.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
libraryTarget: 'commonjs2' // 使用 commonjs2 模块系统
},
resolve: {
extensions: ['.ts', '.js'],
fallback: {
bufferutil: false,
'utf-8-validate': false
}
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: 'ts-loader'
}
]
},
target: 'node', // 指定目标环境为 Node.js
mode: 'production',
optimization: {
minimize: true
}
};