@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 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:
@@ -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'
@@ -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 DEP_NAME = '@opentiny/tiny-robot'
23
- const TARGET_VERSION = '0.5.1-alpha.2'
24
- const STYLE_IMPORT = "import '@opentiny/tiny-robot/dist/style.css'"
25
-
26
- function logUnavailable(label) {
27
- logSkip(`${label} could not be applied`)
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
- // 2. 已存在 => 只更新版本,不调整顺序
168
- const current = semver.valid(currentVersion)
169
-
170
- if (current !== targetVersion) {
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: 'updated',
175
- from: currentVersion,
176
- to: targetVersion,
118
+ type: 'skipped',
177
119
  }
178
120
  }
179
121
 
180
- // 3. 完全一致 => 跳过
181
122
  return {
182
- type: 'skipped',
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}@${TARGET_VERSION}`)
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 getChatFeatureFiles(targetDir) {
203
- const mainEntry = findMainEntry(targetDir)
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
- template: getTemplateFile('src', 'TinyRobotChat.vue'),
210
- action(fileExists) {
211
- return fileExists ? 'overwrite' : 'create'
212
- },
150
+ type: 'file',
213
151
  },
214
152
  {
215
- label: 'main entry style import',
216
- target: mainEntry,
217
- action() {
218
- return 'modify'
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: '.env',
223
- target: path.join(targetDir, '.env'),
224
- template: getTemplateFile('.env.example'),
225
- action(fileExists) {
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: 'package.json',
232
- target: path.join(targetDir, 'package.json'),
233
- action() {
234
- return 'modify'
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
- async function selectFileChanges(files) {
241
- return checkbox({
242
- message: 'Select which file changes to apply (all selected by default):',
243
- choices: files.map((file) => {
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
- return {
258
- name: disabled
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
- value: file.label,
181
+ if (!sourceStat.isDirectory()) {
182
+ return [target]
183
+ }
263
184
 
264
- checked: !disabled,
185
+ if (!targetStat.isDirectory()) {
186
+ return [target]
187
+ }
265
188
 
266
- disabled,
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 isSelected(selectedFiles, label) {
273
- return selectedFiles.includes(label)
274
- }
194
+ export function applyChatFeature(targetDir) {
195
+ const pkgPath = path.join(targetDir, 'package.json')
275
196
 
276
- async function addFeature(targetDir, type) {
277
- invariant(type === 'chat', `unsupported feature: ${type}`)
197
+ invariant(fs.existsSync(pkgPath), 'package.json not found.')
278
198
 
279
- const files = getChatFeatureFiles(targetDir)
199
+ const generatedFiles = getGeneratedFiles(targetDir)
200
+ const conflicts = generatedFiles.flatMap((file) => findCopyConflicts(file.source, file.target))
280
201
 
281
- const selectedFiles = await selectFileChanges(files)
202
+ if (conflicts.length > 0) {
203
+ const conflictPaths = conflicts.map((file) => path.relative(targetDir, file)).join(', ')
282
204
 
283
- if (selectedFiles.length === 0) {
284
- logSkip('No changes selected.')
285
- return
205
+ throw new Error(`Chat feature files already exist: ${conflictPaths}`)
286
206
  }
287
207
 
288
- const componentFile = files.find((f) => {
289
- return f.label === 'TinyRobotChat.vue'
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
- if (isSelected(selectedFiles, 'TinyRobotChat.vue')) {
307
- copyFile(componentFile.template, componentFile.target)
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 mainFile = files.find((f) => {
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
- logUnavailable('main entry style import (main.ts/js not found)')
321
- } else {
322
- if (isSelected(selectedFiles, mainFile.label)) {
323
- const result = ensureStyleImport(mainFile.target)
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
- switch (result.type) {
326
- case 'inserted':
327
- logSuccess('Inserted TinyRobot style import')
328
- break
222
+ throw new Error(`Dependency version conflict: ${conflictDetails}`)
223
+ }
329
224
 
330
- case 'skipped':
331
- logSkip('TinyRobot style import already exists')
332
- break
333
- }
225
+ for (const file of generatedFiles) {
226
+ if (file.type === 'file') {
227
+ copyFile(file.source, file.target)
334
228
  } else {
335
- needsManualStyleImport = true
336
-
337
- logSkippedSelection(mainFile.label)
229
+ copyDirectory(file.source, file.target)
338
230
  }
339
231
  }
340
232
 
341
- if (isSelected(selectedFiles, '.env')) {
342
- const envResult = mergeEnvFile(envFile.template, envFile.target)
233
+ const envResult = mergeEnvFile(getTemplateFile('.env.example'), path.join(targetDir, '.env.example'))
343
234
 
344
- switch (envResult.type) {
345
- case 'created':
346
- envChanged = true
235
+ if (dependencyResults.some(({ result }) => result.type !== 'skipped')) {
236
+ writePackageJson(pkgPath, pkg)
237
+ }
347
238
 
348
- logSuccess('Created .env')
349
- break
239
+ return {
240
+ generatedFiles,
241
+ envResult,
242
+ dependencyResults,
243
+ }
244
+ }
350
245
 
351
- case 'merged':
352
- envChanged = true
246
+ async function addFeature(targetDir, type) {
247
+ invariant(type === 'chat', `unsupported feature: ${type}`)
353
248
 
354
- logSuccess(`Added ${envResult.added} env variables`)
355
- break
249
+ const shouldApply = await confirm({
250
+ message: `Add the complete TinyRobot chat feature to ${targetDir}?`,
251
+ default: true,
252
+ })
356
253
 
357
- case 'skipped':
358
- logSkip('.env already contains required variables')
359
- break
360
- }
361
- } else {
362
- logSkippedSelection('.env')
254
+ if (!shouldApply) {
255
+ logSkip('No changes applied.')
256
+ return
363
257
  }
364
258
 
365
- const pkgPath = path.join(targetDir, 'package.json')
259
+ const result = applyChatFeature(targetDir)
366
260
 
367
- invariant(fs.existsSync(pkgPath), 'package.json not found.')
261
+ console.log('\nChange Results\n')
368
262
 
369
- const pkg = readPackageJson(pkgPath)
263
+ for (const file of result.generatedFiles) {
264
+ logSuccess(`Copied ${file.label}`)
265
+ }
370
266
 
371
- if (isSelected(selectedFiles, 'package.json')) {
372
- const result = ensureDependency(pkg, DEP_NAME, TARGET_VERSION)
267
+ switch (result.envResult.type) {
268
+ case 'created':
269
+ logSuccess('Created .env.example')
270
+ break
373
271
 
374
- if (result.type !== 'skipped') {
375
- dependencyChanged = true
376
- }
272
+ case 'merged':
273
+ logSuccess(`Added ${result.envResult.added} variables to .env.example`)
274
+ break
377
275
 
378
- writePackageJson(pkgPath, pkg)
276
+ case 'skipped':
277
+ logSkip('.env.example already contains required variables')
278
+ break
279
+ }
379
280
 
380
- printDependencyResult(result, DEP_NAME)
381
- } else {
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
- needsManualStyleImport,
389
- envChanged,
390
- dependencyChanged,
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({ needsManualStyleImport, envChanged, dependencyChanged }) {
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
- 'Configure your AI provider API key in the .env file.',
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.0",
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": "47f98d6b156e80289b8b7b5b213b9575dd230210"
36
+ "gitHead": "dc033027731d2d5b863529cc65b5314a1daa33ff"
29
37
  }
@@ -1 +1,2 @@
1
+ VITE_ALIYUN_DASHSCOPE_KEY=
1
2
  VITE_DEEPSEEK_API_KEY=
@@ -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
+ }