host-execution-mcp 1.0.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.
Files changed (126) hide show
  1. package/CHANGELOG.md +330 -0
  2. package/LICENSE +21 -0
  3. package/README.md +92 -0
  4. package/SECURITY.md +72 -0
  5. package/dist/access/docker-runtime.d.ts +38 -0
  6. package/dist/access/docker-runtime.js +61 -0
  7. package/dist/access/docker-runtime.js.map +1 -0
  8. package/dist/access/index.d.ts +110 -0
  9. package/dist/access/index.js +174 -0
  10. package/dist/access/index.js.map +1 -0
  11. package/dist/access/paradi-auth.d.ts +33 -0
  12. package/dist/access/paradi-auth.js +89 -0
  13. package/dist/access/paradi-auth.js.map +1 -0
  14. package/dist/access/paradi-transport.d.ts +10 -0
  15. package/dist/access/paradi-transport.js +39 -0
  16. package/dist/access/paradi-transport.js.map +1 -0
  17. package/dist/access/paradi-username.d.ts +5 -0
  18. package/dist/access/paradi-username.js +14 -0
  19. package/dist/access/paradi-username.js.map +1 -0
  20. package/dist/access/probe.d.ts +14 -0
  21. package/dist/access/probe.js +63 -0
  22. package/dist/access/probe.js.map +1 -0
  23. package/dist/access/session-pool.d.ts +25 -0
  24. package/dist/access/session-pool.js +46 -0
  25. package/dist/access/session-pool.js.map +1 -0
  26. package/dist/access/ssh-transport.d.ts +16 -0
  27. package/dist/access/ssh-transport.js +74 -0
  28. package/dist/access/ssh-transport.js.map +1 -0
  29. package/dist/access/types.d.ts +43 -0
  30. package/dist/access/types.js +21 -0
  31. package/dist/access/types.js.map +1 -0
  32. package/dist/config.d.ts +52 -0
  33. package/dist/config.js +179 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/index.d.ts +2 -0
  36. package/dist/index.js +19 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/instructions.d.ts +4 -0
  39. package/dist/instructions.js +93 -0
  40. package/dist/instructions.js.map +1 -0
  41. package/dist/kubectl/runner.d.ts +32 -0
  42. package/dist/kubectl/runner.js +79 -0
  43. package/dist/kubectl/runner.js.map +1 -0
  44. package/dist/server.d.ts +3 -0
  45. package/dist/server.js +24 -0
  46. package/dist/server.js.map +1 -0
  47. package/dist/ssh/access-client.d.ts +62 -0
  48. package/dist/ssh/access-client.js +470 -0
  49. package/dist/ssh/access-client.js.map +1 -0
  50. package/dist/ssh/access-errors.d.ts +53 -0
  51. package/dist/ssh/access-errors.js +180 -0
  52. package/dist/ssh/access-errors.js.map +1 -0
  53. package/dist/ssh/access-sftp.d.ts +14 -0
  54. package/dist/ssh/access-sftp.js +456 -0
  55. package/dist/ssh/access-sftp.js.map +1 -0
  56. package/dist/ssh/bastion-client.d.ts +60 -0
  57. package/dist/ssh/bastion-client.js +423 -0
  58. package/dist/ssh/bastion-client.js.map +1 -0
  59. package/dist/ssh/bastion-errors.d.ts +62 -0
  60. package/dist/ssh/bastion-errors.js +190 -0
  61. package/dist/ssh/bastion-errors.js.map +1 -0
  62. package/dist/ssh/hop1-sftp.d.ts +14 -0
  63. package/dist/ssh/hop1-sftp.js +437 -0
  64. package/dist/ssh/hop1-sftp.js.map +1 -0
  65. package/dist/tools/copy-job-schemas.d.ts +709 -0
  66. package/dist/tools/copy-job-schemas.js +48 -0
  67. package/dist/tools/copy-job-schemas.js.map +1 -0
  68. package/dist/tools/register.d.ts +14 -0
  69. package/dist/tools/register.js +619 -0
  70. package/dist/tools/register.js.map +1 -0
  71. package/dist/tools/remote-schemas.d.ts +879 -0
  72. package/dist/tools/remote-schemas.js +214 -0
  73. package/dist/tools/remote-schemas.js.map +1 -0
  74. package/dist/utils/copy-executor.d.ts +41 -0
  75. package/dist/utils/copy-executor.js +231 -0
  76. package/dist/utils/copy-executor.js.map +1 -0
  77. package/dist/utils/copy-job-manager.d.ts +34 -0
  78. package/dist/utils/copy-job-manager.js +400 -0
  79. package/dist/utils/copy-job-manager.js.map +1 -0
  80. package/dist/utils/copy-job-store.d.ts +18 -0
  81. package/dist/utils/copy-job-store.js +104 -0
  82. package/dist/utils/copy-job-store.js.map +1 -0
  83. package/dist/utils/copy-job-types.d.ts +133 -0
  84. package/dist/utils/copy-job-types.js +37 -0
  85. package/dist/utils/copy-job-types.js.map +1 -0
  86. package/dist/utils/copy-transfer.d.ts +59 -0
  87. package/dist/utils/copy-transfer.js +86 -0
  88. package/dist/utils/copy-transfer.js.map +1 -0
  89. package/dist/utils/remote-copy.d.ts +14 -0
  90. package/dist/utils/remote-copy.js +115 -0
  91. package/dist/utils/remote-copy.js.map +1 -0
  92. package/dist/utils/remote-executor.d.ts +15 -0
  93. package/dist/utils/remote-executor.js +34 -0
  94. package/dist/utils/remote-executor.js.map +1 -0
  95. package/dist/utils/remote-target.d.ts +22 -0
  96. package/dist/utils/remote-target.js +75 -0
  97. package/dist/utils/remote-target.js.map +1 -0
  98. package/dist/utils/result.d.ts +87 -0
  99. package/dist/utils/result.js +81 -0
  100. package/dist/utils/result.js.map +1 -0
  101. package/dist/utils/shell-quote.d.ts +22 -0
  102. package/dist/utils/shell-quote.js +55 -0
  103. package/dist/utils/shell-quote.js.map +1 -0
  104. package/dist/utils/timeout.d.ts +1 -0
  105. package/dist/utils/timeout.js +18 -0
  106. package/dist/utils/timeout.js.map +1 -0
  107. package/dist/utils/truncate.d.ts +5 -0
  108. package/dist/utils/truncate.js +12 -0
  109. package/dist/utils/truncate.js.map +1 -0
  110. package/dist/version.d.ts +1 -0
  111. package/dist/version.js +7 -0
  112. package/dist/version.js.map +1 -0
  113. package/docs/AGENT_GUIDE.md +161 -0
  114. package/docs/CONFIGURATION.md +130 -0
  115. package/docs/COPY_SPECIALIZED_TEST_CHECKLIST.md +9 -0
  116. package/docs/HOP1_E2E_CHECKLIST.md +128 -0
  117. package/docs/HOP1_E2E_RESULTS.json +647 -0
  118. package/docs/PARADI_ACCESS_PLAN.md +272 -0
  119. package/docs/REMOTE_COPY_JOB_API.md +92 -0
  120. package/docs/TODO.md +66 -0
  121. package/docs/USER_GUIDE.zh-CN.md +93 -0
  122. package/docs/clients/claude-desktop.json.example +12 -0
  123. package/docs/clients/cursor-npx.json.example +12 -0
  124. package/docs/clients/cursor.json.example +12 -0
  125. package/docs/clients/vscode.json.example +12 -0
  126. package/package.json +65 -0
