(args: z.infer<typeof StartArgs>, _ctx: ToolContext)
| 84 | argsSchema = StartArgs; |
| 85 | |
| 86 | async execute(args: z.infer<typeof StartArgs>, _ctx: ToolContext): Promise<ToolResult> { |
| 87 | try { |
| 88 | const info = await registry.start({ |
| 89 | name: args.name, |
| 90 | command: args.command, |
| 91 | cwd: args.cwd, |
| 92 | env: args.env, |
| 93 | replace: args.replace, |
| 94 | }); |
| 95 | const waitMs = args.wait_ms ?? 2000; |
| 96 | if (waitMs > 0) { |
| 97 | await new Promise((resolve) => setTimeout(resolve, waitMs)); |
| 98 | } |
| 99 | const tail = registry.tailLog(args.name, 'combined', 2000); |
| 100 | const proc = registry.get(args.name); |
| 101 | const exited = proc?.exitCode !== null && proc?.exitCode !== undefined; |
| 102 | const status = !exited |
| 103 | ? `running (pid ${info.pid})` |
| 104 | : `exited with code ${proc?.exitCode}`; |
| 105 | // If it failed to start (or the output shows a missing binary/dep), check whether the real |
| 106 | // problem is a broken dependency install and, if so, return an actionable diagnosis instead |
| 107 | // of the raw output the model would otherwise loop on. |
| 108 | if (exited && proc?.exitCode !== 0) { |
| 109 | const diag = diagnoseDevEnv(args.cwd ?? info.cwd, tail || ''); |
| 110 | if (diag) { |
| 111 | return { |
| 112 | content: `${diag}\n\n--- raw output ---\n${tail || '(no output)'}`, |
| 113 | isError: true, |
| 114 | }; |
| 115 | } |
| 116 | } |
| 117 | return { |
| 118 | content: `Started '${args.name}' — ${status}\nCommand: ${args.command}\nCwd: ${info.cwd}\n\nInitial output:\n${tail || '(no output yet)'}`, |
| 119 | }; |
| 120 | } catch (e: any) { |
| 121 | return { content: `[DEV_SERVER_ERROR] ${e?.message ?? String(e)}`, isError: true }; |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | const LogArgs = z.object({ |
nothing calls this directly
no test coverage detected