@shepai/cli 1.120.0-pr360.cadec27 → 1.120.0-pr381.69e0404
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/core/src/infrastructure/services/agents/common/agent-executor-factory.service.js +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts +28 -8
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.js +187 -29
- package/dist/packages/core/src/infrastructure/services/tool-installer/tool-installer.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/tool-installer/tool-installer.service.js +3 -10
- package/dist/packages/core/src/infrastructure/services/tool-installer/tools/claude-code.json +6 -11
- package/dist/src/presentation/cli/commands/install.command.js +1 -8
- package/dist/src/presentation/web/app/actions/check-agent-auth.d.ts +0 -2
- package/dist/src/presentation/web/app/actions/check-agent-auth.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/check-agent-auth.js +1 -8
- package/dist/src/presentation/web/components/features/control-center/control-center-empty-state.js +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +21 -21
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +27 -27
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +27 -27
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +19 -19
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +21 -21
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +27 -27
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +27 -27
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +19 -19
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +19 -19
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/settings/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_ed372306.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_ed372306.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_32e26434.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_32e26434.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__06fef644._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__3b14e5a0._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__3b14e5a0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8b59c03e._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8b59c03e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8c553503._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__994ad9fe._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__994ad9fe._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a022445f._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a022445f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a7de2711._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c9c1cfee._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c9c1cfee._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
- package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_2f6f48b8._.js +1 -1
- package/web/.next/server/chunks/ssr/_2f6f48b8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_dc7b6354._.js → _75b14648._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_dc7b6354._.js.map → _75b14648._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_ab069e13._.js +1 -1
- package/web/.next/server/chunks/ssr/_ab069e13._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_c54a3b9e._.js +3 -0
- package/web/.next/server/chunks/ssr/{_fa4c39d3._.js.map → _c54a3b9e._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
- package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_7af11139._.js → _f9a086c2._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_7af11139._.js.map → _f9a086c2._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_0835b28e._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_0835b28e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_e99d78dd.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_e99d78dd.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_e8034a29._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_e8034a29._.js.map +1 -1
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +33 -33
- package/web/.next/static/chunks/{f22fb5583e3f73c2.js → 1196afca67164e25.js} +1 -1
- package/web/.next/static/chunks/{c1d5418605c0d3c2.js → 178f0d458b01e693.js} +2 -2
- package/web/.next/static/chunks/{b86009f50b8547fc.js → 24cd8f7b65f3914f.js} +1 -1
- package/web/.next/static/chunks/{e91a13da1d85f65d.js → 2b4da8be73cad419.js} +1 -1
- package/web/.next/static/chunks/{bb7a6b49e492e91d.js → 3832835210daff41.js} +1 -1
- package/web/.next/static/chunks/{fd0c3566818fc69a.js → 3d3847fa2cdc7448.js} +1 -1
- package/web/.next/static/chunks/{1519bd8bfeefe6d7.js → 7aea29219c3c2e7f.js} +1 -1
- package/web/.next/static/chunks/{fbcb8f629d51c883.js → a49e43799b9d3d8e.js} +1 -1
- package/web/.next/static/chunks/c981ae761085d1ac.js +2 -0
- package/web/.next/static/chunks/d0e48654cb9ff5e9.js +1 -0
- package/web/.next/server/chunks/ssr/_fa4c39d3._.js +0 -3
- package/web/.next/static/chunks/0734c9c254ffe48f.js +0 -1
- package/web/.next/static/chunks/65c62b10c8ae1559.js +0 -2
- /package/web/.next/static/{Nj697Fdl9C9FmHjZiFrN4 → sptepZ7DWfFSISgedVpcr}/_buildManifest.js +0 -0
- /package/web/.next/static/{Nj697Fdl9C9FmHjZiFrN4 → sptepZ7DWfFSISgedVpcr}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{Nj697Fdl9C9FmHjZiFrN4 → sptepZ7DWfFSISgedVpcr}/_ssgManifest.js +0 -0
|
@@ -45,7 +45,7 @@ export class AgentExecutorFactory {
|
|
|
45
45
|
executor = new ClaudeCodeExecutorService(this.spawn);
|
|
46
46
|
break;
|
|
47
47
|
case 'cursor':
|
|
48
|
-
executor = new CursorExecutorService(this.spawn);
|
|
48
|
+
executor = new CursorExecutorService(this.spawn, _authConfig);
|
|
49
49
|
break;
|
|
50
50
|
case 'dev':
|
|
51
51
|
executor = new DevAgentExecutorService();
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Uses constructor dependency injection for the spawn function
|
|
9
9
|
* to enable testability without mocking node:child_process directly.
|
|
10
10
|
*/
|
|
11
|
-
import type { AgentType, AgentFeature } from '../../../../../domain/generated/output.js';
|
|
11
|
+
import type { AgentType, AgentFeature, AgentConfig } from '../../../../../domain/generated/output.js';
|
|
12
12
|
import type { IAgentExecutor, AgentExecutionOptions, AgentExecutionResult, AgentExecutionStreamEvent } from '../../../../../application/ports/output/agents/agent-executor.interface.js';
|
|
13
13
|
import type { SpawnFunction } from '../types.js';
|
|
14
14
|
/**
|
|
@@ -17,10 +17,11 @@ import type { SpawnFunction } from '../types.js';
|
|
|
17
17
|
*/
|
|
18
18
|
export declare class CursorExecutorService implements IAgentExecutor {
|
|
19
19
|
private readonly spawn;
|
|
20
|
+
private readonly authConfig?;
|
|
20
21
|
readonly agentType: AgentType;
|
|
21
22
|
/** When true, suppresses debug logging (set per-call via options.silent) */
|
|
22
23
|
private silent;
|
|
23
|
-
constructor(spawn: SpawnFunction);
|
|
24
|
+
constructor(spawn: SpawnFunction, authConfig?: AgentConfig | undefined);
|
|
24
25
|
/** Debug logging — writes to stdout so it appears in the worker log file */
|
|
25
26
|
private log;
|
|
26
27
|
supportsFeature(feature: AgentFeature): boolean;
|
|
@@ -33,16 +34,35 @@ export declare class CursorExecutorService implements IAgentExecutor {
|
|
|
33
34
|
private buildArgs;
|
|
34
35
|
private buildStreamArgs;
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
+
* Resolve the cursor CLI's bundled node.exe and index.js paths on Windows.
|
|
37
38
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* The cursor CLI installs to `%LOCALAPPDATA%\cursor-agent\` with:
|
|
40
|
+
* - `agent.cmd` / `cursor-agent.ps1` (shell wrappers)
|
|
41
|
+
* - `versions/<YYYY.MM.DD-commit>/node.exe` + `index.js`
|
|
42
|
+
*
|
|
43
|
+
* Spawning via agent.cmd creates a problematic nesting chain:
|
|
44
|
+
* PowerShell → agent.cmd → PowerShell → cursor-agent.ps1 → node.exe
|
|
45
|
+
* This chain hangs on Windows CI runners. Direct invocation bypasses it.
|
|
46
|
+
*
|
|
47
|
+
* @returns `{ nodePath, indexPath }` or `undefined` if not found
|
|
48
|
+
*/
|
|
49
|
+
private resolveCursorBinary;
|
|
50
|
+
/**
|
|
51
|
+
* Spawn the agent process, handling Windows specially.
|
|
52
|
+
*
|
|
53
|
+
* On Windows, the cursor CLI ships as `agent.cmd` which spawns PowerShell
|
|
54
|
+
* which runs `cursor-agent.ps1` which runs `node.exe index.js`. This triple
|
|
55
|
+
* nesting (PowerShell → cmd.exe → PowerShell → node.exe) hangs on Windows
|
|
56
|
+
* CI runners. Solution: resolve the cursor CLI's bundled `node.exe` and
|
|
57
|
+
* `index.js` directly, bypassing all shell wrappers.
|
|
43
58
|
*
|
|
44
59
|
* On Linux/macOS, spawn `agent` directly — no shell needed.
|
|
45
60
|
*/
|
|
61
|
+
/**
|
|
62
|
+
* Build the subprocess environment: strip CLAUDECODE, inject CURSOR_API_KEY
|
|
63
|
+
* from authConfig when token auth is configured.
|
|
64
|
+
*/
|
|
65
|
+
private buildEnv;
|
|
46
66
|
private spawnAgent;
|
|
47
67
|
private parseStreamLine;
|
|
48
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"cursor-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACZ,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,4EAA4E,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAuBjD;;;GAGG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAOxD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAP9B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAyB;IAEtD,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAS;gBAGJ,KAAK,EAAE,aAAa,EACpB,UAAU,CAAC,EAAE,WAAW,YAAA;IAG3C,4EAA4E;IAC5E,OAAO,CAAC,GAAG;IAMX,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAIzC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA2MtF,aAAa,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC;IAiH3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAyCtB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,eAAe;IAOvB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,mBAAmB;IAwC3B;;;;;;;;;;OAUG;IACH;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,UAAU;IA+ElB,OAAO,CAAC,eAAe;CAgDxB"}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Uses constructor dependency injection for the spawn function
|
|
9
9
|
* to enable testability without mocking node:child_process directly.
|
|
10
10
|
*/
|
|
11
|
-
import { writeFileSync, unlinkSync } from 'node:fs';
|
|
11
|
+
import { writeFileSync, unlinkSync, readdirSync, existsSync } from 'node:fs';
|
|
12
12
|
import { execFileSync } from 'node:child_process';
|
|
13
13
|
import { tmpdir } from 'node:os';
|
|
14
14
|
import { join } from 'node:path';
|
|
@@ -36,11 +36,13 @@ const SUPPORTED_FEATURES = new Set(['session-resume', 'streaming']);
|
|
|
36
36
|
*/
|
|
37
37
|
export class CursorExecutorService {
|
|
38
38
|
spawn;
|
|
39
|
+
authConfig;
|
|
39
40
|
agentType = 'cursor';
|
|
40
41
|
/** When true, suppresses debug logging (set per-call via options.silent) */
|
|
41
42
|
silent = false;
|
|
42
|
-
constructor(spawn) {
|
|
43
|
+
constructor(spawn, authConfig) {
|
|
43
44
|
this.spawn = spawn;
|
|
45
|
+
this.authConfig = authConfig;
|
|
44
46
|
}
|
|
45
47
|
/** Debug logging — writes to stdout so it appears in the worker log file */
|
|
46
48
|
log(message) {
|
|
@@ -58,19 +60,34 @@ export class CursorExecutorService {
|
|
|
58
60
|
// stream-json is unreliable on Windows where shell: true can mangle args.
|
|
59
61
|
const args = this.buildArgs(prompt, options);
|
|
60
62
|
const { proc, tmpFile } = this.spawnAgent(prompt, args, options);
|
|
63
|
+
// Diagnostic: verify streams are connected and process is alive
|
|
64
|
+
this.log(`[diag] proc.pid=${proc.pid}, killed=${proc.killed}`);
|
|
65
|
+
this.log(`[diag] stdout=${!!proc.stdout}, stderr=${!!proc.stderr}, stdin=${!!proc.stdin}`);
|
|
66
|
+
this.log(`[diag] CURSOR_API_KEY set=${!!process.env.CURSOR_API_KEY}, len=${process.env.CURSOR_API_KEY?.length ?? 0}`);
|
|
67
|
+
this.log(`[diag] authConfig.token set=${!!this.authConfig?.token}, method=${this.authConfig?.authMethod ?? 'none'}`);
|
|
68
|
+
this.log(`[diag] platform=${process.platform}, cwd=${options?.cwd ?? '(inherited)'}`);
|
|
61
69
|
return new Promise((resolve, reject) => {
|
|
62
70
|
let lineBuffer = '';
|
|
63
71
|
let stderr = '';
|
|
64
72
|
let timedOut = false;
|
|
65
73
|
let timeoutId;
|
|
74
|
+
let stdoutChunks = 0;
|
|
75
|
+
let stderrChunks = 0;
|
|
76
|
+
const startTime = Date.now();
|
|
66
77
|
// Accumulated from JSON events or raw text fallback
|
|
67
78
|
let resultText = '';
|
|
68
79
|
let rawText = '';
|
|
69
80
|
let sessionId;
|
|
70
81
|
let metadata;
|
|
82
|
+
// Periodic heartbeat to prove the executor is alive and waiting
|
|
83
|
+
const heartbeatId = setInterval(() => {
|
|
84
|
+
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
85
|
+
this.log(`[diag] heartbeat ${elapsed}s: stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}, pid_killed=${proc.killed}`);
|
|
86
|
+
}, 15_000);
|
|
71
87
|
if (options?.timeout) {
|
|
72
88
|
timeoutId = setTimeout(() => {
|
|
73
89
|
timedOut = true;
|
|
90
|
+
this.log(`[diag] TIMEOUT after ${options.timeout}ms — killing process tree`);
|
|
74
91
|
// On Windows, proc.kill() may not kill the entire process tree
|
|
75
92
|
// (PowerShell + child agent process). Use taskkill /T for tree kill.
|
|
76
93
|
if (process.platform === 'win32' && proc.pid) {
|
|
@@ -78,9 +95,11 @@ export class CursorExecutorService {
|
|
|
78
95
|
execFileSync('taskkill', ['/F', '/T', '/PID', String(proc.pid)], {
|
|
79
96
|
stdio: 'ignore',
|
|
80
97
|
});
|
|
98
|
+
this.log(`[diag] taskkill /T /PID ${proc.pid} succeeded`);
|
|
81
99
|
}
|
|
82
100
|
catch {
|
|
83
101
|
proc.kill();
|
|
102
|
+
this.log(`[diag] taskkill failed, fell back to proc.kill()`);
|
|
84
103
|
}
|
|
85
104
|
}
|
|
86
105
|
else {
|
|
@@ -107,17 +126,35 @@ export class CursorExecutorService {
|
|
|
107
126
|
if (parsed.duration_ms !== undefined) {
|
|
108
127
|
metadata = { ...metadata, duration_ms: parsed.duration_ms };
|
|
109
128
|
}
|
|
129
|
+
if (parsed.is_error) {
|
|
130
|
+
this.log(`[diag] result event has is_error=true: ${JSON.stringify(parsed).slice(0, 300)}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (parsed.type === 'error') {
|
|
134
|
+
this.log(`[diag] cursor error event: ${JSON.stringify(parsed).slice(0, 500)}`);
|
|
135
|
+
}
|
|
136
|
+
else if (parsed.type !== 'user' &&
|
|
137
|
+
parsed.type !== 'system' &&
|
|
138
|
+
parsed.type !== 'thinking' &&
|
|
139
|
+
parsed.type !== 'tool_call') {
|
|
140
|
+
this.log(`[diag] unknown event type="${parsed.type}": ${JSON.stringify(parsed).slice(0, 300)}`);
|
|
110
141
|
}
|
|
111
|
-
// user, system, thinking events: logged but don't affect result
|
|
112
142
|
}
|
|
113
143
|
catch {
|
|
114
144
|
// Non-JSON output — accumulate as raw text fallback
|
|
115
|
-
if (line.length > 0)
|
|
145
|
+
if (line.length > 0) {
|
|
116
146
|
rawText += `${line}\n`;
|
|
147
|
+
this.log(`[diag] non-JSON line (${line.length} chars): ${line.slice(0, 200)}`);
|
|
148
|
+
}
|
|
117
149
|
}
|
|
118
150
|
};
|
|
119
151
|
proc.stdout?.on('data', (chunk) => {
|
|
120
|
-
|
|
152
|
+
stdoutChunks++;
|
|
153
|
+
const data = chunk.toString();
|
|
154
|
+
if (stdoutChunks <= 3) {
|
|
155
|
+
this.log(`[diag] stdout chunk #${stdoutChunks}: ${data.length} bytes, first 200: ${data.slice(0, 200).replace(/\n/g, '\\n')}`);
|
|
156
|
+
}
|
|
157
|
+
lineBuffer += data;
|
|
121
158
|
const lines = lineBuffer.split('\n');
|
|
122
159
|
lineBuffer = lines.pop() ?? '';
|
|
123
160
|
for (const line of lines) {
|
|
@@ -127,19 +164,22 @@ export class CursorExecutorService {
|
|
|
127
164
|
}
|
|
128
165
|
});
|
|
129
166
|
proc.stderr?.on('data', (chunk) => {
|
|
167
|
+
stderrChunks++;
|
|
130
168
|
const data = chunk.toString();
|
|
131
169
|
stderr += data;
|
|
132
|
-
this.log(`stderr: ${data.trimEnd()}`);
|
|
170
|
+
this.log(`[diag] stderr chunk #${stderrChunks}: ${data.trimEnd()}`);
|
|
133
171
|
// Detect fatal errors early so callers don't waste time retrying
|
|
134
172
|
if (data.includes('Cannot use this model')) {
|
|
135
173
|
if (timeoutId)
|
|
136
174
|
clearTimeout(timeoutId);
|
|
175
|
+
clearInterval(heartbeatId);
|
|
137
176
|
proc.kill();
|
|
138
177
|
reject(new Error(data.trim()));
|
|
139
178
|
}
|
|
140
179
|
});
|
|
141
180
|
proc.on('error', (error) => {
|
|
142
|
-
this.log(`Process
|
|
181
|
+
this.log(`[diag] Process ERROR event: ${error.message} (code=${error.code})`);
|
|
182
|
+
clearInterval(heartbeatId);
|
|
143
183
|
if (timeoutId)
|
|
144
184
|
clearTimeout(timeoutId);
|
|
145
185
|
if (error.code === 'ENOENT') {
|
|
@@ -150,6 +190,8 @@ export class CursorExecutorService {
|
|
|
150
190
|
}
|
|
151
191
|
});
|
|
152
192
|
proc.on('close', (code) => {
|
|
193
|
+
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
194
|
+
clearInterval(heartbeatId);
|
|
153
195
|
// Clean up temp file on Windows
|
|
154
196
|
if (tmpFile) {
|
|
155
197
|
try {
|
|
@@ -163,7 +205,7 @@ export class CursorExecutorService {
|
|
|
163
205
|
processLine(lineBuffer.trim());
|
|
164
206
|
// Use raw text as fallback when no JSON result was captured
|
|
165
207
|
const finalText = resultText || rawText.trim();
|
|
166
|
-
this.log(`Process
|
|
208
|
+
this.log(`[diag] Process CLOSE: code=${code}, elapsed=${elapsed}s, stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}, result=${finalText.length} chars, raw=${rawText.length} chars`);
|
|
167
209
|
if (timeoutId)
|
|
168
210
|
clearTimeout(timeoutId);
|
|
169
211
|
if (timedOut) {
|
|
@@ -171,9 +213,17 @@ export class CursorExecutorService {
|
|
|
171
213
|
return;
|
|
172
214
|
}
|
|
173
215
|
if (code !== 0 && code !== null) {
|
|
174
|
-
|
|
216
|
+
const errMsg = stderr.trim() || `Process exited with code ${code}`;
|
|
217
|
+
this.log(`[diag] non-zero exit — rejecting with: ${errMsg.slice(0, 200)}`);
|
|
218
|
+
reject(new Error(errMsg));
|
|
175
219
|
return;
|
|
176
220
|
}
|
|
221
|
+
if (stdoutChunks === 0 && stderrChunks === 0) {
|
|
222
|
+
this.log(`[diag] WARNING: process exited with code 0 but produced ZERO output (stdout=0, stderr=0, elapsed=${elapsed}s)`);
|
|
223
|
+
}
|
|
224
|
+
if (!finalText && code === 0) {
|
|
225
|
+
this.log(`[diag] WARNING: process exited code=0 but result is empty (resultText=${resultText.length}, rawText=${rawText.length})`);
|
|
226
|
+
}
|
|
177
227
|
const result = { result: finalText };
|
|
178
228
|
if (sessionId)
|
|
179
229
|
result.sessionId = sessionId;
|
|
@@ -184,20 +234,25 @@ export class CursorExecutorService {
|
|
|
184
234
|
});
|
|
185
235
|
}
|
|
186
236
|
async *executeStream(prompt, options) {
|
|
237
|
+
this.silent = options?.silent ?? false;
|
|
187
238
|
const args = this.buildStreamArgs(prompt, options);
|
|
188
239
|
const { proc, tmpFile } = this.spawnAgent(prompt, args, options);
|
|
240
|
+
this.log(`[stream] pid=${proc.pid}, stdout=${!!proc.stdout}, stderr=${!!proc.stderr}`);
|
|
189
241
|
let lineBuffer = '';
|
|
190
242
|
let stderr = '';
|
|
243
|
+
let stdoutChunks = 0;
|
|
244
|
+
let stderrChunks = 0;
|
|
245
|
+
const startTime = Date.now();
|
|
191
246
|
const queue = [];
|
|
192
247
|
let resolve = null;
|
|
193
248
|
let error = null;
|
|
194
|
-
|
|
249
|
+
const enqueue = (event) => {
|
|
195
250
|
queue.push(event);
|
|
196
251
|
if (resolve) {
|
|
197
252
|
resolve();
|
|
198
253
|
resolve = null;
|
|
199
254
|
}
|
|
200
|
-
}
|
|
255
|
+
};
|
|
201
256
|
function waitForItem() {
|
|
202
257
|
if (queue.length > 0)
|
|
203
258
|
return Promise.resolve();
|
|
@@ -205,8 +260,18 @@ export class CursorExecutorService {
|
|
|
205
260
|
resolve = r;
|
|
206
261
|
});
|
|
207
262
|
}
|
|
263
|
+
// Heartbeat for stream mode too
|
|
264
|
+
const heartbeatId = setInterval(() => {
|
|
265
|
+
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
266
|
+
this.log(`[stream] heartbeat ${elapsed}s: stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}, pid_killed=${proc.killed}`);
|
|
267
|
+
}, 30_000);
|
|
208
268
|
proc.stdout?.on('data', (chunk) => {
|
|
209
|
-
|
|
269
|
+
stdoutChunks++;
|
|
270
|
+
const data = chunk.toString();
|
|
271
|
+
if (stdoutChunks <= 3) {
|
|
272
|
+
this.log(`[stream] stdout chunk #${stdoutChunks}: ${data.length} bytes`);
|
|
273
|
+
}
|
|
274
|
+
lineBuffer += data;
|
|
210
275
|
const lines = lineBuffer.split('\n');
|
|
211
276
|
lineBuffer = lines.pop() ?? '';
|
|
212
277
|
for (const line of lines) {
|
|
@@ -219,13 +284,21 @@ export class CursorExecutorService {
|
|
|
219
284
|
}
|
|
220
285
|
});
|
|
221
286
|
proc.stderr?.on('data', (chunk) => {
|
|
222
|
-
|
|
287
|
+
stderrChunks++;
|
|
288
|
+
const data = chunk.toString();
|
|
289
|
+
stderr += data;
|
|
290
|
+
this.log(`[stream] stderr chunk #${stderrChunks}: ${data.trimEnd()}`);
|
|
223
291
|
});
|
|
224
292
|
proc.on('error', (err) => {
|
|
293
|
+
this.log(`[stream] Process ERROR: ${err.message}`);
|
|
294
|
+
clearInterval(heartbeatId);
|
|
225
295
|
error = err;
|
|
226
296
|
enqueue(null);
|
|
227
297
|
});
|
|
228
298
|
proc.on('close', (code) => {
|
|
299
|
+
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
300
|
+
clearInterval(heartbeatId);
|
|
301
|
+
this.log(`[stream] Process CLOSE: code=${code}, elapsed=${elapsed}s, stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}`);
|
|
229
302
|
if (tmpFile) {
|
|
230
303
|
try {
|
|
231
304
|
unlinkSync(tmpFile);
|
|
@@ -239,8 +312,10 @@ export class CursorExecutorService {
|
|
|
239
312
|
if (event)
|
|
240
313
|
enqueue(event);
|
|
241
314
|
}
|
|
242
|
-
if (code !== 0 && code !== null
|
|
243
|
-
|
|
315
|
+
if (code !== 0 && code !== null) {
|
|
316
|
+
const errMsg = stderr.trim() || `Process exited with code ${code}`;
|
|
317
|
+
this.log(`[stream] non-zero exit: ${errMsg}`);
|
|
318
|
+
enqueue({ type: 'error', content: errMsg, timestamp: new Date() });
|
|
244
319
|
}
|
|
245
320
|
enqueue(null);
|
|
246
321
|
});
|
|
@@ -286,6 +361,10 @@ export class CursorExecutorService {
|
|
|
286
361
|
}
|
|
287
362
|
if (parsed.type === 'user')
|
|
288
363
|
return; // Skip echoed input
|
|
364
|
+
// Log any unrecognized event type so nothing is silently dropped
|
|
365
|
+
if (!['assistant', 'tool_call', 'result', 'user', 'system', 'thinking'].includes(parsed.type)) {
|
|
366
|
+
this.log(`[diag] unhandled stream event type="${parsed.type}": ${line.slice(0, 200)}`);
|
|
367
|
+
}
|
|
289
368
|
}
|
|
290
369
|
catch {
|
|
291
370
|
if (line.length > 0)
|
|
@@ -310,36 +389,115 @@ export class CursorExecutorService {
|
|
|
310
389
|
return args;
|
|
311
390
|
}
|
|
312
391
|
/**
|
|
313
|
-
*
|
|
392
|
+
* Resolve the cursor CLI's bundled node.exe and index.js paths on Windows.
|
|
393
|
+
*
|
|
394
|
+
* The cursor CLI installs to `%LOCALAPPDATA%\cursor-agent\` with:
|
|
395
|
+
* - `agent.cmd` / `cursor-agent.ps1` (shell wrappers)
|
|
396
|
+
* - `versions/<YYYY.MM.DD-commit>/node.exe` + `index.js`
|
|
314
397
|
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
398
|
+
* Spawning via agent.cmd creates a problematic nesting chain:
|
|
399
|
+
* PowerShell → agent.cmd → PowerShell → cursor-agent.ps1 → node.exe
|
|
400
|
+
* This chain hangs on Windows CI runners. Direct invocation bypasses it.
|
|
401
|
+
*
|
|
402
|
+
* @returns `{ nodePath, indexPath }` or `undefined` if not found
|
|
403
|
+
*/
|
|
404
|
+
resolveCursorBinary() {
|
|
405
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
406
|
+
if (!localAppData) {
|
|
407
|
+
this.log('[diag] resolveCursorBinary: LOCALAPPDATA not set');
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
const versionsDir = join(localAppData, 'cursor-agent', 'versions');
|
|
411
|
+
if (!existsSync(versionsDir)) {
|
|
412
|
+
this.log(`[diag] resolveCursorBinary: versions dir not found: ${versionsDir}`);
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
// Find the latest version directory (format: YYYY.MM.DD-commit)
|
|
416
|
+
const versionPattern = /^\d{4}\.\d{1,2}\.\d{1,2}-[a-f0-9]+$/;
|
|
417
|
+
const versions = readdirSync(versionsDir)
|
|
418
|
+
.filter((d) => versionPattern.test(d))
|
|
419
|
+
.sort()
|
|
420
|
+
.reverse();
|
|
421
|
+
if (versions.length === 0) {
|
|
422
|
+
this.log(`[diag] resolveCursorBinary: no version dirs in ${versionsDir}`);
|
|
423
|
+
return undefined;
|
|
424
|
+
}
|
|
425
|
+
const versionDir = join(versionsDir, versions[0]);
|
|
426
|
+
const nodePath = join(versionDir, 'node.exe');
|
|
427
|
+
const indexPath = join(versionDir, 'index.js');
|
|
428
|
+
if (!existsSync(nodePath) || !existsSync(indexPath)) {
|
|
429
|
+
this.log(`[diag] resolveCursorBinary: missing binary — node=${existsSync(nodePath)}, index=${existsSync(indexPath)}`);
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
this.log(`[diag] resolveCursorBinary: resolved version=${versions[0]}`);
|
|
433
|
+
return { nodePath, indexPath };
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Spawn the agent process, handling Windows specially.
|
|
437
|
+
*
|
|
438
|
+
* On Windows, the cursor CLI ships as `agent.cmd` which spawns PowerShell
|
|
439
|
+
* which runs `cursor-agent.ps1` which runs `node.exe index.js`. This triple
|
|
440
|
+
* nesting (PowerShell → cmd.exe → PowerShell → node.exe) hangs on Windows
|
|
441
|
+
* CI runners. Solution: resolve the cursor CLI's bundled `node.exe` and
|
|
442
|
+
* `index.js` directly, bypassing all shell wrappers.
|
|
320
443
|
*
|
|
321
444
|
* On Linux/macOS, spawn `agent` directly — no shell needed.
|
|
322
445
|
*/
|
|
323
|
-
|
|
446
|
+
/**
|
|
447
|
+
* Build the subprocess environment: strip CLAUDECODE, inject CURSOR_API_KEY
|
|
448
|
+
* from authConfig when token auth is configured.
|
|
449
|
+
*/
|
|
450
|
+
buildEnv(extra) {
|
|
324
451
|
const { CLAUDECODE: _, ...cleanEnv } = process.env;
|
|
452
|
+
// Inject CURSOR_API_KEY from stored settings so the cursor CLI authenticates
|
|
453
|
+
// even when the env var isn't inherited (e.g. direct node.exe invocation on Windows).
|
|
454
|
+
if (this.authConfig?.authMethod === 'token' && this.authConfig.token) {
|
|
455
|
+
cleanEnv.CURSOR_API_KEY = this.authConfig.token;
|
|
456
|
+
this.log(`[diag] buildEnv: injected CURSOR_API_KEY from authConfig (${this.authConfig.token.length} chars)`);
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
this.log(`[diag] buildEnv: NO token injection (authMethod=${this.authConfig?.authMethod ?? 'none'}, hasToken=${!!this.authConfig?.token}, envKey=${!!cleanEnv.CURSOR_API_KEY})`);
|
|
460
|
+
}
|
|
461
|
+
const env = extra ? { ...cleanEnv, ...extra } : cleanEnv;
|
|
462
|
+
this.log(`[diag] buildEnv: final CURSOR_API_KEY in env=${!!env.CURSOR_API_KEY}, len=${env.CURSOR_API_KEY?.length ?? 0}`);
|
|
463
|
+
return env;
|
|
464
|
+
}
|
|
465
|
+
spawnAgent(prompt, args, options) {
|
|
325
466
|
const cwd = options?.cwd;
|
|
326
467
|
if (IS_WINDOWS) {
|
|
327
|
-
|
|
468
|
+
const resolved = this.resolveCursorBinary();
|
|
469
|
+
if (resolved) {
|
|
470
|
+
// Direct invocation: node.exe index.js <args>
|
|
471
|
+
// No shell wrappers, no nesting, no hangs.
|
|
472
|
+
const directArgs = [resolved.indexPath, ...args];
|
|
473
|
+
this.log(`Windows direct mode: ${resolved.nodePath}`);
|
|
474
|
+
this.log(`Args: ${directArgs.map((a) => (a.length > 80 ? `${a.slice(0, 77)}...` : a)).join(' ')}`);
|
|
475
|
+
const proc = this.spawn(resolved.nodePath, directArgs, {
|
|
476
|
+
cwd,
|
|
477
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
478
|
+
windowsHide: true,
|
|
479
|
+
env: this.buildEnv({ CURSOR_INVOKED_AS: 'agent' }),
|
|
480
|
+
});
|
|
481
|
+
this.log(`Direct PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
|
|
482
|
+
if (proc.stdin)
|
|
483
|
+
proc.stdin.end();
|
|
484
|
+
return { proc, tmpFile: undefined };
|
|
485
|
+
}
|
|
486
|
+
// Fallback: if direct resolution fails, use PowerShell + temp file
|
|
487
|
+
this.log('[diag] Windows fallback: cursor binary not resolved, using PowerShell + temp file');
|
|
328
488
|
const tmpFile = join(tmpdir(), `shep-cursor-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.txt`);
|
|
329
489
|
writeFileSync(tmpFile, prompt, 'utf8');
|
|
330
|
-
// Build the agent args WITHOUT -p and the prompt (they go via temp file)
|
|
331
490
|
const agentFlags = args.filter((a) => a !== '-p' && a !== prompt).join(' ');
|
|
332
491
|
const safePath = tmpFile.replace(/'/g, "''");
|
|
333
492
|
const psCmd = `$p = Get-Content -Raw '${safePath}'; & agent ${agentFlags} -p $p`;
|
|
334
|
-
this.log(`
|
|
335
|
-
this.log(`PS command: ${psCmd.replace(prompt, `<${prompt.length} chars>`)}`);
|
|
493
|
+
this.log(`[diag] PS command: ${psCmd.replace(prompt, `<${prompt.length} chars>`)}`);
|
|
336
494
|
const proc = this.spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', psCmd], {
|
|
337
495
|
cwd,
|
|
338
496
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
339
497
|
windowsHide: true,
|
|
340
|
-
env:
|
|
498
|
+
env: this.buildEnv(),
|
|
341
499
|
});
|
|
342
|
-
this.log(`PowerShell PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
|
|
500
|
+
this.log(`[diag] PowerShell PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
|
|
343
501
|
if (proc.stdin)
|
|
344
502
|
proc.stdin.end();
|
|
345
503
|
return { proc, tmpFile };
|
|
@@ -347,7 +505,7 @@ export class CursorExecutorService {
|
|
|
347
505
|
// Linux/macOS: spawn agent directly, no shell needed
|
|
348
506
|
const spawnOpts = {
|
|
349
507
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
350
|
-
env:
|
|
508
|
+
env: this.buildEnv(),
|
|
351
509
|
};
|
|
352
510
|
if (cwd)
|
|
353
511
|
spawnOpts.cwd = cwd;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-installer.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/tool-installer/tool-installer.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sEAAsE,CAAC;AAClH,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EAEnB,MAAM,qCAAqC,CAAC;AA2B7C,qBACa,wBAAyB,YAAW,qBAAqB;IACpE;;;OAGG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAoC1E;;;OAGG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IAqB9D;;;OAGG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAChC,OAAO,CAAC,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-installer.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/tool-installer/tool-installer.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sEAAsE,CAAC;AAClH,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EAEnB,MAAM,qCAAqC,CAAC;AA2B7C,qBACa,wBAAyB,YAAW,qBAAqB;IACpE;;;OAGG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAoC1E;;;OAGG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IAqB9D;;;OAGG;IACG,cAAc,CAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAChC,OAAO,CAAC,sBAAsB,CAAC;IAgFlC;;OAEG;IACH,OAAO,CAAC,6BAA6B;CA4BtC"}
|
package/dist/packages/core/src/infrastructure/services/tool-installer/tool-installer.service.js
CHANGED
|
@@ -107,14 +107,9 @@ let ToolInstallerServiceImpl = class ToolInstallerServiceImpl {
|
|
|
107
107
|
}
|
|
108
108
|
return new Promise((resolve) => {
|
|
109
109
|
let output = '';
|
|
110
|
-
// Inherit stdin when running in an interactive terminal so that commands
|
|
111
|
-
// requiring sudo can prompt the user for a password. In non-interactive
|
|
112
|
-
// contexts (web UI, CI) stdin is piped and closed immediately to prevent
|
|
113
|
-
// the process from hanging waiting for input.
|
|
114
|
-
const isInteractive = process.stdin.isTTY === true;
|
|
115
110
|
const child = spawn(installCommand.command, [], {
|
|
116
111
|
shell: true,
|
|
117
|
-
stdio: [
|
|
112
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
118
113
|
env: {
|
|
119
114
|
...process.env,
|
|
120
115
|
DEBIAN_FRONTEND: 'noninteractive',
|
|
@@ -122,10 +117,8 @@ let ToolInstallerServiceImpl = class ToolInstallerServiceImpl {
|
|
|
122
117
|
GPG_TTY: '',
|
|
123
118
|
},
|
|
124
119
|
});
|
|
125
|
-
// Close stdin
|
|
126
|
-
|
|
127
|
-
child.stdin?.end();
|
|
128
|
-
}
|
|
120
|
+
// Close stdin immediately to prevent interactive prompts
|
|
121
|
+
child.stdin?.end();
|
|
129
122
|
// Stream output from stdout and stderr
|
|
130
123
|
const pipeOutput = (stream) => {
|
|
131
124
|
if (!stream)
|
package/dist/packages/core/src/infrastructure/services/tool-installer/tools/claude-code.json
CHANGED
|
@@ -8,21 +8,17 @@
|
|
|
8
8
|
"platforms": ["linux", "darwin", "win32"],
|
|
9
9
|
"iconUrl": "https://cdn.simpleicons.org/claude",
|
|
10
10
|
"binary": "claude",
|
|
11
|
-
"packageManager": "
|
|
11
|
+
"packageManager": "curl",
|
|
12
12
|
"commands": {
|
|
13
|
-
"linux": "
|
|
14
|
-
"darwin": "
|
|
13
|
+
"linux": "curl -fsSL https://claude.ai/install.sh | bash",
|
|
14
|
+
"darwin": "curl -fsSL https://claude.ai/install.sh | bash",
|
|
15
15
|
"win32": "npm install -g @anthropic-ai/claude-code"
|
|
16
16
|
},
|
|
17
17
|
"timeout": 300000,
|
|
18
|
-
"documentationUrl": "https://
|
|
18
|
+
"documentationUrl": "https://code.claude.com/docs/en/overview",
|
|
19
19
|
"verifyCommand": "claude --version",
|
|
20
20
|
"autoInstall": true,
|
|
21
|
-
"openDirectory": {
|
|
22
|
-
"linux": "cd {dir} && exec claude",
|
|
23
|
-
"darwin": "cd {dir} && exec claude",
|
|
24
|
-
"win32": "cd /d {dir} && claude"
|
|
25
|
-
},
|
|
21
|
+
"openDirectory": "cd {dir} && exec claude",
|
|
26
22
|
"spawnOptions": {
|
|
27
23
|
"shell": true,
|
|
28
24
|
"stdio": "inherit",
|
|
@@ -30,7 +26,6 @@
|
|
|
30
26
|
},
|
|
31
27
|
"terminalCommand": {
|
|
32
28
|
"linux": "x-terminal-emulator -e bash -c 'cd {dir} && exec claude'",
|
|
33
|
-
"darwin": "
|
|
34
|
-
"win32": "start cmd /k \"cd /d {dir} && claude\""
|
|
29
|
+
"darwin": "open -a Terminal.app bash -c 'cd {dir} && exec claude'"
|
|
35
30
|
}
|
|
36
31
|
}
|
|
@@ -117,14 +117,7 @@ function printInstallInstructions(metadata) {
|
|
|
117
117
|
// Print open directory command if available
|
|
118
118
|
if (metadata.openDirectory) {
|
|
119
119
|
console.log(fmt.heading('Open Directory'));
|
|
120
|
-
|
|
121
|
-
console.log(`${colors.muted('$')} ${metadata.openDirectory}`);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
for (const [platform, cmd] of Object.entries(metadata.openDirectory)) {
|
|
125
|
-
console.log(`${colors.muted(`[${platform}]`)} ${cmd}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
120
|
+
console.log(`${colors.muted('$')} ${metadata.openDirectory}`);
|
|
128
121
|
console.log();
|
|
129
122
|
}
|
|
130
123
|
// Print documentation link
|
|
@@ -10,8 +10,6 @@ export interface AgentAuthStatus {
|
|
|
10
10
|
binaryName: string | null;
|
|
11
11
|
/** Instructions to authenticate if not authenticated */
|
|
12
12
|
authCommand: string | null;
|
|
13
|
-
/** Platform-resolved install command (e.g. "npm install -g @google/gemini-cli") */
|
|
14
|
-
installCommand: string | null;
|
|
15
13
|
}
|
|
16
14
|
/**
|
|
17
15
|
* Check agent tool installation + auth status.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-agent-auth.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/check-agent-auth.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,aAAa,EAAE,OAAO,CAAC;IACvB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wDAAwD;IACxD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"check-agent-auth.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/check-agent-auth.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,aAAa,EAAE,OAAO,CAAC;IACvB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wDAAwD;IACxD,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA4FD;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAiF/D"}
|
|
@@ -20,7 +20,7 @@ const AGENT_TOOL_MAP = {
|
|
|
20
20
|
};
|
|
21
21
|
const AGENT_BINARY_MAP = {
|
|
22
22
|
'claude-code': 'claude',
|
|
23
|
-
cursor: '
|
|
23
|
+
cursor: 'agent',
|
|
24
24
|
'gemini-cli': 'gemini',
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
@@ -113,7 +113,6 @@ export async function checkAgentAuth() {
|
|
|
113
113
|
label: 'Unknown',
|
|
114
114
|
binaryName: null,
|
|
115
115
|
authCommand: null,
|
|
116
|
-
installCommand: null,
|
|
117
116
|
};
|
|
118
117
|
}
|
|
119
118
|
const label = AGENT_LABELS[agentType] ?? agentType;
|
|
@@ -128,18 +127,15 @@ export async function checkAgentAuth() {
|
|
|
128
127
|
label,
|
|
129
128
|
binaryName: null,
|
|
130
129
|
authCommand: null,
|
|
131
|
-
installCommand: null,
|
|
132
130
|
};
|
|
133
131
|
}
|
|
134
132
|
// Check if tool is installed
|
|
135
133
|
let installed = false;
|
|
136
|
-
let installCommand = null;
|
|
137
134
|
try {
|
|
138
135
|
const useCase = resolve('ListToolsUseCase');
|
|
139
136
|
const tools = await useCase.execute();
|
|
140
137
|
const tool = tools.find((t) => t.id === toolId);
|
|
141
138
|
installed = tool?.status.status === 'available';
|
|
142
|
-
installCommand = tool?.installCommand ?? null;
|
|
143
139
|
}
|
|
144
140
|
catch {
|
|
145
141
|
installed = false;
|
|
@@ -152,7 +148,6 @@ export async function checkAgentAuth() {
|
|
|
152
148
|
label,
|
|
153
149
|
binaryName,
|
|
154
150
|
authCommand: binaryName ? `Install ${label} first` : null,
|
|
155
|
-
installCommand,
|
|
156
151
|
};
|
|
157
152
|
}
|
|
158
153
|
// Tier 1: instant file/env check
|
|
@@ -165,7 +160,6 @@ export async function checkAgentAuth() {
|
|
|
165
160
|
label,
|
|
166
161
|
binaryName,
|
|
167
162
|
authCommand: binaryName,
|
|
168
|
-
installCommand,
|
|
169
163
|
};
|
|
170
164
|
}
|
|
171
165
|
// Tier 2: subprocess verify (best effort, ~200ms)
|
|
@@ -180,6 +174,5 @@ export async function checkAgentAuth() {
|
|
|
180
174
|
label,
|
|
181
175
|
binaryName,
|
|
182
176
|
authCommand: authenticated ? null : binaryName,
|
|
183
|
-
installCommand,
|
|
184
177
|
};
|
|
185
178
|
}
|