upfynai-code 2.9.0 → 2.9.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.
Files changed (229) hide show
  1. package/README.md +91 -66
  2. package/client/dist/api-docs.html +838 -0
  3. package/client/dist/assets/AppContent-BXZDeSIC.js +545 -0
  4. package/client/dist/assets/CanvasFullScreen-mnpCnLZ9.js +1 -0
  5. package/client/dist/assets/CanvasWorkspace-4CqmjAVQ.js +163 -0
  6. package/client/dist/assets/DashboardPanel-zFIFlw56.js +1 -0
  7. package/client/dist/assets/FileTree-B0c_GaB3.js +1 -0
  8. package/client/dist/assets/GitPanel-DUP4zVU4.js +2 -0
  9. package/client/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  10. package/client/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  11. package/client/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  12. package/client/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  13. package/client/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  14. package/client/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  15. package/client/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  16. package/client/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  17. package/client/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  18. package/client/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  19. package/client/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  20. package/client/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  21. package/client/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  22. package/client/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  23. package/client/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  24. package/client/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  25. package/client/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  26. package/client/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  27. package/client/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  28. package/client/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  29. package/client/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  30. package/client/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  31. package/client/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  32. package/client/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  33. package/client/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  34. package/client/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  35. package/client/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  36. package/client/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  37. package/client/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  38. package/client/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  39. package/client/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  40. package/client/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  41. package/client/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  42. package/client/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  43. package/client/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  44. package/client/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  45. package/client/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  46. package/client/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  47. package/client/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  48. package/client/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  49. package/client/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  50. package/client/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  51. package/client/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  52. package/client/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  53. package/client/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  54. package/client/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  55. package/client/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  56. package/client/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  57. package/client/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  58. package/client/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  59. package/client/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  60. package/client/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  61. package/client/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  62. package/client/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  63. package/client/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  64. package/client/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  65. package/client/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  66. package/client/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  67. package/client/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  68. package/client/dist/assets/LoginModal-BRycfsyD.js +13 -0
  69. package/client/dist/assets/MarkdownPreview-DHmk3qzu.js +1 -0
  70. package/client/dist/assets/MermaidBlock-BuBc_G-F.js +2 -0
  71. package/client/dist/assets/Onboarding-BcnaZZ0o.js +1 -0
  72. package/client/dist/assets/PreviewPanel-CqCa92Tf.js +32 -0
  73. package/client/dist/assets/SetupForm-S0g6u5yT.js +1 -0
  74. package/client/dist/assets/WorkflowsPanel-CouH9JDO.js +1 -0
  75. package/client/dist/assets/index-BFuqS0tY.css +1 -0
  76. package/client/dist/assets/index-CNDcVl2g.js +68 -0
  77. package/client/dist/assets/pdf-CE_K4jFx.js +12 -0
  78. package/client/dist/assets/vendor-canvas-BZV40eAE.css +1 -0
  79. package/client/dist/assets/vendor-canvas-D39yWul6.js +49 -0
  80. package/client/dist/assets/vendor-codemirror-CbtmxxaB.js +35 -0
  81. package/client/dist/assets/vendor-diff-DNQpbhrT.js +69 -0
  82. package/client/dist/assets/vendor-i18n-DCFGyhQR.js +1 -0
  83. package/client/dist/assets/vendor-icons-BaD0x9SL.js +711 -0
  84. package/client/dist/assets/vendor-markdown-CimbIo6Y.js +296 -0
  85. package/client/dist/assets/vendor-mermaid-CH7SGc99.js +2556 -0
  86. package/client/dist/assets/vendor-react-96lCPsRK.js +67 -0
  87. package/client/dist/assets/vendor-syntax-DuHI9Ok6.js +16 -0
  88. package/client/dist/assets/vendor-xterm-CZq1hqo1.js +66 -0
  89. package/client/dist/assets/vendor-xterm-qxJ8_QYu.css +32 -0
  90. package/client/dist/clear-cache.html +85 -0
  91. package/client/dist/convert-icons.md +53 -0
  92. package/client/dist/favicon.png +0 -0
  93. package/client/dist/favicon.svg +5 -0
  94. package/client/dist/generate-icons.js +49 -0
  95. package/client/dist/icons/claude-ai-icon.svg +1 -0
  96. package/client/dist/icons/codex-white.svg +3 -0
  97. package/client/dist/icons/codex.svg +3 -0
  98. package/client/dist/icons/cursor-white.svg +12 -0
  99. package/client/dist/icons/cursor.svg +1 -0
  100. package/client/dist/icons/icon-128x128.png +0 -0
  101. package/client/dist/icons/icon-128x128.svg +5 -0
  102. package/client/dist/icons/icon-144x144.png +0 -0
  103. package/client/dist/icons/icon-144x144.svg +5 -0
  104. package/client/dist/icons/icon-152x152.png +0 -0
  105. package/client/dist/icons/icon-152x152.svg +5 -0
  106. package/client/dist/icons/icon-192x192.png +0 -0
  107. package/client/dist/icons/icon-192x192.svg +5 -0
  108. package/client/dist/icons/icon-384x384.png +0 -0
  109. package/client/dist/icons/icon-384x384.svg +5 -0
  110. package/client/dist/icons/icon-512x512.png +0 -0
  111. package/client/dist/icons/icon-512x512.svg +5 -0
  112. package/client/dist/icons/icon-72x72.png +0 -0
  113. package/client/dist/icons/icon-72x72.svg +5 -0
  114. package/client/dist/icons/icon-96x96.png +0 -0
  115. package/client/dist/icons/icon-96x96.svg +5 -0
  116. package/client/dist/icons/icon-template.svg +5 -0
  117. package/client/dist/index.html +119 -0
  118. package/client/dist/logo-128.png +0 -0
  119. package/client/dist/logo-256.png +0 -0
  120. package/client/dist/logo-32.png +0 -0
  121. package/client/dist/logo-512.png +0 -0
  122. package/client/dist/logo-64.png +0 -0
  123. package/client/dist/logo.svg +14 -0
  124. package/client/dist/manifest.json +61 -0
  125. package/client/dist/mcp-docs.html +108 -0
  126. package/client/dist/offline.html +84 -0
  127. package/client/dist/screenshots/cli-selection.png +0 -0
  128. package/client/dist/screenshots/desktop-main.png +0 -0
  129. package/client/dist/screenshots/mobile-chat.png +0 -0
  130. package/client/dist/screenshots/tools-modal.png +0 -0
  131. package/client/dist/sw.js +82 -0
  132. package/commands/upfynai-connect.md +59 -0
  133. package/commands/upfynai-disconnect.md +31 -0
  134. package/commands/upfynai-doctor.md +99 -0
  135. package/commands/upfynai-export.md +49 -0
  136. package/commands/upfynai-local.md +82 -0
  137. package/commands/upfynai-status.md +75 -0
  138. package/commands/upfynai-stop.md +49 -0
  139. package/commands/upfynai-uninstall.md +58 -0
  140. package/commands/upfynai.md +69 -0
  141. package/package.json +143 -82
  142. package/scripts/build-client.js +17 -0
  143. package/scripts/fix-node-pty.js +67 -0
  144. package/scripts/install-commands.js +78 -0
  145. package/server/agent-loop.js +242 -0
  146. package/server/auto-compact.js +99 -0
  147. package/server/claude-sdk.js +797 -0
  148. package/server/cli-ui.js +785 -0
  149. package/server/cli.js +596 -0
  150. package/server/constants/config.js +31 -0
  151. package/server/cursor-cli.js +270 -0
  152. package/server/database/auth.db +0 -0
  153. package/server/database/db.js +1391 -0
  154. package/server/database/init.sql +70 -0
  155. package/server/index.js +3799 -0
  156. package/server/load-env.js +26 -0
  157. package/server/mcp-server.js +621 -0
  158. package/server/middleware/auth.js +176 -0
  159. package/server/middleware/relayHelpers.js +44 -0
  160. package/server/middleware/sandboxRouter.js +174 -0
  161. package/server/openai-codex.js +403 -0
  162. package/server/openrouter.js +137 -0
  163. package/server/projects.js +1807 -0
  164. package/server/provider-factory.js +174 -0
  165. package/server/relay-client.js +379 -0
  166. package/server/routes/agent.js +1226 -0
  167. package/server/routes/auth.js +554 -0
  168. package/server/routes/canvas.js +53 -0
  169. package/server/routes/cli-auth.js +263 -0
  170. package/server/routes/codex.js +396 -0
  171. package/server/routes/commands.js +707 -0
  172. package/server/routes/composio.js +176 -0
  173. package/server/routes/cursor.js +770 -0
  174. package/server/routes/dashboard.js +295 -0
  175. package/server/routes/git.js +1208 -0
  176. package/server/routes/keys.js +34 -0
  177. package/server/routes/mcp-utils.js +48 -0
  178. package/server/routes/mcp.js +661 -0
  179. package/server/routes/payments.js +227 -0
  180. package/server/routes/projects.js +655 -0
  181. package/server/routes/sessions.js +146 -0
  182. package/server/routes/settings.js +261 -0
  183. package/server/routes/taskmaster.js +1928 -0
  184. package/server/routes/user.js +106 -0
  185. package/server/routes/vapi-chat.js +624 -0
  186. package/server/routes/voice.js +235 -0
  187. package/server/routes/webhooks.js +166 -0
  188. package/server/routes/workflows.js +312 -0
  189. package/server/sandbox.js +120 -0
  190. package/server/services/composio.js +204 -0
  191. package/server/services/sessionRegistry.js +139 -0
  192. package/server/services/whisperService.js +84 -0
  193. package/server/services/workflowScheduler.js +206 -0
  194. package/server/tests/relay-flow.test.js +570 -0
  195. package/server/tests/sessions.test.js +259 -0
  196. package/server/utils/commandParser.js +303 -0
  197. package/server/utils/email.js +61 -0
  198. package/server/utils/gitConfig.js +24 -0
  199. package/server/utils/mcp-detector.js +198 -0
  200. package/server/utils/taskmaster-websocket.js +129 -0
  201. package/shared/integrationCatalog.d.ts +12 -0
  202. package/shared/integrationCatalog.js +172 -0
  203. package/shared/modelConstants.js +96 -0
  204. package/bin/cli.js +0 -97
  205. package/dist/agents/claude.js +0 -229
  206. package/dist/agents/codex.js +0 -48
  207. package/dist/agents/cursor.js +0 -48
  208. package/dist/agents/detect.js +0 -51
  209. package/dist/agents/exec.js +0 -31
  210. package/dist/agents/files.js +0 -105
  211. package/dist/agents/git.js +0 -18
  212. package/dist/agents/gitagent.js +0 -67
  213. package/dist/agents/index.js +0 -88
  214. package/dist/agents/shell.js +0 -38
  215. package/dist/agents/utils.js +0 -136
  216. package/scripts/postinstall.js +0 -9
  217. package/scripts/prepublish.js +0 -58
  218. package/src/animation.js +0 -228
  219. package/src/auth.js +0 -122
  220. package/src/config.js +0 -40
  221. package/src/connect.js +0 -416
  222. package/src/launch.js +0 -78
  223. package/src/mcp.js +0 -57
  224. package/src/permissions.js +0 -140
  225. package/src/persistent-shell.js +0 -261
  226. package/src/server.js +0 -54
  227. /package/{dist → shared}/gitagent/index.js +0 -0
  228. /package/{dist → shared}/gitagent/parser.js +0 -0
  229. /package/{dist → shared}/gitagent/prompt-builder.js +0 -0
