@opentiny/tiny-robot-cli 0.5.2-alpha.11 → 0.5.2-alpha.13

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 CHANGED
@@ -15,14 +15,14 @@ npx @opentiny/tiny-robot-cli add chat
15
15
  - `-t, --template <name>`: template name, currently supports `basic`
16
16
  - `-h, --help`: show help
17
17
 
18
- `add chat` copies the complete Chat example from the `chat-add` template, including its runtime, UI configuration, composables, components, and styles. It updates these dependencies to `0.5.2-alpha.10`:
18
+ `add chat` adds a self-contained floating Chat component, including its runtime, UI configuration, composables, component-scoped styles, and supporting components. It updates these OpenTiny dependencies to `0.5.2-alpha.10`:
19
19
 
20
20
  - `@opentiny/tiny-robot`
21
21
  - `@opentiny/tiny-robot-chat`
22
22
  - `@opentiny/tiny-robot-kit`
23
23
  - `@opentiny/tiny-robot-svgs`
24
24
 
25
- Configure the generated `.env` file with the provider API URL and API key before starting the project.
25
+ It also adds `@vueuse/core@13.9.0`. The command does not modify the application entry file or create `src/index.css`. Configure a private environment file from the generated `.env.example` before starting the project; the generated example only includes the Qwen and DeepSeek API key variables. Their API URL variables remain optional overrides for proxies, private gateways, or other regions. The CLI never creates or modifies `.env`.
26
26
 
27
27
  ## Template Documentation
28
28
 
@@ -1,4 +1,4 @@
1
- import { checkbox, select } from '@inquirer/prompts'
1
+ import { confirm, select } from '@inquirer/prompts'
2
2
  import { Argument } from 'commander'
3
3
  import fs from 'node:fs'
4
4
  import path from 'node:path'
@@ -21,26 +21,16 @@ import {
21
21
  } from '../utils.js'
22
22
 
23
23
  const TARGET_VERSION = '0.5.2-alpha.10'
24
+ const VUEUSE_VERSION = '13.9.0'
24
25
  const DEPENDENCIES = {
25
26
  '@opentiny/tiny-robot': TARGET_VERSION,
26
27
  '@opentiny/tiny-robot-chat': TARGET_VERSION,
27
28
  '@opentiny/tiny-robot-kit': TARGET_VERSION,
28
29
  '@opentiny/tiny-robot-svgs': TARGET_VERSION,
30
+ '@vueuse/core': VUEUSE_VERSION,
29
31
  }
30
- const STYLE_IMPORTS = [
31
- "import '@opentiny/tiny-robot/dist/style.css'",
32
- "import '@opentiny/tiny-robot-chat/dist/style.css'",
33
- ]
34
32
 
