mcp-test-timebox 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +192 -0
- package/dist/executor/index.d.ts +8 -0
- package/dist/executor/index.d.ts.map +1 -0
- package/dist/executor/index.js +8 -0
- package/dist/executor/index.js.map +1 -0
- package/dist/executor/process-executor.d.ts +93 -0
- package/dist/executor/process-executor.d.ts.map +1 -0
- package/dist/executor/process-executor.js +161 -0
- package/dist/executor/process-executor.js.map +1 -0
- package/dist/executor/timebox-controller.d.ts +109 -0
- package/dist/executor/timebox-controller.d.ts.map +1 -0
- package/dist/executor/timebox-controller.js +140 -0
- package/dist/executor/timebox-controller.js.map +1 -0
- package/dist/report/index.d.ts +8 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +12 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/log-extractor.d.ts +125 -0
- package/dist/report/log-extractor.d.ts.map +1 -0
- package/dist/report/log-extractor.js +213 -0
- package/dist/report/log-extractor.js.map +1 -0
- package/dist/report/report-generator.d.ts +148 -0
- package/dist/report/report-generator.d.ts.map +1 -0
- package/dist/report/report-generator.js +261 -0
- package/dist/report/report-generator.js.map +1 -0
- package/dist/server.d.ts +27 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +150 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/run-test.d.ts +80 -0
- package/dist/tools/run-test.d.ts.map +1 -0
- package/dist/tools/run-test.js +178 -0
- package/dist/tools/run-test.js.map +1 -0
- package/dist/utils/path-validator.d.ts +52 -0
- package/dist/utils/path-validator.d.ts.map +1 -0
- package/dist/utils/path-validator.js +128 -0
- package/dist/utils/path-validator.js.map +1 -0
- package/dist/validation/command-builder.d.ts +52 -0
- package/dist/validation/command-builder.d.ts.map +1 -0
- package/dist/validation/command-builder.js +143 -0
- package/dist/validation/command-builder.js.map +1 -0
- package/dist/validation/index.d.ts +8 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +12 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/input-schema.d.ts +85 -0
- package/dist/validation/input-schema.d.ts.map +1 -0
- package/dist/validation/input-schema.js +113 -0
- package/dist/validation/input-schema.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 K.Kawanishi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# mcp-test-timebox
|
|
2
|
+
|
|
3
|
+
テスト実行の「終わらない/戻ってこない」問題を防ぐ、タイムボックス付きテスト専用MCPサーバ。
|
|
4
|
+
|
|
5
|
+
## 概要
|
|
6
|
+
|
|
7
|
+
- **必ず結果を返す**: ハードタイムアウト・無出力タイムアウトにより、ハングしても必ずレスポンスを返却
|
|
8
|
+
- **安全な実行**: 任意コマンド実行を禁止し、固定テンプレート(`flutter test`等)のみ許可
|
|
9
|
+
- **成果物の保存**: 実行ごとに `raw.log` / `summary.md` / `summary.json` を生成
|
|
10
|
+
|
|
11
|
+
## インストール
|
|
12
|
+
|
|
13
|
+
### 前提条件
|
|
14
|
+
|
|
15
|
+
- Node.js 18.0.0 以上
|
|
16
|
+
- npm または yarn
|
|
17
|
+
|
|
18
|
+
### インストール手順
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# リポジトリをクローン
|
|
22
|
+
git clone <repository-url>
|
|
23
|
+
cd mcp-test-timebox
|
|
24
|
+
|
|
25
|
+
# 依存関係をインストール
|
|
26
|
+
npm install
|
|
27
|
+
|
|
28
|
+
# ビルド
|
|
29
|
+
npm run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 使用方法
|
|
33
|
+
|
|
34
|
+
### MCPサーバとして起動
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# stdio経由でMCPサーバを起動
|
|
38
|
+
npm run start
|
|
39
|
+
|
|
40
|
+
# または直接実行
|
|
41
|
+
node dist/server.js
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### run_test ツールのパラメータ
|
|
45
|
+
|
|
46
|
+
| パラメータ | 型 | 必須 | 説明 |
|
|
47
|
+
|-----------|-----|------|------|
|
|
48
|
+
| `runner` | string | ✓ | テストランナー(現在は `flutter` のみ) |
|
|
49
|
+
| `scope` | string | ✓ | 実行スコープ: `all`, `file`, `pattern` |
|
|
50
|
+
| `target` | string | △ | `scope` が `file` または `pattern` の場合に必須 |
|
|
51
|
+
| `timeout_ms` | number | ✓ | ハードタイムアウト(ミリ秒) |
|
|
52
|
+
| `no_output_timeout_ms` | number | ✓ | 無出力タイムアウト(ミリ秒) |
|
|
53
|
+
| `max_output_bytes` | number | ✓ | 要約対象の末尾バイト数 |
|
|
54
|
+
| `report_dir` | string | - | レポート出力先(省略時は自動生成) |
|
|
55
|
+
|
|
56
|
+
### 実行例
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"runner": "flutter",
|
|
61
|
+
"scope": "all",
|
|
62
|
+
"timeout_ms": 300000,
|
|
63
|
+
"no_output_timeout_ms": 60000,
|
|
64
|
+
"max_output_bytes": 102400
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"runner": "flutter",
|
|
71
|
+
"scope": "file",
|
|
72
|
+
"target": "test/widget_test.dart",
|
|
73
|
+
"timeout_ms": 300000,
|
|
74
|
+
"no_output_timeout_ms": 60000,
|
|
75
|
+
"max_output_bytes": 102400
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### レスポンス
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"status": "pass",
|
|
84
|
+
"exit_code": 0,
|
|
85
|
+
"duration_ms": 12345,
|
|
86
|
+
"report_dir": ".cache/mcp-test-timebox/reports/20260113-123456",
|
|
87
|
+
"artifacts": {
|
|
88
|
+
"raw_log": ".cache/mcp-test-timebox/reports/20260113-123456/raw.log",
|
|
89
|
+
"summary_md": ".cache/mcp-test-timebox/reports/20260113-123456/summary.md",
|
|
90
|
+
"summary_json": ".cache/mcp-test-timebox/reports/20260113-123456/summary.json"
|
|
91
|
+
},
|
|
92
|
+
"excerpt": "..."
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### ステータス一覧
|
|
97
|
+
|
|
98
|
+
| ステータス | 説明 |
|
|
99
|
+
|-----------|------|
|
|
100
|
+
| `pass` | テスト成功(exit code 0) |
|
|
101
|
+
| `fail` | テスト失敗(exit code ≠ 0) |
|
|
102
|
+
| `timeout` | ハードタイムアウト超過 |
|
|
103
|
+
| `no_output` | 無出力タイムアウト超過 |
|
|
104
|
+
| `error` | バリデーションエラー等 |
|
|
105
|
+
|
|
106
|
+
## MCP設定例
|
|
107
|
+
|
|
108
|
+
### Kiro での設定
|
|
109
|
+
|
|
110
|
+
`.kiro/settings/mcp.json`:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"mcp-test-timebox": {
|
|
116
|
+
"command": "node",
|
|
117
|
+
"args": ["/path/to/mcp-test-timebox/dist/server.js"],
|
|
118
|
+
"disabled": false,
|
|
119
|
+
"autoApprove": []
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Claude Desktop での設定
|
|
126
|
+
|
|
127
|
+
`claude_desktop_config.json`:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"mcpServers": {
|
|
132
|
+
"mcp-test-timebox": {
|
|
133
|
+
"command": "node",
|
|
134
|
+
"args": ["/path/to/mcp-test-timebox/dist/server.js"]
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## 開発
|
|
141
|
+
|
|
142
|
+
### ビルド
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npm run build
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### テスト実行
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# 全テスト実行
|
|
152
|
+
npm run test
|
|
153
|
+
|
|
154
|
+
# ウォッチモード
|
|
155
|
+
npm run test:watch
|
|
156
|
+
|
|
157
|
+
# カバレッジ付き
|
|
158
|
+
npm run test:coverage
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 開発モード(ウォッチビルド)
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npm run dev
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## 成果物
|
|
168
|
+
|
|
169
|
+
テスト実行ごとに以下のファイルが生成されます:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
.cache/mcp-test-timebox/reports/<timestamp>/
|
|
173
|
+
├── raw.log # stdout/stderrの完全ログ(出力元を区別)
|
|
174
|
+
├── summary.md # 人間が読みやすい要約
|
|
175
|
+
└── summary.json # 機械処理用の構造化データ
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### raw.log フォーマット
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
[2026-01-13T12:34:56.789Z] [stdout] Running "flutter test"...
|
|
182
|
+
[2026-01-13T12:34:57.123Z] [stderr] Warning: some warning
|
|
183
|
+
[2026-01-13T12:34:58.456Z] [stdout] ✓ Test passed
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## ドキュメント
|
|
187
|
+
|
|
188
|
+
- [MVP要件](docs/MVP.md) - プロジェクトのMVP仕様
|
|
189
|
+
|
|
190
|
+
## ライセンス
|
|
191
|
+
|
|
192
|
+
MIT License - 詳細は [LICENSE](LICENSE) を参照
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executor モジュール
|
|
3
|
+
*
|
|
4
|
+
* プロセス実行とタイムアウト監視に関するコンポーネントをエクスポート
|
|
5
|
+
*/
|
|
6
|
+
export { TimeboxController, createTimeboxController, type ITimeboxController, type TimeoutType, type TimeoutCallback, } from './timebox-controller.js';
|
|
7
|
+
export { ProcessExecutor, createProcessExecutor, type IProcessExecutor, type ProcessExecutorOptions, type ProcessResult, type ProcessStatus, type LogEntry, } from './process-executor.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/executor/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,QAAQ,GACd,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executor モジュール
|
|
3
|
+
*
|
|
4
|
+
* プロセス実行とタイムアウト監視に関するコンポーネントをエクスポート
|
|
5
|
+
*/
|
|
6
|
+
export { TimeboxController, createTimeboxController, } from './timebox-controller.js';
|
|
7
|
+
export { ProcessExecutor, createProcessExecutor, } from './process-executor.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/executor/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,iBAAiB,EACjB,uBAAuB,GAIxB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,qBAAqB,GAMtB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProcessExecutor - プロセス実行とタイムアウト監視コンポーネント
|
|
3
|
+
*
|
|
4
|
+
* 子プロセスを生成し、タイムアウト監視を行う。
|
|
5
|
+
* - stdinを即座に閉じる
|
|
6
|
+
* - tree-killでプロセスツリーごと終了
|
|
7
|
+
* - stdout/stderrをLogEntryとして収集
|
|
8
|
+
*
|
|
9
|
+
* Requirements:
|
|
10
|
+
* - 3.1: プロセス起動時にstdinを即座に閉じる
|
|
11
|
+
* - 3.2: timeout_ms で指定された時間が経過したらプロセスを強制終了
|
|
12
|
+
* - 3.3: no_output_timeout_ms で指定された時間、出力がなければ強制終了
|
|
13
|
+
* - 3.4: プロセスツリーごと終了する
|
|
14
|
+
* - 3.5: exit code 0 で pass、それ以外で fail を返す
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* ログエントリ - stdout/stderrの出力を記録
|
|
18
|
+
*/
|
|
19
|
+
export interface LogEntry {
|
|
20
|
+
/** Unix timestamp (ms) */
|
|
21
|
+
timestamp: number;
|
|
22
|
+
/** 出力元ストリーム */
|
|
23
|
+
stream: 'stdout' | 'stderr';
|
|
24
|
+
/** 出力データ */
|
|
25
|
+
data: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* プロセス実行結果のステータス
|
|
29
|
+
*/
|
|
30
|
+
export type ProcessStatus = 'pass' | 'fail' | 'timeout' | 'no_output' | 'error';
|
|
31
|
+
/**
|
|
32
|
+
* プロセス実行結果
|
|
33
|
+
*/
|
|
34
|
+
export interface ProcessResult {
|
|
35
|
+
/** 実行ステータス */
|
|
36
|
+
status: ProcessStatus;
|
|
37
|
+
/** 終了コード(タイムアウト時はnull) */
|
|
38
|
+
exitCode: number | null;
|
|
39
|
+
/** 実行時間(ミリ秒) */
|
|
40
|
+
durationMs: number;
|
|
41
|
+
/** ログエントリ(stdout/stderr) */
|
|
42
|
+
logs: LogEntry[];
|
|
43
|
+
/** タイムアウトしたかどうか */
|
|
44
|
+
timedOut: boolean;
|
|
45
|
+
/** 無出力タイムアウトしたかどうか */
|
|
46
|
+
noOutput: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* プロセス実行オプション
|
|
50
|
+
*/
|
|
51
|
+
export interface ProcessExecutorOptions {
|
|
52
|
+
/** 作業ディレクトリ */
|
|
53
|
+
cwd: string;
|
|
54
|
+
/** ハードタイムアウト(ミリ秒) */
|
|
55
|
+
timeoutMs: number;
|
|
56
|
+
/** 無出力タイムアウト(ミリ秒) */
|
|
57
|
+
noOutputTimeoutMs: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* ProcessExecutorインターフェース
|
|
61
|
+
*/
|
|
62
|
+
export interface IProcessExecutor {
|
|
63
|
+
/**
|
|
64
|
+
* プロセスを実行し、タイムアウト監視を行う
|
|
65
|
+
*
|
|
66
|
+
* @param command - 実行するコマンド
|
|
67
|
+
* @param args - コマンド引数
|
|
68
|
+
* @param options - 実行オプション
|
|
69
|
+
* @returns プロセス実行結果
|
|
70
|
+
*/
|
|
71
|
+
execute(command: string, args: string[], options: ProcessExecutorOptions): Promise<ProcessResult>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* ProcessExecutor - プロセス実行の実装
|
|
75
|
+
*/
|
|
76
|
+
export declare class ProcessExecutor implements IProcessExecutor {
|
|
77
|
+
/**
|
|
78
|
+
* プロセスを実行し、タイムアウト監視を行う
|
|
79
|
+
*
|
|
80
|
+
* @param command - 実行するコマンド
|
|
81
|
+
* @param args - コマンド引数
|
|
82
|
+
* @param options - 実行オプション
|
|
83
|
+
* @returns プロセス実行結果
|
|
84
|
+
*/
|
|
85
|
+
execute(command: string, args: string[], options: ProcessExecutorOptions): Promise<ProcessResult>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* ProcessExecutorのファクトリ関数
|
|
89
|
+
*
|
|
90
|
+
* @returns 新しいProcessExecutorインスタンス
|
|
91
|
+
*/
|
|
92
|
+
export declare function createProcessExecutor(): IProcessExecutor;
|
|
93
|
+
//# sourceMappingURL=process-executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-executor.d.ts","sourceRoot":"","sources":["../../src/executor/process-executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe;IACf,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,cAAc;IACd,MAAM,EAAE,aAAa,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,gBAAgB;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,mBAAmB;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,sBAAsB;IACtB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,eAAe;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAGD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,OAAO,CACL,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,aAAa,CAAC,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IACtD;;;;;;;OAOG;IACG,OAAO,CACX,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,aAAa,CAAC;CAwI1B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,gBAAgB,CAExD"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProcessExecutor - プロセス実行とタイムアウト監視コンポーネント
|
|
3
|
+
*
|
|
4
|
+
* 子プロセスを生成し、タイムアウト監視を行う。
|
|
5
|
+
* - stdinを即座に閉じる
|
|
6
|
+
* - tree-killでプロセスツリーごと終了
|
|
7
|
+
* - stdout/stderrをLogEntryとして収集
|
|
8
|
+
*
|
|
9
|
+
* Requirements:
|
|
10
|
+
* - 3.1: プロセス起動時にstdinを即座に閉じる
|
|
11
|
+
* - 3.2: timeout_ms で指定された時間が経過したらプロセスを強制終了
|
|
12
|
+
* - 3.3: no_output_timeout_ms で指定された時間、出力がなければ強制終了
|
|
13
|
+
* - 3.4: プロセスツリーごと終了する
|
|
14
|
+
* - 3.5: exit code 0 で pass、それ以外で fail を返す
|
|
15
|
+
*/
|
|
16
|
+
import { spawn } from 'node:child_process';
|
|
17
|
+
import treeKill from 'tree-kill';
|
|
18
|
+
import { createTimeboxController } from './timebox-controller.js';
|
|
19
|
+
/**
|
|
20
|
+
* ProcessExecutor - プロセス実行の実装
|
|
21
|
+
*/
|
|
22
|
+
export class ProcessExecutor {
|
|
23
|
+
/**
|
|
24
|
+
* プロセスを実行し、タイムアウト監視を行う
|
|
25
|
+
*
|
|
26
|
+
* @param command - 実行するコマンド
|
|
27
|
+
* @param args - コマンド引数
|
|
28
|
+
* @param options - 実行オプション
|
|
29
|
+
* @returns プロセス実行結果
|
|
30
|
+
*/
|
|
31
|
+
async execute(command, args, options) {
|
|
32
|
+
const startTime = Date.now();
|
|
33
|
+
const logs = [];
|
|
34
|
+
const timebox = createTimeboxController();
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
let childProcess = null;
|
|
37
|
+
let resolved = false;
|
|
38
|
+
/**
|
|
39
|
+
* 結果を返す(一度だけ)
|
|
40
|
+
*/
|
|
41
|
+
const finalize = (result) => {
|
|
42
|
+
if (resolved)
|
|
43
|
+
return;
|
|
44
|
+
resolved = true;
|
|
45
|
+
timebox.clear();
|
|
46
|
+
resolve(result);
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* プロセスを強制終了する
|
|
50
|
+
*/
|
|
51
|
+
const killProcess = (timeoutType) => {
|
|
52
|
+
if (childProcess && childProcess.pid) {
|
|
53
|
+
// tree-killでプロセスツリーごと終了(Requirements 3.4)
|
|
54
|
+
treeKill(childProcess.pid, 'SIGKILL', (err) => {
|
|
55
|
+
if (err) {
|
|
56
|
+
// エラーが発生してもログに記録するのみ
|
|
57
|
+
console.error(`[ProcessExecutor] tree-kill error: ${err.message}`);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
const durationMs = Date.now() - startTime;
|
|
62
|
+
const isNoOutput = timeoutType === 'no_output';
|
|
63
|
+
finalize({
|
|
64
|
+
status: isNoOutput ? 'no_output' : 'timeout',
|
|
65
|
+
exitCode: null,
|
|
66
|
+
durationMs,
|
|
67
|
+
logs,
|
|
68
|
+
timedOut: !isNoOutput,
|
|
69
|
+
noOutput: isNoOutput,
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
try {
|
|
73
|
+
// プロセスを起動
|
|
74
|
+
childProcess = spawn(command, args, {
|
|
75
|
+
cwd: options.cwd,
|
|
76
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
77
|
+
// Windowsでは shell: true が必要な場合がある
|
|
78
|
+
shell: process.platform === 'win32',
|
|
79
|
+
});
|
|
80
|
+
// stdinを即座に閉じる(Requirements 3.1)
|
|
81
|
+
if (childProcess.stdin) {
|
|
82
|
+
childProcess.stdin.end();
|
|
83
|
+
}
|
|
84
|
+
// タイムアウト設定(Requirements 3.2, 3.3)
|
|
85
|
+
timebox.setHardTimeout(options.timeoutMs, killProcess);
|
|
86
|
+
timebox.setNoOutputTimeout(options.noOutputTimeoutMs, killProcess);
|
|
87
|
+
// stdout監視
|
|
88
|
+
if (childProcess.stdout) {
|
|
89
|
+
childProcess.stdout.on('data', (data) => {
|
|
90
|
+
const entry = {
|
|
91
|
+
timestamp: Date.now(),
|
|
92
|
+
stream: 'stdout',
|
|
93
|
+
data: data.toString(),
|
|
94
|
+
};
|
|
95
|
+
logs.push(entry);
|
|
96
|
+
timebox.notifyOutput();
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// stderr監視
|
|
100
|
+
if (childProcess.stderr) {
|
|
101
|
+
childProcess.stderr.on('data', (data) => {
|
|
102
|
+
const entry = {
|
|
103
|
+
timestamp: Date.now(),
|
|
104
|
+
stream: 'stderr',
|
|
105
|
+
data: data.toString(),
|
|
106
|
+
};
|
|
107
|
+
logs.push(entry);
|
|
108
|
+
timebox.notifyOutput();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// プロセス終了時
|
|
112
|
+
childProcess.on('close', (code) => {
|
|
113
|
+
const durationMs = Date.now() - startTime;
|
|
114
|
+
// exit code に基づいてステータスを決定(Requirements 3.5)
|
|
115
|
+
const status = code === 0 ? 'pass' : 'fail';
|
|
116
|
+
finalize({
|
|
117
|
+
status,
|
|
118
|
+
exitCode: code,
|
|
119
|
+
durationMs,
|
|
120
|
+
logs,
|
|
121
|
+
timedOut: false,
|
|
122
|
+
noOutput: false,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
// プロセスエラー時
|
|
126
|
+
childProcess.on('error', (err) => {
|
|
127
|
+
const durationMs = Date.now() - startTime;
|
|
128
|
+
finalize({
|
|
129
|
+
status: 'error',
|
|
130
|
+
exitCode: null,
|
|
131
|
+
durationMs,
|
|
132
|
+
logs,
|
|
133
|
+
timedOut: false,
|
|
134
|
+
noOutput: false,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
// spawn自体が失敗した場合
|
|
140
|
+
const durationMs = Date.now() - startTime;
|
|
141
|
+
finalize({
|
|
142
|
+
status: 'error',
|
|
143
|
+
exitCode: null,
|
|
144
|
+
durationMs,
|
|
145
|
+
logs,
|
|
146
|
+
timedOut: false,
|
|
147
|
+
noOutput: false,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* ProcessExecutorのファクトリ関数
|
|
155
|
+
*
|
|
156
|
+
* @returns 新しいProcessExecutorインスタンス
|
|
157
|
+
*/
|
|
158
|
+
export function createProcessExecutor() {
|
|
159
|
+
return new ProcessExecutor();
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=process-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-executor.js","sourceRoot":"","sources":["../../src/executor/process-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TimeboxController - タイムアウト監視コンポーネント
|
|
3
|
+
*
|
|
4
|
+
* プロセス実行のタイムアウトを管理する。
|
|
5
|
+
* - ハードタイムアウト: 指定時間経過で強制終了
|
|
6
|
+
* - 無出力タイムアウト: 出力がない状態が続くと強制終了
|
|
7
|
+
*
|
|
8
|
+
* Requirements:
|
|
9
|
+
* - 3.2: timeout_ms で指定された時間が経過したらプロセスを強制終了
|
|
10
|
+
* - 3.3: no_output_timeout_ms で指定された時間、出力がなければ強制終了
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* タイムアウトの種類
|
|
14
|
+
*/
|
|
15
|
+
export type TimeoutType = 'hard' | 'no_output';
|
|
16
|
+
/**
|
|
17
|
+
* タイムアウト発生時のコールバック
|
|
18
|
+
*/
|
|
19
|
+
export type TimeoutCallback = (type: TimeoutType) => void;
|
|
20
|
+
/**
|
|
21
|
+
* TimeboxControllerインターフェース
|
|
22
|
+
*/
|
|
23
|
+
export interface ITimeboxController {
|
|
24
|
+
/** ハードタイムアウトを設定 */
|
|
25
|
+
setHardTimeout(ms: number, onTimeout: TimeoutCallback): void;
|
|
26
|
+
/** 無出力タイムアウトを設定 */
|
|
27
|
+
setNoOutputTimeout(ms: number, onTimeout: TimeoutCallback): void;
|
|
28
|
+
/** 出力があったことを通知(無出力タイマーをリセット) */
|
|
29
|
+
notifyOutput(): void;
|
|
30
|
+
/** すべてのタイマーをクリア */
|
|
31
|
+
clear(): void;
|
|
32
|
+
/** タイムアウトが発生したかどうか */
|
|
33
|
+
isTimedOut(): boolean;
|
|
34
|
+
/** タイムアウトの種類を取得 */
|
|
35
|
+
getTimeoutType(): TimeoutType | null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* TimeboxController - タイムアウト監視の実装
|
|
39
|
+
*
|
|
40
|
+
* 2種類のタイムアウトを管理:
|
|
41
|
+
* 1. ハードタイムアウト: 設定後、指定時間で必ず発火
|
|
42
|
+
* 2. 無出力タイムアウト: 出力があるたびにリセットされる
|
|
43
|
+
*/
|
|
44
|
+
export declare class TimeboxController implements ITimeboxController {
|
|
45
|
+
/** ハードタイムアウトのタイマーID */
|
|
46
|
+
private hardTimeoutId;
|
|
47
|
+
/** 無出力タイムアウトのタイマーID */
|
|
48
|
+
private noOutputTimeoutId;
|
|
49
|
+
/** 無出力タイムアウトの設定値(リセット用) */
|
|
50
|
+
private noOutputTimeoutMs;
|
|
51
|
+
/** 無出力タイムアウトのコールバック(リセット用) */
|
|
52
|
+
private noOutputCallback;
|
|
53
|
+
/** タイムアウトが発生したかどうか */
|
|
54
|
+
private timedOut;
|
|
55
|
+
/** 発生したタイムアウトの種類 */
|
|
56
|
+
private timeoutType;
|
|
57
|
+
/**
|
|
58
|
+
* ハードタイムアウトを設定する
|
|
59
|
+
*
|
|
60
|
+
* 指定時間経過後、コールバックを呼び出す。
|
|
61
|
+
* 一度設定すると、clear()が呼ばれるまでリセットされない。
|
|
62
|
+
*
|
|
63
|
+
* @param ms - タイムアウト時間(ミリ秒)
|
|
64
|
+
* @param onTimeout - タイムアウト時のコールバック
|
|
65
|
+
*/
|
|
66
|
+
setHardTimeout(ms: number, onTimeout: TimeoutCallback): void;
|
|
67
|
+
/**
|
|
68
|
+
* 無出力タイムアウトを設定する
|
|
69
|
+
*
|
|
70
|
+
* 指定時間、stdout/stderrに出力がない場合にコールバックを呼び出す。
|
|
71
|
+
* notifyOutput()が呼ばれるとタイマーがリセットされる。
|
|
72
|
+
*
|
|
73
|
+
* @param ms - タイムアウト時間(ミリ秒)
|
|
74
|
+
* @param onTimeout - タイムアウト時のコールバック
|
|
75
|
+
*/
|
|
76
|
+
setNoOutputTimeout(ms: number, onTimeout: TimeoutCallback): void;
|
|
77
|
+
/**
|
|
78
|
+
* 出力があったことを通知する
|
|
79
|
+
*
|
|
80
|
+
* 無出力タイマーをリセットする。
|
|
81
|
+
* ハードタイムアウトには影響しない。
|
|
82
|
+
*/
|
|
83
|
+
notifyOutput(): void;
|
|
84
|
+
/**
|
|
85
|
+
* すべてのタイマーをクリアする
|
|
86
|
+
*
|
|
87
|
+
* プロセス終了時や、タイムアウト発生時に呼び出す。
|
|
88
|
+
*/
|
|
89
|
+
clear(): void;
|
|
90
|
+
/**
|
|
91
|
+
* タイムアウトが発生したかどうかを返す
|
|
92
|
+
*/
|
|
93
|
+
isTimedOut(): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* 発生したタイムアウトの種類を返す
|
|
96
|
+
*/
|
|
97
|
+
getTimeoutType(): TimeoutType | null;
|
|
98
|
+
/**
|
|
99
|
+
* 無出力タイマーをリセットする(内部メソッド)
|
|
100
|
+
*/
|
|
101
|
+
private resetNoOutputTimer;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* TimeboxControllerのファクトリ関数
|
|
105
|
+
*
|
|
106
|
+
* @returns 新しいTimeboxControllerインスタンス
|
|
107
|
+
*/
|
|
108
|
+
export declare function createTimeboxController(): ITimeboxController;
|
|
109
|
+
//# sourceMappingURL=timebox-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timebox-controller.d.ts","sourceRoot":"","sources":["../../src/executor/timebox-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,mBAAmB;IACnB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7D,mBAAmB;IACnB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IACjE,gCAAgC;IAChC,YAAY,IAAI,IAAI,CAAC;IACrB,mBAAmB;IACnB,KAAK,IAAI,IAAI,CAAC;IACd,sBAAsB;IACtB,UAAU,IAAI,OAAO,CAAC;IACtB,mBAAmB;IACnB,cAAc,IAAI,WAAW,GAAG,IAAI,CAAC;CACtC;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,uBAAuB;IACvB,OAAO,CAAC,aAAa,CAA8C;IAEnE,uBAAuB;IACvB,OAAO,CAAC,iBAAiB,CAA8C;IAEvE,2BAA2B;IAC3B,OAAO,CAAC,iBAAiB,CAAuB;IAEhD,8BAA8B;IAC9B,OAAO,CAAC,gBAAgB,CAAgC;IAExD,sBAAsB;IACtB,OAAO,CAAC,QAAQ,CAAS;IAEzB,oBAAoB;IACpB,OAAO,CAAC,WAAW,CAA4B;IAE/C;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,IAAI;IAe5D;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,IAAI;IAShE;;;;;OAKG;IACH,YAAY,IAAI,IAAI;IAOpB;;;;OAIG;IACH,KAAK,IAAI,IAAI;IAgBb;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,cAAc,IAAI,WAAW,GAAG,IAAI;IAIpC;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAiB3B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,kBAAkB,CAE5D"}
|