@fractalizer/mcp-cli 0.3.18 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +158 -266
  2. package/dist/commands/connect.command.d.ts +18 -15
  3. package/dist/commands/connect.command.d.ts.map +1 -1
  4. package/dist/commands/connect.command.js +66 -40
  5. package/dist/commands/connect.command.js.map +1 -1
  6. package/dist/commands/disconnect.command.d.ts +5 -17
  7. package/dist/commands/disconnect.command.d.ts.map +1 -1
  8. package/dist/commands/disconnect.command.js +4 -15
  9. package/dist/commands/disconnect.command.js.map +1 -1
  10. package/dist/commands/doctor.command.d.ts +34 -0
  11. package/dist/commands/doctor.command.d.ts.map +1 -0
  12. package/dist/commands/doctor.command.js +321 -0
  13. package/dist/commands/doctor.command.js.map +1 -0
  14. package/dist/commands/index.d.ts +1 -0
  15. package/dist/commands/index.d.ts.map +1 -1
  16. package/dist/commands/index.js +2 -0
  17. package/dist/commands/index.js.map +1 -1
  18. package/dist/commands/list.command.d.ts +4 -9
  19. package/dist/commands/list.command.d.ts.map +1 -1
  20. package/dist/commands/list.command.js +1 -5
  21. package/dist/commands/list.command.js.map +1 -1
  22. package/dist/commands/status.command.d.ts +6 -13
  23. package/dist/commands/status.command.d.ts.map +1 -1
  24. package/dist/commands/status.command.js +18 -12
  25. package/dist/commands/status.command.js.map +1 -1
  26. package/dist/commands/validate.command.d.ts +6 -11
  27. package/dist/commands/validate.command.d.ts.map +1 -1
  28. package/dist/commands/validate.command.js +15 -19
  29. package/dist/commands/validate.command.js.map +1 -1
  30. package/dist/connectors/base/base-connector.d.ts +31 -55
  31. package/dist/connectors/base/base-connector.d.ts.map +1 -1
  32. package/dist/connectors/base/base-connector.js +71 -57
  33. package/dist/connectors/base/base-connector.js.map +1 -1
  34. package/dist/connectors/base/configurable-connector.d.ts +89 -24
  35. package/dist/connectors/base/configurable-connector.d.ts.map +1 -1
  36. package/dist/connectors/base/configurable-connector.js +266 -23
  37. package/dist/connectors/base/configurable-connector.js.map +1 -1
  38. package/dist/connectors/base/connector.interface.d.ts +29 -20
  39. package/dist/connectors/base/connector.interface.d.ts.map +1 -1
  40. package/dist/connectors/base/index.d.ts +0 -1
  41. package/dist/connectors/base/index.d.ts.map +1 -1
  42. package/dist/connectors/base/index.js +0 -1
  43. package/dist/connectors/base/index.js.map +1 -1
  44. package/dist/connectors/claude-code/claude-code.connector.d.ts +171 -21
  45. package/dist/connectors/claude-code/claude-code.connector.d.ts.map +1 -1
  46. package/dist/connectors/claude-code/claude-code.connector.js +368 -43
  47. package/dist/connectors/claude-code/claude-code.connector.js.map +1 -1
  48. package/dist/connectors/connector-factory.d.ts +15 -15
  49. package/dist/connectors/connector-factory.d.ts.map +1 -1
  50. package/dist/connectors/connector-factory.js +61 -18
  51. package/dist/connectors/connector-factory.js.map +1 -1
  52. package/dist/connectors/index.d.ts +0 -4
  53. package/dist/connectors/index.d.ts.map +1 -1
  54. package/dist/connectors/index.js +2 -8
  55. package/dist/connectors/index.js.map +1 -1
  56. package/dist/connectors/registry.d.ts +20 -27
  57. package/dist/connectors/registry.d.ts.map +1 -1
  58. package/dist/connectors/registry.js +34 -34
  59. package/dist/connectors/registry.js.map +1 -1
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/dist/types/{base.types.d.ts → client.types.d.ts} +37 -30
  62. package/dist/types/client.types.d.ts.map +1 -0
  63. package/dist/types/client.types.js +6 -0
  64. package/dist/types/client.types.js.map +1 -0
  65. package/dist/types/doctor.types.d.ts +91 -0
  66. package/dist/types/doctor.types.d.ts.map +1 -0
  67. package/dist/types/doctor.types.js +12 -0
  68. package/dist/types/doctor.types.js.map +1 -0
  69. package/dist/types/launch.types.d.ts +59 -0
  70. package/dist/types/launch.types.d.ts.map +1 -0
  71. package/dist/types/launch.types.js +6 -0
  72. package/dist/types/launch.types.js.map +1 -0
  73. package/dist/types.d.ts +23 -14
  74. package/dist/types.d.ts.map +1 -1
  75. package/dist/types.js +9 -2
  76. package/dist/types.js.map +1 -1
  77. package/dist/utils/command-executor.d.ts +39 -3
  78. package/dist/utils/command-executor.d.ts.map +1 -1
  79. package/dist/utils/command-executor.js +95 -8
  80. package/dist/utils/command-executor.js.map +1 -1
  81. package/dist/utils/config-manager.d.ts +25 -42
  82. package/dist/utils/config-manager.d.ts.map +1 -1
  83. package/dist/utils/config-manager.js +21 -49
  84. package/dist/utils/config-manager.js.map +1 -1
  85. package/dist/utils/index.d.ts +1 -0
  86. package/dist/utils/index.d.ts.map +1 -1
  87. package/dist/utils/index.js +1 -0
  88. package/dist/utils/index.js.map +1 -1
  89. package/dist/utils/interactive-prompter.d.ts +16 -64
  90. package/dist/utils/interactive-prompter.d.ts.map +1 -1
  91. package/dist/utils/interactive-prompter.js +12 -65
  92. package/dist/utils/interactive-prompter.js.map +1 -1
  93. package/dist/utils/launch-spec-helpers.d.ts +38 -0
  94. package/dist/utils/launch-spec-helpers.d.ts.map +1 -0
  95. package/dist/utils/launch-spec-helpers.js +131 -0
  96. package/dist/utils/launch-spec-helpers.js.map +1 -0
  97. package/package.json +2 -2
  98. package/dist/connectors/base/file-based-connector.d.ts +0 -97
  99. package/dist/connectors/base/file-based-connector.d.ts.map +0 -1
  100. package/dist/connectors/base/file-based-connector.js +0 -185
  101. package/dist/connectors/base/file-based-connector.js.map +0 -1
  102. package/dist/connectors/claude-desktop/claude-desktop.connector.d.ts +0 -38
  103. package/dist/connectors/claude-desktop/claude-desktop.connector.d.ts.map +0 -1
  104. package/dist/connectors/claude-desktop/claude-desktop.connector.js +0 -68
  105. package/dist/connectors/claude-desktop/claude-desktop.connector.js.map +0 -1
  106. package/dist/connectors/codex/codex.connector.d.ts +0 -51
  107. package/dist/connectors/codex/codex.connector.d.ts.map +0 -1
  108. package/dist/connectors/codex/codex.connector.js +0 -76
  109. package/dist/connectors/codex/codex.connector.js.map +0 -1
  110. package/dist/connectors/gemini/gemini.connector.d.ts +0 -41
  111. package/dist/connectors/gemini/gemini.connector.d.ts.map +0 -1
  112. package/dist/connectors/gemini/gemini.connector.js +0 -61
  113. package/dist/connectors/gemini/gemini.connector.js.map +0 -1
  114. package/dist/connectors/qwen/qwen.connector.d.ts +0 -41
  115. package/dist/connectors/qwen/qwen.connector.d.ts.map +0 -1
  116. package/dist/connectors/qwen/qwen.connector.js +0 -61
  117. package/dist/connectors/qwen/qwen.connector.js.map +0 -1
  118. package/dist/types/base.types.d.ts.map +0 -1
  119. package/dist/types/base.types.js +0 -6
  120. package/dist/types/base.types.js.map +0 -1
