@agent-spaces/server 0.3.65 → 0.4.0

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 (278) hide show
  1. package/dist/adapters/agent-runtime.js +8 -0
  2. package/dist/adapters/codex-function-tool-bridge.js +152 -0
  3. package/dist/adapters/codex-runtime.js +23 -4
  4. package/dist/adapters/git.js +25 -3
  5. package/dist/adapters/hermes-runtime.js +813 -0
  6. package/dist/adapters/langchain-runtime.js +384 -21
  7. package/dist/adapters/oh-my-pi-runtime.js +858 -0
  8. package/dist/adapters/open-agent-sdk-runtime.js +202 -5
  9. package/dist/adapters/open-agent-sdk-runtime.test.js +35 -0
  10. package/dist/agents/agent-message-parts.js +52 -2
  11. package/dist/agents/issue-task-controller.js +71 -2
  12. package/dist/agents/pull-request-agent.js +115 -0
  13. package/dist/agents/scheduler-agent.js +1 -1
  14. package/dist/agents/title-generator-agent.js +120 -0
  15. package/dist/app.js +61 -4
  16. package/dist/middleware/auth.js +1 -1
  17. package/dist/package.json +2 -1
  18. package/dist/routes/agent-sse.js +70 -6
  19. package/dist/routes/channel.js +23 -2
  20. package/dist/routes/chat-run.js +191 -0
  21. package/dist/routes/chat.js +142 -0
  22. package/dist/routes/command.js +16 -0
  23. package/dist/routes/data.js +189 -0
  24. package/dist/routes/git.js +10 -1
  25. package/dist/routes/import.js +199 -0
  26. package/dist/routes/issue.js +17 -5
  27. package/dist/routes/notification.js +10 -0
  28. package/dist/routes/plugin.js +69 -0
  29. package/dist/routes/version.js +95 -0
  30. package/dist/routes/workflow-hook.js +71 -0
  31. package/dist/routes/workflow.js +282 -7
  32. package/dist/routes/workspace.js +13 -4
  33. package/dist/routes/worktree.js +93 -0
  34. package/dist/services/agent.js +123 -36
  35. package/dist/services/ai-text.js +185 -0
  36. package/dist/services/builtin-tools/index.js +1 -0
  37. package/dist/services/builtin-tools/workflow-editor-tools.js +509 -0
  38. package/dist/services/builtin-tools/workflow-exec-tools.js +320 -0
  39. package/dist/services/chat.js +134 -0
  40. package/dist/services/command-process-manager.js +16 -0
  41. package/dist/services/execution-manager.js +1346 -0
  42. package/dist/services/generated-title.js +59 -0
  43. package/dist/services/gitignore.js +22 -18
  44. package/dist/services/interaction-manager.js +114 -0
  45. package/dist/services/issue-retry.js +25 -0
  46. package/dist/services/notification-center.js +9 -0
  47. package/dist/services/output-style.js +8 -1
  48. package/dist/services/plugin.js +257 -0
  49. package/dist/services/prompt-template.js +8 -1
  50. package/dist/services/pty.js +20 -0
  51. package/dist/services/search.js +16 -6
  52. package/dist/services/skill.js +2 -0
  53. package/dist/services/version.js +69 -0
  54. package/dist/services/workflow-command-runner.js +24 -0
  55. package/dist/services/workflow-trigger-service.js +137 -0
  56. package/dist/services/workflow.js +219 -39
  57. package/dist/services/workspace.js +23 -1
  58. package/dist/services/worktree.js +207 -0
  59. package/dist/storage/agent-store.js +8 -0
  60. package/dist/storage/chat-store.js +151 -0
  61. package/dist/storage/database-store.js +6 -0
  62. package/dist/storage/json-store.js +2 -1
  63. package/dist/storage/kanban-store.js +6 -0
  64. package/dist/storage/workflow-store.js +386 -22
  65. package/dist/storage/worktree-store.js +33 -0
  66. package/dist/web/404.html +1 -1
  67. package/dist/web/__next.__PAGE__.txt +4 -4
  68. package/dist/web/__next._full.txt +21 -21
  69. package/dist/web/__next._head.txt +4 -4
  70. package/dist/web/__next._index.txt +13 -13
  71. package/dist/web/__next._tree.txt +2 -2
  72. package/dist/web/_next/static/chunks/{08x85faoz58i7.js → 0-92u-tiuy40a.js} +4 -4
  73. package/dist/web/_next/static/chunks/{0db14450oqzhg.js → 0-yizd8osmas2.js} +1 -1
  74. package/dist/web/_next/static/chunks/0.ln~3mnv8.69.js +1 -0
  75. package/dist/web/_next/static/chunks/{11cchuwoupmbf.js → 01y3e9pemva7b.js} +10 -10
  76. package/dist/web/_next/static/chunks/02l~tmzrgny.~.js +1 -0
  77. package/dist/web/_next/static/chunks/03f1bwuynjdmk.js +1 -0
  78. package/dist/web/_next/static/chunks/04cb4kc9r2pd1.js +1 -0
  79. package/dist/web/_next/static/chunks/04de6ha5_e3pp.js +1 -0
  80. package/dist/web/_next/static/chunks/09he0p79ov1rn.js +1 -0
  81. package/dist/web/_next/static/chunks/09vpocs4zuc8h.js +1 -0
  82. package/dist/web/_next/static/chunks/0_o2exgr0e_nh.js +1 -0
  83. package/dist/web/_next/static/chunks/{0xbaakj0dfc0..js → 0_y9nskc-xt2k.js} +1 -1
  84. package/dist/web/_next/static/chunks/0clnj-1fi6u8j.js +3 -0
  85. package/dist/web/_next/static/chunks/0gv40by0zzxs_.js +1 -0
  86. package/dist/web/_next/static/chunks/0iusr8y88.kjg.js +1 -0
  87. package/dist/web/_next/static/chunks/{0-a.-hl3wv99k.js → 0j-8418fxvkd1.js} +1 -1
  88. package/dist/web/_next/static/chunks/0j.wxxyoclma5.js +1 -0
  89. package/dist/web/_next/static/chunks/{0.6b64sy0g5.3.js → 0jq.16sozqik..js} +1 -1
  90. package/dist/web/_next/static/chunks/0jw8xdk_fpnp0.css +1 -0
  91. package/dist/web/_next/static/chunks/0l1lkd-wcotma.js +1 -0
  92. package/dist/web/_next/static/chunks/{0u6h-hqc_i52e.js → 0ncxf_zr4n839.js} +1 -1
  93. package/dist/web/_next/static/chunks/0ndo7um~v7vco.js +1 -0
  94. package/dist/web/_next/static/chunks/0ngk.~kan8wq_.js +1 -0
  95. package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +1 -0
  96. package/dist/web/_next/static/chunks/0rt9bofnn6qvn.js +1 -0
  97. package/dist/web/_next/static/chunks/0t64.9jd00hio.js +3 -0
  98. package/dist/web/_next/static/chunks/0taui9--5n6-5.js +1 -0
  99. package/dist/web/_next/static/chunks/0ty39169lh~z3.js +1 -0
  100. package/dist/web/_next/static/chunks/0v8nof~waedlu.js +1 -0
  101. package/dist/web/_next/static/chunks/{0-pi.67w.ymsr.js → 0vcfoagnpf.aw.js} +1 -1
  102. package/dist/web/_next/static/chunks/{0vis~lx0nxgn-.js → 0wwwq.gyyz0vg.js} +3 -3
  103. package/dist/web/_next/static/chunks/0x5lak8yz7pm7.js +1 -0
  104. package/dist/web/_next/static/chunks/0xon_.4-o0_zq.js +7 -0
  105. package/dist/web/_next/static/chunks/0zjnrbt7inime.js +1 -0
  106. package/dist/web/_next/static/chunks/0zwebpitvpuv1.js +1 -0
  107. package/dist/web/_next/static/chunks/0~02o4g-o6n~m.js +1 -0
  108. package/dist/web/_next/static/chunks/{0w7.tb29958zj.js → 0~fjezp.k4qk3.js} +1 -1
  109. package/dist/web/_next/static/chunks/0~sh93re1-_rp.js +1 -0
  110. package/dist/web/_next/static/chunks/10_niiq0k6o-p.js +5 -0
  111. package/dist/web/_next/static/chunks/12tjr0_2ccrtq.js +2 -0
  112. package/dist/web/_next/static/chunks/12x5.go_x9mfk.js +1 -0
  113. package/dist/web/_next/static/chunks/{0l2w0qz1ea7ql.js → 12~5dk9i9d16t.js} +2 -2
  114. package/dist/web/_next/static/chunks/13iye4rv5p31b.js +1 -0
  115. package/dist/web/_next/static/chunks/{01~quun6sefgl.js → 13~y-b.b9rrw4.js} +1 -1
  116. package/dist/web/_next/static/chunks/151wzhb5q0lo5.js +1 -0
  117. package/dist/web/_not-found/__next._full.txt +19 -19
  118. package/dist/web/_not-found/__next._head.txt +4 -4
  119. package/dist/web/_not-found/__next._index.txt +13 -13
  120. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  121. package/dist/web/_not-found/__next._not-found.txt +3 -3
  122. package/dist/web/_not-found/__next._tree.txt +2 -2
  123. package/dist/web/_not-found.html +1 -1
  124. package/dist/web/_not-found.txt +19 -19
  125. package/dist/web/index.html +1 -1
  126. package/dist/web/index.txt +21 -21
  127. package/dist/web/login/__next._full.txt +21 -21
  128. package/dist/web/login/__next._head.txt +4 -4
  129. package/dist/web/login/__next._index.txt +13 -13
  130. package/dist/web/login/__next._tree.txt +2 -2
  131. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  132. package/dist/web/login/__next.login.txt +3 -3
  133. package/dist/web/login.html +1 -1
  134. package/dist/web/login.txt +21 -21
  135. package/dist/web/settings/__next._full.txt +23 -23
  136. package/dist/web/settings/__next._head.txt +4 -4
  137. package/dist/web/settings/__next._index.txt +13 -13
  138. package/dist/web/settings/__next._tree.txt +2 -2
  139. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  140. package/dist/web/settings/__next.settings.txt +5 -5
  141. package/dist/web/settings/agents/__next._full.txt +23 -23
  142. package/dist/web/settings/agents/__next._head.txt +4 -4
  143. package/dist/web/settings/agents/__next._index.txt +13 -13
  144. package/dist/web/settings/agents/__next._tree.txt +2 -2
  145. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  146. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  147. package/dist/web/settings/agents/__next.settings.txt +5 -5
  148. package/dist/web/settings/agents.html +1 -1
  149. package/dist/web/settings/agents.txt +23 -23
  150. package/dist/web/settings/mcps/__next._full.txt +23 -23
  151. package/dist/web/settings/mcps/__next._head.txt +4 -4
  152. package/dist/web/settings/mcps/__next._index.txt +13 -13
  153. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  154. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  155. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  156. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  157. package/dist/web/settings/mcps.html +1 -1
  158. package/dist/web/settings/mcps.txt +23 -23
  159. package/dist/web/settings/models/__next._full.txt +23 -23
  160. package/dist/web/settings/models/__next._head.txt +4 -4
  161. package/dist/web/settings/models/__next._index.txt +13 -13
  162. package/dist/web/settings/models/__next._tree.txt +2 -2
  163. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  164. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  165. package/dist/web/settings/models/__next.settings.txt +5 -5
  166. package/dist/web/settings/models.html +1 -1
  167. package/dist/web/settings/models.txt +23 -23
  168. package/dist/web/settings/output-styles/__next._full.txt +23 -23
  169. package/dist/web/settings/output-styles/__next._head.txt +4 -4
  170. package/dist/web/settings/output-styles/__next._index.txt +13 -13
  171. package/dist/web/settings/output-styles/__next._tree.txt +2 -2
  172. package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
  173. package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
  174. package/dist/web/settings/output-styles/__next.settings.txt +5 -5
  175. package/dist/web/settings/output-styles.html +1 -1
  176. package/dist/web/settings/output-styles.txt +23 -23
  177. package/dist/web/settings/prompts/__next._full.txt +23 -23
  178. package/dist/web/settings/prompts/__next._head.txt +4 -4
  179. package/dist/web/settings/prompts/__next._index.txt +13 -13
  180. package/dist/web/settings/prompts/__next._tree.txt +2 -2
  181. package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
  182. package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
  183. package/dist/web/settings/prompts/__next.settings.txt +5 -5
  184. package/dist/web/settings/prompts.html +1 -1
  185. package/dist/web/settings/prompts.txt +23 -23
  186. package/dist/web/settings/providers/__next._full.txt +23 -23
  187. package/dist/web/settings/providers/__next._head.txt +4 -4
  188. package/dist/web/settings/providers/__next._index.txt +13 -13
  189. package/dist/web/settings/providers/__next._tree.txt +2 -2
  190. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  191. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  192. package/dist/web/settings/providers/__next.settings.txt +5 -5
  193. package/dist/web/settings/providers.html +1 -1
  194. package/dist/web/settings/providers.txt +23 -23
  195. package/dist/web/settings/skills/__next._full.txt +23 -23
  196. package/dist/web/settings/skills/__next._head.txt +4 -4
  197. package/dist/web/settings/skills/__next._index.txt +13 -13
  198. package/dist/web/settings/skills/__next._tree.txt +2 -2
  199. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  200. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  201. package/dist/web/settings/skills/__next.settings.txt +5 -5
  202. package/dist/web/settings/skills.html +1 -1
  203. package/dist/web/settings/skills.txt +23 -23
  204. package/dist/web/settings/tools/__next._full.txt +23 -23
  205. package/dist/web/settings/tools/__next._head.txt +4 -4
  206. package/dist/web/settings/tools/__next._index.txt +13 -13
  207. package/dist/web/settings/tools/__next._tree.txt +2 -2
  208. package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
  209. package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
  210. package/dist/web/settings/tools/__next.settings.txt +5 -5
  211. package/dist/web/settings/tools.html +1 -1
  212. package/dist/web/settings/tools.txt +23 -23
  213. package/dist/web/settings.html +1 -1
  214. package/dist/web/settings.txt +23 -23
  215. package/dist/web/workflows/__next._full.txt +22 -22
  216. package/dist/web/workflows/__next._head.txt +4 -4
  217. package/dist/web/workflows/__next._index.txt +13 -13
  218. package/dist/web/workflows/__next._tree.txt +2 -2
  219. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  220. package/dist/web/workflows/__next.workflows.txt +3 -3
  221. package/dist/web/workflows.html +1 -1
  222. package/dist/web/workflows.txt +22 -22
  223. package/dist/web/workspace/_/__next._full.txt +20 -20
  224. package/dist/web/workspace/_/__next._head.txt +4 -4
  225. package/dist/web/workspace/_/__next._index.txt +13 -13
  226. package/dist/web/workspace/_/__next._tree.txt +2 -2
  227. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  228. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  229. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  230. package/dist/web/workspace/_.html +1 -1
  231. package/dist/web/workspace/_.txt +20 -20
  232. package/dist/web/workspaces/__next._full.txt +21 -21
  233. package/dist/web/workspaces/__next._head.txt +4 -4
  234. package/dist/web/workspaces/__next._index.txt +13 -13
  235. package/dist/web/workspaces/__next._tree.txt +2 -2
  236. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  237. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  238. package/dist/web/workspaces.html +1 -1
  239. package/dist/web/workspaces.txt +21 -21
  240. package/dist/ws/agent-prompt.js +36 -3
  241. package/dist/ws/agent-runner.js +30 -18
  242. package/dist/ws/chat-handler.js +89 -0
  243. package/dist/ws/connection-manager.js +49 -2
  244. package/dist/ws/execution-channels.js +88 -0
  245. package/dist/ws/handler.js +32 -5
  246. package/dist/ws/message-parts.js +130 -12
  247. package/dist/ws/terminal-handler.js +26 -0
  248. package/package.json +13 -1
  249. package/public/avatars/user.jpg +0 -0
  250. package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +0 -1
  251. package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +0 -1
  252. package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +0 -1
  253. package/dist/web/_next/static/chunks/0.n~yfwf4~6w-.js +0 -1
  254. package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +0 -1
  255. package/dist/web/_next/static/chunks/02j4sr9psucf-.js +0 -1
  256. package/dist/web/_next/static/chunks/06si8l0doqe8p.js +0 -1
  257. package/dist/web/_next/static/chunks/08kfx8on9oty~.js +0 -1
  258. package/dist/web/_next/static/chunks/08ugbvzr20drp.js +0 -2
  259. package/dist/web/_next/static/chunks/0_cqojbge48kq.js +0 -5
  260. package/dist/web/_next/static/chunks/0d6~~lv0-6kex.js +0 -3
  261. package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +0 -1
  262. package/dist/web/_next/static/chunks/0ghx3j_.-gdyd.js +0 -1
  263. package/dist/web/_next/static/chunks/0kbvoxla7f751.js +0 -1
  264. package/dist/web/_next/static/chunks/0paaat8y96ayc.js +0 -7
  265. package/dist/web/_next/static/chunks/0pq318f8xgafg.js +0 -1
  266. package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +0 -1
  267. package/dist/web/_next/static/chunks/0xt-8wk21gs7g.js +0 -1
  268. package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +0 -3
  269. package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +0 -1
  270. package/dist/web/_next/static/chunks/0z~vqzbm_q371.js +0 -1
  271. package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +0 -1
  272. package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +0 -1
  273. package/dist/web/_next/static/chunks/1452eibz62gs6.css +0 -1
  274. package/dist/web/_next/static/chunks/16zlreiems5cn.js +0 -1
  275. package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +0 -1
  276. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_buildManifest.js +0 -0
  277. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_clientMiddlewareManifest.js +0 -0
  278. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_ssgManifest.js +0 -0