@@ -0,0 +1,647 @@
1
+ {
2
+ "generatedAt": "2026-07-16T15:46:39.757Z",
3
+ "total": 43,
4
+ "passed": 43,
5
+ "failed": 0,
6
+ "cases": [
7
+ {
8
+ "testId": "C5",
9
+ "exitCode": 0,
10
+ "ok": true,
11
+ "result": {
12
+ "testId": "C5",
13
+ "ms": 4,
14
+ "ok": true,
15
+ "feature": "schema rejects resume",
16
+ "error": null,
17
+ "issues": [
18
+ "Unrecognized key(s) in object: 'resume'"
19
+ ]
20
+ },
21
+ "stderr": ""
22
+ },
23
+ {
24
+ "testId": "G2",
25
+ "exitCode": 0,
26
+ "ok": true,
27
+ "result": {
28
+ "testId": "G2",
29
+ "ms": 3,
30
+ "ok": true,
31
+ "feature": "schema rejects resume and idleTimeoutMs",
32
+ "error": null
33
+ },
34
+ "stderr": ""
35
+ },
36
+ {
37
+ "testId": "E5",
38
+ "exitCode": 0,
39
+ "ok": true,
40
+ "result": {
41
+ "testId": "E5",
42
+ "ms": 4,
43
+ "ok": true,
44
+ "feature": "pre-aborted signal",
45
+ "error": null,
46
+ "message": "Copy aborted"
47
+ },
48
+ "stderr": ""
49
+ },
50
+ {
51
+ "testId": "J1",
52
+ "exitCode": 0,
53
+ "ok": true,
54
+ "result": {
55
+ "testId": "J1",
56
+ "ms": 1407,
57
+ "ok": true,
58
+ "feature": "missing local path",
59
+ "error": null,
60
+ "message": "Local path not found: C:\\Users\\ANDY~1.WAN\\AppData\\Local\\Temp\\missing-J1-mrnogql5-428d.bin"
61
+ },
62
+ "stderr": ""
63
+ },
64
+ {
65
+ "testId": "A1",
66
+ "exitCode": 0,
67
+ "ok": true,
68
+ "result": {
69
+ "testId": "A1",
70
+ "ms": 1516,
71
+ "ok": true,
72
+ "feature": "local→bastion small",
73
+ "error": null,
74
+ "bytes": 4096,
75
+ "skippedBytes": 0,
76
+ "verified": true
77
+ },
78
+ "stderr": ""
79
+ },
80
+ {
81
+ "testId": "A2",
82
+ "exitCode": 0,
83
+ "ok": true,
84
+ "result": {
85
+ "testId": "A2",
86
+ "ms": 3076,
87
+ "ok": true,
88
+ "feature": "bastion→local small",
89
+ "error": null,
90
+ "bytes": 4096,
91
+ "verified": true
92
+ },
93
+ "stderr": ""
94
+ },
95
+ {
96
+ "testId": "A5",
97
+ "exitCode": 0,
98
+ "ok": true,
99
+ "result": {
100
+ "testId": "A5",
101
+ "ms": 2983,
102
+ "ok": true,
103
+ "feature": "overwrite without skip",
104
+ "error": null,
105
+ "skippedBytes": 0
106
+ },
107
+ "stderr": ""
108
+ },
109
+ {
110
+ "testId": "B1",
111
+ "exitCode": 0,
112
+ "ok": true,
113
+ "result": {
114
+ "testId": "B1",
115
+ "ms": 2326,
116
+ "ok": true,
117
+ "feature": "flat directory upload",
118
+ "error": null,
119
+ "message": "Copied local:C:\\Users\\ANDY~1.WAN\\AppData\\Local\\Temp\\hop1e2e-b1-nC5B8Z → bastion:/tmp/hop1e2e_B1-mrnogzeu-e710"
120
+ },
121
+ "stderr": ""
122
+ },
123
+ {
124
+ "testId": "B2",
125
+ "exitCode": 0,
126
+ "ok": true,
127
+ "result": {
128
+ "testId": "B2",
129
+ "ms": 2958,
130
+ "ok": true,
131
+ "feature": "nested directory upload",
132
+ "error": null,
133
+ "message": "Copied local:C:\\Users\\ANDY~1.WAN\\AppData\\Local\\Temp\\hop1e2e-B2-RU7huY → bastion:/tmp/hop1e2e_B2-mrnoh1mr-beb6"
134
+ },
135
+ "stderr": ""
136
+ },
137
+ {
138
+ "testId": "B3",
139
+ "exitCode": 0,
140
+ "ok": true,
141
+ "result": {
142
+ "testId": "B3",
143
+ "ms": 5089,
144
+ "ok": true,
145
+ "feature": "directory download",
146
+ "error": null,
147
+ "verified": true
148
+ },
149
+ "stderr": ""
150
+ },
151
+ {
152
+ "testId": "B4",
153
+ "exitCode": 0,
154
+ "ok": true,
155
+ "result": {
156
+ "testId": "B4",
157
+ "ms": 1773,
158
+ "ok": true,
159
+ "feature": "empty directory upload",
160
+ "error": null,
161
+ "behavior": "creates empty remote dir",
162
+ "verified": true
163
+ },
164
+ "stderr": ""
165
+ },
166
+ {
167
+ "testId": "B5",
168
+ "exitCode": 0,
169
+ "ok": true,
170
+ "result": {
171
+ "testId": "B5",
172
+ "ms": 2685,
173
+ "ok": true,
174
+ "feature": "directory verify=size",
175
+ "error": null,
176
+ "verified": true,
177
+ "bytes": 9
178
+ },
179
+ "stderr": ""
180
+ },
181
+ {
182
+ "testId": "B6",
183
+ "exitCode": 0,
184
+ "ok": true,
185
+ "result": {
186
+ "testId": "B6",
187
+ "ms": 2581,
188
+ "ok": true,
189
+ "feature": "directory verify=size+sha256 rejected",
190
+ "error": null,
191
+ "message": "verify=size+sha256 is not supported for directory transfers"
192
+ },
193
+ "stderr": ""
194
+ },
195
+ {
196
+ "testId": "C1",
197
+ "exitCode": 0,
198
+ "ok": true,
199
+ "result": {
200
+ "testId": "C1",
201
+ "ms": 2465,
202
+ "ok": true,
203
+ "feature": "upload skipIfComplete",
204
+ "error": null,
205
+ "skippedBytes": 4096
206
+ },
207
+ "stderr": ""
208
+ },
209
+ {
210
+ "testId": "C4",
211
+ "exitCode": 0,
212
+ "ok": true,
213
+ "result": {
214
+ "testId": "C4",
215
+ "ms": 2401,
216
+ "ok": true,
217
+ "feature": "default no-skip rewrites",
218
+ "error": null,
219
+ "skippedBytes": 0
220
+ },
221
+ "stderr": ""
222
+ },
223
+ {
224
+ "testId": "D1",
225
+ "exitCode": 0,
226
+ "ok": true,
227
+ "result": {
228
+ "testId": "D1",
229
+ "ms": 1318,
230
+ "ok": true,
231
+ "feature": "verify=none",
232
+ "error": null,
233
+ "verified": false
234
+ },
235
+ "stderr": ""
236
+ },
237
+ {
238
+ "testId": "D2",
239
+ "exitCode": 0,
240
+ "ok": true,
241
+ "result": {
242
+ "testId": "D2",
243
+ "ms": 1375,
244
+ "ok": true,
245
+ "feature": "verify=size default",
246
+ "error": null,
247
+ "verified": true
248
+ },
249
+ "stderr": ""
250
+ },
251
+ {
252
+ "testId": "D3",
253
+ "exitCode": 0,
254
+ "ok": true,
255
+ "result": {
256
+ "testId": "D3",
257
+ "ms": 1741,
258
+ "ok": true,
259
+ "feature": "verify=size+sha256",
260
+ "error": null,
261
+ "verified": true
262
+ },
263
+ "stderr": ""
264
+ },
265
+ {
266
+ "testId": "D4",
267
+ "exitCode": 0,
268
+ "ok": true,
269
+ "result": {
270
+ "testId": "D4",
271
+ "ms": 2621,
272
+ "ok": true,
273
+ "feature": "corrupt remote skip+sha256",
274
+ "error": null,
275
+ "behavior": "threw: sha256 mismatch: local=a2e659dacb4691e887ac0139f8893d04764ee197d70fb73d3190d56113d18e3e remote=c4fbc00b36c112317974383d685eedae211bc646bf28cbc804fbd61335931acf"
276
+ },
277
+ "stderr": ""
278
+ },
279
+ {
280
+ "testId": "C3",
281
+ "exitCode": 0,
282
+ "ok": true,
283
+ "result": {
284
+ "testId": "C3",
285
+ "ms": 3743,
286
+ "ok": true,
287
+ "feature": "same-size wrong content skip risk",
288
+ "error": null,
289
+ "sizeOnlySkipped": true,
290
+ "sizeOnlyKeepsWrong": true,
291
+ "sha256Behavior": "exit=0 skipped=0 verified=true md5Match=true",
292
+ "note": "size-only may false-skip; sha256 must not silent-keep wrong"
293
+ },
294
+ "stderr": ""
295
+ },
296
+ {
297
+ "testId": "F1",
298
+ "exitCode": 0,
299
+ "ok": true,
300
+ "result": {
301
+ "testId": "F1",
302
+ "ms": 1847,
303
+ "ok": true,
304
+ "feature": "async start small → success",
305
+ "error": null,
306
+ "transferId": "copy-mrnohu0m-a3003c"
307
+ },
308
+ "stderr": ""
309
+ },
310
+ {
311
+ "testId": "F3",
312
+ "exitCode": 0,
313
+ "ok": true,
314
+ "result": {
315
+ "testId": "F3",
316
+ "ms": 1582,
317
+ "ok": true,
318
+ "feature": "sync transferId not queryable",
319
+ "error": null,
320
+ "syncTransferId": "copy-mrnohvue-d52863",
321
+ "message": "Unknown transferId: copy-mrnohvue-d52863"
322
+ },
323
+ "stderr": ""
324
+ },
325
+ {
326
+ "testId": "F4",
327
+ "exitCode": 0,
328
+ "ok": true,
329
+ "result": {
330
+ "testId": "F4",
331
+ "ms": 24,
332
+ "ok": true,
333
+ "feature": "orphan reconcile",
334
+ "error": "MCP restarted; in-process worker lost",
335
+ "reconciled": 1
336
+ },
337
+ "stderr": ""
338
+ },
339
+ {
340
+ "testId": "F5",
341
+ "exitCode": 0,
342
+ "ok": true,
343
+ "result": {
344
+ "testId": "F5",
345
+ "ms": 1747,
346
+ "ok": true,
347
+ "feature": "fence success then cancel",
348
+ "error": null,
349
+ "cancelMessage": "Job already finished with status=success"
350
+ },
351
+ "stderr": ""
352
+ },
353
+ {
354
+ "testId": "F6",
355
+ "exitCode": 0,
356
+ "ok": true,
357
+ "result": {
358
+ "testId": "F6",
359
+ "ms": 302,
360
+ "ok": true,
361
+ "feature": "fence cancel wins over late success",
362
+ "error": null,
363
+ "status": "cancelled"
364
+ },
365
+ "stderr": ""
366
+ },
367
+ {
368
+ "testId": "G1",
369
+ "exitCode": 0,
370
+ "ok": true,
371
+ "result": {
372
+ "testId": "G1",
373
+ "ms": 1386,
374
+ "ok": true,
375
+ "feature": "sync blocks and no job file",
376
+ "error": null,
377
+ "elapsed": 869,
378
+ "transferId": "copy-mrnoi0jz-22ce07",
379
+ "jobFileExists": false
380
+ },
381
+ "stderr": ""
382
+ },
383
+ {
384
+ "testId": "J2",
385
+ "exitCode": 0,
386
+ "ok": true,
387
+ "result": {
388
+ "testId": "J2",
389
+ "ms": 1484,
390
+ "ok": true,
391
+ "feature": "missing remote path",
392
+ "error": null,
393
+ "message": "Remote path not found: /tmp/does-not-exist-J2-mrnoi225-fd98.bin"
394
+ },
395
+ "stderr": ""
396
+ },
397
+ {
398
+ "testId": "J3",
399
+ "exitCode": 0,
400
+ "ok": true,
401
+ "result": {
402
+ "testId": "J3",
403
+ "ms": 2562,
404
+ "ok": true,
405
+ "feature": "no copy-meta dependency",
406
+ "error": null,
407
+ "skippedBytes": 0
408
+ },
409
+ "stderr": ""
410
+ },
411
+ {
412
+ "testId": "J4",
413
+ "exitCode": 0,
414
+ "ok": true,
415
+ "result": {
416
+ "testId": "J4",
417
+ "ms": 2304,
418
+ "ok": true,
419
+ "feature": "result field skippedBytes only",
420
+ "error": null,
421
+ "keys": [
422
+ "exitCode",
423
+ "bytes",
424
+ "totalBytes",
425
+ "skippedBytes",
426
+ "transferId",
427
+ "verified",
428
+ "usedFastPath",
429
+ "message",
430
+ "stderr",
431
+ "durationMs"
432
+ ],
433
+ "skippedBytes": 4096,
434
+ "first": 0
435
+ },
436
+ "stderr": ""
437
+ },
438
+ {
439
+ "testId": "I2",
440
+ "exitCode": 0,
441
+ "ok": true,
442
+ "result": {
443
+ "testId": "I2",
444
+ "ms": 2047,
445
+ "ok": true,
446
+ "feature": "parallel Hop1 copies",
447
+ "error": null,
448
+ "t1": "copy-mrnoi888-7fcd1b",
449
+ "t2": "copy-mrnoi88c-b957eb"
450
+ },
451
+ "stderr": ""
452
+ },
453
+ {
454
+ "testId": "H1",
455
+ "exitCode": 0,
456
+ "ok": true,
457
+ "result": {
458
+ "testId": "H1",
459
+ "ms": 4564,
460
+ "ok": true,
461
+ "feature": "pipeline local→pod",
462
+ "error": null,
463
+ "pod": "jd-joyvideo-test-565bb64c45-59c6q",
464
+ "message": "Copied local:C:\\Users\\ANDY~1.WAN\\AppData\\Local\\Temp\\mcp-copy221-fixtures\\small-4k.bin → pod:/tmp/hop1e2e_H1-mrnoia7l-2a37.bin"
465
+ },
466
+ "stderr": ""
467
+ },
468
+ {
469
+ "testId": "H2",
470
+ "exitCode": 0,
471
+ "ok": true,
472
+ "result": {
473
+ "testId": "H2",
474
+ "ms": 3524,
475
+ "ok": true,
476
+ "feature": "pipeline local→host",
477
+ "error": null,
478
+ "host": "10.121.32.74",
479
+ "message": "Copied local:C:\\Users\\ANDY~1.WAN\\AppData\\Local\\Temp\\mcp-copy221-fixtures\\small-4k.bin → host:/tmp/hop1e2e_H2-mrnoie3g-7914.bin"
480
+ },
481
+ "stderr": ""
482
+ },
483
+ {
484
+ "testId": "E3",
485
+ "exitCode": 0,
486
+ "ok": true,
487
+ "result": {
488
+ "testId": "E3",
489
+ "ms": 1078,
490
+ "ok": true,
491
+ "feature": "cancel Hop1 keeps exec SSH",
492
+ "error": null,
493
+ "cancelStatus": "cancelled",
494
+ "finalStatus": "cancelled",
495
+ "sshAlive": "ssh_ok"
496
+ },
497
+ "stderr": ""
498
+ },
499
+ {
500
+ "testId": "E4",
501
+ "exitCode": 0,
502
+ "ok": true,
503
+ "result": {
504
+ "testId": "E4",
505
+ "ms": 1897,
506
+ "ok": true,
507
+ "feature": "SFTP rebuild after cancel",
508
+ "error": null,
509
+ "message": "Copied local:C:\\Users\\ANDY~1.WAN\\AppData\\Local\\Temp\\mcp-copy221-fixtures\\small-4k.bin → bastion:/tmp/hop1e2e_E4-mrnoiifm-aced_b.bin"
510
+ },
511
+ "stderr": ""
512
+ },
513
+ {
514
+ "testId": "E1",
515
+ "exitCode": 0,
516
+ "ok": true,
517
+ "result": {
518
+ "testId": "E1",
519
+ "ms": 3430,
520
+ "ok": true,
521
+ "feature": "async totalTimeout → timeout",
522
+ "error": "Hop1 total timeout after 1500ms",
523
+ "status": "timeout"
524
+ },
525
+ "stderr": ""
526
+ },
527
+ {
528
+ "testId": "E2",
529
+ "exitCode": 0,
530
+ "ok": true,
531
+ "result": {
532
+ "testId": "E2",
533
+ "ms": 3169,
534
+ "ok": true,
535
+ "feature": "sync short totalTimeout",
536
+ "error": null,
537
+ "message": "Hop1 total timeout after 1500ms"
538
+ },
539
+ "stderr": ""
540
+ },
541
+ {
542
+ "testId": "G3",
543
+ "exitCode": 0,
544
+ "ok": true,
545
+ "result": {
546
+ "testId": "G3",
547
+ "ms": 3180,
548
+ "ok": true,
549
+ "feature": "timeoutMs maps to wall budget",
550
+ "error": null,
551
+ "message": "Hop1 total timeout after 1500ms"
552
+ },
553
+ "stderr": ""
554
+ },
555
+ {
556
+ "testId": "A3",
557
+ "exitCode": 0,
558
+ "ok": true,
559
+ "result": {
560
+ "testId": "A3",
561
+ "ms": 21104,
562
+ "ok": true,
563
+ "feature": "local→bastion large",
564
+ "error": null,
565
+ "bytes": 35582074,
566
+ "usedFastPath": true
567
+ },
568
+ "stderr": ""
569
+ },
570
+ {
571
+ "testId": "A4",
572
+ "exitCode": 0,
573
+ "ok": true,
574
+ "result": {
575
+ "testId": "A4",
576
+ "ms": 41434,
577
+ "ok": true,
578
+ "feature": "bastion→local large",
579
+ "error": null,
580
+ "bytes": 35582074,
581
+ "usedFastPath": true
582
+ },
583
+ "stderr": ""
584
+ },
585
+ {
586
+ "testId": "C2",
587
+ "exitCode": 0,
588
+ "ok": true,
589
+ "result": {
590
+ "testId": "C2",
591
+ "ms": 41686,
592
+ "ok": true,
593
+ "feature": "download skip rewrite on size mismatch",
594
+ "error": null,
595
+ "skippedBytes": 0
596
+ },
597
+ "stderr": ""
598
+ },
599
+ {
600
+ "testId": "F2",
601
+ "exitCode": 0,
602
+ "ok": true,
603
+ "result": {
604
+ "testId": "F2",
605
+ "ms": 20667,
606
+ "ok": true,
607
+ "feature": "async large progress shape",
608
+ "error": null,
609
+ "sawProgress": true,
610
+ "finalPercent": 100,
611
+ "skippedBytes": 0
612
+ },
613
+ "stderr": ""
614
+ },
615
+ {
616
+ "testId": "I1",
617
+ "exitCode": 0,
618
+ "ok": true,
619
+ "result": {
620
+ "testId": "I1",
621
+ "ms": 25193,
622
+ "ok": true,
623
+ "feature": "parallel exec during SFTP",
624
+ "error": null,
625
+ "exec": "master001\nexec_parallel_ok",
626
+ "job": "success"
627
+ },
628
+ "stderr": ""
629
+ },
630
+ {
631
+ "testId": "H3",
632
+ "exitCode": 0,
633
+ "ok": true,
634
+ "result": {
635
+ "testId": "H3",
636
+ "ms": 2154,
637
+ "ok": true,
638
+ "feature": "pipeline abort best-effort",
639
+ "error": null,
640
+ "status": "cancelled",
641
+ "note": "Hop2 cancel best-effort; SSH must stay alive",
642
+ "sshAlive": true
643
+ },
644
+ "stderr": ""
645
+ }
646
+ ]
647
+ }