@agent-spaces/server 0.2.9 → 0.3.1

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 (185) hide show
  1. package/dist/adapters/claude-code-runtime/index.js +48 -6
  2. package/dist/adapters/claude-code-runtime/message-format.js +3 -3
  3. package/dist/adapters/git.js +140 -2
  4. package/dist/agents/agent-designer.js +334 -0
  5. package/dist/agents/agent-message-parts.js +8 -0
  6. package/dist/agents/commit-agent.js +9 -2
  7. package/dist/agents/issue-task-controller.js +14 -4
  8. package/dist/app.js +53 -1
  9. package/dist/package.json +1 -1
  10. package/dist/routes/agent-sse.js +4 -1
  11. package/dist/routes/agent.js +15 -0
  12. package/dist/routes/channel.js +1 -1
  13. package/dist/routes/file.js +97 -1
  14. package/dist/routes/git.js +122 -1
  15. package/dist/routes/iframe-bookmarks.js +33 -0
  16. package/dist/routes/speech-recognition.js +80 -0
  17. package/dist/services/agent.js +48 -5
  18. package/dist/services/builtin-tools.js +67 -1
  19. package/dist/services/channel.js +2 -0
  20. package/dist/services/command-process-manager.js +14 -1
  21. package/dist/services/file.js +86 -6
  22. package/dist/services/gitignore.js +65 -0
  23. package/dist/services/issue-retry.js +2 -1
  24. package/dist/services/issue.js +2 -2
  25. package/dist/services/notification-hub/bot-agent.js +9 -1
  26. package/dist/services/persistent-agent-context.js +155 -0
  27. package/dist/services/pty.js +47 -2
  28. package/dist/services/search.js +11 -3
  29. package/dist/services/speech-recognition/base.js +3 -0
  30. package/dist/services/speech-recognition/index.js +16 -0
  31. package/dist/services/speech-recognition/tencent.js +112 -0
  32. package/dist/storage/iframe-bookmarks-store.js +46 -0
  33. package/dist/storage/speech-recognition-store.js +58 -0
  34. package/dist/web/404.html +1 -1
  35. package/dist/web/__next.__PAGE__.txt +4 -4
  36. package/dist/web/__next._full.txt +31 -26
  37. package/dist/web/__next._head.txt +4 -4
  38. package/dist/web/__next._index.txt +17 -13
  39. package/dist/web/__next._tree.txt +2 -2
  40. package/dist/web/_next/static/chunks/0-mtp2au0a0nh.js +189 -0
  41. package/dist/web/_next/static/chunks/00amm3dvu3po9.js +31 -0
  42. package/dist/web/_next/static/chunks/02unyp1obddyn.js +3 -0
  43. package/dist/web/_next/static/chunks/{0-vgd3j-zh3m8.js → 05iy4.c-1cfhd.js} +1 -1
  44. package/dist/web/_next/static/chunks/0d.v1csiyl.1~.js +2 -0
  45. package/dist/web/_next/static/chunks/0egot8y~i-ge3.js +1 -0
  46. package/dist/web/_next/static/chunks/0e~0o6v_awcod.js +1 -0
  47. package/dist/web/_next/static/chunks/0fpf.rso9mhc0.js +1 -0
  48. package/dist/web/_next/static/chunks/0glol5jdarfma.js +2 -0
  49. package/dist/web/_next/static/chunks/0m1ud--um8kjh.js +1 -0
  50. package/dist/web/_next/static/chunks/0o6w-uw2p.phy.js +5 -0
  51. package/dist/web/_next/static/chunks/0os7q6w~-tmg7.css +1 -0
  52. package/dist/web/_next/static/chunks/0plreh4_gkxf9.js +1 -0
  53. package/dist/web/_next/static/chunks/0u7_qd9hzqwac.js +1 -0
  54. package/dist/web/_next/static/chunks/0vesz93a98px4.js +1 -0
  55. package/dist/web/_next/static/chunks/0w3o.7vi.dlm6.js +1 -0
  56. package/dist/web/_next/static/chunks/0xtmr0y4vo-~p.js +1 -0
  57. package/dist/web/_next/static/chunks/0~3uikgc074il.js +1 -0
  58. package/dist/web/_next/static/chunks/{0y5ipuqgqfrob.js → 0~xn0gfqxa20r.js} +3 -3
  59. package/dist/web/_next/static/chunks/11ozf700niy2z.js +1 -0
  60. package/dist/web/_next/static/chunks/{0jmsjlawtg6oa.js → 145gbwitjxamg.js} +1 -1
  61. package/dist/web/_next/static/chunks/{turbopack-0lxiiw.jhevml.js → turbopack-0kcwat8.leugk.js} +1 -1
  62. package/dist/web/_not-found/__next._full.txt +34 -30
  63. package/dist/web/_not-found/__next._head.txt +4 -4
  64. package/dist/web/_not-found/__next._index.txt +17 -13
  65. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  66. package/dist/web/_not-found/__next._not-found.txt +3 -3
  67. package/dist/web/_not-found/__next._tree.txt +2 -2
  68. package/dist/web/_not-found.html +1 -1
  69. package/dist/web/_not-found.txt +34 -30
  70. package/dist/web/index.html +1 -1
  71. package/dist/web/index.txt +31 -26
  72. package/dist/web/login/__next._full.txt +35 -29
  73. package/dist/web/login/__next._head.txt +4 -4
  74. package/dist/web/login/__next._index.txt +17 -13
  75. package/dist/web/login/__next._tree.txt +2 -2
  76. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  77. package/dist/web/login/__next.login.txt +3 -3
  78. package/dist/web/login.html +1 -1
  79. package/dist/web/login.txt +35 -29
  80. package/dist/web/settings/__next._full.txt +36 -34
  81. package/dist/web/settings/__next._head.txt +4 -4
  82. package/dist/web/settings/__next._index.txt +17 -13
  83. package/dist/web/settings/__next._tree.txt +2 -2
  84. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  85. package/dist/web/settings/__next.settings.txt +5 -5
  86. package/dist/web/settings/agents/__next._full.txt +38 -35
  87. package/dist/web/settings/agents/__next._head.txt +4 -4
  88. package/dist/web/settings/agents/__next._index.txt +17 -13
  89. package/dist/web/settings/agents/__next._tree.txt +2 -2
  90. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  91. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  92. package/dist/web/settings/agents/__next.settings.txt +5 -5
  93. package/dist/web/settings/agents.html +1 -1
  94. package/dist/web/settings/agents.txt +38 -35
  95. package/dist/web/settings/mcps/__next._full.txt +38 -35
  96. package/dist/web/settings/mcps/__next._head.txt +4 -4
  97. package/dist/web/settings/mcps/__next._index.txt +17 -13
  98. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  99. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  100. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  101. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  102. package/dist/web/settings/mcps.html +1 -1
  103. package/dist/web/settings/mcps.txt +38 -35
  104. package/dist/web/settings/models/__next._full.txt +38 -35
  105. package/dist/web/settings/models/__next._head.txt +4 -4
  106. package/dist/web/settings/models/__next._index.txt +17 -13
  107. package/dist/web/settings/models/__next._tree.txt +2 -2
  108. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  109. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  110. package/dist/web/settings/models/__next.settings.txt +5 -5
  111. package/dist/web/settings/models.html +1 -1
  112. package/dist/web/settings/models.txt +38 -35
  113. package/dist/web/settings/providers/__next._full.txt +38 -35
  114. package/dist/web/settings/providers/__next._head.txt +4 -4
  115. package/dist/web/settings/providers/__next._index.txt +17 -13
  116. package/dist/web/settings/providers/__next._tree.txt +2 -2
  117. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  118. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  119. package/dist/web/settings/providers/__next.settings.txt +5 -5
  120. package/dist/web/settings/providers.html +1 -1
  121. package/dist/web/settings/providers.txt +38 -35
  122. package/dist/web/settings/skills/__next._full.txt +38 -35
  123. package/dist/web/settings/skills/__next._head.txt +4 -4
  124. package/dist/web/settings/skills/__next._index.txt +17 -13
  125. package/dist/web/settings/skills/__next._tree.txt +2 -2
  126. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  127. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  128. package/dist/web/settings/skills/__next.settings.txt +5 -5
  129. package/dist/web/settings/skills.html +1 -1
  130. package/dist/web/settings/skills.txt +38 -35
  131. package/dist/web/settings.html +1 -1
  132. package/dist/web/settings.txt +36 -34
  133. package/dist/web/workflows/__next._full.txt +36 -31
  134. package/dist/web/workflows/__next._head.txt +4 -4
  135. package/dist/web/workflows/__next._index.txt +17 -13
  136. package/dist/web/workflows/__next._tree.txt +2 -2
  137. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  138. package/dist/web/workflows/__next.workflows.txt +3 -3
  139. package/dist/web/workflows.html +1 -1
  140. package/dist/web/workflows.txt +36 -31
  141. package/dist/web/workspace/_/__next._full.txt +39 -34
  142. package/dist/web/workspace/_/__next._head.txt +4 -4
  143. package/dist/web/workspace/_/__next._index.txt +17 -13
  144. package/dist/web/workspace/_/__next._tree.txt +2 -2
  145. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  146. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  147. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  148. package/dist/web/workspace/_.html +1 -1
  149. package/dist/web/workspace/_.txt +39 -34
  150. package/dist/web/workspaces/__next._full.txt +35 -30
  151. package/dist/web/workspaces/__next._head.txt +4 -4
  152. package/dist/web/workspaces/__next._index.txt +17 -13
  153. package/dist/web/workspaces/__next._tree.txt +2 -2
  154. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  155. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  156. package/dist/web/workspaces.html +1 -1
  157. package/dist/web/workspaces.txt +35 -30
  158. package/dist/ws/agent-prompt.js +48 -18
  159. package/dist/ws/agent-runner.js +4 -1
  160. package/dist/ws/message-parts.js +8 -0
  161. package/dist/ws/terminal-handler.js +1 -1
  162. package/package.json +1 -1
  163. package/public/avatars/1778953228323-5je54x-user.jpg +0 -0
  164. package/public/avatars/1778954057880-g01q6y-user.jpg +0 -0
  165. package/dist/web/_next/static/chunks/0.~n0s82y4zh_.css +0 -1
  166. package/dist/web/_next/static/chunks/002-dxhags4dj.js +0 -2
  167. package/dist/web/_next/static/chunks/02duknlg3rgjp.js +0 -1
  168. package/dist/web/_next/static/chunks/02wkg1tjv~_t2.js +0 -1
  169. package/dist/web/_next/static/chunks/03jy~o.gx._e-.js +0 -3
  170. package/dist/web/_next/static/chunks/04zxh2e-4~-xs.js +0 -1
  171. package/dist/web/_next/static/chunks/07bs5ds68tg38.js +0 -2
  172. package/dist/web/_next/static/chunks/082lp-xqv7r.-.js +0 -183
  173. package/dist/web/_next/static/chunks/0b2tump5duj9j.js +0 -1
  174. package/dist/web/_next/static/chunks/0csqa76ud3h4d.js +0 -1
  175. package/dist/web/_next/static/chunks/0n0j7tw_y1qk2.js +0 -1
  176. package/dist/web/_next/static/chunks/0pep4mkvt3.rh.js +0 -31
  177. package/dist/web/_next/static/chunks/0tkii3.lkbu3u.js +0 -1
  178. package/dist/web/_next/static/chunks/0wz8~mvztv-vq.js +0 -1
  179. package/dist/web/_next/static/chunks/0zl19l5tuoppw.js +0 -1
  180. package/dist/web/_next/static/chunks/0zxtxecpqyoeh.js +0 -1
  181. package/dist/web/_next/static/chunks/12sucjx_z8ftc.js +0 -1
  182. package/dist/web/_next/static/chunks/15_n_khh8cuax.js +0 -5
  183. /package/dist/web/_next/static/{hGfVPALj5nbwettayL9DY → 4dBAEZ275uRqcyeaRmXSx}/_buildManifest.js +0 -0
  184. /package/dist/web/_next/static/{hGfVPALj5nbwettayL9DY → 4dBAEZ275uRqcyeaRmXSx}/_clientMiddlewareManifest.js +0 -0
  185. /package/dist/web/_next/static/{hGfVPALj5nbwettayL9DY → 4dBAEZ275uRqcyeaRmXSx}/_ssgManifest.js +0 -0