@@ -1,7 +1,7 @@
1
1
  // packages/server/src/services/search.ts
2
- import { execSync } from 'node:child_process';
2
+ import { execFileSync } from 'node:child_process';
3
3
  import { readdir, stat, readFile } from 'node:fs/promises';
4
- import { join, relative } from 'node:path';
4
+ import { isAbsolute, join, relative, resolve } from 'node:path';
5
5
  import { minimatch } from 'minimatch';
6
6
  import { resolvePath } from './file.js';
7
7
  import { createGitignoreFilter } from './gitignore.js';
@@ -13,8 +13,15 @@ function isBinaryPath(path) {
13
13
  const ext = path.split('.').pop()?.toLowerCase() || '';
14
14
  return BINARY_EXTENSIONS.has(`.${ext}`) || ext === path;
15
15
  }
16
+ function toWorkspaceRelativePath(basePath, path) {
17
+ const absPath = isAbsolute(path) ? resolve(path) : resolve(basePath, path);
18
+ const relPath = relative(basePath, absPath);
19
+ if (relPath.startsWith('..') || isAbsolute(relPath))
20
+ return null;
21
+ return relPath;
22
+ }
16
23
  function searchWithRipgrep(basePath, options) {
17
- const args = ['rg', '--json', '--max-count', String(options.maxResults || 200)];
24
+ const args = ['--json', '--max-count', String(options.maxResults || 200)];
18
25
  if (!options.caseSensitive)
19
26
  args.push('-i');
20
27
  if (options.regex) {
@@ -25,9 +32,10 @@ function searchWithRipgrep(basePath, options) {
25
32
  }
26
33
  if (options.filePattern)
27
34
  args.push('--glob', options.filePattern);
28
- args.push('--', '.', basePath);
35
+ args.push('--', '.');
29
36
  try {
30
- const output = execSync(args.join(' '), {
37
+ const output = execFileSync('rg', args, {
38
+ cwd: basePath,
31
39
  encoding: 'utf-8',
32
40
  maxBuffer: 50 * 1024 * 1024,
33
41
  timeout: 30000,
@@ -40,7 +48,9 @@ function searchWithRipgrep(basePath, options) {
40
48
  const parsed = JSON.parse(line);
41
49
  if (parsed.type !== 'match')
42
50
  continue;
43
- const filePath = relative(basePath, parsed.data.path.text);
51
+ const filePath = toWorkspaceRelativePath(basePath, parsed.data.path.text);
52
+ if (!filePath)
53
+ continue;
44
54
  const text = parsed.data.lines.text.trimEnd();
45
55
  const sub = parsed.data.submatches[0];
46
56
  if (!sub)
@@ -168,6 +168,8 @@ export function importSkillFromStore(storePath, group) {
168
168
  const candidates = [
169
169
  join(process.cwd(), 'public', 'skills', storePath),
170
170
  join(import.meta.dirname ?? '', '..', 'public', 'skills', storePath),
171
+ join(process.cwd(), 'packages', 'agents', 'skills', storePath),
172
+ join(import.meta.dirname ?? '', '..', '..', '..', 'agents', 'skills', storePath),
171
173
  ];
172
174
  for (const c of candidates) {
173
175
  if (existsSync(c) && statSync(c).isDirectory()) {
@@ -0,0 +1,69 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ let localVersion = '';
6
+ let cachedLatest = null;
7
+ const CACHE_TTL = 3600_000; // 1 hour
8
+ export function getLocalVersion() {
9
+ if (localVersion !== '')
10
+ return localVersion;
11
+ const packageJsonPaths = [
12
+ join(__dirname, '..', '..', 'package.json'),
13
+ join(__dirname, '..', 'package.json'),
14
+ ];
15
+ for (const packageJsonPath of packageJsonPaths) {
16
+ if (!existsSync(packageJsonPath))
17
+ continue;
18
+ try {
19
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
20
+ localVersion = pkg.version || '0.0.0';
21
+ return localVersion;
22
+ }
23
+ catch {
24
+ // Try the next package.json candidate before falling back.
25
+ }
26
+ }
27
+ localVersion = '0.0.0';
28
+ return localVersion;
29
+ }
30
+ export function getCachedLatest() {
31
+ if (cachedLatest && Date.now() - cachedLatest.ts < CACHE_TTL) {
32
+ return cachedLatest.version;
33
+ }
34
+ return null;
35
+ }
36
+ export async function fetchLatestVersion(forceRefresh = false) {
37
+ if (!forceRefresh && cachedLatest && Date.now() - cachedLatest.ts < CACHE_TTL) {
38
+ return cachedLatest.version;
39
+ }
40
+ try {
41
+ const res = await fetch('https://registry.npmjs.org/@agent-spaces/server', {
42
+ signal: AbortSignal.timeout(8000),
43
+ });
44
+ if (!res.ok)
45
+ return cachedLatest?.version ?? null;
46
+ const data = await res.json();
47
+ const latest = data?.['dist-tags']?.latest;
48
+ if (latest) {
49
+ cachedLatest = { version: latest, ts: Date.now() };
50
+ }
51
+ return latest ?? cachedLatest?.version ?? null;
52
+ }
53
+ catch {
54
+ return cachedLatest?.version ?? null;
55
+ }
56
+ }
57
+ export function isNewerVersion(latest, local) {
58
+ const parse = (v) => v.replace(/^v/, '').split('.').map(Number);
59
+ const a = parse(latest);
60
+ const b = parse(local);
61
+ for (let i = 0; i < 3; i++) {
62
+ if ((a[i] || 0) > (b[i] || 0))
63
+ return true;
64
+ if ((a[i] || 0) < (b[i] || 0))
65
+ return false;
66
+ }
67
+ return false;
68
+ }
69
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1,24 @@
1
+ import { exec } from 'child_process';
2
+ import { getWorkspace } from '../storage/workspace-store.js';
3
+ export async function executeCommandNode(workspaceId, node) {
4
+ const workspace = getWorkspace(workspaceId);
5
+ const data = node.data;
6
+ const cwd = data.cwd || workspace?.boundDirs?.[0] || process.cwd();
7
+ return new Promise((resolve) => {
8
+ exec(data.script, {
9
+ cwd,
10
+ env: { ...process.env, ...data.env },
11
+ shell: data.shell || undefined,
12
+ timeout: 300_000,
13
+ maxBuffer: 10 * 1024 * 1024,
14
+ }, (error, stdout, stderr) => {
15
+ resolve({
16
+ success: !error,
17
+ exitCode: error ? (typeof error.code === 'number' ? error.code : 1) : 0,
18
+ stdout: stdout || '',
19
+ stderr: stderr || '',
20
+ });
21
+ });
22
+ });
23
+ }
24
+ //# sourceMappingURL=workflow-command-runner.js.map
@@ -0,0 +1,137 @@
1
+ import * as store from '../storage/workflow-store.js';
2
+ export class WorkflowTriggerService {
3
+ port;
4
+ cronJobs = new Map();
5
+ hookIndex = new Map();
6
+ nodeCron = null;
7
+ executionManager = null;
8
+ constructor(port = 3100) {
9
+ this.port = port;
10
+ try {
11
+ this.nodeCron = require('node-cron');
12
+ }
13
+ catch {
14
+ // node-cron not available
15
+ }
16
+ }
17
+ setExecutionManager(em) {
18
+ this.executionManager = em;
19
+ }
20
+ async start() {
21
+ const workflows = store.listWorkflows();
22
+ for (const wf of workflows) {
23
+ this.registerTriggers(wf);
24
+ }
25
+ console.log(`[TriggerService] Started. ${this.cronJobs.size} cron jobs, ${this.hookIndex.size} hooks registered`);
26
+ }
27
+ reloadWorkflow(workflowId) {
28
+ this.clearTriggersForWorkflow(workflowId);
29
+ const wf = store.getWorkflow(workflowId);
30
+ if (wf)
31
+ this.registerTriggers(wf);
32
+ }
33
+ removeWorkflow(workflowId) {
34
+ this.clearTriggersForWorkflow(workflowId);
35
+ }
36
+ getHookBindings(hookName) {
37
+ return Array.from(this.hookIndex.get(hookName) ?? []);
38
+ }
39
+ getHookConflicts(hookName, excludeWorkflowId) {
40
+ const bindings = this.hookIndex.get(hookName) ?? new Set();
41
+ const ids = Array.from(bindings)
42
+ .map(b => b.workflowId)
43
+ .filter(id => id !== excludeWorkflowId);
44
+ return { conflictWorkflowIds: [...new Set(ids)] };
45
+ }
46
+ getHookUrl(hookName) {
47
+ return `http://localhost:${this.port}/api/workflows/hook/${hookName}`;
48
+ }
49
+ validateCron(cronExpr) {
50
+ if (!this.nodeCron)
51
+ return { valid: false, nextRuns: [], error: 'node-cron not available' };
52
+ if (!this.nodeCron.validate(cronExpr)) {
53
+ return { valid: false, nextRuns: [], error: 'Invalid cron expression' };
54
+ }
55
+ try {
56
+ const CronExpressionParser = require('cron-parser');
57
+ const interval = CronExpressionParser.parse(cronExpr);
58
+ const nextRuns = [];
59
+ for (let i = 0; i < 5; i++) {
60
+ const iso = interval.next().toISOString();
61
+ if (iso)
62
+ nextRuns.push(iso);
63
+ }
64
+ return { valid: true, nextRuns };
65
+ }
66
+ catch (err) {
67
+ return { valid: false, nextRuns: [], error: err.message };
68
+ }
69
+ }
70
+ stop() {
71
+ for (const [, task] of this.cronJobs) {
72
+ task.stop();
73
+ }
74
+ this.cronJobs.clear();
75
+ this.hookIndex.clear();
76
+ }
77
+ registerTriggers(wf) {
78
+ if (!wf.triggers)
79
+ return;
80
+ for (const trigger of wf.triggers) {
81
+ if (!trigger.enabled)
82
+ continue;
83
+ if (trigger.type === 'cron') {
84
+ this.registerCronJob(wf.id, trigger);
85
+ }
86
+ else if (trigger.type === 'hook') {
87
+ this.registerHookBinding(wf.id, trigger);
88
+ }
89
+ }
90
+ }
91
+ registerCronJob(workflowId, trigger) {
92
+ if (!this.nodeCron)
93
+ return;
94
+ const key = `${workflowId}:${trigger.id}`;
95
+ try {
96
+ const task = this.nodeCron.schedule(trigger.cron, () => {
97
+ console.log(`[TriggerService] Cron fired for workflow ${workflowId}`);
98
+ if (this.executionManager) {
99
+ this.executionManager.execute({ workflowId }, '__cron__').catch((err) => {
100
+ console.error(`[TriggerService] Cron execution failed for ${workflowId}: ${err.message}`);
101
+ });
102
+ }
103
+ }, { timezone: trigger.timezone });
104
+ this.cronJobs.set(key, task);
105
+ }
106
+ catch (err) {
107
+ console.error(`[TriggerService] Invalid cron "${trigger.cron}" for workflow ${workflowId}: ${err.message}`);
108
+ }
109
+ }
110
+ registerHookBinding(workflowId, trigger) {
111
+ let bindings = this.hookIndex.get(trigger.hookName);
112
+ if (!bindings) {
113
+ bindings = new Set();
114
+ this.hookIndex.set(trigger.hookName, bindings);
115
+ }
116
+ bindings.add({ workflowId, triggerId: trigger.id });
117
+ }
118
+ clearTriggersForWorkflow(workflowId) {
119
+ for (const [key, task] of this.cronJobs) {
120
+ if (key.startsWith(`${workflowId}:`)) {
121
+ task.stop();
122
+ this.cronJobs.delete(key);
123
+ }
124
+ }
125
+ for (const [hookName, bindings] of this.hookIndex) {
126
+ for (const binding of bindings) {
127
+ if (binding.workflowId === workflowId) {
128
+ bindings.delete(binding);
129
+ }
130
+ }
131
+ if (bindings.size === 0) {
132
+ this.hookIndex.delete(hookName);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ //# sourceMappingURL=workflow-trigger-service.js.map
@@ -1,11 +1,16 @@
1
1
  // packages/server/src/services/workflow.ts
2
- // NOTE: All storage functions are synchronous (matching json-store pattern).
3
- // Service functions are also synchronous where they only call sync storage.
4
- // Route handlers wrap in async for Express compatibility.
5
2
  import { v4 as uuid } from 'uuid';
6
- import * as workflowStore from '../storage/workflow-store.js';
3
+ import * as store from '../storage/workflow-store.js';
7
4
  import { listTemplates } from './agent.js';
8
- // --- DAG Validation ---
5
+ function getAgentData(node) {
6
+ if (node.type !== 'agent')
7
+ return null;
8
+ return node.data;
9
+ }
10
+ function getLabel(node) {
11
+ return node.data.label || node.label;
12
+ }
13
+ // ---- DAG Validation ----
9
14
  function topologicalSort(nodes, edges) {
10
15
  const adj = new Map();
11
16
  const inDegree = new Map();
@@ -14,8 +19,10 @@ function topologicalSort(nodes, edges) {
14
19
  inDegree.set(node.id, 0);
15
20
  }
16
21
  for (const edge of edges) {
17
- adj.get(edge.source).push(edge.target);
18
- inDegree.set(edge.target, (inDegree.get(edge.target) ?? 0) + 1);
22
+ const source = edge.source;
23
+ const target = edge.target;
24
+ adj.get(source).push(target);
25
+ inDegree.set(target, (inDegree.get(target) ?? 0) + 1);
19
26
  }
20
27
  const queue = [];
21
28
  for (const [id, deg] of inDegree) {
@@ -67,12 +74,17 @@ export function validateDAG(template) {
67
74
  }
68
75
  return null;
69
76
  }
70
- // --- Role Staleness Resolution ---
77
+ // ---- Role Staleness Resolution ----
71
78
  function resolveStaleRoles(nodes) {
72
79
  const agentMap = new Map(listTemplates().map(a => [a.id, a]));
73
80
  const invalidIds = [];
74
81
  const resolved = nodes.map(node => {
75
- const agent = agentMap.get(node.data.agentConfigId);
82
+ if (node.type === 'command')
83
+ return node;
84
+ const agentData = getAgentData(node);
85
+ if (!agentData)
86
+ return node;
87
+ const agent = agentMap.get(agentData.agentConfigId);
76
88
  if (!agent) {
77
89
  invalidIds.push(node.id);
78
90
  return node;
@@ -89,39 +101,43 @@ function resolveStaleRoles(nodes) {
89
101
  });
90
102
  return { nodes: resolved, invalidIds };
91
103
  }
92
- // --- CRUD (synchronous) ---
93
- export function listWorkflows() {
94
- return workflowStore.listWorkflows();
104
+ // ---- Workflow CRUD ----
105
+ export function listWorkflows(folderId) {
106
+ return store.listWorkflows(folderId);
95
107
  }
96
108
  export function getWorkflow(workflowId) {
97
- return workflowStore.getWorkflow(workflowId);
109
+ return store.getWorkflow(workflowId);
98
110
  }
99
111
  export function createWorkflow(input) {
100
- const now = new Date().toISOString();
112
+ const now = Date.now();
101
113
  const nodes = input.nodes ?? [];
102
114
  const edges = input.edges ?? [];
103
115
  const { nodes: resolvedNodes, invalidIds } = resolveStaleRoles(nodes);
104
116
  if (invalidIds.length > 0) {
105
117
  throw new Error(`Invalid agent references in nodes: ${invalidIds.join(', ')}`);
106
118
  }
107
- const template = {
119
+ const workflow = {
108
120
  id: uuid(),
109
121
  name: input.name,
122
+ folderId: input.folderId ?? null,
123
+ icon: input.icon,
110
124
  description: input.description,
125
+ tags: input.tags,
111
126
  nodes: resolvedNodes,
112
127
  edges,
113
- viewport: input.viewport,
114
128
  createdAt: now,
115
129
  updatedAt: now,
130
+ triggers: input.triggers,
131
+ groups: input.groups,
116
132
  };
117
- const error = validateDAG(template);
133
+ const error = validateDAG(workflow);
118
134
  if (error)
119
135
  throw new Error(error);
120
- workflowStore.createWorkflow(template);
121
- return template;
136
+ store.createWorkflow(workflow);
137
+ return workflow;
122
138
  }
123
139
  export function updateWorkflow(workflowId, updates) {
124
- const existing = workflowStore.getWorkflow(workflowId);
140
+ const existing = store.getWorkflow(workflowId);
125
141
  if (!existing)
126
142
  throw new Error('Workflow not found');
127
143
  let nodes = updates.nodes ?? existing.nodes;
@@ -138,27 +154,27 @@ export function updateWorkflow(workflowId, updates) {
138
154
  nodes,
139
155
  id: existing.id,
140
156
  createdAt: existing.createdAt,
141
- updatedAt: new Date().toISOString(),
157
+ updatedAt: Date.now(),
142
158
  };
143
159
  if (updates.nodes || updates.edges) {
144
160
  const error = validateDAG(updated);
145
161
  if (error)
146
162
  throw new Error(error);
147
163
  }
148
- workflowStore.updateWorkflow(updated);
164
+ store.updateWorkflow(updated);
149
165
  return updated;
150
166
  }
151
167
  export function deleteWorkflow(workflowId) {
152
- const existing = workflowStore.getWorkflow(workflowId);
168
+ const existing = store.getWorkflow(workflowId);
153
169
  if (!existing)
154
170
  throw new Error('Workflow not found');
155
- workflowStore.deleteWorkflow(workflowId);
171
+ store.deleteWorkflow(workflowId);
156
172
  }
157
173
  export function duplicateWorkflow(workflowId) {
158
- const existing = workflowStore.getWorkflow(workflowId);
174
+ const existing = store.getWorkflow(workflowId);
159
175
  if (!existing)
160
176
  throw new Error('Workflow not found');
161
- const now = new Date().toISOString();
177
+ const now = Date.now();
162
178
  const duplicated = {
163
179
  ...existing,
164
180
  id: uuid(),
@@ -166,9 +182,124 @@ export function duplicateWorkflow(workflowId) {
166
182
  createdAt: now,
167
183
  updatedAt: now,
168
184
  };
169
- workflowStore.createWorkflow(duplicated);
185
+ store.createWorkflow(duplicated);
170
186
  return duplicated;
171
187
  }
188
+ // ---- Folder CRUD ----
189
+ export function listFolders() {
190
+ return store.listWorkflowFolders();
191
+ }
192
+ export function createFolder(input) {
193
+ const folders = store.listWorkflowFolders();
194
+ const siblings = folders.filter(f => f.parentId === (input.parentId ?? null));
195
+ const folder = {
196
+ id: uuid(),
197
+ name: input.name,
198
+ parentId: input.parentId ?? null,
199
+ order: siblings.length > 0 ? Math.max(...siblings.map(f => f.order)) + 1 : 0,
200
+ createdAt: Date.now(),
201
+ };
202
+ store.createWorkflowFolder(folder);
203
+ return folder;
204
+ }
205
+ export function updateFolder(id, updates) {
206
+ store.updateWorkflowFolder(id, updates);
207
+ }
208
+ export function deleteFolder(id) {
209
+ store.deleteWorkflowFolder(id);
210
+ }
211
+ // ---- Version CRUD ----
212
+ export function listVersions(workflowId) {
213
+ return store.listVersions(workflowId);
214
+ }
215
+ export function createVersion(workflowId, name) {
216
+ const workflow = store.getWorkflow(workflowId);
217
+ if (!workflow)
218
+ throw new Error('Workflow not found');
219
+ const version = {
220
+ id: uuid(),
221
+ workflowId,
222
+ name: name || `v${store.listVersions(workflowId).length + 1}`,
223
+ snapshot: {
224
+ nodes: JSON.parse(JSON.stringify(workflow.nodes)),
225
+ edges: JSON.parse(JSON.stringify(workflow.edges)),
226
+ },
227
+ createdAt: Date.now(),
228
+ };
229
+ store.addVersion(workflowId, version);
230
+ return version;
231
+ }
232
+ export function getVersion(workflowId, versionId) {
233
+ return store.getVersion(workflowId, versionId);
234
+ }
235
+ export function deleteVersion(workflowId, versionId) {
236
+ store.deleteVersion(workflowId, versionId);
237
+ }
238
+ export function clearVersions(workflowId) {
239
+ store.clearVersions(workflowId);
240
+ }
241
+ // ---- Execution Log CRUD ----
242
+ export function listExecutionLogs(workflowId) {
243
+ return store.listExecutionLogs(workflowId);
244
+ }
245
+ export function listAllExecutionLogs(limit) {
246
+ return store.listAllExecutionLogs(limit);
247
+ }
248
+ export function getExecutionLog(workflowId, logId) {
249
+ return store.getExecutionLog(workflowId, logId);
250
+ }
251
+ export function deleteExecutionLog(workflowId, logId) {
252
+ store.deleteExecutionLog(workflowId, logId);
253
+ }
254
+ export function clearExecutionLogs(workflowId) {
255
+ store.clearExecutionLogs(workflowId);
256
+ }
257
+ // ---- Staging ----
258
+ export function loadStaging(workflowId) {
259
+ return store.loadStaging(workflowId);
260
+ }
261
+ export function saveStaging(workflowId, nodes) {
262
+ store.saveStaging(workflowId, nodes);
263
+ }
264
+ export function clearStaging(workflowId) {
265
+ store.clearStaging(workflowId);
266
+ }
267
+ // ---- Operation History ----
268
+ export function loadOperationHistory(workflowId) {
269
+ return store.loadOperationHistory(workflowId);
270
+ }
271
+ export function saveOperationHistory(workflowId, entries) {
272
+ store.saveOperationHistory(workflowId, entries);
273
+ }
274
+ export function clearOperationHistory(workflowId) {
275
+ store.clearOperationHistory(workflowId);
276
+ }
277
+ // ---- Workflow Agent Chat ----
278
+ export function loadChat(workflowId) {
279
+ return store.loadChat(workflowId);
280
+ }
281
+ export function saveChat(workflowId, messages) {
282
+ store.saveChat(workflowId, messages);
283
+ }
284
+ export function clearChat(workflowId) {
285
+ store.clearChat(workflowId);
286
+ }
287
+ // ---- Plugin Config Schemes ----
288
+ export function listPluginSchemes(workflowId, pluginId) {
289
+ return store.listPluginSchemes(workflowId, pluginId);
290
+ }
291
+ export function readPluginScheme(workflowId, pluginId, schemeName) {
292
+ return store.readPluginScheme(workflowId, pluginId, schemeName);
293
+ }
294
+ export function createPluginScheme(workflowId, pluginId, schemeName) {
295
+ store.savePluginScheme(workflowId, pluginId, schemeName, {});
296
+ }
297
+ export function savePluginScheme(workflowId, pluginId, schemeName, data) {
298
+ store.savePluginScheme(workflowId, pluginId, schemeName, data);
299
+ }
300
+ export function deletePluginScheme(workflowId, pluginId, schemeName) {
301
+ store.deletePluginScheme(workflowId, pluginId, schemeName);
302
+ }
172
303
  export function mapWorkflowToTaskDrafts(template) {
173
304
  const dependsOn = new Map();
174
305
  for (const node of template.nodes) {
@@ -177,27 +308,76 @@ export function mapWorkflowToTaskDrafts(template) {
177
308
  for (const edge of template.edges) {
178
309
  dependsOn.get(edge.target)?.push(edge.source);
179
310
  }
180
- return template.nodes.map(node => ({
181
- key: node.id,
182
- title: node.data.taskTitleTemplate || node.data.label,
183
- description: node.data.taskDescriptionTemplate || `Task assigned to ${node.data.label} (${node.data.role})`,
184
- agentConfigId: node.data.agentConfigId,
185
- dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
186
- sandboxDirs: undefined,
187
- }));
311
+ return template.nodes.map(node => {
312
+ const label = getLabel(node);
313
+ if (node.type === 'command') {
314
+ return {
315
+ key: node.id,
316
+ title: label,
317
+ description: `Run command: ${label}`,
318
+ agentConfigId: undefined,
319
+ dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
320
+ sandboxDirs: undefined,
321
+ commandNode: node,
322
+ };
323
+ }
324
+ const agentData = getAgentData(node);
325
+ const taskTitle = (agentData?.taskTitleTemplate || label);
326
+ const taskDesc = agentData?.taskDescriptionTemplate || `Task assigned to ${label} (${agentData?.role || 'unknown'})`;
327
+ return {
328
+ key: node.id,
329
+ title: taskTitle,
330
+ description: taskDesc,
331
+ agentConfigId: agentData?.agentConfigId,
332
+ dependsOnKeys: dependsOn.get(node.id)?.length ? dependsOn.get(node.id) : undefined,
333
+ sandboxDirs: undefined,
334
+ };
335
+ });
188
336
  }
189
- // --- Run-time Validation ---
337
+ // ---- Run-time Validation ----
190
338
  export function validateWorkflowForRun(_workspaceId, template, memberAgentIds) {
191
339
  const agentMap = new Map(listTemplates().map((a) => [a.id, a]));
192
340
  for (const node of template.nodes) {
193
- const agent = agentMap.get(node.data.agentConfigId);
341
+ if (node.type === 'command')
342
+ continue;
343
+ const agentData = getAgentData(node);
344
+ if (!agentData)
345
+ continue;
346
+ const label = getLabel(node);
347
+ const agent = agentMap.get(agentData.agentConfigId);
194
348
  if (!agent)
195
- return `Agent "${node.data.label}" (${node.data.agentConfigId}) no longer exists`;
349
+ return `Agent "${label}" (${agentData.agentConfigId}) no longer exists`;
196
350
  if (!agent.enabled)
197
351
  return `Agent "${agent.name}" is disabled`;
198
- if (!memberAgentIds.has(node.data.agentConfigId))
352
+ if (!memberAgentIds.has(agentData.agentConfigId))
199
353
  return `Agent "${agent.name}" is not in the issue channel members`;
200
354
  }
201
355
  return null;
202
356
  }
357
+ // ---- Cron Validation ----
358
+ export function validateCron(cronExpr) {
359
+ try {
360
+ const { validate } = require('node-cron');
361
+ if (!validate(cronExpr)) {
362
+ return { valid: false, nextRuns: [], error: 'Invalid cron expression' };
363
+ }
364
+ }
365
+ catch {
366
+ // If node-cron validate fails to load, continue with cron-parser
367
+ }
368
+ try {
369
+ const CronExpressionParser = require('cron-parser');
370
+ const interval = CronExpressionParser.parse(cronExpr);
371
+ const nextRuns = [];
372
+ for (let i = 0; i < 5; i++) {
373
+ const iso = interval.next().toISOString();
374
+ if (iso)
375
+ nextRuns.push(iso);
376
+ }
377
+ return { valid: true, nextRuns };
378
+ }
379
+ catch (err) {
380
+ return { valid: false, nextRuns: [], error: err.message };
381
+ }
382
+ }
203
383
  //# sourceMappingURL=workflow.js.map
@@ -1,5 +1,6 @@
1
1
  import { v4 as uuid } from 'uuid';
2
2
  import { listWorkspaces, getWorkspace, createWorkspace, updateWorkspace, deleteWorkspace } from '../storage/workspace-store.js';
3
+ import { listWorktrees } from '../storage/worktree-store.js';
3
4
  import { ensureDir } from '../storage/json-store.js';
4
5
  import { join } from 'node:path';
5
6
  import { existsSync, writeFileSync } from 'node:fs';
@@ -21,7 +22,14 @@ function initAgentspace(agentspaceDir) {
21
22
  }
22
23
  }
23
24
  export function getAll() {
24
- return listWorkspaces();
25
+ const workspaces = listWorkspaces();
26
+ const workspaceIds = new Set(workspaces.map((workspace) => workspace.id));
27
+ const worktreeWorkspaces = workspaces
28
+ .filter((workspace) => !workspace.isWorktree)
29
+ .flatMap((workspace) => listWorktrees(workspace.id)
30
+ .filter((worktree) => worktree.status === 'active' && !workspaceIds.has(worktree.id))
31
+ .map(worktreeToWorkspace));
32
+ return [...workspaces, ...worktreeWorkspaces];
25
33
  }
26
34
  export function getById(id) {
27
35
  return getWorkspace(id);
@@ -72,4 +80,18 @@ export function remove(id) {
72
80
  deleteWorkspace(id);
73
81
  return true;
74
82
  }
83
+ function worktreeToWorkspace(worktree) {
84
+ return {
85
+ id: worktree.id,
86
+ name: `${worktree.name} (Worktree)`,
87
+ boundDirs: [worktree.path],
88
+ agentspaceDir: join(worktree.path, '.agentspace'),
89
+ isWorktree: true,
90
+ parentWorkspaceId: worktree.workspaceId,
91
+ createdAt: worktree.createdAt,
92
+ updatedAt: worktree.updatedAt,
93
+ activeChannels: [],
94
+ activeIssues: [],
95
+ };
96
+ }
75
97
  //# sourceMappingURL=workspace.js.map