@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 +2 -2
- package/bin/commands/add.js +110 -290
- package/bin/utils.js +6 -2
- package/package.json +4 -3
- package/templates/chat/.env.example +1 -4
- package/templates/chat/README.md +10 -8
- package/templates/chat/package.json +1 -1
- package/templates/chat/src/TinyRobotChat.vue +132 -52
- package/templates/chat/src/main.ts +0 -3
- package/templates/chat/src/tiny-robot-chat/components/ComposerTools.vue +72 -0
- package/templates/chat/src/{composables → tiny-robot-chat/composables}/useWindow.ts +18 -8
- package/templates/chat/src/tiny-robot-chat/config/chat-runtime.ts +26 -0
- package/templates/chat/src/{config → tiny-robot-chat/config}/chat-ui.ts +4 -22
- package/templates/chat/src/components/ComposerTools.vue +0 -99
- package/templates/chat/src/config/chat-runtime.ts +0 -57
- package/templates/chat/src/index.css +0 -116
- /package/templates/chat/src/{components → tiny-robot-chat/components}/WindowHeader.vue +0 -0
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`
|
|
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`
|
|
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
|
|
package/bin/commands/add.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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: '
|
|
188
|
-
from: currentVersion,
|
|
189
|
-
to: targetVersion,
|
|
122
|
+
type: 'skipped',
|
|
190
123
|
}
|
|
191
124
|
}
|
|
192
125
|
|
|
193
|
-
// 3. 完全一致 => 跳过
|
|
194
126
|
return {
|
|
195
|
-
type: '
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
308
|
-
|
|
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
|
-
|
|
185
|
+
if (!sourceStat.isDirectory()) {
|
|
186
|
+
return [target]
|
|
187
|
+
}
|
|
313
188
|
|
|
314
|
-
|
|
189
|
+
if (!targetStat.isDirectory()) {
|
|
190
|
+
return [target]
|
|
191
|
+
}
|
|
315
192
|
|
|
316
|
-
|
|
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
|
|
323
|
-
|
|
324
|
-
}
|
|
198
|
+
export function applyChatFeature(targetDir) {
|
|
199
|
+
const pkgPath = path.join(targetDir, 'package.json')
|
|
325
200
|
|
|
326
|
-
|
|
327
|
-
invariant(type === 'chat', `unsupported feature: ${type}`)
|
|
201
|
+
invariant(fs.existsSync(pkgPath), 'package.json not found.')
|
|
328
202
|
|
|
329
|
-
const
|
|
203
|
+
const generatedFiles = getGeneratedFiles(targetDir)
|
|
204
|
+
const conflicts = generatedFiles.flatMap((file) => findCopyConflicts(file.source, file.target))
|
|
330
205
|
|
|
331
|
-
|
|
206
|
+
if (conflicts.length > 0) {
|
|
207
|
+
const conflictPaths = conflicts.map((file) => path.relative(targetDir, file)).join(', ')
|
|
332
208
|
|
|
333
|
-
|
|
334
|
-
logSkip('No changes selected.')
|
|
335
|
-
return
|
|
209
|
+
throw new Error(`Chat feature files already exist: ${conflictPaths}`)
|
|
336
210
|
}
|
|
337
211
|
|
|
338
|
-
const
|
|
339
|
-
|
|
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
|
-
|
|
357
|
-
|
|
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
|
-
|
|
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
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
226
|
+
throw new Error(`Dependency version conflict: ${conflictDetails}`)
|
|
376
227
|
}
|
|
377
228
|
|
|
378
|
-
const
|
|
379
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
logSkippedSelection(mainFile.label)
|
|
233
|
+
copyDirectory(file.source, file.target)
|
|
403
234
|
}
|
|
404
235
|
}
|
|
405
236
|
|
|
406
|
-
|
|
407
|
-
const envResult = mergeEnvFile(envFile.template, envFile.target)
|
|
237
|
+
const envResult = mergeEnvFile(getTemplateFile('.env.example'), path.join(targetDir, '.env.example'))
|
|
408
238
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
239
|
+
if (dependencyResults.some(({ result }) => result.type !== 'skipped')) {
|
|
240
|
+
writePackageJson(pkgPath, pkg)
|
|
241
|
+
}
|
|
412
242
|
|
|
413
|
-
|
|
414
|
-
|
|
243
|
+
return {
|
|
244
|
+
generatedFiles,
|
|
245
|
+
envResult,
|
|
246
|
+
dependencyResults,
|
|
247
|
+
}
|
|
248
|
+
}
|
|
415
249
|
|
|
416
|
-
|
|
417
|
-
|
|
250
|
+
async function addFeature(targetDir, type) {
|
|
251
|
+
invariant(type === 'chat', `unsupported feature: ${type}`)
|
|
418
252
|
|
|
419
|
-
|
|
420
|
-
|
|
253
|
+
const shouldApply = await confirm({
|
|
254
|
+
message: `Add the complete TinyRobot chat feature to ${targetDir}?`,
|
|
255
|
+
default: true,
|
|
256
|
+
})
|
|
421
257
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
} else {
|
|
427
|
-
logSkippedSelection('.env')
|
|
258
|
+
if (!shouldApply) {
|
|
259
|
+
logSkip('No changes applied.')
|
|
260
|
+
return
|
|
428
261
|
}
|
|
429
262
|
|
|
430
|
-
const
|
|
263
|
+
const result = applyChatFeature(targetDir)
|
|
431
264
|
|
|
432
|
-
|
|
265
|
+
console.log('\nChange Results\n')
|
|
433
266
|
|
|
434
|
-
const
|
|
267
|
+
for (const file of result.generatedFiles) {
|
|
268
|
+
logSuccess(`Copied ${file.label}`)
|
|
269
|
+
}
|
|
435
270
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
271
|
+
switch (result.envResult.type) {
|
|
272
|
+
case 'created':
|
|
273
|
+
logSuccess('Created .env.example')
|
|
274
|
+
break
|
|
439
275
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
276
|
+
case 'merged':
|
|
277
|
+
logSuccess(`Added ${result.envResult.added} variables to .env.example`)
|
|
278
|
+
break
|
|
443
279
|
|
|
444
|
-
|
|
445
|
-
|
|
280
|
+
case 'skipped':
|
|
281
|
+
logSkip('.env.example already contains required variables')
|
|
282
|
+
break
|
|
283
|
+
}
|
|
446
284
|
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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({
|
|
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
|
-
'
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
36
|
+
"gitHead": "afeda129637cd33e0771ff9fee90a268a1ff55d5"
|
|
36
37
|
}
|
package/templates/chat/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# chat-add
|
|
2
2
|
|
|
3
|
-
`chat-add`
|
|
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
|
-
|
|
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/
|
|
25
|
-
- `src/config/chat-
|
|
26
|
-
- `src/
|
|
27
|
-
- `src/
|
|
28
|
-
- `src/
|
|
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
|
|