@@ -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, oldStatus: issue.status, newStatus: 'planned' });
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, oldStatus: 'planned', newStatus: 'in_progress' });
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,8 @@ 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 iframeBookmarksRouter from './routes/iframe-bookmarks.js';
31
+ import speechRecognitionRouter, { handleSpeechStream } from './routes/speech-recognition.js';
30
32
  import { getUserSettings, setUserAvatarUrl, removeUserAvatarUrl } from './storage/user-settings-store.js';
31
33
  import { authMiddleware, verifyToken } from './middleware/auth.js';
32
34
  import { handleConnection } from './ws/handler.js';
@@ -100,6 +102,27 @@ app.use('/api/workspaces/:id/commands', commandRouter);
100
102
  app.use('/api/workspaces/:id/agents', agentRouter);
101
103
  app.use('/api/workspaces/:id/tasks', taskRouter);
102
104
  app.use('/api/workspaces/:id/git', gitRouter);
105
+ // Global git config (no workspace context needed)
106
+ import { gitGetConfig as _gitGetConfig, gitSetConfig as _gitSetConfig } from './adapters/git.js';
107
+ app.get('/api/git-config', async (_req, res) => {
108
+ try {
109
+ const config = await _gitGetConfig('global');
110
+ res.json(config);
111
+ }
112
+ catch (err) {
113
+ res.status(500).json({ error: err.message });
114
+ }
115
+ });
116
+ app.post('/api/git-config', async (req, res) => {
117
+ try {
118
+ const { name, email, proxy } = req.body;
119
+ await _gitSetConfig('global', { name, email, proxy });
120
+ res.json({ ok: true });
121
+ }
122
+ catch (err) {
123
+ res.status(500).json({ error: err.message });
124
+ }
125
+ });
103
126
  app.use('/api/workspaces/:id/search', searchRouter);