35
- function logUnavailable(label) {
36
- logSkip(`${label} could not be applied`)
37
- }
38
-
39
- function logSkippedSelection(label) {
40
- logSkip(`${label} change was not selected`)
41
- }
42
-
43
- async function resolveTargetPackage(cwd) {
33
+ export async function resolveTargetPackage(cwd) {
44
34
  const workspaceRoot = findWorkspaceRoot(cwd)
45
35
 
46
36
  if (workspaceRoot) {
@@ -51,9 +41,12 @@ async function resolveTargetPackage(cwd) {
51
41
  }
52
42
 
53
43
  const workspacePatterns = findWorkspacePackages(workspaceRoot)
54
-
55
44
  const packageDirs = listPackages(workspaceRoot, workspacePatterns)
56
45
 
46
+ if (workspacePatterns.length === 0 && fs.existsSync(path.join(workspaceRoot, 'package.json'))) {
47
+ return workspaceRoot
48
+ }
49
+
57
50
  invariant(packageDirs.length > 0, 'no packages found in workspace.')
58
51
 
59
52
  return select({
@@ -72,7 +65,6 @@ async function resolveTargetPackage(cwd) {
72
65
  }
73
66
 
74
67
  console.error('Error: no package.json found.')
75
-
76
68
  process.exit(1)
77
69
  }
78
70
 
@@ -88,62 +80,8 @@ function writePackageJson(pkgPath, pkg) {
88
80
  fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
89
81
  }
90
82
 
91
- function findMainEntry(targetDir) {
92
- for (const file of ['src/main.ts', 'src/main.js']) {
93
- const fullPath = path.join(targetDir, file)
94
-
95
- if (fs.existsSync(fullPath)) {
96
- return fullPath
97
- }
98
- }
99
-
100
- return null
101
- }
102
-
103
- function ensureStyleImport(mainFile) {
104
- const content = fs.readFileSync(mainFile, 'utf-8')
105
- const lines = content.split('\n')
106
- let inserted = false
107
-
108
- for (const styleImport of STYLE_IMPORTS) {
109
- if (lines.some((line) => line.trim() === styleImport)) {
110
- continue
111
- }
112
-
113
- let lastImportIndex = -1
114
-
115
- for (let i = 0; i < lines.length; i++) {
116
- if (/^\s*import\s/.test(lines[i])) {
117
- lastImportIndex = i
118
- continue
119
- }
120
-
121
- if (lastImportIndex !== -1) {
122
- break
123
- }
124
- }
125
-
126
- if (lastImportIndex === -1) {
127
- lines.unshift(styleImport)
128
- } else {
129
- lines.splice(lastImportIndex + 1, 0, styleImport)
130
- }
131
-
132
- inserted = true
133
- }
134
-
135
- if (inserted) {
136
- fs.writeFileSync(mainFile, lines.join('\n'))
137
- }
138
-
139
- return {
140
- type: inserted ? 'inserted' : 'skipped',
141
- }
142
- }
143
-
144
83
  function insertDependencyOrdered(dependencies, name, version) {
145
84
  const next = {}
146
-
147
85
  let inserted = false
148
86
 
149
87
  for (const [key, value] of Object.entries(dependencies)) {
@@ -167,7 +105,6 @@ function ensureDependency(pkg, name, targetVersion) {
167
105
 
168
106
  const currentVersion = pkg.dependencies[name]
169
107
 
170
- // 1. 不存在 => 新增(使用 ordered insert)
171
108
  if (!currentVersion) {
172
109
  pkg.dependencies = insertDependencyOrdered(pkg.dependencies, name, targetVersion)
173
110
 
@@ -177,22 +114,19 @@ function ensureDependency(pkg, name, targetVersion) {
177
114
  }
178
115
  }
179
116
 
180
- // 2. 已存在 => 只更新版本,不调整顺序
181
- const current = semver.valid(currentVersion)
182
-
183
- if (current !== targetVersion) {
184
- pkg.dependencies[name] = targetVersion
185
-
117
+ if (
118
+ currentVersion.startsWith('workspace:') ||
119
+ semver.satisfies(targetVersion, currentVersion, { includePrerelease: true })
120
+ ) {
186
121
  return {
187
- type: 'updated',
188
- from: currentVersion,
189
- to: targetVersion,
122
+ type: 'skipped',
190
123
  }
191
124
  }
192
125
 
193
- // 3. 完全一致 => 跳过
194
126
  return {
195
- type: 'skipped',
127
+ type: 'conflict',
128
+ from: currentVersion,
129
+ to: targetVersion,
196
130
  }
197
131
  }
198
132
 
@@ -202,278 +136,168 @@ function printDependencyResult(result, name) {
202
136
  logSuccess(`Added ${name}@${result.to}`)
203
137
  break
204
138
 
205
- case 'updated':
206
- logSuccess(`Updated ${name} from ${result.from} to ${result.to}`)
207
- break
208
-
209
139
  case 'skipped':
210
140
  logSkip(`${name} already satisfies required version`)
211
141
  break
212
142
  }
213
143
  }
214
144
 
215
- function getChatFeatureFiles(targetDir) {
216
- const mainEntry = findMainEntry(targetDir)
145
+ function getGeneratedFiles(targetDir) {
217
146
  const templateSource = getTemplateFile('src')
147
+ const supportTarget = path.join(targetDir, 'src/tiny-robot-chat')
218
148
 
219
149
  return [
220
150
  {
221
151
  label: 'TinyRobotChat.vue',
152
+ source: path.join(templateSource, 'TinyRobotChat.vue'),
222
153
  target: path.join(targetDir, 'src/TinyRobotChat.vue'),
223
- template: getTemplateFile('src', 'TinyRobotChat.vue'),
224
- action(fileExists) {
225
- return fileExists ? 'overwrite' : 'create'
226
- },
227
- },
228
- {
229
- label: 'main entry style imports',
230
- target: mainEntry,
231
- action() {
232
- return 'modify'
233
- },
154
+ type: 'file',
234
155
  },
235
156
  {
236
157
  label: 'Chat UI components',
237
- target: path.join(targetDir, 'src/components'),
238
- template: path.join(templateSource, 'components'),
239
- action() {
240
- return 'merge'
241
- },
158
+ source: path.join(templateSource, 'tiny-robot-chat/components'),
159
+ target: path.join(supportTarget, 'components'),
160
+ type: 'directory',
242
161
  },
243
162
  {
244
163
  label: 'Chat composables',
245
- target: path.join(targetDir, 'src/composables'),
246
- template: path.join(templateSource, 'composables'),
247
- action() {
248
- return 'merge'
249
- },
164
+ source: path.join(templateSource, 'tiny-robot-chat/composables'),
165
+ target: path.join(supportTarget, 'composables'),
166
+ type: 'directory',
250
167
  },
251
168
  {
252
169
  label: 'Chat configuration',
253
- target: path.join(targetDir, 'src/config'),
254
- template: path.join(templateSource, 'config'),
255
- action() {
256
- return 'merge'
257
- },
258
- },
259
- {
260
- label: 'Chat app styles',
261
- target: path.join(targetDir, 'src/index.css'),
262
- template: path.join(templateSource, 'index.css'),
263
- action(fileExists) {
264
- return fileExists ? 'overwrite' : 'create'
265
- },
266
- },
267
- {
268
- label: '.env',
269
- target: path.join(targetDir, '.env'),
270
- template: getTemplateFile('.env.example'),
271
- action(fileExists) {
272
- return fileExists ? 'modify' : 'create'
273
- },
274
- },
275
-
276
- {
277
- label: 'package.json',
278
- target: path.join(targetDir, 'package.json'),
279
- action() {
280
- return 'modify'
281
- },
170
+ source: path.join(templateSource, 'tiny-robot-chat/config'),
171
+ target: path.join(supportTarget, 'config'),
172
+ type: 'directory',
282
173
  },
283
174
  ]
284
175
  }
285
176
 
286
- async function selectFileChanges(files) {
287
- return checkbox({
288
- message: 'Select which file changes to apply (all selected by default):',
289
- choices: files.map((file) => {
290
- const exists = file.target ? fs.existsSync(file.target) : false
291
-
292
- const disabled = !file.target
293
-
294
- const descriptions = {
295
- 'TinyRobotChat.vue': 'integrate TinyRobot chat component',
296
- 'main entry style imports': 'import TinyRobot styles',
297
- 'Chat UI components': 'copy Chat UI components',
298
- 'Chat composables': 'copy Chat composables',
299
- 'Chat configuration': 'copy Chat runtime and UI configuration',
300
- 'Chat app styles': 'copy Chat application styles',
301
- '.env': 'add environment variables',
302
- 'package.json': 'add TinyRobot dependencies',
303
- }
304
-
305
- const description = descriptions[file.label]
177
+ function findCopyConflicts(source, target) {
178
+ if (!fs.existsSync(target)) {
179
+ return []
180
+ }
306
181
 
307
- return {
308
- name: disabled
309
- ? `${file.action(false)} ${file.label} — ${description} (main.ts/js not found)`
310
- : `${file.action(exists)} ${file.label} — ${description}`,
182
+ const sourceStat = fs.statSync(source)
183
+ const targetStat = fs.statSync(target)
311
184
 
312
- value: file.label,
185
+ if (!sourceStat.isDirectory()) {
186
+ return [target]
187
+ }
313
188
 
314
- checked: !disabled,
189
+ if (!targetStat.isDirectory()) {
190
+ return [target]
191
+ }
315
192
 
316
- disabled,
317
- }
318
- }),
193
+ return fs.readdirSync(source, { withFileTypes: true }).flatMap((entry) => {
194
+ return findCopyConflicts(path.join(source, entry.name), path.join(target, entry.name))
319
195
  })
320
196
  }
321
197
 
322
- function isSelected(selectedFiles, label) {
323
- return selectedFiles.includes(label)
324
- }
198
+ export function applyChatFeature(targetDir) {
199
+ const pkgPath = path.join(targetDir, 'package.json')
325
200
 
326
- async function addFeature(targetDir, type) {
327
- invariant(type === 'chat', `unsupported feature: ${type}`)
201
+ invariant(fs.existsSync(pkgPath), 'package.json not found.')
328
202
 
329
- const files = getChatFeatureFiles(targetDir)
203
+ const generatedFiles = getGeneratedFiles(targetDir)
204
+ const conflicts = generatedFiles.flatMap((file) => findCopyConflicts(file.source, file.target))
330
205
 
331
- const selectedFiles = await selectFileChanges(files)
206
+ if (conflicts.length > 0) {
207
+ const conflictPaths = conflicts.map((file) => path.relative(targetDir, file)).join(', ')
332
208
 
333
- if (selectedFiles.length === 0) {
334
- logSkip('No changes selected.')
335
- return
209
+ throw new Error(`Chat feature files already exist: ${conflictPaths}`)
336
210
  }
337
211
 
338
- const componentFile = files.find((f) => {
339
- return f.label === 'TinyRobotChat.vue'
340
- })
341
-
342
- const envFile = files.find((f) => {
343
- return f.label === '.env'
344
- })
345
-
346
- invariant(componentFile, 'TinyRobotChat.vue config missing.')
347
-
348
- invariant(envFile, '.env config missing.')
349
-
350
- console.log('\nChange Results\n')
351
-
352
- let needsManualStyleImport = false
353
- let envChanged = false
354
- let dependencyChanged = false
212
+ const pkg = readPackageJson(pkgPath)
213
+ const dependencyResults = []
355
214
 
356
- if (isSelected(selectedFiles, 'TinyRobotChat.vue')) {
357
- copyFile(componentFile.template, componentFile.target)
358
- logSuccess(`Copied ${componentFile.label}`)
359
- } else {
360
- logSkippedSelection(componentFile.label)
215
+ for (const [name, version] of Object.entries(DEPENDENCIES)) {
216
+ dependencyResults.push({ name, result: ensureDependency(pkg, name, version) })
361
217
  }
362
218
 
363
- for (const file of files.filter((item) => item.label.startsWith('Chat '))) {
364
- if (!isSelected(selectedFiles, file.label)) {
365
- logSkippedSelection(file.label)
366
- continue
367
- }
219
+ const dependencyConflicts = dependencyResults.filter(({ result }) => result.type === 'conflict')
368
220
 
369
- if (file.target.endsWith('.css')) {
370
- copyFile(file.template, file.target)
371
- } else {
372
- copyDirectory(file.template, file.target)
373
- }
221
+ if (dependencyConflicts.length > 0) {
222
+ const conflictDetails = dependencyConflicts
223
+ .map(({ name, result }) => `${name}: found ${result.from}, required ${result.to}`)
224
+ .join('; ')
374
225
 
375
- logSuccess(`Copied ${file.label}`)
226
+ throw new Error(`Dependency version conflict: ${conflictDetails}`)
376
227
  }
377
228
 
378
- const mainFile = files.find((f) => {
379
- return f.label === 'main entry style imports'
380
- })
381
-
382
- if (!mainFile?.target) {
383
- needsManualStyleImport = true
384
-
385
- logUnavailable('main entry style imports (main.ts/js not found)')
386
- } else {
387
- if (isSelected(selectedFiles, mainFile.label)) {
388
- const result = ensureStyleImport(mainFile.target)
389
-
390
- switch (result.type) {
391
- case 'inserted':
392
- logSuccess('Inserted TinyRobot style import')
393
- break
394
-
395
- case 'skipped':
396
- logSkip('TinyRobot style import already exists')
397
- break
398
- }
229
+ for (const file of generatedFiles) {
230
+ if (file.type === 'file') {
231
+ copyFile(file.source, file.target)
399
232
  } else {
400
- needsManualStyleImport = true
401
-
402
- logSkippedSelection(mainFile.label)
233
+ copyDirectory(file.source, file.target)
403
234
  }
404
235
  }
405
236
 
406
- if (isSelected(selectedFiles, '.env')) {
407
- const envResult = mergeEnvFile(envFile.template, envFile.target)
237
+ const envResult = mergeEnvFile(getTemplateFile('.env.example'), path.join(targetDir, '.env.example'))
408
238
 
409
- switch (envResult.type) {
410
- case 'created':
411
- envChanged = true
239
+ if (dependencyResults.some(({ result }) => result.type !== 'skipped')) {
240
+ writePackageJson(pkgPath, pkg)
241
+ }
412
242
 
413
- logSuccess('Created .env')
414
- break
243
+ return {
244
+ generatedFiles,
245
+ envResult,
246
+ dependencyResults,
247
+ }
248
+ }
415
249
 
416
- case 'merged':
417
- envChanged = true
250
+ async function addFeature(targetDir, type) {
251
+ invariant(type === 'chat', `unsupported feature: ${type}`)
418
252
 
419
- logSuccess(`Added ${envResult.added} env variables`)
420
- break
253
+ const shouldApply = await confirm({
254
+ message: `Add the complete TinyRobot chat feature to ${targetDir}?`,
255
+ default: true,
256
+ })
421
257
 
422
- case 'skipped':
423
- logSkip('.env already contains required variables')
424
- break
425
- }
426
- } else {
427
- logSkippedSelection('.env')
258
+ if (!shouldApply) {
259
+ logSkip('No changes applied.')
260
+ return
428
261
  }
429
262
 
430
- const pkgPath = path.join(targetDir, 'package.json')
263
+ const result = applyChatFeature(targetDir)
431
264
 
432
- invariant(fs.existsSync(pkgPath), 'package.json not found.')
265
+ console.log('\nChange Results\n')
433
266
 
434
- const pkg = readPackageJson(pkgPath)
267
+ for (const file of result.generatedFiles) {
268
+ logSuccess(`Copied ${file.label}`)
269
+ }
435
270
 
436
- if (isSelected(selectedFiles, 'package.json')) {
437
- for (const [name, version] of Object.entries(DEPENDENCIES)) {
438
- const result = ensureDependency(pkg, name, version)
271
+ switch (result.envResult.type) {
272
+ case 'created':
273
+ logSuccess('Created .env.example')
274
+ break
439
275
 
440
- if (result.type !== 'skipped') {
441
- dependencyChanged = true
442
- }
276
+ case 'merged':
277
+ logSuccess(`Added ${result.envResult.added} variables to .env.example`)
278
+ break
443
279
 
444
- printDependencyResult(result, name)
445
- }
280
+ case 'skipped':
281
+ logSkip('.env.example already contains required variables')
282
+ break
283
+ }
446
284
 
447
- writePackageJson(pkgPath, pkg)
448
- } else {
449
- logSkippedSelection('package.json')
285
+ for (const { name, result: dependencyResult } of result.dependencyResults) {
286
+ printDependencyResult(dependencyResult, name)
450
287
  }
451
288
 
452
289
  console.log(`\nSuccessfully added "${type}" feature to ${targetDir}`)
453
290
 
454
291
  printNextSteps({
455
- needsManualStyleImport,
456
- envChanged,
457
- dependencyChanged,
292
+ envChanged: result.envResult.type !== 'skipped',
293
+ dependencyChanged: result.dependencyResults.some(({ result: dependencyResult }) => {
294
+ return dependencyResult.type !== 'skipped'
295
+ }),
458
296
  })
459
297
  }
460
298
 
461
- function printNextSteps({ needsManualStyleImport, envChanged, dependencyChanged }) {
462
- const steps = []
463
-
464
- if (needsManualStyleImport) {
465
- steps.push(
466
- [
467
- 'Import TinyRobot styles in your application entry file.',
468
- '',
469
- 'Example:',
470
- '',
471
- ...STYLE_IMPORTS.map((styleImport) => ` ${styleImport}`),
472
- ].join('\n'),
473
- )
474
- }
475
-
476
- steps.push(
299
+ function printNextSteps({ envChanged, dependencyChanged }) {
300
+ const steps = [
477
301
  [
478
302
  'Render <TinyRobotChat /> near your main application component.',
479
303
  '',
@@ -488,12 +312,12 @@ function printNextSteps({ needsManualStyleImport, envChanged, dependencyChanged
488
312
  ' <TinyRobotChat />',
489
313
  ' </template>',
490
314
  ].join('\n'),
491
- )
315
+ ]
492
316
 
493
317
  if (envChanged) {
494
318
  steps.push(
495
319
  [
496
- 'Configure your AI provider API key in the .env file.',
320
+ 'Copy the required variables from .env.example to your private environment file and configure a provider key.',
497
321
  '',
498
322
  'Example:',
499
323
  '',
@@ -506,10 +330,6 @@ function printNextSteps({ needsManualStyleImport, envChanged, dependencyChanged
506
330
  steps.push(['Install or update project dependencies.', '', 'Example:', '', ' pnpm install'].join('\n'))
507
331
  }
508
332
 
509
- if (steps.length === 0) {
510
- return
511
- }
512
-
513
333
  console.log('\nNext Steps\n')
514
334
 
515
335
  for (const [index, step] of steps.entries()) {
package/bin/utils.js CHANGED
@@ -196,8 +196,12 @@ export function findWorkspacePackages(workspaceRoot) {
196
196
  (pattern) => typeof pattern === 'string' && pattern.length > 0 && !pattern.startsWith('!'),
197
197
  )
198
198
  : []
199
- } catch {
200
- return []
199
+ } catch (error) {
200
+ const reason = error instanceof Error ? error.message : String(error)
201
+
202
+ throw new Error(`Invalid pnpm workspace configuration in ${workspaceFile}: ${reason}`, {
203
+ cause: error,
204
+ })
201
205
  }
202
206
  }
203
207
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentiny/tiny-robot-cli",
3
- "version": "0.5.2-alpha.11",
3
+ "version": "0.5.2-alpha.13",
4
4
  "description": "CLI to scaffold TinyRobot product projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,7 +19,8 @@
19
19
  "README.md"
20
20
  ],
21
21
  "scripts": {
22
- "start": "node ./bin/cli.js"
22
+ "start": "node ./bin/cli.js",
23
+ "test": "node --test ./test/*.test.js"
23
24
  },
24
25
  "license": "MIT",
25
26
  "engines": {
@@ -32,5 +33,5 @@
32
33
  "semver": "^7.8.1",
33
34
  "yaml": "^2.9.0"
34
35
  },
35
- "gitHead": "09317e7ea1f51d2b8b6a97ebb7b2ceee9be4a963"
36
+ "gitHead": "afeda129637cd33e0771ff9fee90a268a1ff55d5"
36
37
  }
@@ -1,5 +1,2 @@
1
- VITE_DEEPSEEK_API_KEY=
2
- VITE_DEEPSEEK_API_URL=
3
- VITE_QWEN_API_URL=
4
1
  VITE_ALIYUN_DASHSCOPE_KEY=
5
- VITE_AMAP_MCP_URL=
2
+ VITE_DEEPSEEK_API_KEY=
@@ -1,6 +1,6 @@
1
1
  # chat-add
2
2
 
3
- `chat-add` 是一个独立的 Chat 扩展模板,提供通用工作助手场景的最小工程结构。
3
+ `chat-add` 是一个可嵌入现有 Vue 应用的 Chat 扩展模板,提供通用工作助手场景的最小工程结构。
4
4
 
5
5
  ## 启动
6
6
 
@@ -17,19 +17,21 @@ pnpm --filter chat-add build
17
17
 
18
18
  ## 接入真实模型
19
19
 
20
- 在 `packages/chat-add/.env.local` 中配置 `VITE_QWEN_API_URL`、`VITE_ALIYUN_DASHSCOPE_KEY`、`VITE_DEEPSEEK_API_URL` 和 `VITE_DEEPSEEK_API_KEY`,重启 Vite 后即可使用模型选择器中的真实模型。配置读取逻辑位于 `src/config/chat-runtime.ts`。
20
+ 将生成的 `.env.example` 复制为 `.env.local`,按需配置 `VITE_ALIYUN_DASHSCOPE_KEY` 和 `VITE_DEEPSEEK_API_KEY`,重启 Vite 后即可使用模型选择器中的真实模型。
21
+
22
+ Qwen 和 DeepSeek 默认使用 `@opentiny/tiny-robot-chat` 内置的官方接口地址。仅在使用代理、私有网关或其他地域接口时,才需要在 `.env.local` 中额外配置 `VITE_QWEN_API_URL` 或 `VITE_DEEPSEEK_API_URL`。配置读取逻辑位于 `src/tiny-robot-chat/config/chat-runtime.ts`。
21
23
 
22
24
  ## 目录职责
23
25
 
24
- - `src/config/chat-ui.ts`: Chat UI、提示词、模板和菜单配置。
25
- - `src/config/chat-runtime.ts`: MCP 示例和模型配置。
26
- - `src/components`: 窗口头部和输入区工具。
27
- - `src/composables/useWindow.ts`: floating、fullscreen 窗口状态。
28
- - `src/index.css`: 模板公共样式和 Surface 样式变量。
26
+ - `src/TinyRobotChat.vue`: 悬浮入口、Chat 主体以及组件作用域样式。
27
+ - `src/tiny-robot-chat/config/chat-ui.ts`: Chat UI、提示词和模板配置。
28
+ - `src/tiny-robot-chat/config/chat-runtime.ts`: 模型配置和留给后续集成的空 MCP Server 列表。
29
+ - `src/tiny-robot-chat/components`: 窗口头部和输入区工具。
30
+ - `src/tiny-robot-chat/composables/useWindow.ts`: floating、fullscreen 窗口状态。
29
31
 
30
32
  ## 替换数据
31
33
 
32
- 直接修改 `src/config/chat-ui.ts` 和 `src/config/chat-runtime.ts` 中导出的数据;组件通过 props 使用这些配置。
34
+ 直接修改 `src/tiny-robot-chat/config/chat-ui.ts` 和 `src/tiny-robot-chat/config/chat-runtime.ts` 中导出的数据;组件通过 props 使用这些配置。
33
35
 
34
36
  ## 切换窗口模式
35
37
 
@@ -14,7 +14,7 @@
14
14
  "@opentiny/tiny-robot-chat": "0.5.2-alpha.10",
15
15
  "@opentiny/tiny-robot-kit": "0.5.2-alpha.10",
16
16
  "@opentiny/tiny-robot-svgs": "0.5.2-alpha.10",
17
- "@vueuse/core": "13.1.0",
17
+ "@vueuse/core": "13.9.0",
18
18
  "vue": "^3.5.40"
19
19
  },
20
20
  "devDependencies": {