zerg-ztc 0.1.11 → 0.1.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.
Files changed (122) hide show
  1. package/bin/ztc-audio-darwin-arm64 +0 -0
  2. package/dist/utils/dictation_native.d.ts.map +1 -1
  3. package/dist/utils/dictation_native.js +43 -23
  4. package/dist/utils/dictation_native.js.map +1 -1
  5. package/package.json +5 -4
  6. package/packages/ztc-dictation/Cargo.toml +0 -43
  7. package/packages/ztc-dictation/README.md +0 -65
  8. package/packages/ztc-dictation/index.d.ts +0 -16
  9. package/packages/ztc-dictation/index.js +0 -74
  10. package/packages/ztc-dictation/package.json +0 -41
  11. package/packages/ztc-dictation/src/main.rs +0 -430
  12. package/src/App.tsx +0 -910
  13. package/src/agent/agent.ts +0 -534
  14. package/src/agent/backends/anthropic.ts +0 -86
  15. package/src/agent/backends/gemini.ts +0 -119
  16. package/src/agent/backends/inception.ts +0 -23
  17. package/src/agent/backends/index.ts +0 -17
  18. package/src/agent/backends/openai.ts +0 -23
  19. package/src/agent/backends/openai_compatible.ts +0 -143
  20. package/src/agent/backends/types.ts +0 -83
  21. package/src/agent/commands/clipboard.ts +0 -77
  22. package/src/agent/commands/config.ts +0 -204
  23. package/src/agent/commands/debug.ts +0 -23
  24. package/src/agent/commands/dictation.ts +0 -11
  25. package/src/agent/commands/emulation.ts +0 -80
  26. package/src/agent/commands/execution.ts +0 -9
  27. package/src/agent/commands/help.ts +0 -20
  28. package/src/agent/commands/history.ts +0 -13
  29. package/src/agent/commands/index.ts +0 -48
  30. package/src/agent/commands/input_mode.ts +0 -22
  31. package/src/agent/commands/keybindings.ts +0 -40
  32. package/src/agent/commands/model.ts +0 -11
  33. package/src/agent/commands/models.ts +0 -116
  34. package/src/agent/commands/permissions.ts +0 -64
  35. package/src/agent/commands/retry.ts +0 -9
  36. package/src/agent/commands/shell.ts +0 -68
  37. package/src/agent/commands/skills.ts +0 -54
  38. package/src/agent/commands/status.ts +0 -19
  39. package/src/agent/commands/types.ts +0 -88
  40. package/src/agent/commands/update.ts +0 -32
  41. package/src/agent/factory.ts +0 -60
  42. package/src/agent/index.ts +0 -20
  43. package/src/agent/runtime/capabilities.ts +0 -7
  44. package/src/agent/runtime/memory.ts +0 -23
  45. package/src/agent/runtime/policy.ts +0 -48
  46. package/src/agent/runtime/session.ts +0 -18
  47. package/src/agent/runtime/tracing.ts +0 -23
  48. package/src/agent/tools/file.ts +0 -178
  49. package/src/agent/tools/index.ts +0 -52
  50. package/src/agent/tools/screenshot.ts +0 -821
  51. package/src/agent/tools/search.ts +0 -138
  52. package/src/agent/tools/shell.ts +0 -69
  53. package/src/agent/tools/skills.ts +0 -28
  54. package/src/agent/tools/types.ts +0 -14
  55. package/src/agent/tools/zerg.ts +0 -50
  56. package/src/cli.tsx +0 -163
  57. package/src/components/ActivityLine.tsx +0 -23
  58. package/src/components/FullScreen.tsx +0 -79
  59. package/src/components/Header.tsx +0 -27
  60. package/src/components/InputArea.tsx +0 -1660
  61. package/src/components/MessageList.tsx +0 -71
  62. package/src/components/SingleMessage.tsx +0 -298
  63. package/src/components/StatusBar.tsx +0 -55
  64. package/src/components/index.tsx +0 -8
  65. package/src/config/types.ts +0 -19
  66. package/src/config.ts +0 -186
  67. package/src/debug/logger.ts +0 -14
  68. package/src/emulation/README.md +0 -24
  69. package/src/emulation/catalog.ts +0 -82
  70. package/src/emulation/trace_style.ts +0 -8
  71. package/src/emulation/types.ts +0 -7
  72. package/src/skills/index.ts +0 -36
  73. package/src/skills/loader.ts +0 -135
  74. package/src/skills/registry.ts +0 -6
  75. package/src/skills/types.ts +0 -10
  76. package/src/types.ts +0 -84
  77. package/src/ui/README.md +0 -44
  78. package/src/ui/core/factory.ts +0 -9
  79. package/src/ui/core/index.ts +0 -4
  80. package/src/ui/core/input.ts +0 -38
  81. package/src/ui/core/input_segments.ts +0 -410
  82. package/src/ui/core/input_state.ts +0 -17
  83. package/src/ui/core/layout_yoga.ts +0 -122
  84. package/src/ui/core/style.ts +0 -38
  85. package/src/ui/core/types.ts +0 -54
  86. package/src/ui/ink/index.tsx +0 -1
  87. package/src/ui/ink/render.tsx +0 -60
  88. package/src/ui/views/activity_line.ts +0 -33
  89. package/src/ui/views/app.ts +0 -111
  90. package/src/ui/views/header.ts +0 -44
  91. package/src/ui/views/input_area.ts +0 -255
  92. package/src/ui/views/message_list.ts +0 -443
  93. package/src/ui/views/status_bar.ts +0 -114
  94. package/src/ui/vue/index.ts +0 -53
  95. package/src/ui/web/frame_render.tsx +0 -148
  96. package/src/ui/web/index.tsx +0 -1
  97. package/src/ui/web/render.tsx +0 -41
  98. package/src/utils/clipboard.ts +0 -39
  99. package/src/utils/clipboard_image.ts +0 -40
  100. package/src/utils/dictation.ts +0 -467
  101. package/src/utils/dictation_native.ts +0 -258
  102. package/src/utils/diff.ts +0 -52
  103. package/src/utils/image_preview.ts +0 -36
  104. package/src/utils/models.ts +0 -98
  105. package/src/utils/path_complete.ts +0 -173
  106. package/src/utils/path_format.ts +0 -99
  107. package/src/utils/shell.ts +0 -72
  108. package/src/utils/spinner_frames.ts +0 -1
  109. package/src/utils/spinner_verbs.ts +0 -23
  110. package/src/utils/table.ts +0 -171
  111. package/src/utils/tool_summary.ts +0 -56
  112. package/src/utils/tool_trace.ts +0 -346
  113. package/src/utils/update.ts +0 -44
  114. package/src/utils/version.ts +0 -15
  115. package/src/web/index.html +0 -352
  116. package/src/web/mirror-favicon.svg +0 -4
  117. package/src/web/mirror.html +0 -641
  118. package/src/web/mirror_hook.ts +0 -25
  119. package/src/web/mirror_server.ts +0 -204
  120. package/tsconfig.json +0 -22
  121. package/vite.config.ts +0 -363
  122. /package/{packages/ztc-dictation/bin → bin}/.gitkeep +0 -0