104
127
  app.use('/api/workspaces/:id/notifications', notificationRouter);
105
128
  app.use('/api/agents', agentRouter);
@@ -108,6 +131,8 @@ app.use('/api/folder', folderRouter);
108
131
  app.use('/api/skills', skillRouter);
109
132
  app.use('/api/mcps', mcpRouter);
110
133
  app.use('/api/subscriptions', subscriptionRouter);
134
+ app.use('/api/speech-recognition', speechRecognitionRouter);
135
+ app.use('/api/iframe-bookmarks', iframeBookmarksRouter);
111
136
  // Serve static web frontend in production (after API routes, before catch-all)
112
137
  const webDir = resolveRuntimeDir('web');
113
138
  if (existsSync(webDir)) {
@@ -134,7 +159,18 @@ else {
134
159
  console.warn('[server] web frontend not found at', webDir, '- run \`pnpm build\` first');
135
160
  }
136
161
  const server = createServer(app);
137
- const wss = new WebSocketServer({ server, path: '/ws' });
162
+ const wss = new WebSocketServer({ noServer: true });
163
+ // Speech recognition WebSocket on /ws/speech
164
+ const speechWss = new WebSocketServer({ noServer: true });
165
+ speechWss.on('connection', (ws, req) => {
166
+ const token = new URL(req.url || '', `http://localhost:${PORT}`).searchParams.get('token');
167
+ if (!verifyToken(token)) {
168
+ ws.close(4003, 'Unauthorized');
169
+ return;
170
+ }
171
+ const configId = new URL(req.url || '', `http://localhost:${PORT}`).searchParams.get('configId') || undefined;
172
+ handleSpeechStream(ws, configId);
173
+ });
138
174
  wss.on('connection', (ws, req) => {
139
175
  const url = new URL(req.url || '', `http://localhost:${PORT}`);
140
176
  const workspaceId = url.searchParams.get('workspaceId');
@@ -149,6 +185,22 @@ wss.on('connection', (ws, req) => {
149
185
  }
150
186
  handleConnection(ws, workspaceId);
151
187
  });
188
+ server.on('upgrade', (req, socket, head) => {
189
+ const { pathname } = new URL(req.url || '/', `http://localhost:${PORT}`);
190
+ if (pathname === '/ws') {
191
+ wss.handleUpgrade(req, socket, head, (ws) => {
192
+ wss.emit('connection', ws, req);
193
+ });
194
+ return;
195
+ }
196
+ if (pathname === '/ws/speech') {
197
+ speechWss.handleUpgrade(req, socket, head, (ws) => {
198
+ speechWss.emit('connection', ws, req);
199
+ });
200
+ return;
201
+ }
202
+ socket.destroy();
203
+ });
152
204
  const HOST = process.env.HOST || '0.0.0.0';
153
205
  server.listen(PORT, HOST, () => {
154
206
  console.log(`[server] listening on http://${HOST}:${PORT}`);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-spaces/server",
3
- "version": "0.2.7",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "app.js",
6
6
  "packageManager": "pnpm@10.17.1",
@@ -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
- }), agentService.resolveWorkingDir(workspaceId, preset), {
75
+ }), workingDir, {
73
76
  maxTurns: normalizeMaxTurns(body.maxTurns),
74
77
  mcpServers,
75
78
  skills,
@@ -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;
@@ -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
  }
@@ -12,7 +12,8 @@ router.get('/tree', async (req, res) => {
12
12
  return;
13
13
  }
14
14
  const path = req.query.path || '';
15
- const tree = await fileService.readTree(ws, path);
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
@@ -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
@@ -0,0 +1,80 @@
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 } = req.body;
20
+ const updated = store.updateSpeechConfig(req.params.id, { label, credentials });
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
+ const config = configId
37
+ ? store.getSpeechConfig(configId)
38
+ : store.getDefaultSpeechConfig();
39
+ if (!config) {
40
+ clientWs.close(4004, 'No speech recognition config found');
41
+ return;
42
+ }
43
+ try {
44
+ const session = await createSpeechSession(config);
45
+ session.onResult((result) => {
46
+ if (clientWs.readyState === 1) {
47
+ clientWs.send(JSON.stringify(result));
48
+ }
49
+ });
50
+ session.onError((err) => {
51
+ if (clientWs.readyState === 1) {
52
+ clientWs.send(JSON.stringify({ error: err.message }));
53
+ }
54
+ });
55
+ session.onClose(() => {
56
+ if (clientWs.readyState === 1) {
57
+ clientWs.close(1000, 'Session ended');
58
+ }
59
+ });
60
+ clientWs.on('message', (data) => {
61
+ if (Buffer.isBuffer(data)) {
62
+ session.sendAudio(data);
63
+ }
64
+ else {
65
+ const msg = data.toString();
66
+ if (msg === '{"type":"end"}' || msg === '{"type":"end"}') {
67
+ session.end();
68
+ }
69
+ }
70
+ });
71
+ clientWs.on('close', () => {
72
+ session.end();
73
+ });
74
+ }
75
+ catch (err) {
76
+ clientWs.close(1011, err.message || 'Failed to create speech session');
77
+ }
78
+ }
79
+ export default router;
80
+ //# sourceMappingURL=speech-recognition.js.map
@@ -9,6 +9,7 @@ import { listChannels, updateChannel } from './channel.js';
9
9
  import { ensureDir, getDataDir } from '../storage/json-store.js';