@@ -1,19 +1,7 @@
1
1
  /**
2
- * Base types for MCP CLI Framework (no external dependencies)
2
+ * Client-related types for MCP CLI Framework (framework-agnostic)
3
3
  * @packageDocumentation
4
4
  */
5
- /**
6
- * Базовая конфигурация для любого MCP сервера
7
- * Все MCP серверы должны расширять этот интерфейс
8
- */
9
- export interface BaseMCPServerConfig {
10
- /** Абсолютный путь к директории проекта */
11
- projectPath: string;
12
- /** Уровень логирования */
13
- logLevel?: 'debug' | 'info' | 'warn' | 'error';
14
- /** Дополнительные переменные окружения для MCP сервера */
15
- env?: Record<string, string>;
16
- }
17
5
  /**
18
6
  * Информация о MCP клиенте (Claude Desktop, Claude Code и т.д.)
19
7
  */
@@ -43,6 +31,13 @@ export interface ConnectionStatus {
43
31
  configPath: string;
44
32
  /** Время последнего изменения конфига */
45
33
  lastModified?: Date;
34
+ /**
35
+ * Scope записи для коннекторов, которые управляют несколькими scope.
36
+ * На текущий момент используется только {@link ClaudeCodeConnector}
37
+ * (`user` — `~/.claude.json` (без projects), `local` — приватная запись
38
+ * для текущего проекта, `project` — `.mcp.json` в корне проекта).
39
+ */
40
+ scope?: 'user' | 'project' | 'local';
46
41
  /** Дополнительная информация */
47
42
  metadata?: Record<string, unknown>;
48
43
  };
@@ -51,6 +46,9 @@ export interface ConnectionStatus {
51
46
  }
52
47
  /**
53
48
  * Конфигурация MCP сервера для записи в файл клиента (JSON/TOML)
49
+ *
50
+ * @internal Используется только реализациями коннекторов. Публичный API
51
+ * принимает {@link ServerLaunchSpec}.
54
52
  */
