xiaozuoassistant 0.2.20 → 0.2.21

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.
@@ -18,88 +18,88 @@ export class StructuredMemory {
18
18
  initTables() {
19
19
  try {
20
20
  // Facts table: key-value or simple structured data
21
- this.db.prepare(`
22
- CREATE TABLE IF NOT EXISTS facts (
23
- id INTEGER PRIMARY KEY AUTOINCREMENT,
24
- category TEXT NOT NULL,
25
- key TEXT NOT NULL,
26
- value TEXT NOT NULL,
27
- confidence REAL DEFAULT 1.0,
28
- source TEXT,
29
- timestamp INTEGER
30
- )
21
+ this.db.prepare(`
22
+ CREATE TABLE IF NOT EXISTS facts (
23
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
24
+ category TEXT NOT NULL,
25
+ key TEXT NOT NULL,
26
+ value TEXT NOT NULL,
27
+ confidence REAL DEFAULT 1.0,
28
+ source TEXT,
29
+ timestamp INTEGER
30
+ )
31
31
  `).run();
32
32
  // User Profile: specialized facts (multi-user)
33
33
  // V3: Simplified profile (name, email only)
34
- this.db.prepare(`
35
- CREATE TABLE IF NOT EXISTS user_profile_v3 (
36
- id INTEGER PRIMARY KEY AUTOINCREMENT,
37
- user_id TEXT NOT NULL,
38
- name TEXT,
39
- email TEXT,
40
- updated_at INTEGER,
41
- UNIQUE(user_id)
42
- )
34
+ this.db.prepare(`
35
+ CREATE TABLE IF NOT EXISTS user_profile_v3 (
36
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
37
+ user_id TEXT NOT NULL,
38
+ name TEXT,
39
+ email TEXT,
40
+ updated_at INTEGER,
41
+ UNIQUE(user_id)
42
+ )
43
43
  `).run();
44
44
  this.migrateUserProfileV3();
45
45
  // Graph nodes (Entities)
46
- this.db.prepare(`
47
- CREATE TABLE IF NOT EXISTS entities (
48
- id INTEGER PRIMARY KEY AUTOINCREMENT,
49
- name TEXT UNIQUE NOT NULL,
50
- type TEXT NOT NULL,
51
- metadata TEXT
52
- )
46
+ this.db.prepare(`
47
+ CREATE TABLE IF NOT EXISTS entities (
48
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
49
+ name TEXT UNIQUE NOT NULL,
50
+ type TEXT NOT NULL,
51
+ metadata TEXT
52
+ )
53
53
  `).run();
54
54
  // Graph edges (Relations)
55
- this.db.prepare(`
56
- CREATE TABLE IF NOT EXISTS relations (
57
- id INTEGER PRIMARY KEY AUTOINCREMENT,
58
- source_id INTEGER,
59
- target_id INTEGER,
60
- relation TEXT NOT NULL,
61
- weight REAL DEFAULT 1.0,
62
- FOREIGN KEY(source_id) REFERENCES entities(id),
63
- FOREIGN KEY(target_id) REFERENCES entities(id)
64
- )
55
+ this.db.prepare(`
56
+ CREATE TABLE IF NOT EXISTS relations (
57
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
58
+ source_id INTEGER,
59
+ target_id INTEGER,
60
+ relation TEXT NOT NULL,
61
+ weight REAL DEFAULT 1.0,
62
+ FOREIGN KEY(source_id) REFERENCES entities(id),
63
+ FOREIGN KEY(target_id) REFERENCES entities(id)
64
+ )
65
65
  `).run();
66
66
  // Projects
67
- this.db.prepare(`
68
- CREATE TABLE IF NOT EXISTS projects (
69
- id TEXT PRIMARY KEY,
70
- name TEXT NOT NULL,
71
- description TEXT,
72
- departments TEXT,
73
- members TEXT,
74
- directory TEXT,
75
- created_at INTEGER,
76
- updated_at INTEGER
77
- )
67
+ this.db.prepare(`
68
+ CREATE TABLE IF NOT EXISTS projects (
69
+ id TEXT PRIMARY KEY,
70
+ name TEXT NOT NULL,
71
+ description TEXT,
72
+ departments TEXT,
73
+ members TEXT,
74
+ directory TEXT,
75
+ created_at INTEGER,
76
+ updated_at INTEGER
77
+ )
78
78
  `).run();
79
79
  // Notebooks
80
- this.db.prepare(`
81
- CREATE TABLE IF NOT EXISTS notebooks (
82
- id TEXT PRIMARY KEY,
83
- name TEXT NOT NULL,
84
- description TEXT,
85
- keywords TEXT,
86
- created_at INTEGER,
87
- updated_at INTEGER
88
- )
80
+ this.db.prepare(`
81
+ CREATE TABLE IF NOT EXISTS notebooks (
82
+ id TEXT PRIMARY KEY,
83
+ name TEXT NOT NULL,
84
+ description TEXT,
85
+ keywords TEXT,
86
+ created_at INTEGER,
87
+ updated_at INTEGER
88
+ )
89
89
  `).run();
90
90
  // Notes
91
- this.db.prepare(`
92
- CREATE TABLE IF NOT EXISTS notes (
93
- id TEXT PRIMARY KEY,
94
- notebook_id TEXT NOT NULL,
95
- title TEXT NOT NULL,
96
- content TEXT,
97
- is_todo INTEGER DEFAULT 0,
98
- done INTEGER DEFAULT 0,
99
- created_at INTEGER,
100
- updated_at INTEGER,
101
- FOREIGN KEY(notebook_id) REFERENCES notebooks(id) ON DELETE CASCADE
102
- )
91
+ this.db.prepare(`
92
+ CREATE TABLE IF NOT EXISTS notes (
93
+ id TEXT PRIMARY KEY,
94
+ notebook_id TEXT NOT NULL,
95
+ title TEXT NOT NULL,
96
+ content TEXT,
97
+ is_todo INTEGER DEFAULT 0,
98
+ done INTEGER DEFAULT 0,
99
+ created_at INTEGER,
100
+ updated_at INTEGER,
101
+ FOREIGN KEY(notebook_id) REFERENCES notebooks(id) ON DELETE CASCADE
102
+ )
103
103
  `).run();
104
104
  }
105
105
  catch (error) {
@@ -147,9 +147,9 @@ export class StructuredMemory {
147
147
  }
148
148
  }
149
149
  addFact(category, key, value, source = 'user') {
150
- const stmt = this.db.prepare(`
151
- INSERT INTO facts (category, key, value, source, timestamp)
152
- VALUES (?, ?, ?, ?, ?)
150
+ const stmt = this.db.prepare(`
151
+ INSERT INTO facts (category, key, value, source, timestamp)
152
+ VALUES (?, ?, ?, ?, ?)
153
153
  `);
154
154
  stmt.run(category, key, value, source, Date.now());
155
155
  }
@@ -168,10 +168,10 @@ export class StructuredMemory {
168
168
  return;
169
169
  }
170
170
  // Use dynamic SQL for specific column update
171
- this.db.prepare(`
172
- INSERT INTO user_profile_v3 (user_id, ${key}, updated_at)
173
- VALUES (?, ?, ?)
174
- ON CONFLICT(user_id) DO UPDATE SET ${key} = excluded.${key}, updated_at = excluded.updated_at
171
+ this.db.prepare(`
172
+ INSERT INTO user_profile_v3 (user_id, ${key}, updated_at)
173
+ VALUES (?, ?, ?)
174
+ ON CONFLICT(user_id) DO UPDATE SET ${key} = excluded.${key}, updated_at = excluded.updated_at
175
175
  `).run(uid, value, now);
176
176
  }
177
177
  getUserProfile(userId) {
@@ -205,9 +205,9 @@ export class StructuredMemory {
205
205
  // --- Projects ---
206
206
  createProject(id, name, description, departments, members, directory) {
207
207
  const now = Date.now();
208
- this.db.prepare(`
209
- INSERT INTO projects (id, name, description, departments, members, directory, created_at, updated_at)
210
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
208
+ this.db.prepare(`
209
+ INSERT INTO projects (id, name, description, departments, members, directory, created_at, updated_at)
210
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
211
211
  `).run(id, name, description || null, departments || null, members || null, directory || null, now, now);
212
212
  }
213
213
  getProject(id) {
@@ -231,9 +231,9 @@ export class StructuredMemory {
231
231
  throw new Error('Project not found');
232
232
  const next = { ...current, ...patch };
233
233
  const now = Date.now();
234
- this.db.prepare(`
235
- UPDATE projects SET name = ?, description = ?, departments = ?, members = ?, directory = ?, updated_at = ?
236
- WHERE id = ?
234
+ this.db.prepare(`
235
+ UPDATE projects SET name = ?, description = ?, departments = ?, members = ?, directory = ?, updated_at = ?
236
+ WHERE id = ?
237
237
  `).run(next.name, next.description || null, next.departments || null, next.members || null, next.directory || null, now, id);
238
238
  }
239
239
  deleteProject(id) {
@@ -255,9 +255,9 @@ export class StructuredMemory {
255
255
  // --- Notebooks ---
256
256
  createNotebook(id, name, description, keywords) {
257
257
  const now = Date.now();
258
- this.db.prepare(`
259
- INSERT INTO notebooks (id, name, description, keywords, created_at, updated_at)
260
- VALUES (?, ?, ?, ?, ?, ?)
258
+ this.db.prepare(`
259
+ INSERT INTO notebooks (id, name, description, keywords, created_at, updated_at)
260
+ VALUES (?, ?, ?, ?, ?, ?)
261
261
  `).run(id, name, description || null, keywords || null, now, now);
262
262
  }
263
263
  getNotebook(id) {
@@ -279,9 +279,9 @@ export class StructuredMemory {
279
279
  throw new Error('Notebook not found');
280
280
  const next = { ...current, ...patch };
281
281
  const now = Date.now();
282
- this.db.prepare(`
283
- UPDATE notebooks SET name = ?, description = ?, keywords = ?, updated_at = ?
284
- WHERE id = ?
282
+ this.db.prepare(`
283
+ UPDATE notebooks SET name = ?, description = ?, keywords = ?, updated_at = ?
284
+ WHERE id = ?
285
285
  `).run(next.name, next.description || null, next.keywords || null, now, id);
286
286
  }
287
287
  deleteNotebook(id) {
@@ -301,9 +301,9 @@ export class StructuredMemory {
301
301
  // --- Notes ---
302
302
  createNote(id, notebookId, title, content, isTodo = false, done = false) {
303
303
  const now = Date.now();
304
- this.db.prepare(`
305
- INSERT INTO notes (id, notebook_id, title, content, is_todo, done, created_at, updated_at)
306
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
304
+ this.db.prepare(`
305
+ INSERT INTO notes (id, notebook_id, title, content, is_todo, done, created_at, updated_at)
306
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
307
307
  `).run(id, notebookId, title, content || null, isTodo ? 1 : 0, done ? 1 : 0, now, now);
308
308
  }
309
309
  getNote(id) {
@@ -327,9 +327,9 @@ export class StructuredMemory {
327
327
  throw new Error('Note not found');
328
328
  const next = { ...current, ...patch };
329
329
  const now = Date.now();
330
- this.db.prepare(`
331
- UPDATE notes SET title = ?, content = ?, is_todo = ?, done = ?, updated_at = ?
332
- WHERE id = ?
330
+ this.db.prepare(`
331
+ UPDATE notes SET title = ?, content = ?, is_todo = ?, done = ?, updated_at = ?
332
+ WHERE id = ?
333
333
  `).run(next.title, next.content || null, next.isTodo ? 1 : 0, next.done ? 1 : 0, now, id);
334
334
  }
335
335
  deleteNote(id) {
@@ -43,10 +43,10 @@ export class TaskQueue {
43
43
  const context = await memory.getRelevantContext(run.userContent, run.sessionId);
44
44
  const sessionAlias = session.meta.alias ? `Session Alias: ${session.meta.alias}` : '';
45
45
  const workspaceLine = session.meta.workspace ? `Current Workspace: ${session.meta.workspace}` : '';
46
- const enhancedSystemPrompt = `You are xiaozuoAssistant, a helpful AI assistant.
47
- ${sessionAlias}
48
- ${workspaceLine}
49
- Here is some relevant context from your memory:
46
+ const enhancedSystemPrompt = `You are xiaozuoAssistant, a helpful AI assistant.
47
+ ${sessionAlias}
48
+ ${workspaceLine}
49
+ Here is some relevant context from your memory:
50
50
  ${context}`;
51
51
  const ctx = {
52
52
  sessionId: run.sessionId,
@@ -239,13 +239,15 @@ app.get('/api/config', async (req, res) => {
239
239
  memoryMaintenanceCron: config.scheduler?.memoryMaintenanceCron,
240
240
  sessionRetentionDays: config.scheduler?.sessionRetentionDays,
241
241
  workspace: config.workspace,
242
+ feishuAppId: Array.isArray(config.channels?.feishu) ? config.channels?.feishu[0]?.appId : config.channels?.feishu?.appId || '',
243
+ feishuAppSecret: Array.isArray(config.channels?.feishu) ? config.channels?.feishu[0]?.appSecret : config.channels?.feishu?.appSecret || '',
242
244
  feishu: config.channels?.feishu || []
243
245
  });
244
246
  });
245
247
  // 更新配置并写入 config.json
246
248
  app.post('/api/config', async (req, res) => {
247
249
  try {
248
- const { userId, apiKey, baseURL, model, embeddingModel, temperature, systemPrompt, memoryMaintenanceCron, sessionRetentionDays, workspace, maxHistoryMessages, maxToolIterations, feishu } = req.body;
250
+ const { userId, apiKey, baseURL, model, embeddingModel, temperature, systemPrompt, memoryMaintenanceCron, sessionRetentionDays, workspace, maxHistoryMessages, maxToolIterations, feishu, feishuAppId, feishuAppSecret } = req.body;
249
251
  // 更新配置对象
250
252
  const newConfig = { ...config };
251
253
  if (userId !== undefined)
@@ -273,6 +275,20 @@ app.post('/api/config', async (req, res) => {
273
275
  newConfig.channels = {};
274
276
  newConfig.channels.feishu = feishu;
275
277
  }
278
+ else if (feishuAppId !== undefined || feishuAppSecret !== undefined) {
279
+ if (!newConfig.channels)
280
+ newConfig.channels = {};
281
+ const currentFeishu = newConfig.channels.feishu;
282
+ const firstBot = Array.isArray(currentFeishu)
283
+ ? (currentFeishu[0] || { name: 'default', appId: '', appSecret: '' })
284
+ : { name: 'default', appId: currentFeishu?.appId || '', appSecret: currentFeishu?.appSecret || '' };
285
+ newConfig.channels.feishu = [{
286
+ ...firstBot,
287
+ name: firstBot.name || 'default',
288
+ appId: feishuAppId !== undefined ? feishuAppId : firstBot.appId,
289
+ appSecret: feishuAppSecret !== undefined ? feishuAppSecret : firstBot.appSecret
290
+ }];
291
+ }
276
292
  // System prompt is fully controlled by the UI (identity + base prompt).
277
293
  let restartScheduler = false;
278
294
  if (memoryMaintenanceCron !== undefined) {
@@ -304,7 +320,7 @@ app.post('/api/config', async (req, res) => {
304
320
  initScheduler();
305
321
  }
306
322
  // 如果飞书配置变了,尝试重启或启动飞书通道
307
- if (feishu !== undefined) {
323
+ if (feishu !== undefined || feishuAppId !== undefined || feishuAppSecret !== undefined) {
308
324
  const existingFeishu = channels.find(c => c.name === 'feishu');
309
325
  if (existingFeishu) {
310
326
  // Restart existing channel
package/package.json CHANGED
@@ -1,116 +1,116 @@
1
- {
2
- "name": "xiaozuoassistant",
3
- "version": "0.2.20",
4
- "description": "A local-first personal AI assistant with multi-channel support and enhanced memory.",
5
- "author": "mantle.lau",
6
- "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/mantlelau-lgtm/claw.git"
10
- },
11
- "keywords": [
12
- "ai",
13
- "assistant",
14
- "office",
15
- "automation",
16
- "local-first",
17
- "agent"
18
- ],
19
- "type": "module",
20
- "main": "dist/server/index.js",
21
- "bin": {
22
- "xiaozuoAssistant": "bin/cli.js",
23
- "mybot": "bin/cli.js"
24
- },
25
- "files": [
26
- "dist",
27
- "bin",
28
- "scripts",
29
- "public",
30
- "config.json",
31
- "README.md"
32
- ],
33
- "scripts": {
34
- "client:dev": "vite",
35
- "build": "if exist dist rmdir /s /q dist && tsc -b && vite build && tsc -p tsconfig.server.json",
36
- "lint": "eslint .",
37
- "preview": "vite preview",
38
- "check": "tsc --noEmit",
39
- "server:dev": "nodemon --watch src --watch config.json --exec tsx src/index.ts",
40
- "dev": "concurrently \"npm run client:dev\" \"npm run server:dev\"",
41
- "postinstall": "node scripts/init-app-home.cjs || true"
42
- },
43
- "dependencies": {
44
- "@lancedb/lancedb": "0.22.3",
45
- "@larksuiteoapi/node-sdk": "^1.59.0",
46
- "@types/node-cron": "^3.0.11",
47
- "apache-arrow": "18.1.0",
48
- "axios": "^1.13.6",
49
- "better-sqlite3": "^12.6.2",
50
- "bindings": "^1.5.0",
51
- "clsx": "^2.1.1",
52
- "commander": "^14.0.3",
53
- "cors": "^2.8.5",
54
- "dotenv": "^17.3.1",
55
- "express": "^4.21.2",
56
- "fs-extra": "^11.3.4",
57
- "i18next": "^25.8.17",
58
- "i18next-browser-languagedetector": "^8.2.1",
59
- "i18next-http-backend": "^3.0.2",
60
- "lucide-react": "^0.511.0",
61
- "mammoth": "^1.11.0",
62
- "node-cron": "^4.2.1",
63
- "officegen": "^0.6.5",
64
- "openai": "^6.27.0",
65
- "pptxgenjs": "^4.0.1",
66
- "qrcode-terminal": "^0.12.0",
67
- "react": "^18.3.1",
68
- "react-dom": "^18.3.1",
69
- "react-i18next": "^16.5.6",
70
- "react-router-dom": "^7.3.0",
71
- "socket.io": "^4.8.3",
72
- "socket.io-client": "^4.8.3",
73
- "tailwind-merge": "^3.0.2",
74
- "tar": "^7.5.11",
75
- "telegraf": "^4.16.3",
76
- "uuid": "^13.0.0",
77
- "wechaty": "^1.20.2",
78
- "winston": "^3.19.0",
79
- "winston-daily-rotate-file": "^5.0.0",
80
- "xlsx": "^0.18.5",
81
- "zustand": "^5.0.3"
82
- },
83
- "devDependencies": {
84
- "@eslint/js": "^9.25.0",
85
- "@types/better-sqlite3": "^7.6.13",
86
- "@types/cors": "^2.8.19",
87
- "@types/express": "^4.17.21",
88
- "@types/fs-extra": "^11.0.4",
89
- "@types/node": "^22.15.30",
90
- "@types/qrcode-terminal": "^0.12.2",
91
- "@types/react": "^18.3.12",
92
- "@types/react-dom": "^18.3.1",
93
- "@types/socket.io": "^3.0.1",
94
- "@types/tar": "^6.1.13",
95
- "@types/uuid": "^10.0.0",
96
- "@vercel/node": "^5.3.6",
97
- "@vitejs/plugin-react": "^4.4.1",
98
- "autoprefixer": "^10.4.21",
99
- "babel-plugin-react-dev-locator": "^1.0.0",
100
- "concurrently": "^9.2.1",
101
- "eslint": "^9.25.0",
102
- "eslint-plugin-react-hooks": "^5.2.0",
103
- "eslint-plugin-react-refresh": "^0.4.19",
104
- "globals": "^16.0.0",
105
- "nodemon": "^3.1.10",
106
- "postcss": "^8.5.3",
107
- "tailwindcss": "^3.4.17",
108
- "tsx": "^4.21.0",
109
- "typescript": "~5.8.3",
110
- "typescript-eslint": "^8.30.1",
111
- "vite": "^6.3.5",
112
- "vite-plugin-trae-solo-badge": "^1.0.0",
113
- "vite-tsconfig-paths": "^5.1.4",
114
- "wait-on": "^9.0.4"
115
- }
116
- }
1
+ {
2
+ "name": "xiaozuoassistant",
3
+ "version": "0.2.21",
4
+ "description": "A local-first personal AI assistant with multi-channel support and enhanced memory.",
5
+ "author": "mantle.lau",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/mantlelau-lgtm/claw.git"
10
+ },
11
+ "keywords": [
12
+ "ai",
13
+ "assistant",
14
+ "office",
15
+ "automation",
16
+ "local-first",
17
+ "agent"
18
+ ],
19
+ "type": "module",
20
+ "main": "dist/server/index.js",
21
+ "bin": {
22
+ "xiaozuoAssistant": "bin/cli.js",
23
+ "mybot": "bin/cli.js"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "bin",
28
+ "scripts",
29
+ "public",
30
+ "config.json",
31
+ "README.md"
32
+ ],
33
+ "scripts": {
34
+ "client:dev": "vite",
35
+ "build": "rm -rf dist && tsc -b && vite build && tsc -p tsconfig.server.json",
36
+ "lint": "eslint .",
37
+ "preview": "vite preview",
38
+ "check": "tsc --noEmit",
39
+ "server:dev": "nodemon --watch src --watch config.json --exec tsx src/index.ts",
40
+ "dev": "concurrently \"npm run client:dev\" \"npm run server:dev\"",
41
+ "postinstall": "node scripts/init-app-home.cjs || true"
42
+ },
43
+ "dependencies": {
44
+ "@lancedb/lancedb": "0.22.3",
45
+ "@larksuiteoapi/node-sdk": "^1.59.0",
46
+ "@types/node-cron": "^3.0.11",
47
+ "apache-arrow": "18.1.0",
48
+ "axios": "^1.13.6",
49
+ "better-sqlite3": "^12.6.2",
50
+ "bindings": "^1.5.0",
51
+ "clsx": "^2.1.1",
52
+ "commander": "^14.0.3",
53
+ "cors": "^2.8.5",
54
+ "dotenv": "^17.3.1",
55
+ "express": "^4.21.2",
56
+ "fs-extra": "^11.3.4",
57
+ "i18next": "^25.8.17",
58
+ "i18next-browser-languagedetector": "^8.2.1",
59
+ "i18next-http-backend": "^3.0.2",
60
+ "lucide-react": "^0.511.0",
61
+ "mammoth": "^1.11.0",
62
+ "node-cron": "^4.2.1",
63
+ "officegen": "^0.6.5",
64
+ "openai": "^6.27.0",
65
+ "pptxgenjs": "^4.0.1",
66
+ "qrcode-terminal": "^0.12.0",
67
+ "react": "^18.3.1",
68
+ "react-dom": "^18.3.1",
69
+ "react-i18next": "^16.5.6",
70
+ "react-router-dom": "^7.3.0",
71
+ "socket.io": "^4.8.3",
72
+ "socket.io-client": "^4.8.3",
73
+ "tailwind-merge": "^3.0.2",
74
+ "tar": "^7.5.11",
75
+ "telegraf": "^4.16.3",
76
+ "uuid": "^13.0.0",
77
+ "wechaty": "^1.20.2",
78
+ "winston": "^3.19.0",
79
+ "winston-daily-rotate-file": "^5.0.0",
80
+ "xlsx": "^0.18.5",
81
+ "zustand": "^5.0.3"
82
+ },
83
+ "devDependencies": {
84
+ "@eslint/js": "^9.25.0",
85
+ "@types/better-sqlite3": "^7.6.13",
86
+ "@types/cors": "^2.8.19",
87
+ "@types/express": "^4.17.21",
88
+ "@types/fs-extra": "^11.0.4",
89
+ "@types/node": "^22.15.30",
90
+ "@types/qrcode-terminal": "^0.12.2",
91
+ "@types/react": "^18.3.12",
92
+ "@types/react-dom": "^18.3.1",
93
+ "@types/socket.io": "^3.0.1",
94
+ "@types/tar": "^6.1.13",
95
+ "@types/uuid": "^10.0.0",
96
+ "@vercel/node": "^5.3.6",
97
+ "@vitejs/plugin-react": "^4.4.1",
98
+ "autoprefixer": "^10.4.21",
99
+ "babel-plugin-react-dev-locator": "^1.0.0",
100
+ "concurrently": "^9.2.1",
101
+ "eslint": "^9.25.0",
102
+ "eslint-plugin-react-hooks": "^5.2.0",
103
+ "eslint-plugin-react-refresh": "^0.4.19",
104
+ "globals": "^16.0.0",
105
+ "nodemon": "^3.1.10",
106
+ "postcss": "^8.5.3",
107
+ "tailwindcss": "^3.4.17",
108
+ "tsx": "^4.21.0",
109
+ "typescript": "~5.8.3",
110
+ "typescript-eslint": "^8.30.1",
111
+ "vite": "^6.3.5",
112
+ "vite-plugin-trae-solo-badge": "^1.0.0",
113
+ "vite-tsconfig-paths": "^5.1.4",
114
+ "wait-on": "^9.0.4"
115
+ }
116
+ }
@@ -1,4 +1,4 @@
1
- <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <rect width="32" height="32" fill="#0A0B0D"/>
3
- <path d="M26.6677 23.7149H8.38057V20.6496H5.33301V8.38159H26.6677V23.7149ZM8.38057 20.6496H23.6201V11.4482H8.38057V20.6496ZM16.0011 16.0021L13.8461 18.1705L11.6913 16.0021L13.8461 13.8337L16.0011 16.0021ZM22.0963 16.0008L19.9414 18.1691L17.7865 16.0008L19.9414 13.8324L22.0963 16.0008Z" fill="#32F08C"/>
4
- </svg>
1
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="32" height="32" fill="#0A0B0D"/>
3
+ <path d="M26.6677 23.7149H8.38057V20.6496H5.33301V8.38159H26.6677V23.7149ZM8.38057 20.6496H23.6201V11.4482H8.38057V20.6496ZM16.0011 16.0021L13.8461 18.1705L11.6913 16.0021L13.8461 13.8337L16.0011 16.0021ZM22.0963 16.0008L19.9414 18.1691L17.7865 16.0008L19.9414 13.8324L22.0963 16.0008Z" fill="#32F08C"/>
4
+ </svg>