package/bin/cli.js DELETED
@@ -1,97 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from 'commander';
4
- import { readFileSync } from 'fs';
5
- import { dirname, join } from 'path';
6
- import { fileURLToPath } from 'url';
7
- import { login, logout, status } from '../src/auth.js';
8
- import { openHosted, startLocal } from '../src/launch.js';
9
- import { connect } from '../src/connect.js';
10
- import { mcp } from '../src/mcp.js';
11
- import { readConfig, writeConfig, clearConfig, DEFAULTS, displayUrl } from '../src/config.js';
12
- import chalk from 'chalk';
13
-
14
- const __dirname = dirname(fileURLToPath(import.meta.url));
15
- const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
16
-
17
- const program = new Command();
18
-
19
- program
20
- .name('upfynai-code')
21
- .description('Launch Upfyn AI coding environment from your terminal')
22
- .version(pkg.version)
23
- .option('--local', 'Start a local server instead of opening the hosted app')
24
- .action(async (options) => {
25
- if (options.local) {
26
- await startLocal();
27
- } else {
28
- await openHosted();
29
- }
30
- });
31
-
32
- program
33
- .command('login')
34
- .description('Authenticate with your Upfyn account')
35
- .option('--server <url>', 'Server URL to authenticate against')
36
- .action(async (options) => {
37
- await login(options);
38
- });
39
-
40
- program
41
- .command('logout')
42
- .description('Clear saved credentials')
43
- .action(async () => {
44
- await logout();
45
- });
46
-
47
- program
48
- .command('status')
49
- .description('Show current auth status and config')
50
- .action(async () => {
51
- await status();
52
- });
53
-
54
- program
55
- .command('connect')
56
- .description('Connect local machine to the remote server (bridges Claude Code, shell, files, git)')
57
- .option('--server <url>', 'Server URL to connect to')
58
- .option('--key <token>', 'Connection token — get from Settings > Keys & Connect')
59
- .action(async (options) => {
60
- await connect(options);
61
- });
62
-
63
- program
64
- .command('mcp')
65
- .description('Show MCP integration config for Claude / Cursor')
66
- .option('--server <url>', 'Server URL')
67
- .option('--key <token>', 'Relay token')
68
- .action(async (options) => {
69
- await mcp(options);
70
- });
71
-
72
- program
73
- .command('config')
74
- .description('View or update CLI configuration')
75
- .option('--server <url>', 'Set the server URL')
76
- .option('--reset', 'Reset config to defaults')
77
- .action((options) => {
78
- if (options.reset) {
79
- clearConfig();
80
- console.log(chalk.green('\n Config reset to defaults.\n'));
81
- return;
82
- }
83
- if (options.server) {
84
- const cleaned = options.server.replace(/\/+$/, '');
85
- writeConfig({ serverUrl: cleaned });
86
- console.log(chalk.green(`\n Server set to: ${displayUrl(cleaned)}\n`));
87
- return;
88
- }
89
- // Show current config
90
- const config = readConfig();
91
- console.log(chalk.bold('\n Upfyn Code — Config\n'));
92
- console.log(` Server: ${chalk.cyan(displayUrl(config.serverUrl))}`);
93
- console.log(` Local port: ${chalk.cyan(config.localPort)}`);
94
- console.log(` Logged in: ${config.token ? chalk.green('Yes') : chalk.yellow('No')}\n`);
95
- });
96
-
97
- program.parse();
@@ -1,229 +0,0 @@
1
- /**
2
- * Claude Agent
3
- * Handles: claude-query, claude-task-query
4
- *
5
- * These are STREAMING actions — they use ctx.stream() to send chunks
6
- * and return a Promise that resolves when the process completes.
7
- *
8
- * Three modes:
9
- * - sdk (connect.js default) — full SDK streaming with rich messages (tool_use, text, result)
10
- * - simple --print (relay-client.js) — basic stdout streaming
11
- *
12
- * The caller chooses the mode via ctx.streamMode ('structured' | 'simple').
13
- * 'structured' now maps to SDK mode for rich message forwarding.
14
- */
15
- import { query } from '@anthropic-ai/claude-agent-sdk';
16
- import { spawn } from 'child_process';
17
- import os from 'os';
18
-
19
- export default {
20
- name: 'claude',
21
- actions: {
22
- 'claude-query': async (params, ctx) => {
23
- const { command, options } = params;
24
- const mode = ctx.streamMode || 'structured';
25
-
26
- if (mode === 'simple') {
27
- const resolveBinary = ctx.resolveBinary || ((name) => name);
28
- return runSimpleClaudeQuery(command, options, ctx, resolveBinary);
29
- }
30
- return runSDKClaudeQuery(command, options, ctx);
31
- },
32
-
33
- 'claude-task-query': async (params, ctx) => {
34
- const { command, options } = params;
35
- return runSDKClaudeTaskQuery(command, options, ctx);
36
- },
37
- },
38
- };
39
-
40
- /**
41
- * Build SDK options from relay command options.
42
- * Maps the relay format to SDK Options type.
43
- */
44
- function buildSDKOptions(options = {}) {
45
- const sdkOptions = {
46
- cwd: options.projectPath || os.homedir(),
47
- systemPrompt: { type: 'preset', preset: 'claude_code' },
48
- settingSources: ['project', 'user', 'local'],
49
- };
50
-
51
- if (options.sessionId) {
52
- sdkOptions.resume = options.sessionId;
53
- }
54
-
55
- if (options.model) {
56
- sdkOptions.model = options.model;
57
- }
58
-
59
- if (options.maxBudgetUsd) {
60
- sdkOptions.maxBudgetUsd = options.maxBudgetUsd;
61
- }
62
-
63
- if (options.permissionMode && options.permissionMode !== 'default') {
64
- sdkOptions.permissionMode = options.permissionMode;
65
- }
66
-
67
- if (options.allowedTools && options.allowedTools.length > 0) {
68
- sdkOptions.allowedTools = options.allowedTools;
69
- }
70
-
71
- if (options.disallowedTools && options.disallowedTools.length > 0) {
72
- sdkOptions.disallowedTools = options.disallowedTools;
73
- }
74
-
75
- if (options.maxTurns) {
76
- sdkOptions.maxTurns = options.maxTurns;
77
- }
78
-
79
- // Pass through tools preset to make all Claude Code tools available
80
- sdkOptions.tools = { type: 'preset', preset: 'claude_code' };
81
-
82
- return sdkOptions;
83
- }
84
-
85
- /**
86
- * SDK-based structured streaming mode.
87
- * Forwards rich SDK messages (tool_use, tool_result, text, system, result)
88
- * through ctx.stream() for the relay to forward to the frontend.
89
- */
90
- async function runSDKClaudeQuery(command, options, ctx) {
91
- const sdkOptions = buildSDKOptions(options);
92
-
93
- // Set stream-close timeout for interactive tools
94
- const prevStreamTimeout = process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
95
- process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = '300000';
96
-
97
- let queryInstance;
98
- try {
99
- queryInstance = query({
100
- prompt: command || '',
101
- options: sdkOptions,
102
- });
103
- } finally {
104
- // Restore immediately — Query constructor already captured the value
105
- if (prevStreamTimeout !== undefined) {
106
- process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT = prevStreamTimeout;
107
- } else {
108
- delete process.env.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT;
109
- }
110
- }
111
-
112
- // Track the query instance for abort support (uses .interrupt() instead of .kill())
113
- if (ctx.trackProcess) {
114
- ctx.trackProcess(ctx.requestId, { instance: queryInstance, action: 'claude-query' });
115
- }
116
-
117
- let capturedSessionId = null;
118
-
119
- try {
120
- for await (const message of queryInstance) {
121
- // Capture session ID from first message that has one
122
- if (message.session_id && !capturedSessionId) {
123
- capturedSessionId = message.session_id;
124
- }
125
-
126
- // Forward the full SDK message — the backend routeViaRelay() will
127
- // detect 'claude-sdk-message' type and forward directly to frontend,
128
- // matching the same format as queryClaudeSDK() in claude-sdk.js
129
- ctx.stream({
130
- type: 'claude-sdk-message',
131
- data: message,
132
- sessionId: capturedSessionId,
133
- });
134
- }
135
-
136
- return { exitCode: 0, sessionId: capturedSessionId };
137
- } catch (error) {
138
- ctx.stream({ type: 'claude-error', content: error.message || 'SDK query failed' });
139
- return { exitCode: 1, sessionId: capturedSessionId };
140
- } finally {
141
- if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
142
- }
143
- }
144
-
145
- /**
146
- * SDK-based sub-agent for read-only research tasks.
147
- * Restricts tools to read-only operations.
148
- */
149
- async function runSDKClaudeTaskQuery(command, options, ctx) {
150
- const sdkOptions = buildSDKOptions(options);
151
-
152
- // Restrict to read-only tools
153
- sdkOptions.allowedTools = ['View', 'Glob', 'Grep', 'LS', 'Read'];
154
-
155
- let queryInstance;
156
- try {
157
- queryInstance = query({
158
- prompt: command || '',
159
- options: sdkOptions,
160
- });
161
- } catch (error) {
162
- ctx.stream({ type: 'claude-error', content: error.message || 'SDK task query failed' });
163
- return { exitCode: 1 };
164
- }
165
-
166
- if (ctx.trackProcess) {
167
- ctx.trackProcess(ctx.requestId, { instance: queryInstance, action: 'claude-task-query' });
168
- }
169
-
170
- let capturedSessionId = null;
171
-
172
- try {
173
- for await (const message of queryInstance) {
174
- if (message.session_id && !capturedSessionId) {
175
- capturedSessionId = message.session_id;
176
- }
177
-
178
- ctx.stream({
179
- type: 'claude-sdk-message',
180
- data: message,
181
- sessionId: capturedSessionId,
182
- });
183
- }
184
-
185
- return { exitCode: 0, sessionId: capturedSessionId };
186
- } catch (error) {
187
- ctx.stream({ type: 'claude-error', content: error.message || 'SDK task query failed' });
188
- return { exitCode: 1, sessionId: capturedSessionId };
189
- } finally {
190
- if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
191
- }
192
- }
193
-
194
- /**
195
- * Simple streaming mode (--print).
196
- * Just pipes stdout/stderr chunks. Kept for backward compat with relay-client.js.
197
- */
198
- function runSimpleClaudeQuery(command, options, ctx, resolveBinary) {
199
- return new Promise((resolve) => {
200
- const args = ['--print'];
201
- if (options?.sessionId) args.push('--continue', options.sessionId);
202
-
203
- const proc = spawn(resolveBinary('claude'), [...args, command || ''], {
204
- shell: true,
205
- cwd: options?.projectPath || os.homedir(),
206
- env: process.env,
207
- });
208
-
209
- if (ctx.trackProcess) ctx.trackProcess(ctx.requestId, { proc, action: 'claude-query' });
210
-
211
- proc.stdout.on('data', (chunk) => {
212
- ctx.stream({ type: 'claude-response', content: chunk.toString() });
213
- });
214
-
215
- proc.stderr.on('data', (chunk) => {
216
- ctx.stream({ type: 'claude-error', content: chunk.toString() });
217
- });
218
-
219
- proc.on('close', (code) => {
220
- if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
221
- resolve({ exitCode: code });
222
- });
223
-
224
- proc.on('error', () => {
225
- if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
226
- resolve({ exitCode: 1 });
227
- });
228
- });
229
- }
@@ -1,48 +0,0 @@
1
- /**
2
- * Codex Agent
3
- * Handles: codex-query
4
- * Spawns the OpenAI Codex CLI and streams output.
5
- */
6
- import { spawn } from 'child_process';
7
- import os from 'os';
8
-
9
- export default {
10
- name: 'codex',
11
- actions: {
12
- 'codex-query': async (params, ctx) => {
13
- const { command, options } = params;
14
- const resolveBinary = ctx.resolveBinary || ((name) => name);
15
-
16
- return new Promise((resolve) => {
17
- const args = ['--quiet'];
18
- if (options?.model) args.push('--model', options.model);
19
-
20
- const proc = spawn(resolveBinary('codex'), [...args, command || ''], {
21
- shell: true,
22
- cwd: options?.projectPath || options?.cwd || os.homedir(),
23
- env: process.env,
24
- });
25
-
26
- if (ctx.trackProcess) ctx.trackProcess(ctx.requestId, { proc, action: 'codex-query' });
27
-
28
- proc.stdout.on('data', (chunk) => {
29
- ctx.stream({ type: 'codex-response', content: chunk.toString() });
30
- });
31
-
32
- proc.stderr.on('data', (chunk) => {
33
- ctx.stream({ type: 'codex-error', content: chunk.toString() });
34
- });
35
-
36
- proc.on('close', (code) => {
37
- if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
38
- resolve({ exitCode: code });
39
- });
40
-
41
- proc.on('error', () => {
42
- if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
43
- resolve({ exitCode: 1 });
44
- });
45
- });
46
- },
47
- },
48
- };
@@ -1,48 +0,0 @@
1
- /**
2
- * Cursor Agent
3
- * Handles: cursor-query
4
- * Spawns the Cursor Agent CLI and streams output.
5
- */
6
- import { spawn } from 'child_process';
7
- import os from 'os';
8
-
9
- export default {
10
- name: 'cursor',
11
- actions: {
12
- 'cursor-query': async (params, ctx) => {
13
- const { command, options } = params;
14
- const resolveBinary = ctx.resolveBinary || ((name) => name);
15
-
16
- return new Promise((resolve) => {
17
- const args = [];
18
- if (options?.model) args.push('--model', options.model);
19
-
20
- const proc = spawn(resolveBinary('cursor-agent'), [...args, command || ''], {
21
- shell: true,
22
- cwd: options?.projectPath || options?.cwd || os.homedir(),
23
- env: process.env,
24
- });
25
-
26
- if (ctx.trackProcess) ctx.trackProcess(ctx.requestId, { proc, action: 'cursor-query' });
27
-
28
- proc.stdout.on('data', (chunk) => {
29
- ctx.stream({ type: 'cursor-response', content: chunk.toString() });
30
- });
31
-
32
- proc.stderr.on('data', (chunk) => {
33
- ctx.stream({ type: 'cursor-error', content: chunk.toString() });
34
- });
35
-
36
- proc.on('close', (code) => {
37
- if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
38
- resolve({ exitCode: code });
39
- });
40
-
41
- proc.on('error', () => {
42
- if (ctx.untrackProcess) ctx.untrackProcess(ctx.requestId);
43
- resolve({ exitCode: 1 });
44
- });
45
- });
46
- },
47
- },
48
- };
@@ -1,51 +0,0 @@
1
- /**
2
- * Detect Agent
3
- * Handles: detect-agents
4
- * Checks which AI CLI agents are installed on the local machine.
5
- */
6
- import { execSync } from 'child_process';
7
- import fs from 'fs';
8
- import path from 'path';
9
-
10
- const AGENT_DEFINITIONS = [
11
- { name: 'claude', binary: 'claude', label: 'Claude Code' },
12
- { name: 'codex', binary: 'codex', label: 'OpenAI Codex' },
13
- { name: 'cursor', binary: 'cursor-agent', label: 'Cursor Agent' },
14
- ];
15
-
16
- export default {
17
- name: 'detect',
18
- actions: {
19
- 'detect-agents': async (params, ctx) => {
20
- const isWindows = process.platform === 'win32';
21
- const whichCmd = isWindows ? 'where' : 'which';
22
- const localBinDir = ctx.localBinDir || null;
23
-
24
- const agents = {};
25
- for (const agent of AGENT_DEFINITIONS) {
26
- try {
27
- const result = execSync(`${whichCmd} ${agent.binary}`, {
28
- stdio: 'pipe',
29
- timeout: 5000,
30
- }).toString().trim();
31
- agents[agent.name] = {
32
- installed: true,
33
- path: result.split('\n')[0].trim(),
34
- label: agent.label,
35
- };
36
- } catch {
37
- // Check local node_modules/.bin as fallback
38
- if (localBinDir) {
39
- const localPath = path.join(localBinDir, agent.binary + (isWindows ? '.cmd' : ''));
40
- if (fs.existsSync(localPath)) {
41
- agents[agent.name] = { installed: true, path: localPath, label: agent.label };
42
- continue;
43
- }
44
- }
45
- agents[agent.name] = { installed: false, label: agent.label };
46
- }
47
- }
48
- return { agents };
49
- },
50
- },
51
- };
@@ -1,31 +0,0 @@
1
- /**
2
- * Exec Agent
3
- * Handles: exec (arbitrary command execution)
4
- */
5
- import { execSync } from 'child_process';
6
-
7
- export default {
8
- name: 'exec',
9
- actions: {
10
- 'exec': async (params) => {
11
- const { command, timeout: cmdTimeout = 60000, cwd } = params;
12
- if (!command) throw new Error('No command provided');
13
-
14
- try {
15
- const output = execSync(command, {
16
- encoding: 'utf8',
17
- timeout: cmdTimeout,
18
- cwd: cwd || process.cwd(),
19
- stdio: ['pipe', 'pipe', 'pipe'],
20
- });
21
- return { output, exitCode: 0 };
22
- } catch (execErr) {
23
- return {
24
- output: execErr.stdout || '',
25
- stderr: execErr.stderr || '',
26
- exitCode: execErr.status || 1,
27
- };
28
- }
29
- },
30
- },
31
- };
@@ -1,105 +0,0 @@
1
- /**
2
- * File System Agent
3
- * Handles: file-read, file-write, file-tree, browse-dirs, validate-path, create-folder
4
- */
5
- import { promises as fsPromises } from 'fs';
6
- import os from 'os';
7
- import path from 'path';
8
- import {
9
- buildFileTree,
10
- resolveTildePath,
11
- isBlockedPath,
12
- BLOCKED_READ_PATTERNS,
13
- BLOCKED_WRITE_PATTERNS,
14
- } from './utils.js';
15
-
16
- export default {
17
- name: 'files',
18
- actions: {
19
- 'file-read': async (params) => {
20
- let { filePath, encoding } = params;
21
- if (!filePath || typeof filePath !== 'string') throw new Error('Invalid file path');
22
-
23
- filePath = resolveTildePath(filePath);
24
- const normalizedPath = path.resolve(filePath);
25
- if (isBlockedPath(normalizedPath, BLOCKED_READ_PATTERNS)) throw new Error('Access denied');
26
-
27
- const content = await fsPromises.readFile(normalizedPath, encoding === 'base64' ? null : 'utf8');
28
- const result = encoding === 'base64' ? content.toString('base64') : content;
29
- return { content: result };
30
- },
31
-
32
- 'file-write': async (params) => {
33
- let { filePath, content } = params;
34
- if (!filePath || typeof filePath !== 'string') throw new Error('Invalid file path');
35
-
36
- filePath = resolveTildePath(filePath);
37
- const normalizedPath = path.resolve(filePath);
38
- if (isBlockedPath(normalizedPath, BLOCKED_WRITE_PATTERNS)) throw new Error('Access denied');
39
-
40
- const parentDir = path.dirname(normalizedPath);
41
- await fsPromises.mkdir(parentDir, { recursive: true }).catch(() => {});
42
- await fsPromises.writeFile(normalizedPath, content, 'utf8');
43
- return { success: true };
44
- },
45
-
46
- 'file-tree': async (params) => {
47
- const { dirPath, depth, maxDepth } = params;
48
- const treeDepth = depth || maxDepth || 3;
49
- const resolvedDir = dirPath ? path.resolve(dirPath) : process.cwd();
50
- const tree = await buildFileTree(resolvedDir, treeDepth, 0, {
51
- maxEntries: 200,
52
- includeStats: true,
53
- skipDotfilesAtRoot: true,
54
- });
55
- return { files: tree };
56
- },
57
-
58
- 'browse-dirs': async (params) => {
59
- const { dirPath: browsePath } = params;
60
- let targetDir = resolveTildePath(browsePath);
61
- targetDir = path.resolve(targetDir);
62
-
63
- let drives = [];
64
- // On Windows, detect available drives
65
- if (process.platform === 'win32') {
66
- try {
67
- const { execSync } = await import('child_process');
68
- const wmicOut = execSync('wmic logicaldisk get name', { encoding: 'utf8', timeout: 5000 });
69
- drives = wmicOut.split('\n')
70
- .map(l => l.trim())
71
- .filter(l => /^[A-Z]:$/.test(l))
72
- .map(d => ({ name: d + '\\', path: d + '\\', type: 'drive' }));
73
- } catch { /* wmic not available */ }
74
- }
75
-
76
- const entries = await fsPromises.readdir(targetDir, { withFileTypes: true });
77
- const dirs = entries
78
- .filter(e => e.isDirectory() && !e.name.startsWith('.'))
79
- .map(e => ({ name: e.name, path: path.join(targetDir, e.name), type: 'directory' }))
80
- .sort((a, b) => a.name.localeCompare(b.name));
81
-
82
- return { path: targetDir, suggestions: dirs, drives, homedir: os.homedir() };
83
- },
84
-
85
- 'validate-path': async (params) => {
86
- const { targetPath } = params;
87
- const checkPath = resolveTildePath(targetPath);
88
- const resolved = path.resolve(checkPath);
89
-
90
- try {
91
- const stats = await fsPromises.stat(resolved);
92
- return { exists: true, isDirectory: stats.isDirectory(), resolvedPath: resolved };
93
- } catch {
94
- return { exists: false, resolvedPath: resolved };
95
- }
96
- },
97
-
98
- 'create-folder': async (params) => {
99
- const { folderPath } = params;
100
- const mkPath = path.resolve(resolveTildePath(folderPath));
101
- await fsPromises.mkdir(mkPath, { recursive: true });
102
- return { success: true, path: mkPath };
103
- },
104
- },
105
- };
@@ -1,18 +0,0 @@
1
- /**
2
- * Git Agent
3
- * Handles: git-operation
4
- */
5
- import path from 'path';
6
- import { execCommand } from './utils.js';
7
-
8
- export default {
9
- name: 'git',
10
- actions: {
11
- 'git-operation': async (params) => {
12
- const { gitCommand, cwd } = params;
13
- const resolvedCwd = cwd ? path.resolve(cwd) : process.cwd();
14
- const result = await execCommand('git', [gitCommand], { cwd: resolvedCwd });
15
- return { stdout: result };
16
- },
17
- },
18
- };
@@ -1,67 +0,0 @@
1
- /**
2
- * Gitagent Agent
3
- * Handles: gitagent-detect, gitagent-parse
4
- *
5
- * Relay-compatible actions for detecting and parsing gitagent directory structures
6
- * on the user's local machine.
7
- */
8
- import fs from 'fs';
9
- import { promises as fsp } from 'fs';
10
- import path from 'path';
11
- import { detectGitagent, parseGitagentRepo } from '../gitagent/parser.js';
12
-
13
- /** fs-backed helpers injected into the parser */
14
- async function fsFileExists(filePath) {
15
- try {
16
- await fsp.access(filePath, fs.constants.F_OK);
17
- return true;
18
- } catch {
19
- return false;
20
- }
21
- }
22
-
23
- async function fsReadFile(filePath) {
24
- try {
25
- return await fsp.readFile(filePath, 'utf8');
26
- } catch {
27
- return null;
28
- }
29
- }
30
-
31
- async function fsListDir(dirPath) {
32
- try {
33
- return await fsp.readdir(dirPath);
34
- } catch {
35
- return [];
36
- }
37
- }
38
-
39
- export default {
40
- name: 'gitagent',
41
- actions: {
42
- /**
43
- * Lightweight check for agent.yaml existence.
44
- * @param {{ projectPath: string }} params
45
- * @returns {{ detected: boolean }}
46
- */
47
- 'gitagent-detect': async (params) => {
48
- const projectPath = params.projectPath;
49
- if (!projectPath) return { detected: false };
50
- const detected = await detectGitagent(projectPath, fsFileExists);
51
- return { detected };
52
- },
53
-
54
- /**
55
- * Full parse — returns the complete GitagentDefinition as JSON.
56
- * @param {{ projectPath: string }} params
57
- * @returns {{ definition: object|null }}
58
- */
59
- 'gitagent-parse': async (params) => {
60
- const projectPath = params.projectPath;
61
- if (!projectPath) return { definition: null };
62
-
63
- const definition = await parseGitagentRepo(projectPath, fsReadFile, fsListDir);
64
- return { definition };
65
- },
66
- },
67
- };