55
53
  export interface MCPClientServerConfig {
56
54
  command: string;
@@ -60,6 +58,8 @@ export interface MCPClientServerConfig {
60
58
  /**
61
59
  * Базовая структура конфигурационного файла MCP клиента
62
60
  * Generic тип для разных форматов (mcpServers, mcp_servers и т.д.)
61
+ *
62
+ * @internal Используется только реализациями коннекторов.
63
63
  */
64
64
  export type MCPClientConfig<TKey extends string = 'mcpServers'> = {
65
65
  [K in TKey]?: Record<string, MCPClientServerConfig>;
@@ -70,9 +70,11 @@ export type MCPClientConfig<TKey extends string = 'mcpServers'> = {
70
70
  export type PromptType = 'input' | 'password' | 'select' | 'confirm' | 'number';
71
71
  /**
72
72
  * Определение промпта для сбора конфигурации
73
- * Generic по типу конфигурации и ключу поля
73
+ *
74
+ * @template TDomainConfig - Тип доменной конфигурации MCP сервера (произвольный объект)
75
+ * @template K - Ключ поля в конфигурации
74
76
  */
75
- export interface ConfigPromptDefinition<TConfig extends BaseMCPServerConfig, K extends keyof TConfig = keyof TConfig> {
77
+ export interface ConfigPromptDefinition<TDomainConfig extends object, K extends keyof TDomainConfig = keyof TDomainConfig> {
76
78
  /** Имя поля в конфигурации */
77
79
  name: K;
78
80
  /** Тип промпта */
@@ -80,37 +82,42 @@ export interface ConfigPromptDefinition<TConfig extends BaseMCPServerConfig, K e
80
82
  /** Сообщение для пользователя */
81
83
  message: string;
82
84
  /** Значение по умолчанию (может быть функцией от сохраненной конфигурации) */
83
- default?: TConfig[K] | ((savedConfig?: Partial<TConfig>) => TConfig[K] | undefined);
85
+ default?: TDomainConfig[K] | ((savedConfig?: Partial<TDomainConfig>) => TDomainConfig[K] | undefined);
84
86
  /** Функция валидации */
85
- validate?: (value: TConfig[K]) => string | true;
87
+ validate?: (value: TDomainConfig[K]) => string | true;
86
88
  /** Варианты выбора (для type: 'select') */
87
89
  choices?: Array<{
88
90
  name: string;
89
- value: TConfig[K];
91
+ value: TDomainConfig[K];
90
92
  }>;
91
93
  /** Условное отображение промпта */
92
- when?: (answers: Partial<TConfig>) => boolean;
94
+ when?: (answers: Partial<TDomainConfig>) => boolean;
93
95
  /** Маска для ввода (для type: 'password') */
94
96
  mask?: string;
95
97
  }
96
98
  /**
97
99
  * Опции для ConfigManager
100
+ *
101
+ * @template TDomainConfig - Тип доменной конфигурации MCP сервера (произвольный объект)
98
102
  */
99
- export interface ConfigManagerOptions<TConfig extends BaseMCPServerConfig> {
103
+ export interface ConfigManagerOptions<TDomainConfig extends object> {
100
104
  /** Название проекта (для ~/.{projectName}/config.json) */
101
105
  projectName: string;
102
106
  /**
103
- * Поля конфигурации, которые можно сохранять (без секретов!)
104
- * Например: ['orgId', 'logLevel', 'apiBase']
105
- */
106
- safeFields: Array<keyof TConfig>;
107
- /**
108
- * Опционально: кастомная сериализация перед записью в файл
107
+ * Кастомная сериализация перед записью в файл.
108
+ *
109
+ * Обязательное поле — гарантия защиты от случайного сохранения секретов:
110
+ * адаптер сервера обязан явно решить, какие поля попадают в config.json
111
+ * (например, исключить token и сохранить только orgId/apiBase).
112
+ *
113
+ * Если в твоём адаптере хочется писать «весь объект как есть» — передай
114
+ * `serialize: (cfg) => ({ ...cfg })`. Это явное согласие на сохранение
115
+ * всех полей.
109
116
  */
110
- serialize?: (config: TConfig) => Record<string, unknown>;
117
+ serialize: (config: TDomainConfig) => Record<string, unknown>;
111
118
  /**
112
- * Опционально: кастомная десериализация после чтения из файла
119
+ * Опционально: кастомная десериализация после чтения из файла.
113
120
  */
114
- deserialize?: (data: Record<string, unknown>) => Partial<TConfig>;
121
+ deserialize?: (data: Record<string, unknown>) => Partial<TDomainConfig>;
115
122
  }
116
- //# sourceMappingURL=base.types.d.ts.map
123
+ //# sourceMappingURL=client.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.types.d.ts","sourceRoot":"","sources":["../../src/types/client.types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Client-related types for MCP CLI Framework (framework-agnostic)
3
+ * @packageDocumentation
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=client.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.types.js","sourceRoot":"","sources":["../../src/types/client.types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Типы для команды самодиагностики `doctor`.
3
+ *
4
+ * Команда `doctor` запускает набор проверок (client-level + доменные) и
5
+ * возвращает агрегированный {@link DoctorReport}. Доменные проверки
6
+ * передаются вызывающим кодом через `extraChecks` — framework к доменной
7
+ * модели агностичен.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ /**
12
+ * Статус результата одной проверки.
13
+ *
14
+ * - `ok` — проверка пройдена.
15
+ * - `warn` — есть подозрение/неполные данные, но не блокер
16
+ * (например, путь выглядит относительным, файл конфига отсутствует).
17
+ * - `fail` — проблема, из-за которой сервер точно не заработает.
18
+ * - `skip` — проверка пропущена по объективным причинам (например, сервер не
19
+ * подключен к этому клиенту, нет данных для проверки).
20
+ * `skip` не учитывается в `summary.fail/warn/ok`.
21
+ */
22
+ export type DoctorCheckStatus = 'ok' | 'warn' | 'fail' | 'skip';
23
+ /**
24
+ * Результат выполнения одной проверки.
25
+ */
26
+ export interface DoctorCheckResult {
27
+ /** Итоговый статус. */
28
+ status: DoctorCheckStatus;
29
+ /** Человекочитаемое сообщение о результате. */
30
+ message: string;
31
+ /** Многострочные детали (например, путь к файлу, версия). Опционально. */
32
+ details?: string[];
33
+ /** Рекомендация по исправлению. Для `warn`/`fail`. Опционально. */
34
+ hint?: string;
35
+ }
36
+ /**
37
+ * Описание одной проверки.
38
+ *
39
+ * `run` — асинхронная функция, выполняющая проверку. Не должна выбрасывать
40
+ * исключений (любое исключение перехватывается `doctorCommand` и трактуется
41
+ * как `fail`).
42
+ */
43
+ export interface DoctorCheck {
44
+ /** Короткое имя проверки для вывода (например, `bundle-accessible`). */
45
+ name: string;
46
+ /** Развёрнутое описание того, что проверяется. */
47
+ description: string;
48
+ /**
49
+ * Опциональная группа для визуальной группировки в выводе
50
+ * (например, имя клиента `Claude Code` или домен `Yandex Tracker`).
51
+ */
52
+ group?: string;
53
+ /** Функция выполнения проверки. */
54
+ run: () => Promise<DoctorCheckResult>;
55
+ }
56
+ /**
57
+ * Агрегированный отчёт о всех проверках.
58
+ */
59
+ export interface DoctorReport {
60
+ /**
61
+ * Результаты проверок в детерминированном порядке (соответствует порядку,
62
+ * в котором проверки были собраны: client-level → extraChecks).
63
+ */
64
+ checks: Array<{
65
+ check: DoctorCheck;
66
+ result: DoctorCheckResult;
67
+ }>;
68
+ /**
69
+ * Сводка по статусам (без учёта `skip`).
70
+ */
71
+ summary: {
72
+ ok: number;
73
+ warn: number;
74
+ fail: number;
75
+ skip: number;
76
+ };
77
+ }
78
+ import type { IConnectorRegistry } from '../types.js';
79
+ /**
80
+ * Опции для команды `doctor`.
81
+ */
82
+ export interface DoctorCommandOptions {
83
+ /** Реестр коннекторов MCP клиентов. */
84
+ registry: IConnectorRegistry;
85
+ /**
86
+ * Дополнительные доменные проверки (например, валидация бандла сервера,
87
+ * проверка локальной конфигурации). Выполняются параллельно с client-level.
88
+ */
89
+ extraChecks?: DoctorCheck[];
90
+ }
91
+ //# sourceMappingURL=doctor.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.types.d.ts","sourceRoot":"","sources":["../../src/types/doctor.types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uBAAuB;IACvB,MAAM,EAAE,iBAAiB,CAAC;IAE1B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAEhB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IAEb,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,mCAAmC;IACnC,GAAG,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,CAAC,CAAC;IAEjE;;OAEG;IACH,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAID,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,QAAQ,EAAE,kBAAkB,CAAC;IAE7B;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;CAC7B"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Типы для команды самодиагностики `doctor`.
3
+ *
4
+ * Команда `doctor` запускает набор проверок (client-level + доменные) и
5
+ * возвращает агрегированный {@link DoctorReport}. Доменные проверки
6
+ * передаются вызывающим кодом через `extraChecks` — framework к доменной
7
+ * модели агностичен.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=doctor.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.types.js","sourceRoot":"","sources":["../../src/types/doctor.types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Launch specification types for MCP servers (framework-agnostic)
3
+ * @packageDocumentation
4
+ */
5
+ /**
6
+ * Спецификация запуска MCP сервера
7
+ *
8
+ * Готовая «команда + аргументы + переменные окружения» для записи в конфиг клиента.
9
+ * Публичный контракт между вызывающим кодом (адаптером домена) и framework: framework
10
+ * ничего не знает о доменных полях, оперируя только `ServerLaunchSpec`.
11
+ *
12
+ * Структурно совпадает с `MCPClientServerConfig` (internal), но логически выполняет
13
+ * другую роль: spec — это вход в `connector.connect()`, а `MCPClientServerConfig` — формат
14
+ * записи в JSON/TOML файлах клиентов.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const spec: ServerLaunchSpec = {
19
+ * command: 'node',
20
+ * args: ['/abs/path/dist/server.bundle.cjs'],
21
+ * env: { API_TOKEN: 'xxx', ORG_ID: 'my-org' },
22
+ * };
23
+ *
24
+ * await connector.connect(spec);
25
+ * ```
26
+ */
27
+ export interface ServerLaunchSpec {
28
+ /** Команда для запуска (обычно `node` или абсолютный путь к исполняемому файлу) */
29
+ command: string;
30
+ /**
31
+ * Аргументы команды (включая путь к скрипту и Node-флаги при необходимости).
32
+ *
33
+ * `readonly` — гарантия неизменяемости. Если коннектору нужно изменить
34
+ * массив, он должен сделать локальную копию.
35
+ */
36
+ args: readonly string[];
37
+ /** Переменные окружения, передаваемые серверу */
38
+ env: Record<string, string>;
39
+ /**
40
+ * Рабочая директория для процесса сервера (опционально).
41
+ *
42
+ * Если задано — должно быть абсолютным путём. Поддержка зависит от конкретного
43
+ * клиента: для file-based клиентов (Claude Desktop, Gemini, Qwen, Codex)
44
+ * сохраняется в конфиге и читается обратно; для Claude Code не поддерживается
45
+ * (выводится warning).
46
+ *
47
+ * Если `undefined` — поле не пишется в конфиг, наследуется текущий cwd процесса.
48
+ */
49
+ cwd?: string;
50
+ /**
51
+ * Метка «сервер выключен» (опционально). Если `true`, клиент НЕ должен
52
+ * запускать процесс сервера. Поддержка зависит от клиента: file-based клиенты
53
+ * пишут поле в JSON/TOML; Claude Code эту опцию не поддерживает (warning).
54
+ *
55
+ * Если `undefined` — поле не пишется в конфиг (по умолчанию активный).
56
+ */
57
+ disabled?: boolean;
58
+ }
59
+ //# sourceMappingURL=launch.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.types.d.ts","sourceRoot":"","sources":["../../src/types/launch.types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;OAKG;IACH,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IAExB,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5B;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Launch specification types for MCP servers (framework-agnostic)
3
+ * @packageDocumentation
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=launch.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.types.js","sourceRoot":"","sources":["../../src/types/launch.types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/dist/types.d.ts CHANGED
@@ -2,36 +2,45 @@
2
2
  * Core types for MCP CLI Framework
3
3
  * @packageDocumentation
4
4
  */
5
- export { type BaseMCPServerConfig, type MCPClientInfo, type ConnectionStatus, type MCPClientServerConfig, type MCPClientConfig, type PromptType, type ConfigPromptDefinition, type ConfigManagerOptions, } from './types/base.types.js';
6
- import type { BaseMCPServerConfig, ConnectionStatus, ConfigPromptDefinition } from './types/base.types.js';
5
+ export { type MCPClientInfo, type ConnectionStatus, type PromptType, type ConfigPromptDefinition, type ConfigManagerOptions, } from './types/client.types.js';
6
+ export { type ServerLaunchSpec } from './types/launch.types.js';
7
+ export { type DoctorCheck, type DoctorCheckResult, type DoctorCheckStatus, type DoctorReport, type DoctorCommandOptions, } from './types/doctor.types.js';
8
+ import type { ConnectionStatus, ConfigPromptDefinition } from './types/client.types.js';
9
+ import type { ServerLaunchSpec } from './types/launch.types.js';
7
10
  import type { MCPConnector } from './connectors/base/connector.interface.js';
8
11
  import type { ConfigManager } from './utils/config-manager.js';
9
12
  /**
10
13
  * Опции для команды connect
14
+ *
15
+ * @template TDomainConfig - Тип доменной конфигурации MCP сервера (произвольный объект)
11
16
  */
12
- export interface ConnectCommandOptions<TConfig extends BaseMCPServerConfig> {
17
+ export interface ConnectCommandOptions<TDomainConfig extends object> {
13
18
  /** Реестр коннекторов */
14
- registry: IConnectorRegistry<TConfig>;
19
+ registry: IConnectorRegistry;
15
20
  /** Менеджер конфигурации */
16
- configManager: ConfigManager<TConfig>;
17
- /** Промпты для сбора конфигурации */
18
- configPrompts: ConfigPromptDefinition<TConfig>[];
21
+ configManager: ConfigManager<TDomainConfig>;
22
+ /** Промпты для сбора доменной конфигурации */
23
+ configPrompts: ConfigPromptDefinition<TDomainConfig>[];
24
+ /**
25
+ * Адаптер: доменная конфигурация → спецификация запуска MCP сервера.
26
+ * Framework не знает структуру `TDomainConfig`, эта функция превращает её в
27
+ * `{ command, args, env }` для записи в конфиг клиента.
28
+ */
29
+ buildServerLaunch: (config: TDomainConfig) => ServerLaunchSpec;
19
30
  /** CLI опции (из commander) */
20
31
  cliOptions?: {
21
32
  client?: string;
22
33
  };
23
- /** Опционально: функция для добавления projectPath к конфигу */
24
- buildConfig?: (serverConfig: Omit<TConfig, 'projectPath'>) => TConfig;
25
34
  }
26
35
  /**
27
36
  * Интерфейс для реестра MCP коннекторов
28
37
  * @internal - используется только для типизации command options
29
38
  */
30
- export interface IConnectorRegistry<TConfig extends BaseMCPServerConfig = BaseMCPServerConfig> {
31
- register(connector: MCPConnector<TConfig>): void;
32
- get(name: string): MCPConnector<TConfig> | undefined;
33
- getAll(): MCPConnector<TConfig>[];
34
- findInstalled(): Promise<MCPConnector<TConfig>[]>;
39
+ export interface IConnectorRegistry {
40
+ register(connector: MCPConnector): void;
41
+ get(name: string): MCPConnector | undefined;
42
+ getAll(): MCPConnector[];
43
+ findInstalled(): Promise<MCPConnector[]>;
35
44
  checkAllStatuses(): Promise<Map<string, ConnectionStatus>>;
36
45
  }
37
46
  export type { MCPConnector };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,GAC1B,MAAM,uBAAuB,CAAC;AAG/B,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,qBAAqB,CAAC,OAAO,SAAS,mBAAmB;IACxE,yBAAyB;IACzB,QAAQ,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAEtC,4BAA4B;IAC5B,aAAa,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAEtC,qCAAqC;IACrC,aAAa,EAAE,sBAAsB,CAAC,OAAO,CAAC,EAAE,CAAC;IAEjD,+BAA+B;IAC/B,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF,gEAAgE;IAChE,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,OAAO,CAAC;CACvE;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IAC3F,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC;IACrD,MAAM,IAAI,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;IAClC,aAAa,IAAI,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClD,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAC5D;AAGD,YAAY,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D;;;;GAIG;AACH,MAAM,WAAW,qBAAqB,CAAC,aAAa,SAAS,MAAM;IACjE,yBAAyB;IACzB,QAAQ,EAAE,kBAAkB,CAAC;IAE7B,4BAA4B;IAC5B,aAAa,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAE5C,8CAA8C;IAC9C,aAAa,EAAE,sBAAsB,CAAC,aAAa,CAAC,EAAE,CAAC;IAEvD;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,gBAAgB,CAAC;IAE/D,+BAA+B;IAC/B,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;IACxC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IAC5C,MAAM,IAAI,YAAY,EAAE,CAAC;IACzB,aAAa,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACzC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;CAC5D;AAGD,YAAY,EAAE,YAAY,EAAE,CAAC"}
package/dist/types.js CHANGED
@@ -2,6 +2,13 @@
2
2
  * Core types for MCP CLI Framework
3
3
  * @packageDocumentation
4
4
  */
5
- // Re-export all base types
6
- export {} from './types/base.types.js';
5
+ // Re-export client-related types.
6
+ // Note: MCPClientServerConfig / MCPClientConfig — internal-only (используются
7
+ // внутри ConfigurableConnector для типизации raw-JSON/TOML структур). НЕ
8
+ // re-export.
9
+ export {} from './types/client.types.js';
10
+ // Re-export launch spec
11
+ export {} from './types/launch.types.js';
12
+ // Re-export doctor types (Stage 1.3)
13
+ export {} from './types/doctor.types.js';
7
14
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,2BAA2B;AAC3B,OAAO,EASN,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,kCAAkC;AAClC,8EAA8E;AAC9E,yEAAyE;AACzE,aAAa;AACb,OAAO,EAMN,MAAM,yBAAyB,CAAC;AAEjC,wBAAwB;AACxB,OAAO,EAAyB,MAAM,yBAAyB,CAAC;AAEhE,qCAAqC;AACrC,OAAO,EAMN,MAAM,yBAAyB,CAAC"}
@@ -4,6 +4,16 @@
4
4
  * @module CommandExecutor
5
5
  * @description Утилита для выполнения shell команд с различными режимами вывода
6
6
  */
7
+ /**
8
+ * Опции для {@link CommandExecutor.exec}.
9
+ */
10
+ export interface ExecOptions {
11
+ /**
12
+ * Таймаут выполнения в миллисекундах. При превышении — процесс убивается
13
+ * `SIGKILL`, выбрасывается `Error('Timeout: <command> exceeded <ms>ms')`.
14
+ */
15
+ timeout?: number;
16
+ }
7
17
  /**
8
18
  * Класс для выполнения shell команд
9
19
  *
@@ -12,6 +22,12 @@
12
22
  * // Выполнить команду и получить вывод
13
23
  * const output = CommandExecutor.exec('echo "test"');
14
24
  *
25
+ * // С таймаутом
26
+ * const output = CommandExecutor.exec('claude mcp list', { timeout: 5000 });
27
+ *
28
+ * // Безопасное выполнение без shell-интерпретации
29
+ * const out = CommandExecutor.execFile('claude', ['mcp', 'list'], { timeout: 5000 });
30
+ *
15
31
  * // Проверить наличие команды
16
32
  * if (CommandExecutor.isCommandAvailable('node')) {
17
33
  * console.log('Node.js установлен');
@@ -20,11 +36,12 @@
20
36
  */
21
37
  export declare class CommandExecutor {
22
38
  /**
23
- * Выполнить команду и вернуть stdout
39
+ * Выполнить команду и вернуть stdout.
24
40
  *
25
41
  * @param command - Команда для выполнения
42
+ * @param options - Опции выполнения (включая `timeout`)
26
43
  * @returns Вывод команды
27
- * @throws {Error} Если команда завершилась с ошибкой
44
+ * @throws {Error} Если команда завершилась с ошибкой или превысила таймаут
28
45
  *
29
46
  * @example
30
47
  * ```typescript
@@ -32,7 +49,26 @@ export declare class CommandExecutor {
32
49
  * console.log(nodeVersion); // v22.21.1
33
50
  * ```
34
51
  */
35
- static exec(command: string): string;
52
+ static exec(command: string, options?: ExecOptions): string;
53
+ /**
54
+ * Выполнить команду через `execFileSync` без shell-интерпретации.
55
+ *
56
+ * Этот метод предпочтительнее {@link exec} в случаях, когда команда и её
57
+ * аргументы фиксированы и НЕ должны интерпретироваться шеллом
58
+ * (избегаем риска инъекции, кросс-платформенных проблем с кавычками и т.п.).
59
+ *
60
+ * @param command - Имя исполняемого файла (резолвится через PATH) или абсолютный путь
61
+ * @param args - Аргументы команды (каждый — отдельный элемент массива)
62
+ * @param options - Опции (включая `timeout` в мс)
63
+ * @returns stdout команды
64
+ * @throws {Error} Если команда завершилась с ненулевым кодом или превысила таймаут
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const out = CommandExecutor.execFile('claude', ['mcp', 'list'], { timeout: 5000 });
69
+ * ```
70
+ */
71
+ static execFile(command: string, args: string[], options?: ExecOptions): string;
36
72
  /**
37
73
  * Выполнить команду тихо (подавить вывод)
38
74
  *
@@ -1 +1 @@
1
- {"version":3,"file":"command-executor.d.ts","sourceRoot":"","sources":["../../src/utils/command-executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAe;IAC1B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAQpC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQxC;;;;;;;;;;;;;OAaG;WACU,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB5E;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;CAQpD"}
1
+ {"version":3,"file":"command-executor.d.ts","sourceRoot":"","sources":["../../src/utils/command-executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,eAAe;IAC1B;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM;IA4B/D;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,WAAgB,GAAG,MAAM;IA6BnF;;;;;;;;;;OAUG;IACH,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQxC;;;;;;;;;;;;;OAaG;WACU,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB5E;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;CAQpD"}
@@ -4,7 +4,31 @@
4
4
  * @module CommandExecutor
5
5
  * @description Утилита для выполнения shell команд с различными режимами вывода
6
6
  */
7
- import { execSync, spawn } from 'node:child_process';
7
+ import { execFileSync, execSync, spawn } from 'node:child_process';
8
+ /**
9
+ * Максимальная длина stderr в сообщении об ошибке.
10
+ *
11
+ * Цель — не раздувать message длинными выводами CLI (некоторые программы
12
+ * пишут многостраничный traceback). 200 символов достаточно для диагностики.
13
+ */
14
+ const STDERR_PREVIEW_LIMIT = 200;
15
+ /**
16
+ * Извлечь и обрезать stderr из объекта ошибки `execSync`/`execFileSync`.
17
+ */
18
+ function extractStderr(err) {
19
+ if (typeof err !== 'object' || err === null)
20
+ return undefined;
21
+ const stderr = err.stderr;
22
+ if (stderr === undefined || stderr === null)
23
+ return undefined;
24
+ const text = typeof stderr === 'string' ? stderr : String(stderr);
25
+ const trimmed = text.trim();
26
+ if (trimmed.length === 0)
27
+ return undefined;
28
+ return trimmed.length > STDERR_PREVIEW_LIMIT
29
+ ? `${trimmed.slice(0, STDERR_PREVIEW_LIMIT)}…`
30
+ : trimmed;
31
+ }
8
32
  /**
9
33
  * Класс для выполнения shell команд
10
34
  *
@@ -13,6 +37,12 @@ import { execSync, spawn } from 'node:child_process';
13
37
  * // Выполнить команду и получить вывод
14
38
  * const output = CommandExecutor.exec('echo "test"');
15
39
  *
40
+ * // С таймаутом
41
+ * const output = CommandExecutor.exec('claude mcp list', { timeout: 5000 });
42
+ *
43
+ * // Безопасное выполнение без shell-интерпретации
44
+ * const out = CommandExecutor.execFile('claude', ['mcp', 'list'], { timeout: 5000 });
45
+ *
16
46
  * // Проверить наличие команды
17
47
  * if (CommandExecutor.isCommandAvailable('node')) {
18
48
  * console.log('Node.js установлен');
@@ -21,11 +51,12 @@ import { execSync, spawn } from 'node:child_process';
21
51
  */
22
52
  export class CommandExecutor {
23
53
  /**
24
- * Выполнить команду и вернуть stdout
54
+ * Выполнить команду и вернуть stdout.
25
55
  *
26
56
  * @param command - Команда для выполнения
57
+ * @param options - Опции выполнения (включая `timeout`)
27
58
  * @returns Вывод команды
28
- * @throws {Error} Если команда завершилась с ошибкой
59
+ * @throws {Error} Если команда завершилась с ошибкой или превысила таймаут
29
60
  *
30
61
  * @example
31
62
  * ```typescript
@@ -33,12 +64,68 @@ export class CommandExecutor {
33
64
  * console.log(nodeVersion); // v22.21.1
34
65
  * ```
35
66
  */
36
- static exec(command) {
67
+ static exec(command, options = {}) {
68
+ const execOptions = {
69
+ encoding: 'utf-8',
70
+ stdio: ['pipe', 'pipe', 'pipe'],
71
+ };
72
+ if (options.timeout !== undefined) {
73
+ execOptions.timeout = options.timeout;
74
+ execOptions.killSignal = 'SIGKILL';
75
+ }
37
76
  try {
38
- return execSync(command, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
77
+ return execSync(command, execOptions);
39
78
  }
40
- catch {
41
- throw new Error(`Command failed: ${command}`);
79
+ catch (err) {
80
+ // `execSync` бросает ошибку с `signal === 'SIGKILL'` при таймауте.
81
+ const errorObj = err;
82
+ if (options.timeout !== undefined &&
83
+ (errorObj?.signal === 'SIGKILL' || errorObj?.code === 'ETIMEDOUT')) {
84
+ throw new Error(`Timeout: ${command} exceeded ${String(options.timeout)}ms`);
85
+ }
86
+ const stderr = extractStderr(err);
87
+ throw new Error(stderr ? `Command failed: ${command} (${stderr})` : `Command failed: ${command}`);
88
+ }
89
+ }
90
+ /**
91
+ * Выполнить команду через `execFileSync` без shell-интерпретации.
92
+ *
93
+ * Этот метод предпочтительнее {@link exec} в случаях, когда команда и её
94
+ * аргументы фиксированы и НЕ должны интерпретироваться шеллом
95
+ * (избегаем риска инъекции, кросс-платформенных проблем с кавычками и т.п.).
96
+ *
97
+ * @param command - Имя исполняемого файла (резолвится через PATH) или абсолютный путь
98
+ * @param args - Аргументы команды (каждый — отдельный элемент массива)
99
+ * @param options - Опции (включая `timeout` в мс)
100
+ * @returns stdout команды
101
+ * @throws {Error} Если команда завершилась с ненулевым кодом или превысила таймаут
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const out = CommandExecutor.execFile('claude', ['mcp', 'list'], { timeout: 5000 });
106
+ * ```
107
+ */
108
+ static execFile(command, args, options = {}) {
109
+ const execOptions = {
110
+ encoding: 'utf-8',
111
+ stdio: ['pipe', 'pipe', 'pipe'],
112
+ };
113
+ if (options.timeout !== undefined) {
114
+ execOptions.timeout = options.timeout;
115
+ execOptions.killSignal = 'SIGKILL';
116
+ }
117
+ const displayCmd = `${command} ${args.join(' ')}`.trim();
118
+ try {
119
+ return execFileSync(command, args, execOptions);
120
+ }
121
+ catch (err) {
122
+ const errorObj = err;
123
+ if (options.timeout !== undefined &&
124
+ (errorObj?.signal === 'SIGKILL' || errorObj?.code === 'ETIMEDOUT')) {
125
+ throw new Error(`Timeout: ${displayCmd} exceeded ${String(options.timeout)}ms`);
126
+ }
127
+ const stderr = extractStderr(err);
128
+ throw new Error(stderr ? `Command failed: ${displayCmd} (${stderr})` : `Command failed: ${displayCmd}`);
42
129
  }
43
130
  }
44
131
  /**
@@ -82,7 +169,7 @@ export class CommandExecutor {
82
169
  resolve();
83
170
  }
84
171
  else {
85
- reject(new Error(`Command exited with code ${code}`));
172
+ reject(new Error(`Command exited with code ${String(code)}`));
86
173
  }
87
174
  });
88
175
  child.on('error', reject);