@agent-spaces/server 0.3.0 → 0.3.2
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/dist/adapters/claude-code-runtime/index.js +48 -6
- package/dist/adapters/claude-code-runtime/message-format.js +3 -3
- package/dist/adapters/git.js +140 -2
- package/dist/agents/agent-designer.js +334 -0
- package/dist/agents/agent-message-parts.js +8 -0
- package/dist/agents/commit-agent.js +9 -2
- package/dist/agents/issue-agent-runner.js +0 -1
- package/dist/agents/issue-task-controller.js +14 -4
- package/dist/app.js +51 -1
- package/dist/package.json +1 -1
- package/dist/routes/agent-sse.js +4 -1
- package/dist/routes/agent.js +15 -0
- package/dist/routes/channel.js +1 -1
- package/dist/routes/file.js +97 -1
- package/dist/routes/git.js +122 -1
- package/dist/routes/iframe-bookmarks.js +33 -0
- package/dist/routes/issue.js +12 -0
- package/dist/routes/speech-recognition.js +99 -0
- package/dist/services/agent.js +48 -5
- package/dist/services/builtin-tools.js +67 -1
- package/dist/services/channel.js +2 -0
- package/dist/services/command-process-manager.js +14 -1
- package/dist/services/file.js +86 -6
- package/dist/services/gitignore.js +65 -0
- package/dist/services/issue-retry.js +2 -1
- package/dist/services/issue.js +13 -4
- package/dist/services/notification-hub/bot-agent.js +9 -1
- package/dist/services/persistent-agent-context.js +155 -0
- package/dist/services/pty.js +47 -2
- package/dist/services/search.js +11 -3
- package/dist/services/speech-recognition/base.js +3 -0
- package/dist/services/speech-recognition/index.js +16 -0
- package/dist/services/speech-recognition/tencent.js +117 -0
- package/dist/storage/iframe-bookmarks-store.js +46 -0
- package/dist/storage/speech-recognition-store.js +61 -0
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +4 -4
- package/dist/web/__next._full.txt +31 -26
- package/dist/web/__next._head.txt +4 -4
- package/dist/web/__next._index.txt +17 -13
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/chunks/00amm3dvu3po9.js +31 -0
- package/dist/web/_next/static/chunks/01hrib8z5bn-6.js +5 -0
- package/dist/web/_next/static/chunks/08nbjuc5i-ewu.js +3 -0
- package/dist/web/_next/static/chunks/0anuxr11yn5iv.js +2 -0
- package/dist/web/_next/static/chunks/0cxrev-hb8gkk.js +2 -0
- package/dist/web/_next/static/chunks/0egot8y~i-ge3.js +1 -0
- package/dist/web/_next/static/chunks/0e~0o6v_awcod.js +1 -0
- package/dist/web/_next/static/chunks/0f_dcx0nu5z7o.js +1 -0
- package/dist/web/_next/static/chunks/0fpf.rso9mhc0.js +1 -0
- package/dist/web/_next/static/chunks/0jc-4igj54z00.js +189 -0
- package/dist/web/_next/static/chunks/0plreh4_gkxf9.js +1 -0
- package/dist/web/_next/static/chunks/0u7_qd9hzqwac.js +1 -0
- package/dist/web/_next/static/chunks/0uo5x.bu.5u2-.css +1 -0
- package/dist/web/_next/static/chunks/0utxlfbckz4f5.js +1 -0
- package/dist/web/_next/static/chunks/0vesz93a98px4.js +1 -0
- package/dist/web/_next/static/chunks/0w1iudd1d46sw.js +8 -0
- package/dist/web/_next/static/chunks/0w3o.7vi.dlm6.js +1 -0
- package/dist/web/_next/static/chunks/0xtmr0y4vo-~p.js +1 -0
- package/dist/web/_next/static/chunks/11ozf700niy2z.js +1 -0
- package/dist/web/_next/static/chunks/{0jmsjlawtg6oa.js → 145gbwitjxamg.js} +1 -1
- package/dist/web/_next/static/chunks/{turbopack-0lxiiw.jhevml.js → turbopack-0kcwat8.leugk.js} +1 -1
- package/dist/web/_not-found/__next._full.txt +34 -30
- package/dist/web/_not-found/__next._head.txt +4 -4
- package/dist/web/_not-found/__next._index.txt +17 -13
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dist/web/_not-found/__next._not-found.txt +3 -3
- package/dist/web/_not-found/__next._tree.txt +2 -2
- package/dist/web/_not-found.html +1 -1
- package/dist/web/_not-found.txt +34 -30
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +31 -26
- package/dist/web/login/__next._full.txt +35 -29
- package/dist/web/login/__next._head.txt +4 -4
- package/dist/web/login/__next._index.txt +17 -13
- package/dist/web/login/__next._tree.txt +2 -2
- package/dist/web/login/__next.login.__PAGE__.txt +4 -4
- package/dist/web/login/__next.login.txt +3 -3
- package/dist/web/login.html +1 -1
- package/dist/web/login.txt +35 -29
- package/dist/web/settings/__next._full.txt +36 -34
- package/dist/web/settings/__next._head.txt +4 -4
- package/dist/web/settings/__next._index.txt +17 -13
- package/dist/web/settings/__next._tree.txt +2 -2
- package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
- package/dist/web/settings/__next.settings.txt +5 -5
- package/dist/web/settings/agents/__next._full.txt +38 -35
- package/dist/web/settings/agents/__next._head.txt +4 -4
- package/dist/web/settings/agents/__next._index.txt +17 -13
- package/dist/web/settings/agents/__next._tree.txt +2 -2
- package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
- package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
- package/dist/web/settings/agents/__next.settings.txt +5 -5
- package/dist/web/settings/agents.html +1 -1
- package/dist/web/settings/agents.txt +38 -35
- package/dist/web/settings/mcps/__next._full.txt +38 -35
- package/dist/web/settings/mcps/__next._head.txt +4 -4
- package/dist/web/settings/mcps/__next._index.txt +17 -13
- package/dist/web/settings/mcps/__next._tree.txt +2 -2
- package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
- package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
- package/dist/web/settings/mcps/__next.settings.txt +5 -5
- package/dist/web/settings/mcps.html +1 -1
- package/dist/web/settings/mcps.txt +38 -35
- package/dist/web/settings/models/__next._full.txt +38 -35
- package/dist/web/settings/models/__next._head.txt +4 -4
- package/dist/web/settings/models/__next._index.txt +17 -13
- package/dist/web/settings/models/__next._tree.txt +2 -2
- package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
- package/dist/web/settings/models/__next.settings.models.txt +3 -3
- package/dist/web/settings/models/__next.settings.txt +5 -5
- package/dist/web/settings/models.html +1 -1
- package/dist/web/settings/models.txt +38 -35
- package/dist/web/settings/providers/__next._full.txt +38 -35
- package/dist/web/settings/providers/__next._head.txt +4 -4
- package/dist/web/settings/providers/__next._index.txt +17 -13
- package/dist/web/settings/providers/__next._tree.txt +2 -2
- package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
- package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
- package/dist/web/settings/providers/__next.settings.txt +5 -5
- package/dist/web/settings/providers.html +1 -1
- package/dist/web/settings/providers.txt +38 -35
- package/dist/web/settings/skills/__next._full.txt +38 -35
- package/dist/web/settings/skills/__next._head.txt +4 -4
- package/dist/web/settings/skills/__next._index.txt +17 -13
- package/dist/web/settings/skills/__next._tree.txt +2 -2
- package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
- package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
- package/dist/web/settings/skills/__next.settings.txt +5 -5
- package/dist/web/settings/skills.html +1 -1
- package/dist/web/settings/skills.txt +38 -35
- package/dist/web/settings.html +1 -1
- package/dist/web/settings.txt +36 -34
- package/dist/web/workflows/__next._full.txt +36 -31
- package/dist/web/workflows/__next._head.txt +4 -4
- package/dist/web/workflows/__next._index.txt +17 -13
- package/dist/web/workflows/__next._tree.txt +2 -2
- package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
- package/dist/web/workflows/__next.workflows.txt +3 -3
- package/dist/web/workflows.html +1 -1
- package/dist/web/workflows.txt +36 -31
- package/dist/web/workspace/_/__next._full.txt +39 -34
- package/dist/web/workspace/_/__next._head.txt +4 -4
- package/dist/web/workspace/_/__next._index.txt +17 -13
- package/dist/web/workspace/_/__next._tree.txt +2 -2
- package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
- package/dist/web/workspace/_/__next.workspace.txt +3 -3
- package/dist/web/workspace/_.html +1 -1
- package/dist/web/workspace/_.txt +39 -34
- package/dist/web/workspaces/__next._full.txt +35 -30
- package/dist/web/workspaces/__next._head.txt +4 -4
- package/dist/web/workspaces/__next._index.txt +17 -13
- package/dist/web/workspaces/__next._tree.txt +2 -2
- package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
- package/dist/web/workspaces/__next.workspaces.txt +3 -3
- package/dist/web/workspaces.html +1 -1
- package/dist/web/workspaces.txt +35 -30
- package/dist/ws/agent-prompt.js +48 -18
- package/dist/ws/agent-runner.js +10 -3
- package/dist/ws/message-parts.js +8 -0
- package/dist/ws/terminal-handler.js +1 -1
- package/package.json +1 -1
- package/public/avatars/1778953228323-5je54x-user.jpg +0 -0
- package/public/avatars/1778954057880-g01q6y-user.jpg +0 -0
- package/dist/web/_next/static/chunks/002-dxhags4dj.js +0 -2
- package/dist/web/_next/static/chunks/00fu7sjtftd3f.js +0 -1
- package/dist/web/_next/static/chunks/00s_1ft2-qz0r.js +0 -1
- package/dist/web/_next/static/chunks/02wkg1tjv~_t2.js +0 -1
- package/dist/web/_next/static/chunks/03jy~o.gx._e-.js +0 -3
- package/dist/web/_next/static/chunks/07bs5ds68tg38.js +0 -2
- package/dist/web/_next/static/chunks/0b1w9_ne4c2ez.js +0 -1
- package/dist/web/_next/static/chunks/0bzechrx76-ix.js +0 -1
- package/dist/web/_next/static/chunks/0csqa76ud3h4d.js +0 -1
- package/dist/web/_next/static/chunks/0fxdvo63ywaa~.js +0 -1
- package/dist/web/_next/static/chunks/0k5bmi-es-2nj.js +0 -1
- package/dist/web/_next/static/chunks/0pep4mkvt3.rh.js +0 -31
- package/dist/web/_next/static/chunks/0tqwdxb-8t_n1.js +0 -1
- package/dist/web/_next/static/chunks/0y5ipuqgqfrob.js +0 -8
- package/dist/web/_next/static/chunks/0yc41k980zabo.js +0 -1
- package/dist/web/_next/static/chunks/0zhhkzv4.0koq.js +0 -183
- package/dist/web/_next/static/chunks/0zl19l5tuoppw.js +0 -1
- package/dist/web/_next/static/chunks/102lz3yanx8i~.css +0 -1
- package/dist/web/_next/static/chunks/13wm9.vry~.lh.js +0 -5
- /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web/_next/static/{XzP71HbFE1fuE_bLT-x7i → 9X6R42UNn3h4WL_zj_Yh4}/_ssgManifest.js +0 -0
|
@@ -35,7 +35,6 @@ export async function runIssueAutomation(workspaceId, issueId, ctx) {
|
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
console.warn(`[issue-runner] no workflow configured for issue workspaceId=${workspaceId} issueId=${issueId}`);
|
|
38
|
-
markIssueError(workspaceId, issueId, 'No workflow configured for issue', ctx);
|
|
39
38
|
}
|
|
40
39
|
export function hasActiveIssueAutomation(workspaceId) {
|
|
41
40
|
return agentService.list(workspaceId).some((session) => session.status === 'active' &&
|
|
@@ -7,6 +7,7 @@ import * as workspaceService from '../services/workspace.js';
|
|
|
7
7
|
import { mapWorkflowToTaskDrafts, validateWorkflowForRun } from '../services/workflow.js';
|
|
8
8
|
import { createIssueFunctionTools } from '../services/builtin-tools.js';
|
|
9
9
|
import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
|
|
10
|
+
import { prependPersistentAgentContext } from '../services/persistent-agent-context.js';
|
|
10
11
|
import { completeIssueAgentProgress, createIssueAgentProgress, createIssueAgentProgressTracker } from './issue-agent-progress.js';
|
|
11
12
|
const ACTIVE_TASK_STATUSES = ['running', 'reviewing', 'retrying', 'waiting_review'];
|
|
12
13
|
export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, ctx) {
|
|
@@ -45,7 +46,7 @@ export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, c
|
|
|
45
46
|
},
|
|
46
47
|
});
|
|
47
48
|
const runtime = createRuntimeForPreset(taskSyncPreset);
|
|
48
|
-
const result = await runtime.execute(buildTaskSyncPrompt(issue, input, taskSyncWorkingDir), taskSyncWorkingDir, {
|
|
49
|
+
const result = await runtime.execute(buildIssueAgentPrompt(workspaceId, buildTaskSyncPrompt(issue, input, taskSyncWorkingDir), taskSyncWorkingDir, taskSyncPreset), taskSyncWorkingDir, {
|
|
49
50
|
maxTurns: 20,
|
|
50
51
|
mcpServers: undefined,
|
|
51
52
|
functionTools: createTaskSyncTools(workspaceId, issue, taskSyncPreset, ctx),
|
|
@@ -184,7 +185,7 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
|
|
|
184
185
|
agentId: taskAgent.id,
|
|
185
186
|
data: `[debug] workflow agentConfigId=${taskAgentPreset.id} role=${taskAgentPreset.role} taskAgentConfigId=${runningTask.agentConfigId || '(fallback)'} runtime=${taskAgentPreset.runtimeKind ?? 'open-agent-sdk'}`,
|
|
186
187
|
});
|
|
187
|
-
const result = await runtime.execute(buildTaskAgentPrompt(issue, runningTask, taskAgentPreset, agentWorkingDir), agentWorkingDir, {
|
|
188
|
+
const result = await runtime.execute(buildIssueAgentPrompt(workspaceId, buildTaskAgentPrompt(issue, runningTask, taskAgentPreset, agentWorkingDir), agentWorkingDir, taskAgentPreset), agentWorkingDir, {
|
|
188
189
|
maxTurns: 100,
|
|
189
190
|
mcpServers: agentService.getMcpServers(taskAgentPreset.mcps),
|
|
190
191
|
functionTools: createCurrentIssueTools(workspaceId, issue, taskAgentPreset),
|
|
@@ -324,10 +325,10 @@ export function createTasksFromWorkflow(workspaceId, issueId, template, ctx) {
|
|
|
324
325
|
}
|
|
325
326
|
const drafts = mapWorkflowToTaskDrafts(template);
|
|
326
327
|
issueService.updateStatus(workspaceId, issueId, 'planned');
|
|
327
|
-
ctx.broadcast('issue.status_changed', { issueId,
|
|
328
|
+
ctx.broadcast('issue.status_changed', { issueId, from: issue.status, to: 'planned' });
|
|
328
329
|
replaceIssueTasksFromDrafts(workspaceId, issueId, ctx, drafts);
|
|
329
330
|
issueService.updateStatus(workspaceId, issueId, 'in_progress');
|
|
330
|
-
ctx.broadcast('issue.status_changed', { issueId,
|
|
331
|
+
ctx.broadcast('issue.status_changed', { issueId, from: 'planned', to: 'in_progress' });
|
|
331
332
|
ctx.broadcast('issue.updated', issueService.getById(workspaceId, issueId));
|
|
332
333
|
scheduleRunnableIssueTasks(workspaceId, issueId, ctx).catch((err) => {
|
|
333
334
|
console.error(`[workflow] task scheduling failed for issue ${issueId}:`, err);
|
|
@@ -435,6 +436,15 @@ function createRuntimeForPreset(preset) {
|
|
|
435
436
|
function resolveIssueWorkspaceRoot(workspaceId) {
|
|
436
437
|
return workspaceService.getById(workspaceId)?.boundDirs?.[0] || process.cwd();
|
|
437
438
|
}
|
|
439
|
+
function buildIssueAgentPrompt(workspaceId, prompt, workingDir, preset) {
|
|
440
|
+
return prependPersistentAgentContext(prompt, {
|
|
441
|
+
workspaceId,
|
|
442
|
+
workingDir,
|
|
443
|
+
boundDirs: workspaceService.getById(workspaceId)?.boundDirs,
|
|
444
|
+
includeWorkspacePrompt: false,
|
|
445
|
+
excludeNativeClaudeMd: preset.runtimeKind === 'claude-code',
|
|
446
|
+
});
|
|
447
|
+
}
|
|
438
448
|
function buildTaskSyncPrompt(issue, input, workingDir) {
|
|
439
449
|
return [
|
|
440
450
|
'You are the issue task synchronization controller.',
|
package/dist/app.js
CHANGED
|
@@ -27,6 +27,7 @@ import subscriptionRouter from './routes/subscription.js';
|
|
|
27
27
|
import agentSseRouter from './routes/agent-sse.js';
|
|
28
28
|
import searchRouter from './routes/search.js';
|
|
29
29
|
import notificationRouter from './routes/notification.js';
|
|
30
|
+
import speechRecognitionRouter, { handleSpeechStream } from './routes/speech-recognition.js';
|
|
30
31
|
import { getUserSettings, setUserAvatarUrl, removeUserAvatarUrl } from './storage/user-settings-store.js';
|
|
31
32
|
import { authMiddleware, verifyToken } from './middleware/auth.js';
|
|
32
33
|
import { handleConnection } from './ws/handler.js';
|
|
@@ -100,6 +101,27 @@ app.use('/api/workspaces/:id/commands', commandRouter);
|
|
|
100
101
|
app.use('/api/workspaces/:id/agents', agentRouter);
|
|
101
102
|
app.use('/api/workspaces/:id/tasks', taskRouter);
|
|
102
103
|
app.use('/api/workspaces/:id/git', gitRouter);
|
|
104
|
+
// Global git config (no workspace context needed)
|
|
105
|
+
import { gitGetConfig as _gitGetConfig, gitSetConfig as _gitSetConfig } from './adapters/git.js';
|
|
106
|
+
app.get('/api/git-config', async (_req, res) => {
|
|
107
|
+
try {
|
|
108
|
+
const config = await _gitGetConfig('global');
|
|
109
|
+
res.json(config);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
res.status(500).json({ error: err.message });
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
app.post('/api/git-config', async (req, res) => {
|
|
116
|
+
try {
|
|
117
|
+
const { name, email, proxy } = req.body;
|
|
118
|
+
await _gitSetConfig('global', { name, email, proxy });
|
|
119
|
+
res.json({ ok: true });
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
res.status(500).json({ error: err.message });
|
|
123
|
+
}
|
|
124
|
+
});
|
|
103
125
|
app.use('/api/workspaces/:id/search', searchRouter);
|
|
104
126
|
app.use('/api/workspaces/:id/notifications', notificationRouter);
|
|
105
127
|
app.use('/api/agents', agentRouter);
|
|
@@ -108,6 +130,7 @@ app.use('/api/folder', folderRouter);
|
|
|
108
130
|
app.use('/api/skills', skillRouter);
|
|
109
131
|
app.use('/api/mcps', mcpRouter);
|
|
110
132
|
app.use('/api/subscriptions', subscriptionRouter);
|
|
133
|
+
app.use('/api/speech-recognition', speechRecognitionRouter);
|
|
111
134
|
// Serve static web frontend in production (after API routes, before catch-all)
|
|
112
135
|
const webDir = resolveRuntimeDir('web');
|
|
113
136
|
if (existsSync(webDir)) {
|
|
@@ -134,7 +157,18 @@ else {
|
|
|
134
157
|
console.warn('[server] web frontend not found at', webDir, '- run \`pnpm build\` first');
|
|
135
158
|
}
|
|
136
159
|
const server = createServer(app);
|
|
137
|
-
const wss = new WebSocketServer({
|
|
160
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
161
|
+
// Speech recognition WebSocket on /ws/speech
|
|
162
|
+
const speechWss = new WebSocketServer({ noServer: true });
|
|
163
|
+
speechWss.on('connection', (ws, req) => {
|
|
164
|
+
const token = new URL(req.url || '', `http://localhost:${PORT}`).searchParams.get('token');
|
|
165
|
+
if (!verifyToken(token)) {
|
|
166
|
+
ws.close(4003, 'Unauthorized');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const configId = new URL(req.url || '', `http://localhost:${PORT}`).searchParams.get('configId') || undefined;
|
|
170
|
+
handleSpeechStream(ws, configId);
|
|
171
|
+
});
|
|
138
172
|
wss.on('connection', (ws, req) => {
|
|
139
173
|
const url = new URL(req.url || '', `http://localhost:${PORT}`);
|
|
140
174
|
const workspaceId = url.searchParams.get('workspaceId');
|
|
@@ -149,6 +183,22 @@ wss.on('connection', (ws, req) => {
|
|
|
149
183
|
}
|
|
150
184
|
handleConnection(ws, workspaceId);
|
|
151
185
|
});
|
|
186
|
+
server.on('upgrade', (req, socket, head) => {
|
|
187
|
+
const { pathname } = new URL(req.url || '/', `http://localhost:${PORT}`);
|
|
188
|
+
if (pathname === '/ws') {
|
|
189
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
190
|
+
wss.emit('connection', ws, req);
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (pathname === '/ws/speech') {
|
|
195
|
+
speechWss.handleUpgrade(req, socket, head, (ws) => {
|
|
196
|
+
speechWss.emit('connection', ws, req);
|
|
197
|
+
});
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
socket.destroy();
|
|
201
|
+
});
|
|
152
202
|
const HOST = process.env.HOST || '0.0.0.0';
|
|
153
203
|
server.listen(PORT, HOST, () => {
|
|
154
204
|
console.log(`[server] listening on http://${HOST}:${PORT}`);
|
package/dist/package.json
CHANGED
package/dist/routes/agent-sse.js
CHANGED
|
@@ -45,6 +45,7 @@ router.post('/run', async (req, res) => {
|
|
|
45
45
|
const configDir = agentService.getAgentConfigDir(workspaceId, { ...preset, skills: requestedSkills });
|
|
46
46
|
const skills = agentService.getAvailableSkillNames(configDir, requestedSkills);
|
|
47
47
|
const output = [];
|
|
48
|
+
const workingDir = agentService.resolveWorkingDir(workspaceId, preset);
|
|
48
49
|
let completed = false;
|
|
49
50
|
prepareSse(res);
|
|
50
51
|
writeSse(res, 'session', { session, workspaceId });
|
|
@@ -68,8 +69,10 @@ router.post('/run', async (req, res) => {
|
|
|
68
69
|
mcpServers: Object.keys(mcpServers ?? {}),
|
|
69
70
|
skills,
|
|
70
71
|
boundDirs: workspace.boundDirs,
|
|
72
|
+
workingDir,
|
|
73
|
+
excludeNativeClaudeMd: preset.runtimeKind === 'claude-code',
|
|
71
74
|
builtInTools: [],
|
|
72
|
-
}),
|
|
75
|
+
}), workingDir, {
|
|
73
76
|
maxTurns: normalizeMaxTurns(body.maxTurns),
|
|
74
77
|
mcpServers,
|
|
75
78
|
skills,
|
package/dist/routes/agent.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import * as agentService from '../services/agent.js';
|
|
3
|
+
import { generateAgentDesign } from '../agents/agent-designer.js';
|
|
3
4
|
const router = Router({ mergeParams: true });
|
|
4
5
|
router.get('/usage/dashboard', (req, res) => {
|
|
5
6
|
const days = Number(req.query.days ?? 30);
|
|
@@ -21,6 +22,20 @@ router.post('/presets', (req, res) => {
|
|
|
21
22
|
const preset = agentService.createPreset(req.params.id, body);
|
|
22
23
|
res.status(201).json(preset);
|
|
23
24
|
});
|
|
25
|
+
router.post('/presets/generate', async (req, res) => {
|
|
26
|
+
const { prompt } = req.body;
|
|
27
|
+
if (!prompt?.trim()) {
|
|
28
|
+
res.status(400).json({ error: 'prompt is required' });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
res.json(await generateAgentDesign(prompt));
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
console.error('[agent-designer] generate failed', err);
|
|
36
|
+
res.status(400).json({ error: err instanceof Error ? err.message : 'agent generation failed' });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
24
39
|
router.post('/presets/test-connection', async (req, res) => {
|
|
25
40
|
const workspaceId = req.params.id;
|
|
26
41
|
const data = req.body;
|
package/dist/routes/channel.js
CHANGED
|
@@ -24,7 +24,7 @@ router.post('/', (req, res) => {
|
|
|
24
24
|
router.put('/:channelId', (req, res) => {
|
|
25
25
|
const { id, channelId } = req.params;
|
|
26
26
|
const patch = {};
|
|
27
|
-
for (const key of ['name', 'type', 'issueId', 'members', 'pinnedMentionId', 'draft', 'todos']) {
|
|
27
|
+
for (const key of ['name', 'type', 'issueId', 'members', 'pinnedMentionId', 'draft', 'todos', 'archived']) {
|
|
28
28
|
if (Object.hasOwn(req.body, key))
|
|
29
29
|
patch[key] = req.body[key];
|
|
30
30
|
}
|
package/dist/routes/file.js
CHANGED
|
@@ -12,7 +12,8 @@ router.get('/tree', async (req, res) => {
|
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
const path = req.query.path || '';
|
|
15
|
-
const
|
|
15
|
+
const depth = parseInt(req.query.depth) || undefined;
|
|
16
|
+
const tree = await fileService.readTree(ws, path, depth);
|
|
16
17
|
res.json(tree);
|
|
17
18
|
});
|
|
18
19
|
router.get('/content', async (req, res) => {
|
|
@@ -144,5 +145,100 @@ router.post('/reveal', async (req, res) => {
|
|
|
144
145
|
res.json({ ok: true, path: fullPath });
|
|
145
146
|
});
|
|
146
147
|
});
|
|
148
|
+
router.post('/rename', async (req, res) => {
|
|
149
|
+
const ws = fileService.getWorkspace(req.params.id);
|
|
150
|
+
if (!ws) {
|
|
151
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const { oldPath, newPath } = req.body;
|
|
155
|
+
if (!oldPath || !newPath) {
|
|
156
|
+
res.status(400).json({ error: 'oldPath and newPath are required' });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const ok = await fileService.renamePath(ws, oldPath, newPath);
|
|
160
|
+
if (!ok) {
|
|
161
|
+
res.status(500).json({ error: 'Failed to rename' });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
res.json({ ok: true });
|
|
165
|
+
});
|
|
166
|
+
router.post('/copy', async (req, res) => {
|
|
167
|
+
const ws = fileService.getWorkspace(req.params.id);
|
|
168
|
+
if (!ws) {
|
|
169
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const { srcPath, destPath } = req.body;
|
|
173
|
+
if (!srcPath || !destPath) {
|
|
174
|
+
res.status(400).json({ error: 'srcPath and destPath are required' });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const ok = await fileService.copyPath(ws, srcPath, destPath);
|
|
178
|
+
if (!ok) {
|
|
179
|
+
res.status(500).json({ error: 'Failed to copy' });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
res.json({ ok: true });
|
|
183
|
+
});
|
|
184
|
+
router.post('/import-url', async (req, res) => {
|
|
185
|
+
const ws = fileService.getWorkspace(req.params.id);
|
|
186
|
+
if (!ws) {
|
|
187
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const { url, targetDir } = req.body;
|
|
191
|
+
if (!url) {
|
|
192
|
+
res.status(400).json({ error: 'url is required' });
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const result = await fileService.importFromUrl(ws, url, targetDir || '');
|
|
196
|
+
if (!result) {
|
|
197
|
+
res.status(500).json({ error: 'Failed to import from URL' });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
res.json({ ok: true, path: result });
|
|
201
|
+
});
|
|
202
|
+
router.post('/import-path', async (req, res) => {
|
|
203
|
+
const ws = fileService.getWorkspace(req.params.id);
|
|
204
|
+
if (!ws) {
|
|
205
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const { absPath, targetDir } = req.body;
|
|
209
|
+
if (!absPath) {
|
|
210
|
+
res.status(400).json({ error: 'absPath is required' });
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const result = await fileService.importFromAbsPath(ws, absPath, targetDir || '');
|
|
214
|
+
if (!result) {
|
|
215
|
+
res.status(500).json({ error: 'Failed to import file' });
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
res.json({ ok: true, path: result });
|
|
219
|
+
});
|
|
220
|
+
router.post('/upload', async (req, res) => {
|
|
221
|
+
const ws = fileService.getWorkspace(req.params.id);
|
|
222
|
+
if (!ws) {
|
|
223
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const { targetDir, files } = req.body;
|
|
227
|
+
if (!Array.isArray(files) || files.length === 0) {
|
|
228
|
+
res.status(400).json({ error: 'files are required' });
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const results = [];
|
|
232
|
+
for (const f of files) {
|
|
233
|
+
if (!f.name || !f.content)
|
|
234
|
+
continue;
|
|
235
|
+
const buffer = Buffer.from(f.content, 'base64');
|
|
236
|
+
const relPath = targetDir ? `${targetDir}/${f.name}` : f.name;
|
|
237
|
+
const ok = await fileService.writeFileBinary(ws, relPath, buffer);
|
|
238
|
+
if (ok)
|
|
239
|
+
results.push(relPath);
|
|
240
|
+
}
|
|
241
|
+
res.json({ ok: true, paths: results });
|
|
242
|
+
});
|
|
147
243
|
export default router;
|
|
148
244
|
//# sourceMappingURL=file.js.map
|
package/dist/routes/git.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import { gitStatus, gitDiff, gitLog, gitCommit, gitDiscard, gitDiscardAll, gitBranches, gitCheckout, gitInit, gitGenerateCommitMsg, gitPush, gitPull, gitGetRemotes, gitAddRemote } from '../adapters/git.js';
|
|
2
|
+
import { gitStatus, gitDiff, gitLog, gitCommit, gitDiscard, gitDiscardAll, gitBranches, gitCheckout, gitInit, gitGenerateCommitMsg, gitPush, gitPull, gitGetRemotes, gitAddRemote, gitCheckoutDetached, gitCherryPick, gitCreateBranch, gitDeleteBranch, gitCreateTag, gitCommitDiff, gitGetRemoteUrl, gitMergeBase, gitGetConfig, gitSetConfig } from '../adapters/git.js';
|
|
3
3
|
const router = Router({ mergeParams: true });
|
|
4
4
|
router.get('/status', async (req, res) => {
|
|
5
5
|
try {
|
|
@@ -149,5 +149,126 @@ router.post('/remotes', async (req, res) => {
|
|
|
149
149
|
res.status(500).json({ error: err.message });
|
|
150
150
|
}
|
|
151
151
|
});
|
|
152
|
+
router.post('/checkout-detached', async (req, res) => {
|
|
153
|
+
try {
|
|
154
|
+
const { commitHash } = req.body;
|
|
155
|
+
if (!commitHash) {
|
|
156
|
+
res.status(400).json({ error: 'commitHash is required' });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
await gitCheckoutDetached(req.params.id, commitHash);
|
|
160
|
+
res.json({ ok: true });
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
res.status(500).json({ error: err.message });
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
router.post('/cherry-pick', async (req, res) => {
|
|
167
|
+
try {
|
|
168
|
+
const { commitHash } = req.body;
|
|
169
|
+
if (!commitHash) {
|
|
170
|
+
res.status(400).json({ error: 'commitHash is required' });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
await gitCherryPick(req.params.id, commitHash);
|
|
174
|
+
res.json({ ok: true });
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
res.status(500).json({ error: err.message });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
router.post('/create-branch', async (req, res) => {
|
|
181
|
+
try {
|
|
182
|
+
const { name, startPoint } = req.body;
|
|
183
|
+
if (!name) {
|
|
184
|
+
res.status(400).json({ error: 'name is required' });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await gitCreateBranch(req.params.id, name, startPoint);
|
|
188
|
+
res.json({ ok: true });
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
res.status(500).json({ error: err.message });
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
router.post('/delete-branch', async (req, res) => {
|
|
195
|
+
try {
|
|
196
|
+
const { name, force } = req.body;
|
|
197
|
+
if (!name) {
|
|
198
|
+
res.status(400).json({ error: 'name is required' });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
await gitDeleteBranch(req.params.id, name, force);
|
|
202
|
+
res.json({ ok: true });
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
res.status(500).json({ error: err.message });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
router.post('/create-tag', async (req, res) => {
|
|
209
|
+
try {
|
|
210
|
+
const { name, commitHash } = req.body;
|
|
211
|
+
if (!name) {
|
|
212
|
+
res.status(400).json({ error: 'name is required' });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
await gitCreateTag(req.params.id, name, commitHash);
|
|
216
|
+
res.json({ ok: true });
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
res.status(500).json({ error: err.message });
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
router.get('/commit-diff', async (req, res) => {
|
|
223
|
+
try {
|
|
224
|
+
const hash = req.query.hash;
|
|
225
|
+
if (!hash) {
|
|
226
|
+
res.status(400).json({ error: 'hash is required' });
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const diffs = await gitCommitDiff(req.params.id, hash);
|
|
230
|
+
res.json(diffs);
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
res.status(500).json({ error: err.message });
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
router.get('/remote-url', async (req, res) => {
|
|
237
|
+
try {
|
|
238
|
+
const url = await gitGetRemoteUrl(req.params.id);
|
|
239
|
+
res.json({ url });
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
res.status(500).json({ error: err.message });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
router.get('/merge-base', async (req, res) => {
|
|
246
|
+
try {
|
|
247
|
+
const hash = await gitMergeBase(req.params.id);
|
|
248
|
+
res.json({ hash });
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
res.status(500).json({ error: err.message });
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
router.get('/config', async (req, res) => {
|
|
255
|
+
try {
|
|
256
|
+
const config = await gitGetConfig('local', req.params.id);
|
|
257
|
+
res.json(config);
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
res.status(500).json({ error: err.message });
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
router.post('/config', async (req, res) => {
|
|
264
|
+
try {
|
|
265
|
+
const { name, email, proxy } = req.body;
|
|
266
|
+
await gitSetConfig('local', { name, email, proxy }, req.params.id);
|
|
267
|
+
res.json({ ok: true });
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
res.status(500).json({ error: err.message });
|
|
271
|
+
}
|
|
272
|
+
});
|
|
152
273
|
export default router;
|
|
153
274
|
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import * as store from '../storage/iframe-bookmarks-store.js';
|
|
3
|
+
const router = Router();
|
|
4
|
+
router.get('/', (_req, res) => {
|
|
5
|
+
res.json(store.listBookmarks());
|
|
6
|
+
});
|
|
7
|
+
router.post('/', (req, res) => {
|
|
8
|
+
const { title, url, size } = req.body;
|
|
9
|
+
if (!title || !url) {
|
|
10
|
+
res.status(400).json({ error: 'title and url are required' });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
res.status(201).json(store.addBookmark({ title, url, size }));
|
|
14
|
+
});
|
|
15
|
+
router.put('/:id', (req, res) => {
|
|
16
|
+
const { title, url, size } = req.body;
|
|
17
|
+
const updated = store.updateBookmark(req.params.id, { title, url, size });
|
|
18
|
+
if (!updated) {
|
|
19
|
+
res.status(404).json({ error: 'bookmark not found' });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
res.json(updated);
|
|
23
|
+
});
|
|
24
|
+
router.delete('/:id', (req, res) => {
|
|
25
|
+
const ok = store.removeBookmark(req.params.id);
|
|
26
|
+
if (!ok) {
|
|
27
|
+
res.status(404).json({ error: 'bookmark not found' });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
res.json({ ok: true });
|
|
31
|
+
});
|
|
32
|
+
export default router;
|
|
33
|
+
//# sourceMappingURL=iframe-bookmarks.js.map
|
package/dist/routes/issue.js
CHANGED
|
@@ -5,6 +5,7 @@ import * as channelService from '../services/channel.js';
|
|
|
5
5
|
import { broadcastToWorkspace } from '../ws/connection-manager.js';
|
|
6
6
|
import { stopChannelRuns } from '../ws/agent-runner.js';
|
|
7
7
|
import * as agentService from '../services/agent.js';
|
|
8
|
+
import * as taskService from '../services/task.js';
|
|
8
9
|
import { retryIssue } from '../services/issue-retry.js';
|
|
9
10
|
import { hasActiveIssueAutomation, runIssueAutomation } from '../agents/issue-agent-runner.js';
|
|
10
11
|
import * as workflowService from '../services/workflow.js';
|
|
@@ -83,6 +84,17 @@ router.post('/:issueId/start', (req, res) => {
|
|
|
83
84
|
const workspaceId = req.params.id;
|
|
84
85
|
const { issueId } = req.params;
|
|
85
86
|
const before = issueService.getById(workspaceId, issueId);
|
|
87
|
+
if (!before) {
|
|
88
|
+
res.status(404).json({ error: 'issue not found' });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Pre-check: must have workflow or existing tasks
|
|
92
|
+
const hasWorkflow = Boolean(before.workflowId);
|
|
93
|
+
const existingTasks = taskService.list(workspaceId, issueId);
|
|
94
|
+
if (!hasWorkflow && existingTasks.length === 0) {
|
|
95
|
+
res.status(400).json({ error: 'No workflow or tasks configured for this issue' });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
86
98
|
const issue = issueService.updateStatus(workspaceId, issueId, 'planned');
|
|
87
99
|
if (!issue) {
|
|
88
100
|
res.status(404).json({ error: 'issue not found' });
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import * as store from '../storage/speech-recognition-store.js';
|
|
3
|
+
import { createSpeechSession } from '../services/speech-recognition/index.js';
|
|
4
|
+
const router = Router();
|
|
5
|
+
// REST: CRUD for speech recognition configs
|
|
6
|
+
router.get('/', (_req, res) => {
|
|
7
|
+
res.json(store.listSpeechConfigs());
|
|
8
|
+
});
|
|
9
|
+
router.post('/', (req, res) => {
|
|
10
|
+
const { provider, label, credentials } = req.body;
|
|
11
|
+
if (!provider || !credentials) {
|
|
12
|
+
res.status(400).json({ error: 'provider and credentials are required' });
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const item = store.createSpeechConfig({ provider, label: label || provider, credentials });
|
|
16
|
+
res.status(201).json(item);
|
|
17
|
+
});
|
|
18
|
+
router.put('/:id', (req, res) => {
|
|
19
|
+
const { label, credentials, enabled } = req.body;
|
|
20
|
+
const updated = store.updateSpeechConfig(req.params.id, { label, credentials, enabled });
|
|
21
|
+
if (!updated) {
|
|
22
|
+
res.status(404).json({ error: 'Config not found' });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
res.json(updated);
|
|
26
|
+
});
|
|
27
|
+
router.delete('/:id', (req, res) => {
|
|
28
|
+
if (!store.deleteSpeechConfig(req.params.id)) {
|
|
29
|
+
res.status(404).json({ error: 'Config not found' });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
res.status(204).end();
|
|
33
|
+
});
|
|
34
|
+
// Export a handler for WebSocket upgrade (speech streaming)
|
|
35
|
+
export async function handleSpeechStream(clientWs, configId) {
|
|
36
|
+
console.log('[speech-ws] new connection, configId:', configId || '(default)');
|
|
37
|
+
const config = configId
|
|
38
|
+
? store.getSpeechConfig(configId)
|
|
39
|
+
: store.getDefaultSpeechConfig();
|
|
40
|
+
if (!config) {
|
|
41
|
+
console.warn('[speech-ws] no config found, closing');
|
|
42
|
+
clientWs.close(4004, 'No speech recognition config found');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
console.log('[speech-ws] using config:', config.id, 'provider:', config.provider);
|
|
46
|
+
try {
|
|
47
|
+
console.log('[speech-ws] creating session...');
|
|
48
|
+
const session = await createSpeechSession(config);
|
|
49
|
+
console.log('[speech-ws] session created');
|
|
50
|
+
let audioPackets = 0;
|
|
51
|
+
let lastLogTime = Date.now();
|
|
52
|
+
session.onResult((result) => {
|
|
53
|
+
console.log('[speech-ws] result:', result.text, 'isFinal:', result.isFinal);
|
|
54
|
+
if (clientWs.readyState === 1) {
|
|
55
|
+
clientWs.send(JSON.stringify(result));
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
session.onError((err) => {
|
|
59
|
+
console.error('[speech-ws] session error:', err.message);
|
|
60
|
+
if (clientWs.readyState === 1) {
|
|
61
|
+
clientWs.send(JSON.stringify({ error: err.message }));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
session.onClose(() => {
|
|
65
|
+
console.log('[speech-ws] session closed, total audio packets:', audioPackets);
|
|
66
|
+
if (clientWs.readyState === 1) {
|
|
67
|
+
clientWs.close(1000, 'Session ended');
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
clientWs.on('message', (data) => {
|
|
71
|
+
if (Buffer.isBuffer(data)) {
|
|
72
|
+
session.sendAudio(data);
|
|
73
|
+
audioPackets++;
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
if (audioPackets <= 3 || now - lastLogTime > 5000) {
|
|
76
|
+
console.log('[speech-ws] audio packet #', audioPackets, 'size:', data.byteLength);
|
|
77
|
+
lastLogTime = now;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const msg = data.toString();
|
|
82
|
+
console.log('[speech-ws] text message:', msg);
|
|
83
|
+
if (msg === '{"type":"end"}' || msg === '{"type":"end"}') {
|
|
84
|
+
session.end();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
clientWs.on('close', () => {
|
|
89
|
+
console.log('[speech-ws] client disconnected, total audio packets:', audioPackets);
|
|
90
|
+
session.end();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
console.error('[speech-ws] failed to create session:', err.message);
|
|
95
|
+
clientWs.close(1011, err.message || 'Failed to create speech session');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export default router;
|
|
99
|
+
//# sourceMappingURL=speech-recognition.js.map
|