10
10
  import { extractUsageFromOutput } from '../storage/usage.js';
11
11
  const DEFAULT_AGENT_ROLE = 'agent';
12
+ export const AGENT_GENERATOR_PRESET_ID = 'agent-generator';
12
13
  const VALID_RUNTIME_KINDS = ['open-agent-sdk', 'claude-code', 'codex', 'langchain'];
13
14
  const VALID_TOOL_NAMES = new Set(BUILT_IN_AGENT_TOOLS.map((tool) => tool.name));
14
15
  const ANTHROPIC_BRIDGE_PROVIDERS = [
@@ -24,11 +25,15 @@ export function isValidRole(role) {
24
25
  export function listTemplates() {
25
26
  const root = getGlobalAgentTemplatesDir();
26
27
  if (!existsSync(root))
27
- return [];
28
- return readdirSync(root, { withFileTypes: true })
28
+ return [getDefaultAgentGeneratorPreset()];
29
+ const templates = readdirSync(root, { withFileTypes: true })
29
30
  .filter((entry) => entry.isDirectory())
30
31
  .map((entry) => readAgentTemplate(entry.name))
31
32
  .filter((template) => Boolean(template));
33
+ if (!templates.some((template) => template.id === AGENT_GENERATOR_PRESET_ID)) {
34
+ templates.unshift(getDefaultAgentGeneratorPreset());
35
+ }
36
+ return templates;
32
37
  }
33
38
  export async function testConnection(workspaceId, data) {
34
39
  if (workspaceId) {
@@ -509,21 +514,57 @@ function ensureWorkspaceAgentCopy(preset, agentspaceDir) {
509
514
  }
510
515
  export function readAgentTemplate(agentId) {
511
516
  const filePath = join(getGlobalAgentTemplateDir(agentId), 'agent.json');
512
- if (!existsSync(filePath))
513
- return null;
517
+ if (!existsSync(filePath)) {
518
+ return agentId === AGENT_GENERATOR_PRESET_ID ? getDefaultAgentGeneratorPreset() : null;
519
+ }
514
520
  try {
515
521
  return JSON.parse(readFileSync(filePath, 'utf-8'));
516
522
  }
517
523
  catch {
518
- return null;
524
+ return agentId === AGENT_GENERATOR_PRESET_ID ? getDefaultAgentGeneratorPreset() : null;
519
525
  }
520
526
  }
527
+ function getDefaultAgentGeneratorPreset() {
528
+ return {
529
+ id: AGENT_GENERATOR_PRESET_ID,
530
+ name: 'Agent Generator',
531
+ role: 'agent',
532
+ description: '根据提示词生成其他 Agent 的名称、描述和 Markdown 系统提示。',
533
+ runtimeKind: 'claude-code',
534
+ modelProvider: undefined,
535
+ modelId: '',
536
+ apiBase: '',
537
+ apiKey: '',
538
+ workingDir: '',
539
+ mcps: {},
540
+ skills: [],
541
+ tools: [],
542
+ systemPrompt: [
543
+ '# Role',
544
+ 'You are an Agent Spaces agent designer.',
545
+ '',
546
+ '# Responsibilities',
547
+ '- Convert user requirements into production-ready Agent preset metadata.',
548
+ '- Return only valid JSON with name, description, and systemPrompt.',
549
+ '- Write the systemPrompt in clear Markdown with role, responsibilities, workflow, constraints, and output expectations.',
550
+ '',
551
+ '# Constraints',
552
+ '- Do not include Markdown code fences around JSON.',
553
+ '- Keep names concise and descriptions practical.',
554
+ ].join('\n'),
555
+ temperature: 0.2,
556
+ maxTokens: 4096,
557
+ enabled: true,
558
+ };
559
+ }
521
560
  function sanitizeMarkdownFilename(name) {
522
561
  const raw = basename(name).replace(/\.md$/i, '');
523
562
  const safe = raw.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'skill';
524
563
  return `${safe}.md`;
525
564
  }
526
565
  export function deletePreset(workspaceId, presetId) {
566
+ if (presetId === AGENT_GENERATOR_PRESET_ID)
567
+ return false;
527
568
  const ws = getWorkspace(workspaceId);
528
569
  if (!ws)
529
570
  return null;
@@ -611,6 +652,8 @@ export function updateGlobalPreset(presetId, data) {
611
652
  return updated;
612
653
  }
613
654
  export function deleteGlobalPreset(presetId) {
655
+ if (presetId === AGENT_GENERATOR_PRESET_ID)
656
+ return false;
614
657
  const templateDir = getGlobalAgentTemplateDir(presetId);
615
658
  if (!existsSync(templateDir))
616
659
  return false;