@opentiny/tiny-robot-cli 0.5.2-alpha.16 → 0.5.2-alpha.18
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.
- package/README.md +1 -49
- package/bin/commands/add.js +270 -561
- package/bin/commands/create.js +3 -6
- package/bin/utils.js +45 -147
- package/package.json +3 -5
- package/templates/basic/.env.example +0 -8
- package/templates/basic/README.md +22 -53
- package/templates/basic/index.html +2 -2
- package/templates/basic/package.json +16 -12
- package/templates/basic/public/modelcontextprotocol.png +0 -0
- package/templates/basic/src/App.vue +119 -112
- package/templates/basic/src/components/ChatList.vue +82 -0
- package/templates/basic/src/components/ChatSender.vue +125 -0
- package/templates/basic/src/components/ConversationHistory.vue +136 -0
- package/templates/basic/src/components/HistoryDrawerButton.vue +43 -0
- package/templates/basic/src/components/McpServerPickerButton.vue +278 -0
- package/templates/basic/src/components/ThemeToggleButton.vue +44 -0
- package/templates/basic/src/components/icons/IconDeepThink.vue +29 -0
- package/templates/basic/src/components/icons/IconModelAliyunBailian.vue +51 -0
- package/templates/basic/src/components/icons/IconModelDeepseek.vue +29 -0
- package/templates/basic/src/components/icons/IconMoon.vue +29 -0
- package/templates/basic/src/components/icons/IconPlugin.vue +29 -0
- package/templates/basic/src/components/icons/IconSun.vue +35 -0
- package/templates/basic/src/components/icons/IconWebSearch.vue +36 -0
- package/templates/basic/src/components/icons/index.ts +7 -0
- package/templates/basic/src/composables/useChat.ts +129 -0
- package/templates/basic/src/composables/useMcp.ts +170 -0
- package/templates/basic/src/composables/useModel.ts +82 -0
- package/templates/basic/src/main.ts +2 -2
- package/templates/basic/src/mcpServers.ts +40 -0
- package/templates/basic/src/models.ts +81 -0
- package/templates/basic/tsconfig.app.json +5 -2
- package/templates/basic/tsconfig.node.json +6 -3
- package/templates/basic/vite.config.ts +1 -6
- package/templates/chat/.env.example +0 -9
- package/templates/chat/src/TinyRobotChat.vue +27 -88
- package/bin/runtime-version.js +0 -37
- package/templates/basic/public/favicon.svg +0 -1
- package/templates/basic/public/icons.svg +0 -24
- package/templates/chat/README.md +0 -46
- package/templates/chat/index.html +0 -12
- package/templates/chat/package.json +0 -28
- package/templates/chat/src/components/ComposerTools.vue +0 -99
- package/templates/chat/src/components/WindowHeader.vue +0 -192
- package/templates/chat/src/composables/useWindow.ts +0 -83
- package/templates/chat/src/config/chat-runtime.ts +0 -82
- package/templates/chat/src/config/chat-ui.ts +0 -290
- package/templates/chat/src/index.css +0 -109
- package/templates/chat/src/main.ts +0 -7
- package/templates/chat/tsconfig.app.json +0 -13
- package/templates/chat/tsconfig.json +0 -7
- package/templates/chat/tsconfig.node.json +0 -19
- package/templates/chat/vite.config.ts +0 -16
package/bin/commands/create.js
CHANGED
|
@@ -2,7 +2,6 @@ import { input, select } from '@inquirer/prompts'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import process from 'node:process'
|
|
4
4
|
|
|
5
|
-
import { resolveRuntimeVersion } from '../runtime-version.js'
|
|
6
5
|
import {
|
|
7
6
|
DEFAULT_PROJECT_NAME,
|
|
8
7
|
DEFAULT_TEMPLATE,
|
|
@@ -120,13 +119,12 @@ function printCreateSuccess(projectName) {
|
|
|
120
119
|
console.log()
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
async function createProject(initialProjectName, initialTemplateName,
|
|
124
|
-
const runtime = resolveRuntimeVersion(runtimeVersion)
|
|
122
|
+
async function createProject(initialProjectName, initialTemplateName, skipPrompt) {
|
|
125
123
|
const options = await resolveCreateOptions(initialProjectName, initialTemplateName, skipPrompt)
|
|
126
124
|
|
|
127
125
|
const { templateDir, targetDir } = validateCreateOptions(options)
|
|
128
126
|
|
|
129
|
-
scaffoldProject(templateDir, targetDir, options.projectName
|
|
127
|
+
scaffoldProject(templateDir, targetDir, options.projectName)
|
|
130
128
|
|
|
131
129
|
printCreateSuccess(options.projectName)
|
|
132
130
|
}
|
|
@@ -136,11 +134,10 @@ export function registerCreateCommand(program) {
|
|
|
136
134
|
.command('create [project-name]')
|
|
137
135
|
.description('Create a TinyRobot project from template')
|
|
138
136
|
.option('-t, --template <name>', 'template name')
|
|
139
|
-
.option('--runtime-version <version>', 'override the TinyRobot runtime version')
|
|
140
137
|
.action((projectName, options) => {
|
|
141
138
|
const skipPrompt = !process.stdout.isTTY
|
|
142
139
|
|
|
143
|
-
createProject(projectName ?? '', options.template ?? '',
|
|
140
|
+
createProject(projectName ?? '', options.template ?? '', skipPrompt).catch((error) => {
|
|
144
141
|
if (error instanceof Error && error.name === 'ExitPromptError') {
|
|
145
142
|
console.error('\nOperation cancelled.')
|
|
146
143
|
process.exit(1)
|
package/bin/utils.js
CHANGED
|
@@ -5,7 +5,6 @@ import pc from 'picocolors'
|
|
|
5
5
|
import yaml from 'yaml'
|
|
6
6
|
|
|
7
7
|
const TEMPLATE_PLACEHOLDER = '__PROJECT_NAME__'
|
|
8
|
-
const RUNTIME_VERSION_PLACEHOLDER = '__TINY_ROBOT_VERSION__'
|
|
9
8
|
|
|
10
9
|
export const BUILTIN_TEMPLATES = ['basic']
|
|
11
10
|
export const DEFAULT_TEMPLATE = 'basic'
|
|
@@ -37,7 +36,10 @@ export function logError(message) {
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
export function invariant(condition, message) {
|
|
40
|
-
if (!condition)
|
|
39
|
+
if (!condition) {
|
|
40
|
+
console.error(`Error: ${message}`)
|
|
41
|
+
process.exit(1)
|
|
42
|
+
}
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export function exists(file) {
|
|
@@ -86,10 +88,6 @@ export function validateProjectName(name) {
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
export function copyTemplate(sourceDir, targetDir) {
|
|
89
|
-
copyDirectory(sourceDir, targetDir)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function copyDirectory(sourceDir, targetDir) {
|
|
93
91
|
fs.cpSync(sourceDir, targetDir, {
|
|
94
92
|
recursive: true,
|
|
95
93
|
filter: (source) => {
|
|
@@ -112,7 +110,7 @@ function renameSpecialFiles(targetDir) {
|
|
|
112
110
|
}
|
|
113
111
|
|
|
114
112
|
function replaceTemplateVariables(targetDir, variables) {
|
|
115
|
-
const replaceFiles = ['package.json', 'README.md'
|
|
113
|
+
const replaceFiles = ['package.json', 'README.md']
|
|
116
114
|
|
|
117
115
|
for (const relativePath of replaceFiles) {
|
|
118
116
|
const file = path.join(targetDir, relativePath)
|
|
@@ -131,14 +129,13 @@ function replaceTemplateVariables(targetDir, variables) {
|
|
|
131
129
|
}
|
|
132
130
|
}
|
|
133
131
|
|
|
134
|
-
export function scaffoldProject(templateDir, targetDir, projectName
|
|
132
|
+
export function scaffoldProject(templateDir, targetDir, projectName) {
|
|
135
133
|
copyTemplate(templateDir, targetDir)
|
|
136
134
|
|
|
137
135
|
renameSpecialFiles(targetDir)
|
|
138
136
|
|
|
139
137
|
replaceTemplateVariables(targetDir, {
|
|
140
138
|
[TEMPLATE_PLACEHOLDER]: projectName,
|
|
141
|
-
[RUNTIME_VERSION_PLACEHOLDER]: runtimeVersion,
|
|
142
139
|
})
|
|
143
140
|
}
|
|
144
141
|
|
|
@@ -169,24 +166,20 @@ export function findProjectRoot(cwd) {
|
|
|
169
166
|
}
|
|
170
167
|
|
|
171
168
|
export function findSubPackageRoot(cwd, workspaceRoot) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (dir === root) return null
|
|
177
|
-
if (isPackageDir(dir)) return dir
|
|
169
|
+
return findUp(cwd, (dir) => {
|
|
170
|
+
if (dir === workspaceRoot) {
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
178
173
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
dir = parent
|
|
182
|
-
}
|
|
174
|
+
return isPackageDir(dir)
|
|
175
|
+
})
|
|
183
176
|
}
|
|
184
177
|
|
|
185
178
|
export function findWorkspacePackages(workspaceRoot) {
|
|
186
179
|
const workspaceFile = resolveWorkspaceFile(workspaceRoot)
|
|
187
180
|
|
|
188
181
|
if (!workspaceFile) {
|
|
189
|
-
return
|
|
182
|
+
return []
|
|
190
183
|
}
|
|
191
184
|
|
|
192
185
|
try {
|
|
@@ -194,139 +187,56 @@ export function findWorkspacePackages(workspaceRoot) {
|
|
|
194
187
|
|
|
195
188
|
const config = yaml.parse(content)
|
|
196
189
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (!Array.isArray(config.packages)) {
|
|
206
|
-
throw new Error('packages must be an array')
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (config.packages.some((pattern) => typeof pattern !== 'string' || pattern.trim().length === 0)) {
|
|
210
|
-
throw new Error('packages entries must be non-empty strings')
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return config.packages.map((pattern) => pattern.trim())
|
|
214
|
-
} catch (error) {
|
|
215
|
-
const detail = error instanceof Error ? `: ${error.message}` : ''
|
|
216
|
-
throw new Error(`Failed to read ${path.basename(workspaceFile)}${detail}`)
|
|
190
|
+
return Array.isArray(config?.packages)
|
|
191
|
+
? config.packages.filter(
|
|
192
|
+
(pattern) => typeof pattern === 'string' && pattern.length > 0 && !pattern.startsWith('!'),
|
|
193
|
+
)
|
|
194
|
+
: []
|
|
195
|
+
} catch {
|
|
196
|
+
return []
|
|
217
197
|
}
|
|
218
198
|
}
|
|
219
199
|
|
|
220
|
-
const WORKSPACE_SCAN_IGNORES = new Set(['.git', 'node_modules'])
|
|
221
|
-
|
|
222
200
|
function normalizeWorkspacePattern(pattern) {
|
|
223
|
-
|
|
224
|
-
if (normalized.startsWith('./')) normalized = normalized.slice(2)
|
|
225
|
-
return normalized.replace(/\/$/, '')
|
|
201
|
+
return pattern.replace(/\*\*?/g, '').replace(/\/$/, '')
|
|
226
202
|
}
|
|
227
203
|
|
|
228
|
-
function
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
for (let index = 0; index < pattern.length; index += 1) {
|
|
232
|
-
const char = pattern[index]
|
|
233
|
-
|
|
234
|
-
if (char === '*') {
|
|
235
|
-
if (pattern[index + 1] === '*') {
|
|
236
|
-
while (pattern[index + 1] === '*') index += 1
|
|
237
|
-
if (pattern[index + 1] === '/') {
|
|
238
|
-
source += '(?:.*/)?'
|
|
239
|
-
index += 1
|
|
240
|
-
} else {
|
|
241
|
-
source += '.*'
|
|
242
|
-
}
|
|
243
|
-
} else {
|
|
244
|
-
source += '[^/]*'
|
|
245
|
-
}
|
|
246
|
-
continue
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (char === '?') {
|
|
250
|
-
source += '[^/]'
|
|
251
|
-
continue
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (char === '{') {
|
|
255
|
-
const close = pattern.indexOf('}', index + 1)
|
|
256
|
-
if (close !== -1) {
|
|
257
|
-
const alternatives = pattern
|
|
258
|
-
.slice(index + 1, close)
|
|
259
|
-
.split(',')
|
|
260
|
-
.map((item) => globSource(item))
|
|
261
|
-
source += `(?:${alternatives.join('|')})`
|
|
262
|
-
index = close
|
|
263
|
-
continue
|
|
264
|
-
}
|
|
265
|
-
}
|
|
204
|
+
export function listPackages(workspaceRoot, patterns) {
|
|
205
|
+
const packageDirs = []
|
|
266
206
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
source += pattern.slice(index, close + 1)
|
|
271
|
-
index = close
|
|
272
|
-
continue
|
|
273
|
-
}
|
|
207
|
+
const addPackage = (dir) => {
|
|
208
|
+
if (isPackageDir(dir)) {
|
|
209
|
+
packageDirs.push(dir)
|
|
274
210
|
}
|
|
275
|
-
|
|
276
|
-
source += char.replace(/[\\.^$+()|]/g, '\\$&')
|
|
277
211
|
}
|
|
278
212
|
|
|
279
|
-
|
|
280
|
-
|
|
213
|
+
for (const pattern of patterns) {
|
|
214
|
+
const base = normalizeWorkspacePattern(pattern)
|
|
281
215
|
|
|
282
|
-
|
|
283
|
-
const normalized = normalizeWorkspacePattern(pattern)
|
|
284
|
-
if (normalized === '.') return relativePath === ''
|
|
285
|
-
return new RegExp(`^${globSource(normalized)}$`).test(relativePath)
|
|
286
|
-
}
|
|
216
|
+
const fullPath = path.join(workspaceRoot, base)
|
|
287
217
|
|
|
288
|
-
|
|
289
|
-
|
|
218
|
+
if (!exists(fullPath)) {
|
|
219
|
+
continue
|
|
220
|
+
}
|
|
290
221
|
|
|
291
|
-
|
|
292
|
-
|
|
222
|
+
if (pattern.includes('*')) {
|
|
223
|
+
const entries = fs.readdirSync(fullPath, {
|
|
224
|
+
withFileTypes: true,
|
|
225
|
+
})
|
|
293
226
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
227
|
+
for (const entry of entries) {
|
|
228
|
+
if (entry.isDirectory()) {
|
|
229
|
+
addPackage(path.join(fullPath, entry.name))
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
addPackage(fullPath)
|
|
297
234
|
}
|
|
298
235
|
}
|
|
299
236
|
|
|
300
|
-
visit(workspaceRoot)
|
|
301
237
|
return packageDirs
|
|
302
238
|
}
|
|
303
239
|
|
|
304
|
-
export function listPackages(workspaceRoot, patterns = undefined) {
|
|
305
|
-
const configuredPatterns = patterns ?? ['**']
|
|
306
|
-
const includePatterns = configuredPatterns.filter((pattern) => !pattern.startsWith('!'))
|
|
307
|
-
const excludePatterns = configuredPatterns
|
|
308
|
-
.filter((pattern) => pattern.startsWith('!'))
|
|
309
|
-
.map((pattern) => pattern.slice(1))
|
|
310
|
-
|
|
311
|
-
if (includePatterns.length === 0) return []
|
|
312
|
-
|
|
313
|
-
const root = path.resolve(workspaceRoot)
|
|
314
|
-
const packageDirs = collectPackageDirs(root)
|
|
315
|
-
const matched = packageDirs.filter((dir) => {
|
|
316
|
-
const relativePath = path.relative(root, dir).replaceAll('\\', '/')
|
|
317
|
-
return (
|
|
318
|
-
includePatterns.some((pattern) => matchesWorkspacePattern(relativePath, pattern)) &&
|
|
319
|
-
!excludePatterns.some((pattern) => matchesWorkspacePattern(relativePath, pattern))
|
|
320
|
-
)
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
return [...new Set(matched)].sort((left, right) => {
|
|
324
|
-
const leftRelative = path.relative(root, left)
|
|
325
|
-
const rightRelative = path.relative(root, right)
|
|
326
|
-
return leftRelative.localeCompare(rightRelative)
|
|
327
|
-
})
|
|
328
|
-
}
|
|
329
|
-
|
|
330
240
|
function ensureDir(file) {
|
|
331
241
|
fs.mkdirSync(path.dirname(file), {
|
|
332
242
|
recursive: true,
|
|
@@ -376,19 +286,6 @@ export function mergeEnvFile(templateFile, targetFile) {
|
|
|
376
286
|
|
|
377
287
|
const targetContent = fs.readFileSync(targetFile, 'utf-8')
|
|
378
288
|
|
|
379
|
-
const result = mergeEnvContent(templateContent, targetContent)
|
|
380
|
-
|
|
381
|
-
if (result.type === 'merged') {
|
|
382
|
-
fs.writeFileSync(targetFile, result.content)
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
return {
|
|
386
|
-
type: result.type,
|
|
387
|
-
...(result.added === undefined ? {} : { added: result.added }),
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export function mergeEnvContent(templateContent, targetContent) {
|
|
392
289
|
const templateEnv = parseEnv(templateContent)
|
|
393
290
|
|
|
394
291
|
const targetEnv = parseEnv(targetContent)
|
|
@@ -411,9 +308,10 @@ export function mergeEnvContent(templateContent, targetContent) {
|
|
|
411
308
|
|
|
412
309
|
const nextContent = targetTrimmed ? `${targetTrimmed}\n${appendLines.join('\n')}\n` : `${appendLines.join('\n')}\n`
|
|
413
310
|
|
|
311
|
+
fs.writeFileSync(targetFile, nextContent)
|
|
312
|
+
|
|
414
313
|
return {
|
|
415
314
|
type: 'merged',
|
|
416
315
|
added: appendLines.length,
|
|
417
|
-
content: nextContent,
|
|
418
316
|
}
|
|
419
317
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentiny/tiny-robot-cli",
|
|
3
|
-
"version": "0.5.2-alpha.
|
|
3
|
+
"version": "0.5.2-alpha.18",
|
|
4
4
|
"description": "CLI to scaffold TinyRobot product projects",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,8 +19,7 @@
|
|
|
19
19
|
"README.md"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
-
"start": "node ./bin/cli.js"
|
|
23
|
-
"test": "node --test test/*.test.js"
|
|
22
|
+
"start": "node ./bin/cli.js"
|
|
24
23
|
},
|
|
25
24
|
"license": "MIT",
|
|
26
25
|
"engines": {
|
|
@@ -28,11 +27,10 @@
|
|
|
28
27
|
},
|
|
29
28
|
"dependencies": {
|
|
30
29
|
"@inquirer/prompts": "^8.3.2",
|
|
31
|
-
"@vue/compiler-sfc": "^3.5.40",
|
|
32
30
|
"commander": "^14.0.3",
|
|
33
31
|
"picocolors": "^1.1.1",
|
|
34
32
|
"semver": "^7.8.1",
|
|
35
33
|
"yaml": "^2.9.0"
|
|
36
34
|
},
|
|
37
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "5c71743e5b961694e8121d714f7126fb76eee35a"
|
|
38
36
|
}
|
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
# Optional custom model endpoints. Leave blank to use official Provider defaults.
|
|
2
|
-
VITE_QWEN_API_URL=
|
|
3
|
-
VITE_DEEPSEEK_API_URL=
|
|
4
|
-
|
|
5
|
-
# Optional custom MCP endpoint. Leave blank to use the official endpoint.
|
|
6
|
-
VITE_AMAP_MCP_URL=
|
|
7
|
-
|
|
8
|
-
# Optional local direct-connection credentials.
|
|
9
1
|
VITE_ALIYUN_DASHSCOPE_KEY=
|
|
10
2
|
VITE_DEEPSEEK_API_KEY=
|
|
@@ -1,71 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TinyRobot AI chat starter built with Vue 3 + Vite.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Vue 3 + Vite + TypeScript project scaffold
|
|
8
|
+
- TinyRobot chat UI with `TrBubbleList`, `TrSender`, and markdown rendering
|
|
9
|
+
- Conversation management via `useConversation`
|
|
10
|
+
- Model switch with thinking/search capability toggles
|
|
11
|
+
- MCP server picker for add/toggle/delete server usage
|
|
12
|
+
- MCP transport support for both `sse` and `streamableHttp`
|
|
13
|
+
- Tool calling pipeline through `toolPlugin` + MCP `listTools` / `callTool`
|
|
14
|
+
- Theme toggle and responsive layout for desktop/mobile
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
const mcpServers = [
|
|
11
|
-
{ id: 'amap-maps', name: '高德地图', baseUrl: 'https://...' },
|
|
12
|
-
{
|
|
13
|
-
id: 'model-context-protocol-mcp',
|
|
14
|
-
name: 'Model Context Protocol MCP',
|
|
15
|
-
baseUrl: '/modelcontextprotocol-mcp',
|
|
16
|
-
installed: true,
|
|
17
|
-
},
|
|
18
|
-
]
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
`installed: true` means initially installed only. The Server remains disabled and does not connect or discover Tools until enabled.
|
|
22
|
-
|
|
23
|
-
| Server | Transport | Authentication |
|
|
24
|
-
| -------------------------- | --------------- | ---------------- |
|
|
25
|
-
| 高德地图 | Streamable HTTP | Optional API Key |
|
|
26
|
-
| Model Context Protocol MCP | Streamable HTTP | None |
|
|
27
|
-
|
|
28
|
-
Tools are discovered dynamically with `client.listTools()`. Tool choices are stored in the next user message's `runConfig` metadata snapshot, so changing a Tool affects subsequent turns without modifying a request already in progress.
|
|
29
|
-
|
|
30
|
-
Server installation, enabling, Tool loading, retry, de-duplication, and Tool calls are handled by the default adapter in `@opentiny/tiny-robot-chat`. If a Server cannot load its Tools, it remains installed but is automatically disabled. Other enabled Servers and normal chat remain available.
|
|
31
|
-
|
|
32
|
-
## Model connection modes
|
|
33
|
-
|
|
34
|
-
- API Key only: connects directly to the official Qwen or DeepSeek endpoint.
|
|
35
|
-
- `apiUrl` + API Key: connects to a custom service with frontend authentication.
|
|
36
|
-
- `apiUrl` only: connects to a backend proxy without frontend authentication.
|
|
37
|
-
|
|
38
|
-
The Chat package does not prompt for or validate missing API Keys. The upstream service or backend proxy decides whether authentication is required.
|
|
16
|
+
## Setup
|
|
39
17
|
|
|
40
|
-
|
|
18
|
+
1. Copy environment variables:
|
|
41
19
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Qwen models support deep thinking and web search. DeepSeek models support deep thinking. Unsupported capabilities remain visible as disabled buttons so the available model capabilities are clear.
|
|
47
|
-
|
|
48
|
-
DeepSeek models inherit the provider's thinking effort options: `low`, `high`, and `max`, with `high` as the default. The effort selector appears only after `深度思考` is enabled. To customize a model, set `efforts` and `defaultEffort` on that model in `src/App.vue`; use `thinkingRequired: true` only for models that cannot disable thinking.
|
|
49
|
-
|
|
50
|
-
## Environment variables
|
|
20
|
+
```bash
|
|
21
|
+
cp .env.example .env
|
|
22
|
+
```
|
|
51
23
|
|
|
52
|
-
|
|
24
|
+
2. Fill your provider keys in `.env`:
|
|
53
25
|
|
|
54
26
|
```env
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
VITE_AMAP_MCP_URL=
|
|
58
|
-
VITE_ALIYUN_DASHSCOPE_KEY=
|
|
59
|
-
VITE_DEEPSEEK_API_KEY=
|
|
27
|
+
VITE_ALIYUN_DASHSCOPE_KEY=your_dashscope_key
|
|
28
|
+
VITE_DEEPSEEK_API_KEY=your_deepseek_key
|
|
60
29
|
```
|
|
61
30
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Variables prefixed with `VITE_` are exposed to browser code. Direct browser connections are intended only for servers without secrets and with CORS enabled. Production applications should keep credentials behind a backend or BFF proxy.
|
|
31
|
+
`VITE_ALIYUN_DASHSCOPE_KEY` is also used by configured MCP servers that require DashScope authorization.
|
|
65
32
|
|
|
66
33
|
## Development
|
|
67
34
|
|
|
68
35
|
```bash
|
|
36
|
+
pnpm install
|
|
69
37
|
pnpm dev
|
|
70
38
|
```
|
|
71
39
|
|
|
@@ -73,4 +41,5 @@ pnpm dev
|
|
|
73
41
|
|
|
74
42
|
```bash
|
|
75
43
|
pnpm build
|
|
44
|
+
pnpm preview
|
|
76
45
|
```
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/
|
|
5
|
+
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>
|
|
7
|
+
<title>TinyRobot AI Chat</title>
|
|
8
8
|
</head>
|
|
9
9
|
<body>
|
|
10
10
|
<div id="app"></div>
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "__PROJECT_NAME__",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite",
|
|
8
8
|
"build": "vue-tsc -b && vite build",
|
|
9
|
-
"preview": "vite preview"
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"type-check": "vue-tsc --noEmit"
|
|
10
11
|
},
|
|
11
12
|
"dependencies": {
|
|
12
|
-
"@
|
|
13
|
-
"@opentiny/tiny-robot": "
|
|
14
|
-
"@opentiny/tiny-robot-
|
|
15
|
-
"
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1.18.1",
|
|
14
|
+
"@opentiny/tiny-robot": "^0.5.0",
|
|
15
|
+
"@opentiny/tiny-robot-kit": "^0.5.0",
|
|
16
|
+
"@opentiny/tiny-robot-svgs": "^0.5.0",
|
|
17
|
+
"dompurify": "^3.3.1",
|
|
18
|
+
"markdown-it": "^14.1.0",
|
|
19
|
+
"vue": "^3.5.30"
|
|
16
20
|
},
|
|
17
21
|
"devDependencies": {
|
|
18
|
-
"@types/node": "^24.
|
|
19
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
20
|
-
"@vue/tsconfig": "^0.9.
|
|
21
|
-
"typescript": "~
|
|
22
|
-
"vite": "^8.
|
|
23
|
-
"vue-tsc": "^3.
|
|
22
|
+
"@types/node": "^24.12.0",
|
|
23
|
+
"@vitejs/plugin-vue": "^6.0.5",
|
|
24
|
+
"@vue/tsconfig": "^0.9.0",
|
|
25
|
+
"typescript": "~5.9.3",
|
|
26
|
+
"vite": "^8.0.1",
|
|
27
|
+
"vue-tsc": "^3.2.5"
|
|
24
28
|
}
|
|
25
29
|
}
|
|
Binary file
|