@@ -1,204 +0,0 @@
1
- import { createServer, IncomingMessage, ServerResponse } from 'http';
2
- import { readFile, mkdir, writeFile } from 'fs/promises';
3
- import { resolve, join, basename } from 'path';
4
- import { homedir } from 'os';
5
- import { randomUUID } from 'crypto';
6
- import { InputBus, InputEvent } from '../ui/core/input.js';
7
- import { LayoutNode, computeLayout, LayoutFrame } from '../ui/core/index.js';
8
-
9
- interface Client {
10
- id: string;
11
- res: ServerResponse;
12
- cols: number;
13
- rows: number;
14
- }
15
-
16
- export class MirrorServer {
17
- private clients = new Map<string, Client>();
18
- private port: number;
19
- private server = createServer(this.handleRequest.bind(this));
20
- private lastTree: LayoutNode | null = null;
21
- private inputBus?: InputBus;
22
-
23
- constructor(port: number, inputBus?: InputBus) {
24
- this.port = port;
25
- this.inputBus = inputBus;
26
- }
27
-
28
- start(): void {
29
- this.server.listen(this.port);
30
- }
31
-
32
- stop(): void {
33
- this.server.close();
34
- }
35
-
36
- publish(tree: LayoutNode): void {
37
- this.lastTree = tree;
38
- for (const client of this.clients.values()) {
39
- this.sendLayout(client, tree);
40
- }
41
- }
42
-
43
- private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
44
- const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
45
-
46
- if (url.pathname === '/') {
47
- const htmlPath = new URL('./mirror.html', import.meta.url);
48
- const html = await readFile(htmlPath, 'utf-8').catch(async () => {
49
- const fallback = resolve(process.cwd(), 'src/web/mirror.html');
50
- return readFile(fallback, 'utf-8');
51
- });
52
- res.writeHead(200, { 'Content-Type': 'text/html' });
53
- res.end(html);
54
- return;
55
- }
56
-
57
- if (url.pathname === '/mirror-favicon.svg' || url.pathname === '/favicon.svg' || url.pathname === '/favicon.ico') {
58
- const iconPath = new URL('./mirror-favicon.svg', import.meta.url);
59
- const icon = await readFile(iconPath, 'utf-8').catch(async () => {
60
- const fallback = resolve(process.cwd(), 'src/web/mirror-favicon.svg');
61
- return readFile(fallback, 'utf-8');
62
- });
63
- const contentType = url.pathname === '/favicon.ico' ? 'image/x-icon' : 'image/svg+xml';
64
- res.writeHead(200, { 'Content-Type': contentType, 'Cache-Control': 'no-cache' });
65
- res.end(icon);
66
- return;
67
- }
68
-
69
- if (url.pathname === '/events') {
70
- const id = url.searchParams.get('id') || `client_${Date.now()}`;
71
- res.writeHead(200, {
72
- 'Content-Type': 'text/event-stream',
73
- 'Cache-Control': 'no-cache',
74
- Connection: 'keep-alive'
75
- });
76
- res.write('\n');
77
-
78
- const client: Client = {
79
- id,
80
- res,
81
- cols: 80,
82
- rows: 24
83
- };
84
- this.clients.set(id, client);
85
-
86
- if (this.lastTree) {
87
- this.sendLayout(client, this.lastTree);
88
- }
89
-
90
- req.on('close', () => {
91
- this.clients.delete(id);
92
- });
93
- return;
94
- }
95
-
96
- if (url.pathname === '/size' && req.method === 'POST') {
97
- const id = url.searchParams.get('id');
98
- if (!id || !this.clients.has(id)) {
99
- res.writeHead(400);
100
- res.end();
101
- return;
102
- }
103
-
104
- const body = await new Promise<string>(resolve => {
105
- let data = '';
106
- req.on('data', chunk => { data += chunk; });
107
- req.on('end', () => resolve(data));
108
- });
109
-
110
- try {
111
- const parsed = JSON.parse(body) as { cols: number; rows: number };
112
- const client = this.clients.get(id);
113
- if (client) {
114
- client.cols = parsed.cols;
115
- client.rows = parsed.rows;
116
- if (this.lastTree) {
117
- this.sendLayout(client, this.lastTree);
118
- }
119
- }
120
- } catch {
121
- // Ignore invalid payloads
122
- }
123
-
124
- res.writeHead(204);
125
- res.end();
126
- return;
127
- }
128
-
129
- if (url.pathname === '/input' && req.method === 'POST') {
130
- const body = await new Promise<string>(resolve => {
131
- let data = '';
132
- req.on('data', chunk => { data += chunk; });
133
- req.on('end', () => resolve(data));
134
- });
135
-
136
- try {
137
- const parsed = JSON.parse(body) as InputEvent;
138
- this.inputBus?.emit(parsed);
139
- } catch {
140
- // Ignore invalid payloads
141
- }
142
-
143
- res.writeHead(204);
144
- res.end();
145
- return;
146
- }
147
-
148
- if (url.pathname === '/upload' && req.method === 'POST') {
149
- const buffer = await new Promise<Buffer>(resolve => {
150
- const chunks: Buffer[] = [];
151
- req.on('data', chunk => { chunks.push(Buffer.from(chunk)); });
152
- req.on('end', () => resolve(Buffer.concat(chunks)));
153
- });
154
- const headerName = req.headers['x-filename'];
155
- const rawName = typeof headerName === 'string' && headerName.trim().length > 0 ? headerName : 'upload.bin';
156
- const safeName = basename(rawName).replace(/[^a-zA-Z0-9._-]/g, '_');
157
- const dir = join(homedir(), '.ztc', 'uploads');
158
- await mkdir(dir, { recursive: true });
159
- const filePath = join(dir, `${Date.now()}-${randomUUID().slice(0, 8)}-${safeName}`);
160
- await writeFile(filePath, buffer);
161
- res.writeHead(200, { 'Content-Type': 'application/json' });
162
- res.end(JSON.stringify({ path: filePath }));
163
- return;
164
- }
165
-
166
- if (url.pathname === '/file' && req.method === 'GET') {
167
- const rawPath = url.searchParams.get('path');
168
- if (!rawPath) {
169
- res.writeHead(400);
170
- res.end();
171
- return;
172
- }
173
- const allowedRoots = [
174
- join(homedir(), '.ztc', 'uploads'),
175
- join(homedir(), '.ztc', 'clipboard')
176
- ];
177
- const resolvedPath = resolve(rawPath);
178
- const allowed = allowedRoots.some(root => resolvedPath.startsWith(resolve(root)));
179
- if (!allowed) {
180
- res.writeHead(403);
181
- res.end();
182
- return;
183
- }
184
- try {
185
- const data = await readFile(resolvedPath);
186
- res.writeHead(200, { 'Content-Type': 'application/octet-stream' });
187
- res.end(data);
188
- } catch {
189
- res.writeHead(404);
190
- res.end();
191
- }
192
- return;
193
- }
194
-
195
- res.writeHead(404);
196
- res.end();
197
- }
198
-
199
- private sendLayout(client: Client, tree: LayoutNode): void {
200
- const frame = computeLayout(tree, client.cols, client.rows);
201
- const payload = JSON.stringify(frame);
202
- client.res.write(`data: ${payload}\n\n`);
203
- }
204
- }
package/tsconfig.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "lib": ["ES2022"],
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "resolveJsonModule": true,
14
- "declaration": true,
15
- "declarationMap": true,
16
- "sourceMap": true,
17
- "jsx": "react-jsx",
18
- "jsxImportSource": "react"
19
- },
20
- "include": ["src/**/*"],
21
- "exclude": ["node_modules", "dist"]
22
- }
package/vite.config.ts DELETED
@@ -1,363 +0,0 @@
1
- import { defineConfig } from 'vite';
2
- import react from '@vitejs/plugin-react';
3
- import { resolve, join, basename } from 'path';
4
- import { mkdir, writeFile } from 'fs/promises';
5
- import { homedir } from 'os';
6
- import { randomUUID } from 'crypto';
7
- import { loadEmulationProfiles } from './src/emulation/catalog.js';
8
- import { configStore } from './src/config.js';
9
- import { runShellCommand, resolveWorkingDir } from './src/utils/shell.js';
10
- import { createAgentFromConfig } from './src/agent/factory.js';
11
- import { listModels } from './src/utils/models.js';
12
- import { autoActivateSkills, buildSkillPrompt } from './src/skills/index.js';
13
- import { getSkillRegistry } from './src/skills/registry.js';
14
-
15
- export default defineConfig({
16
- root: 'web/dev',
17
- plugins: [
18
- react(),
19
- {
20
- name: 'ztc-emulation',
21
- configureServer(server) {
22
- let skillCache: any[] | null = null;
23
- const loadSkills = async () => {
24
- if (skillCache) return skillCache;
25
- skillCache = await getSkillRegistry();
26
- return skillCache;
27
- };
28
- server.middlewares.use('/__emulation/list', (_req, res) => {
29
- const payload = JSON.stringify(loadEmulationProfiles().map(profile => ({
30
- id: profile.id,
31
- name: profile.name,
32
- description: profile.description,
33
- systemPrompt: profile.systemPrompt,
34
- tools: profile.tools
35
- })));
36
- res.statusCode = 200;
37
- res.setHeader('Content-Type', 'application/json');
38
- res.end(payload);
39
- });
40
- server.middlewares.use('/__config/get', async (_req, res) => {
41
- await configStore.load(true);
42
- const payload = JSON.stringify({
43
- config: configStore.get(),
44
- locationLabel: configStore.getConfigPath()
45
- });
46
- res.statusCode = 200;
47
- res.setHeader('Content-Type', 'application/json');
48
- res.end(payload);
49
- });
50
- server.middlewares.use('/__config/save', async (req, res) => {
51
- const body = await new Promise<string>((resolve) => {
52
- let data = '';
53
- req.on('data', chunk => { data += chunk; });
54
- req.on('end', () => resolve(data));
55
- });
56
- try {
57
- const parsed = JSON.parse(body) as { config?: Record<string, unknown> };
58
- if (parsed.config && typeof parsed.config === 'object') {
59
- configStore.setAll(parsed.config);
60
- await configStore.save();
61
- }
62
- } catch {
63
- // ignore invalid payloads
64
- }
65
- const payload = JSON.stringify({
66
- config: configStore.get(),
67
- locationLabel: configStore.getConfigPath()
68
- });
69
- res.statusCode = 200;
70
- res.setHeader('Content-Type', 'application/json');
71
- res.end(payload);
72
- });
73
- server.middlewares.use('/__shell/cwd', (_req, res) => {
74
- const payload = JSON.stringify({ cwd: process.cwd() });
75
- res.statusCode = 200;
76
- res.setHeader('Content-Type', 'application/json');
77
- res.end(payload);
78
- });
79
- server.middlewares.use('/__shell/resolve', async (req, res) => {
80
- const body = await new Promise<string>((resolve) => {
81
- let data = '';
82
- req.on('data', chunk => { data += chunk; });
83
- req.on('end', () => resolve(data));
84
- });
85
- try {
86
- const parsed = JSON.parse(body) as { cwd?: string; path?: string };
87
- const base = parsed.cwd || process.cwd();
88
- const next = await resolveWorkingDir(base, parsed.path || '');
89
- const payload = JSON.stringify({ cwd: next });
90
- res.statusCode = 200;
91
- res.setHeader('Content-Type', 'application/json');
92
- res.end(payload);
93
- return;
94
- } catch (err) {
95
- res.statusCode = 400;
96
- res.setHeader('Content-Type', 'application/json');
97
- res.end(JSON.stringify({ error: 'Invalid path' }));
98
- return;
99
- }
100
- });
101
- server.middlewares.use('/__shell/run', async (req, res) => {
102
- const body = await new Promise<string>((resolve) => {
103
- let data = '';
104
- req.on('data', chunk => { data += chunk; });
105
- req.on('end', () => resolve(data));
106
- });
107
- try {
108
- const parsed = JSON.parse(body) as { command?: string; cwd?: string };
109
- const command = parsed.command || '';
110
- const cwd = parsed.cwd || process.cwd();
111
- const result = await runShellCommand(command, cwd);
112
- const payload = JSON.stringify(result);
113
- res.statusCode = 200;
114
- res.setHeader('Content-Type', 'application/json');
115
- res.end(payload);
116
- return;
117
- } catch (err) {
118
- res.statusCode = 500;
119
- res.setHeader('Content-Type', 'application/json');
120
- res.end(JSON.stringify({ error: 'Command failed' }));
121
- return;
122
- }
123
- });
124
- server.middlewares.use('/__upload', async (req, res) => {
125
- if (req.method !== 'POST') {
126
- res.statusCode = 405;
127
- res.end();
128
- return;
129
- }
130
- const buffer = await new Promise<Buffer>((resolve) => {
131
- const chunks: Buffer[] = [];
132
- req.on('data', chunk => { chunks.push(Buffer.from(chunk)); });
133
- req.on('end', () => resolve(Buffer.concat(chunks)));
134
- });
135
- const headerName = req.headers['x-filename'];
136
- const rawName = typeof headerName === 'string' && headerName.trim().length > 0 ? headerName : 'upload.bin';
137
- const safeName = basename(rawName).replace(/[^a-zA-Z0-9._-]/g, '_');
138
- const dir = join(homedir(), '.ztc', 'uploads');
139
- await mkdir(dir, { recursive: true });
140
- const filePath = join(dir, `${Date.now()}-${randomUUID().slice(0, 8)}-${safeName}`);
141
- await writeFile(filePath, buffer);
142
- res.statusCode = 200;
143
- res.setHeader('Content-Type', 'application/json');
144
- res.end(JSON.stringify({ path: filePath }));
145
- });
146
- server.middlewares.use('/__file', async (req, res) => {
147
- const url = new URL(req.url || '', 'http://localhost');
148
- const rawPath = url.searchParams.get('path');
149
- if (!rawPath) {
150
- res.statusCode = 400;
151
- res.end();
152
- return;
153
- }
154
- const allowedRoots = [
155
- join(homedir(), '.ztc', 'uploads'),
156
- join(homedir(), '.ztc', 'clipboard')
157
- ];
158
- const resolvedPath = resolve(rawPath);
159
- const allowed = allowedRoots.some(root => resolvedPath.startsWith(resolve(root)));
160
- if (!allowed) {
161
- res.statusCode = 403;
162
- res.end();
163
- return;
164
- }
165
- try {
166
- const data = await (await import('fs/promises')).readFile(resolvedPath);
167
- res.statusCode = 200;
168
- res.setHeader('Content-Type', 'application/octet-stream');
169
- res.end(data);
170
- } catch {
171
- res.statusCode = 404;
172
- res.end();
173
- }
174
- });
175
- server.middlewares.use('/__agent/run', async (req, res) => {
176
- const body = await new Promise<string>((resolve) => {
177
- let data = '';
178
- req.on('data', chunk => { data += chunk; });
179
- req.on('end', () => resolve(data));
180
- });
181
- try {
182
- const parsed = JSON.parse(body) as { messages?: unknown[] };
183
- const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
184
- const lastUser = [...messages].reverse().find(msg => msg && typeof msg === 'object' && (msg as any).role === 'user');
185
- const text = lastUser && typeof (lastUser as any).content === 'string' ? (lastUser as any).content : '';
186
- const skills = await loadSkills();
187
- const activated = autoActivateSkills(text, skills);
188
- const skillPrompt = buildSkillPrompt(activated);
189
- await configStore.load();
190
- const provider = configStore.getProvider();
191
- const apiKey = configStore.getApiKey(provider);
192
- const agent = createAgentFromConfig({
193
- config: configStore.get(),
194
- provider,
195
- apiKey,
196
- openaiCompatibleBaseUrl: configStore.getOpenAICompatibleBaseUrl(),
197
- emulationId: configStore.getEmulationId(),
198
- skillPrompt
199
- });
200
- if (!agent) {
201
- res.statusCode = 400;
202
- res.setHeader('Content-Type', 'application/json');
203
- res.end(JSON.stringify({ error: 'No API key configured' }));
204
- return;
205
- }
206
- const result = await agent.run(messages as any);
207
- res.statusCode = 200;
208
- res.setHeader('Content-Type', 'application/json');
209
- res.end(JSON.stringify({
210
- content: result.content,
211
- toolCalls: result.toolCalls,
212
- usage: result.usage
213
- }));
214
- return;
215
- } catch (err) {
216
- const message = err instanceof Error ? err.message : 'Agent error';
217
- res.statusCode = 500;
218
- res.setHeader('Content-Type', 'application/json');
219
- res.end(JSON.stringify({ error: message }));
220
- }
221
- });
222
- server.middlewares.use('/__agent/stream', async (req, res) => {
223
- const body = await new Promise<string>((resolve) => {
224
- let data = '';
225
- req.on('data', chunk => { data += chunk; });
226
- req.on('end', () => resolve(data));
227
- });
228
- res.writeHead(200, {
229
- 'Content-Type': 'text/event-stream',
230
- 'Cache-Control': 'no-cache',
231
- Connection: 'keep-alive'
232
- });
233
- const send = (event: string, payload: unknown) => {
234
- res.write(`event: ${event}\n`);
235
- res.write(`data: ${JSON.stringify(payload)}\n\n`);
236
- };
237
- let cleanup: (() => void) | null = null;
238
- try {
239
- const parsed = JSON.parse(body) as { messages?: unknown[] };
240
- const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
241
- const lastUser = [...messages].reverse().find(msg => msg && typeof msg === 'object' && (msg as any).role === 'user');
242
- const text = lastUser && typeof (lastUser as any).content === 'string' ? (lastUser as any).content : '';
243
- const skills = await loadSkills();
244
- const activated = autoActivateSkills(text, skills);
245
- const skillPrompt = buildSkillPrompt(activated);
246
- if (activated.length > 0) {
247
- send('skill_activated', { skills: activated.map(skill => ({ id: skill.id, name: skill.name })) });
248
- }
249
- await configStore.load();
250
- const provider = configStore.getProvider();
251
- const apiKey = configStore.getApiKey(provider);
252
- const agent = createAgentFromConfig({
253
- config: configStore.get(),
254
- provider,
255
- apiKey,
256
- openaiCompatibleBaseUrl: configStore.getOpenAICompatibleBaseUrl(),
257
- emulationId: configStore.getEmulationId(),
258
- skillPrompt
259
- });
260
- if (!agent) {
261
- send('error', { error: 'No API key configured' });
262
- res.end();
263
- return;
264
- }
265
- cleanup = agent.on((event) => {
266
- if (event.type === 'stream_start') {
267
- send('stream_start', {});
268
- } else if (event.type === 'stream_delta') {
269
- send('stream_delta', { content: event.content });
270
- } else if (event.type === 'stream_end') {
271
- send('stream_end', {});
272
- } else if (event.type === 'token_usage') {
273
- send('token_usage', { usage: event.usage });
274
- } else if (event.type === 'tool_start') {
275
- send('tool_start', { tool: event.tool, args: event.args });
276
- } else if (event.type === 'tool_end') {
277
- send('tool_end', { tool: event.tool, result: event.result });
278
- } else if (event.type === 'tool_error') {
279
- send('tool_error', { tool: event.tool, error: event.error });
280
- }
281
- });
282
- const result = await agent.run(messages as any);
283
- send('result', {
284
- content: result.content,
285
- toolCalls: result.toolCalls,
286
- usage: result.usage
287
- });
288
- res.end();
289
- } catch (err) {
290
- const message = err instanceof Error ? err.message : 'Agent error';
291
- send('error', { error: message });
292
- res.end();
293
- } finally {
294
- cleanup?.();
295
- }
296
- });
297
- server.middlewares.use('/__models/list', async (req, res) => {
298
- try {
299
- await configStore.load();
300
- const url = new URL(req.url || '', 'http://localhost');
301
- const provider = url.searchParams.get('provider') || configStore.getProvider();
302
- const models = await listModels({
303
- provider,
304
- apiKey: configStore.getApiKey(provider),
305
- baseUrl: configStore.getOpenAICompatibleBaseUrl()
306
- });
307
- res.statusCode = 200;
308
- res.setHeader('Content-Type', 'application/json');
309
- res.end(JSON.stringify({ models }));
310
- } catch (err) {
311
- const message = err instanceof Error ? err.message : 'Models error';
312
- res.statusCode = 500;
313
- res.setHeader('Content-Type', 'application/json');
314
- res.end(JSON.stringify({ error: message }));
315
- }
316
- });
317
- server.middlewares.use('/__skills/list', async (_req, res) => {
318
- try {
319
- const skills = await loadSkills();
320
- res.statusCode = 200;
321
- res.setHeader('Content-Type', 'application/json');
322
- res.end(JSON.stringify({ skills }));
323
- } catch (err) {
324
- const message = err instanceof Error ? err.message : 'Skills error';
325
- res.statusCode = 500;
326
- res.setHeader('Content-Type', 'application/json');
327
- res.end(JSON.stringify({ error: message }));
328
- }
329
- });
330
- }
331
- }
332
- ],
333
- esbuild: {
334
- target: 'esnext'
335
- },
336
- resolve: {
337
- alias: [
338
- { find: '@src', replacement: resolve(__dirname, 'src') }
339
- ]
340
- },
341
- build: {
342
- target: 'esnext',
343
- outDir: '../../dist-web',
344
- emptyOutDir: true
345
- },
346
- optimizeDeps: {
347
- esbuildOptions: {
348
- target: 'esnext'
349
- }
350
- },
351
- ssr: {
352
- target: 'esnext'
353
- },
354
- define: {
355
- 'import.meta.env.LEGACY': 'false'
356
- },
357
- server: {
358
- port: 5175,
359
- fs: {
360
- allow: ['.']
361
- }
362
- }
363
- });
File without changes