@opentiny/tiny-robot-cli 0.5.0 → 0.5.2-alpha.12
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 +10 -0
- package/bin/commands/add.js +118 -229
- package/bin/utils.js +4 -0
- package/package.json +11 -3
- package/templates/chat/.env.example +1 -0
- package/templates/chat/README.md +38 -0
- package/templates/chat/index.html +12 -0
- package/templates/chat/package.json +28 -0
- package/templates/chat/src/TinyRobotChat.vue +170 -29
- package/templates/chat/src/main.ts +4 -0
- package/templates/chat/src/tiny-robot-chat/components/ComposerTools.vue +72 -0
- package/templates/chat/src/tiny-robot-chat/components/WindowHeader.vue +192 -0
- package/templates/chat/src/tiny-robot-chat/composables/useWindow.ts +93 -0
- package/templates/chat/src/tiny-robot-chat/config/chat-runtime.ts +26 -0
- package/templates/chat/src/tiny-robot-chat/config/chat-ui.ts +271 -0
- package/templates/chat/tsconfig.app.json +13 -0
- package/templates/chat/tsconfig.json +7 -0
- package/templates/chat/tsconfig.node.json +19 -0
- package/templates/chat/vite.config.ts +16 -0
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ A lightweight CLI for scaffolding TinyRobot-based product projects.
|
|
|
7
7
|
```bash
|
|
8
8
|
npx @opentiny/tiny-robot-cli create my-app
|
|
9
9
|
pnpm dlx @opentiny/tiny-robot-cli create my-app
|
|
10
|
+
npx @opentiny/tiny-robot-cli add chat
|
|
10
11
|
```
|
|
11
12
|
|
|
12
13
|
## Options
|
|
@@ -14,6 +15,15 @@ pnpm dlx @opentiny/tiny-robot-cli create my-app
|
|
|
14
15
|
- `-t, --template <name>`: template name, currently supports `basic`
|
|
15
16
|
- `-h, --help`: show help
|
|
16
17
|
|
|
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
|
+
|
|
20
|
+
- `@opentiny/tiny-robot`
|
|
21
|
+
- `@opentiny/tiny-robot-chat`
|
|
22
|
+
- `@opentiny/tiny-robot-kit`
|
|
23
|
+
- `@opentiny/tiny-robot-svgs`
|
|
24
|
+
|
|
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
|
+
|
|
17
27
|
## Template Documentation
|
|
18
28
|
|
|
19
29
|
Template-specific features and environment variables are documented in each template directory, for example:
|
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'
|
|
@@ -7,6 +7,7 @@ import semver from 'semver'
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
copyFile,
|
|
10
|
+
copyDirectory,
|
|
10
11
|
findProjectRoot,
|
|
11
12
|
findSubPackageRoot,
|
|
12
13
|
findWorkspacePackages,
|
|
@@ -19,16 +20,14 @@ import {
|
|
|
19
20
|
mergeEnvFile,
|
|
20
21
|
} from '../utils.js'
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
function logSkippedSelection(label) {
|
|
31
|
-
logSkip(`${label} change was not selected`)
|
|
23
|
+
const TARGET_VERSION = '0.5.2-alpha.10'
|
|
24
|
+
const VUEUSE_VERSION = '13.9.0'
|
|
25
|
+
const DEPENDENCIES = {
|
|
26
|
+
'@opentiny/tiny-robot': TARGET_VERSION,
|
|
27
|
+
'@opentiny/tiny-robot-chat': TARGET_VERSION,
|
|
28
|
+
'@opentiny/tiny-robot-kit': TARGET_VERSION,
|
|
29
|
+
'@opentiny/tiny-robot-svgs': TARGET_VERSION,
|
|
30
|
+
'@vueuse/core': VUEUSE_VERSION,
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
async function resolveTargetPackage(cwd) {
|
|
@@ -42,7 +41,6 @@ async function resolveTargetPackage(cwd) {
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
const workspacePatterns = findWorkspacePackages(workspaceRoot)
|
|
45
|
-
|
|
46
44
|
const packageDirs = listPackages(workspaceRoot, workspacePatterns)
|
|
47
45
|
|
|
48
46
|
invariant(packageDirs.length > 0, 'no packages found in workspace.')
|
|
@@ -63,7 +61,6 @@ async function resolveTargetPackage(cwd) {
|
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
console.error('Error: no package.json found.')
|
|
66
|
-
|
|
67
64
|
process.exit(1)
|
|
68
65
|
}
|
|
69
66
|
|
|
@@ -79,58 +76,8 @@ function writePackageJson(pkgPath, pkg) {
|
|
|
79
76
|
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
|
|
80
77
|
}
|
|
81
78
|
|
|
82
|
-
function findMainEntry(targetDir) {
|
|
83
|
-
for (const file of ['src/main.ts', 'src/main.js']) {
|
|
84
|
-
const fullPath = path.join(targetDir, file)
|
|
85
|
-
|
|
86
|
-
if (fs.existsSync(fullPath)) {
|
|
87
|
-
return fullPath
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return null
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function ensureStyleImport(mainFile) {
|
|
95
|
-
const content = fs.readFileSync(mainFile, 'utf-8')
|
|
96
|
-
|
|
97
|
-
if (content.includes(STYLE_IMPORT)) {
|
|
98
|
-
return {
|
|
99
|
-
type: 'skipped',
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const lines = content.split('\n')
|
|
104
|
-
|
|
105
|
-
let lastImportIndex = -1
|
|
106
|
-
|
|
107
|
-
for (let i = 0; i < lines.length; i++) {
|
|
108
|
-
if (/^\s*import\s/.test(lines[i])) {
|
|
109
|
-
lastImportIndex = i
|
|
110
|
-
continue
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (lastImportIndex !== -1) {
|
|
114
|
-
break
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (lastImportIndex === -1) {
|
|
119
|
-
lines.unshift(STYLE_IMPORT)
|
|
120
|
-
} else {
|
|
121
|
-
lines.splice(lastImportIndex + 1, 0, STYLE_IMPORT)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
fs.writeFileSync(mainFile, lines.join('\n'))
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
type: 'inserted',
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
79
|
function insertDependencyOrdered(dependencies, name, version) {
|
|
132
80
|
const next = {}
|
|
133
|
-
|
|
134
81
|
let inserted = false
|
|
135
82
|
|
|
136
83
|
for (const [key, value] of Object.entries(dependencies)) {
|
|
@@ -154,7 +101,6 @@ function ensureDependency(pkg, name, targetVersion) {
|
|
|
154
101
|
|
|
155
102
|
const currentVersion = pkg.dependencies[name]
|
|
156
103
|
|
|
157
|
-
// 1. 不存在 => 新增(使用 ordered insert)
|
|
158
104
|
if (!currentVersion) {
|
|
159
105
|
pkg.dependencies = insertDependencyOrdered(pkg.dependencies, name, targetVersion)
|
|
160
106
|
|
|
@@ -164,33 +110,26 @@ function ensureDependency(pkg, name, targetVersion) {
|
|
|
164
110
|
}
|
|
165
111
|
}
|
|
166
112
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
pkg.dependencies[name] = targetVersion
|
|
172
|
-
|
|
113
|
+
if (
|
|
114
|
+
currentVersion.startsWith('workspace:') ||
|
|
115
|
+
semver.satisfies(targetVersion, currentVersion, { includePrerelease: true })
|
|
116
|
+
) {
|
|
173
117
|
return {
|
|
174
|
-
type: '
|
|
175
|
-
from: currentVersion,
|
|
176
|
-
to: targetVersion,
|
|
118
|
+
type: 'skipped',
|
|
177
119
|
}
|
|
178
120
|
}
|
|
179
121
|
|
|
180
|
-
// 3. 完全一致 => 跳过
|
|
181
122
|
return {
|
|
182
|
-
type: '
|
|
123
|
+
type: 'conflict',
|
|
124
|
+
from: currentVersion,
|
|
125
|
+
to: targetVersion,
|
|
183
126
|
}
|
|
184
127
|
}
|
|
185
128
|
|
|
186
129
|
function printDependencyResult(result, name) {
|
|
187
130
|
switch (result.type) {
|
|
188
131
|
case 'added':
|
|
189
|
-
logSuccess(`Added ${name}@${
|
|
190
|
-
break
|
|
191
|
-
|
|
192
|
-
case 'updated':
|
|
193
|
-
logSuccess(`Updated ${name} from ${result.from} to ${result.to}`)
|
|
132
|
+
logSuccess(`Added ${name}@${result.to}`)
|
|
194
133
|
break
|
|
195
134
|
|
|
196
135
|
case 'skipped':
|
|
@@ -199,208 +138,162 @@ function printDependencyResult(result, name) {
|
|
|
199
138
|
}
|
|
200
139
|
}
|
|
201
140
|
|
|
202
|
-
function
|
|
203
|
-
const
|
|
141
|
+
function getGeneratedFiles(targetDir) {
|
|
142
|
+
const templateSource = getTemplateFile('src')
|
|
143
|
+
const supportTarget = path.join(targetDir, 'src/tiny-robot-chat')
|
|
204
144
|
|
|
205
145
|
return [
|
|
206
146
|
{
|
|
207
147
|
label: 'TinyRobotChat.vue',
|
|
148
|
+
source: path.join(templateSource, 'TinyRobotChat.vue'),
|
|
208
149
|
target: path.join(targetDir, 'src/TinyRobotChat.vue'),
|
|
209
|
-
|
|
210
|
-
action(fileExists) {
|
|
211
|
-
return fileExists ? 'overwrite' : 'create'
|
|
212
|
-
},
|
|
150
|
+
type: 'file',
|
|
213
151
|
},
|
|
214
152
|
{
|
|
215
|
-
label: '
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
},
|
|
153
|
+
label: 'Chat UI components',
|
|
154
|
+
source: path.join(templateSource, 'tiny-robot-chat/components'),
|
|
155
|
+
target: path.join(supportTarget, 'components'),
|
|
156
|
+
type: 'directory',
|
|
220
157
|
},
|
|
221
158
|
{
|
|
222
|
-
label: '
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return fileExists ? 'modify' : 'create'
|
|
227
|
-
},
|
|
159
|
+
label: 'Chat composables',
|
|
160
|
+
source: path.join(templateSource, 'tiny-robot-chat/composables'),
|
|
161
|
+
target: path.join(supportTarget, 'composables'),
|
|
162
|
+
type: 'directory',
|
|
228
163
|
},
|
|
229
|
-
|
|
230
164
|
{
|
|
231
|
-
label: '
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
},
|
|
165
|
+
label: 'Chat configuration',
|
|
166
|
+
source: path.join(templateSource, 'tiny-robot-chat/config'),
|
|
167
|
+
target: path.join(supportTarget, 'config'),
|
|
168
|
+
type: 'directory',
|
|
236
169
|
},
|
|
237
170
|
]
|
|
238
171
|
}
|
|
239
172
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const exists = file.target ? fs.existsSync(file.target) : false
|
|
245
|
-
|
|
246
|
-
const disabled = !file.target
|
|
247
|
-
|
|
248
|
-
const descriptions = {
|
|
249
|
-
'TinyRobotChat.vue': 'integrate TinyRobot chat component',
|
|
250
|
-
'main entry style import': 'import TinyRobot styles',
|
|
251
|
-
'.env': 'add environment variables',
|
|
252
|
-
'package.json': 'add TinyRobot dependencies',
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const description = descriptions[file.label]
|
|
173
|
+
function findCopyConflicts(source, target) {
|
|
174
|
+
if (!fs.existsSync(target)) {
|
|
175
|
+
return []
|
|
176
|
+
}
|
|
256
177
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
? `${file.action(false)} ${file.label} — ${description} (main.ts/js not found)`
|
|
260
|
-
: `${file.action(exists)} ${file.label} — ${description}`,
|
|
178
|
+
const sourceStat = fs.statSync(source)
|
|
179
|
+
const targetStat = fs.statSync(target)
|
|
261
180
|
|
|
262
|
-
|
|
181
|
+
if (!sourceStat.isDirectory()) {
|
|
182
|
+
return [target]
|
|
183
|
+
}
|
|
263
184
|
|
|
264
|
-
|
|
185
|
+
if (!targetStat.isDirectory()) {
|
|
186
|
+
return [target]
|
|
187
|
+
}
|
|
265
188
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}),
|
|
189
|
+
return fs.readdirSync(source, { withFileTypes: true }).flatMap((entry) => {
|
|
190
|
+
return findCopyConflicts(path.join(source, entry.name), path.join(target, entry.name))
|
|
269
191
|
})
|
|
270
192
|
}
|
|
271
193
|
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
}
|
|
194
|
+
export function applyChatFeature(targetDir) {
|
|
195
|
+
const pkgPath = path.join(targetDir, 'package.json')
|
|
275
196
|
|
|
276
|
-
|
|
277
|
-
invariant(type === 'chat', `unsupported feature: ${type}`)
|
|
197
|
+
invariant(fs.existsSync(pkgPath), 'package.json not found.')
|
|
278
198
|
|
|
279
|
-
const
|
|
199
|
+
const generatedFiles = getGeneratedFiles(targetDir)
|
|
200
|
+
const conflicts = generatedFiles.flatMap((file) => findCopyConflicts(file.source, file.target))
|
|
280
201
|
|
|
281
|
-
|
|
202
|
+
if (conflicts.length > 0) {
|
|
203
|
+
const conflictPaths = conflicts.map((file) => path.relative(targetDir, file)).join(', ')
|
|
282
204
|
|
|
283
|
-
|
|
284
|
-
logSkip('No changes selected.')
|
|
285
|
-
return
|
|
205
|
+
throw new Error(`Chat feature files already exist: ${conflictPaths}`)
|
|
286
206
|
}
|
|
287
207
|
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
const envFile = files.find((f) => {
|
|
293
|
-
return f.label === '.env'
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
invariant(componentFile, 'TinyRobotChat.vue config missing.')
|
|
297
|
-
|
|
298
|
-
invariant(envFile, '.env config missing.')
|
|
299
|
-
|
|
300
|
-
console.log('\nChange Results\n')
|
|
301
|
-
|
|
302
|
-
let needsManualStyleImport = false
|
|
303
|
-
let envChanged = false
|
|
304
|
-
let dependencyChanged = false
|
|
208
|
+
const pkg = readPackageJson(pkgPath)
|
|
209
|
+
const dependencyResults = []
|
|
305
210
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
logSuccess(`Copied ${componentFile.label}`)
|
|
309
|
-
} else {
|
|
310
|
-
logSkippedSelection(componentFile.label)
|
|
211
|
+
for (const [name, version] of Object.entries(DEPENDENCIES)) {
|
|
212
|
+
dependencyResults.push({ name, result: ensureDependency(pkg, name, version) })
|
|
311
213
|
}
|
|
312
214
|
|
|
313
|
-
const
|
|
314
|
-
return f.label === 'main entry style import'
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
if (!mainFile?.target) {
|
|
318
|
-
needsManualStyleImport = true
|
|
215
|
+
const dependencyConflicts = dependencyResults.filter(({ result }) => result.type === 'conflict')
|
|
319
216
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
217
|
+
if (dependencyConflicts.length > 0) {
|
|
218
|
+
const conflictDetails = dependencyConflicts
|
|
219
|
+
.map(({ name, result }) => `${name}: found ${result.from}, required ${result.to}`)
|
|
220
|
+
.join('; ')
|
|
324
221
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
logSuccess('Inserted TinyRobot style import')
|
|
328
|
-
break
|
|
222
|
+
throw new Error(`Dependency version conflict: ${conflictDetails}`)
|
|
223
|
+
}
|
|
329
224
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}
|
|
225
|
+
for (const file of generatedFiles) {
|
|
226
|
+
if (file.type === 'file') {
|
|
227
|
+
copyFile(file.source, file.target)
|
|
334
228
|
} else {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
logSkippedSelection(mainFile.label)
|
|
229
|
+
copyDirectory(file.source, file.target)
|
|
338
230
|
}
|
|
339
231
|
}
|
|
340
232
|
|
|
341
|
-
|
|
342
|
-
const envResult = mergeEnvFile(envFile.template, envFile.target)
|
|
233
|
+
const envResult = mergeEnvFile(getTemplateFile('.env.example'), path.join(targetDir, '.env.example'))
|
|
343
234
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
235
|
+
if (dependencyResults.some(({ result }) => result.type !== 'skipped')) {
|
|
236
|
+
writePackageJson(pkgPath, pkg)
|
|
237
|
+
}
|
|
347
238
|
|
|
348
|
-
|
|
349
|
-
|
|
239
|
+
return {
|
|
240
|
+
generatedFiles,
|
|
241
|
+
envResult,
|
|
242
|
+
dependencyResults,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
350
245
|
|
|
351
|
-
|
|
352
|
-
|
|
246
|
+
async function addFeature(targetDir, type) {
|
|
247
|
+
invariant(type === 'chat', `unsupported feature: ${type}`)
|
|
353
248
|
|
|
354
|
-
|
|
355
|
-
|
|
249
|
+
const shouldApply = await confirm({
|
|
250
|
+
message: `Add the complete TinyRobot chat feature to ${targetDir}?`,
|
|
251
|
+
default: true,
|
|
252
|
+
})
|
|
356
253
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
} else {
|
|
362
|
-
logSkippedSelection('.env')
|
|
254
|
+
if (!shouldApply) {
|
|
255
|
+
logSkip('No changes applied.')
|
|
256
|
+
return
|
|
363
257
|
}
|
|
364
258
|
|
|
365
|
-
const
|
|
259
|
+
const result = applyChatFeature(targetDir)
|
|
366
260
|
|
|
367
|
-
|
|
261
|
+
console.log('\nChange Results\n')
|
|
368
262
|
|
|
369
|
-
const
|
|
263
|
+
for (const file of result.generatedFiles) {
|
|
264
|
+
logSuccess(`Copied ${file.label}`)
|
|
265
|
+
}
|
|
370
266
|
|
|
371
|
-
|
|
372
|
-
|
|
267
|
+
switch (result.envResult.type) {
|
|
268
|
+
case 'created':
|
|
269
|
+
logSuccess('Created .env.example')
|
|
270
|
+
break
|
|
373
271
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
272
|
+
case 'merged':
|
|
273
|
+
logSuccess(`Added ${result.envResult.added} variables to .env.example`)
|
|
274
|
+
break
|
|
377
275
|
|
|
378
|
-
|
|
276
|
+
case 'skipped':
|
|
277
|
+
logSkip('.env.example already contains required variables')
|
|
278
|
+
break
|
|
279
|
+
}
|
|
379
280
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
logSkippedSelection('package.json')
|
|
281
|
+
for (const { name, result: dependencyResult } of result.dependencyResults) {
|
|
282
|
+
printDependencyResult(dependencyResult, name)
|
|
383
283
|
}
|
|
384
284
|
|
|
385
285
|
console.log(`\nSuccessfully added "${type}" feature to ${targetDir}`)
|
|
386
286
|
|
|
387
287
|
printNextSteps({
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
288
|
+
envChanged: result.envResult.type !== 'skipped',
|
|
289
|
+
dependencyChanged: result.dependencyResults.some(({ result: dependencyResult }) => {
|
|
290
|
+
return dependencyResult.type !== 'skipped'
|
|
291
|
+
}),
|
|
391
292
|
})
|
|
392
293
|
}
|
|
393
294
|
|
|
394
|
-
function printNextSteps({
|
|
395
|
-
const steps = [
|
|
396
|
-
|
|
397
|
-
if (needsManualStyleImport) {
|
|
398
|
-
steps.push(
|
|
399
|
-
['Import TinyRobot styles in your application entry file.', '', 'Example:', '', ` ${STYLE_IMPORT}`].join('\n'),
|
|
400
|
-
)
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
steps.push(
|
|
295
|
+
function printNextSteps({ envChanged, dependencyChanged }) {
|
|
296
|
+
const steps = [
|
|
404
297
|
[
|
|
405
298
|
'Render <TinyRobotChat /> near your main application component.',
|
|
406
299
|
'',
|
|
@@ -415,12 +308,12 @@ function printNextSteps({ needsManualStyleImport, envChanged, dependencyChanged
|
|
|
415
308
|
' <TinyRobotChat />',
|
|
416
309
|
' </template>',
|
|
417
310
|
].join('\n'),
|
|
418
|
-
|
|
311
|
+
]
|
|
419
312
|
|
|
420
313
|
if (envChanged) {
|
|
421
314
|
steps.push(
|
|
422
315
|
[
|
|
423
|
-
'
|
|
316
|
+
'Copy the required variables from .env.example to your private environment file and configure a provider key.',
|
|
424
317
|
'',
|
|
425
318
|
'Example:',
|
|
426
319
|
'',
|
|
@@ -433,10 +326,6 @@ function printNextSteps({ needsManualStyleImport, envChanged, dependencyChanged
|
|
|
433
326
|
steps.push(['Install or update project dependencies.', '', 'Example:', '', ' pnpm install'].join('\n'))
|
|
434
327
|
}
|
|
435
328
|
|
|
436
|
-
if (steps.length === 0) {
|
|
437
|
-
return
|
|
438
|
-
}
|
|
439
|
-
|
|
440
329
|
console.log('\nNext Steps\n')
|
|
441
330
|
|
|
442
331
|
for (const [index, step] of steps.entries()) {
|
package/bin/utils.js
CHANGED
|
@@ -88,6 +88,10 @@ export function validateProjectName(name) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export function copyTemplate(sourceDir, targetDir) {
|
|
91
|
+
copyDirectory(sourceDir, targetDir)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function copyDirectory(sourceDir, targetDir) {
|
|
91
95
|
fs.cpSync(sourceDir, targetDir, {
|
|
92
96
|
recursive: true,
|
|
93
97
|
filter: (source) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opentiny/tiny-robot-cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2-alpha.12",
|
|
4
4
|
"description": "CLI to scaffold TinyRobot product projects",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/opentiny/tiny-robot.git"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
5
12
|
"type": "module",
|
|
6
13
|
"bin": {
|
|
7
14
|
"tiny-robot-cli": "./bin/cli.js"
|
|
@@ -12,7 +19,8 @@
|
|
|
12
19
|
"README.md"
|
|
13
20
|
],
|
|
14
21
|
"scripts": {
|
|
15
|
-
"start": "node ./bin/cli.js"
|
|
22
|
+
"start": "node ./bin/cli.js",
|
|
23
|
+
"test": "node --test ./test/*.test.js"
|
|
16
24
|
},
|
|
17
25
|
"license": "MIT",
|
|
18
26
|
"engines": {
|
|
@@ -25,5 +33,5 @@
|
|
|
25
33
|
"semver": "^7.8.1",
|
|
26
34
|
"yaml": "^2.9.0"
|
|
27
35
|
},
|
|
28
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "dc033027731d2d5b863529cc65b5314a1daa33ff"
|
|
29
37
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# chat-add
|
|
2
|
+
|
|
3
|
+
`chat-add` 是一个可嵌入现有 Vue 应用的 Chat 扩展模板,提供通用工作助手场景的最小工程结构。
|
|
4
|
+
|
|
5
|
+
## 启动
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
pnpm --filter chat-add dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 构建
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
pnpm --filter chat-add type-check
|
|
15
|
+
pnpm --filter chat-add build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 接入真实模型
|
|
19
|
+
|
|
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`。
|
|
23
|
+
|
|
24
|
+
## 目录职责
|
|
25
|
+
|
|
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 窗口状态。
|
|
31
|
+
|
|
32
|
+
## 替换数据
|
|
33
|
+
|
|
34
|
+
直接修改 `src/tiny-robot-chat/config/chat-ui.ts` 和 `src/tiny-robot-chat/config/chat-runtime.ts` 中导出的数据;组件通过 props 使用这些配置。
|
|
35
|
+
|
|
36
|
+
## 切换窗口模式
|
|
37
|
+
|
|
38
|
+
默认显示 AI 头像按钮,点击后展开 Chat。窗口头部提供 floating、fullscreen 两种模式。floating 支持拖拽、缩放和位置恢复;fullscreen 铺满对应区域,可使用 Escape 返回悬浮窗口。点击关闭后返回 AI 头像按钮。
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Chat Add Template</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="/src/main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vue-tsc -b && vite build",
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"type-check": "vue-tsc -b"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@opentiny/tiny-robot": "0.5.2-alpha.10",
|
|
14
|
+
"@opentiny/tiny-robot-chat": "0.5.2-alpha.10",
|
|
15
|
+
"@opentiny/tiny-robot-kit": "0.5.2-alpha.10",
|
|
16
|
+
"@opentiny/tiny-robot-svgs": "0.5.2-alpha.10",
|
|
17
|
+
"@vueuse/core": "13.9.0",
|
|
18
|
+
"vue": "^3.5.40"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^24.13.3",
|
|
22
|
+
"@vitejs/plugin-vue": "^6.0.8",
|
|
23
|
+
"@vue/tsconfig": "^0.9.1",
|
|
24
|
+
"typescript": "~6.0.2",
|
|
25
|
+
"vite": "^8.2.0",
|
|
26
|
+
"vue-tsc": "^3.3.8"
|
|
27
|
+
}
|
|
28
|
+
}
|