octoparse-cli 0.1.14

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 (278) hide show
  1. package/README.md +271 -0
  2. package/RUNTIME_SECURITY_NOTICE.txt +41 -0
  3. package/SECURITY.md +24 -0
  4. package/dist/cli/args.js +34 -0
  5. package/dist/cli/help.js +214 -0
  6. package/dist/cli/output.js +39 -0
  7. package/dist/commands/auth.js +283 -0
  8. package/dist/commands/capabilities.js +118 -0
  9. package/dist/commands/cloud.js +241 -0
  10. package/dist/commands/data.js +220 -0
  11. package/dist/commands/doctor.js +73 -0
  12. package/dist/commands/env.js +63 -0
  13. package/dist/commands/local.js +251 -0
  14. package/dist/commands/run.js +622 -0
  15. package/dist/commands/runs.js +171 -0
  16. package/dist/commands/task.js +101 -0
  17. package/dist/index.js +133 -0
  18. package/dist/runtime/account-capabilities.js +71 -0
  19. package/dist/runtime/api-client.js +290 -0
  20. package/dist/runtime/artifacts.js +33 -0
  21. package/dist/runtime/auth.js +94 -0
  22. package/dist/runtime/bridge-hub.js +173 -0
  23. package/dist/runtime/client-headers.js +23 -0
  24. package/dist/runtime/cloud-data.js +75 -0
  25. package/dist/runtime/config.js +48 -0
  26. package/dist/runtime/data-exporter.js +267 -0
  27. package/dist/runtime/engine-host.js +449 -0
  28. package/dist/runtime/local-runs.js +92 -0
  29. package/dist/runtime/naming.js +13 -0
  30. package/dist/runtime/run-control.js +363 -0
  31. package/dist/runtime/run-services.js +380 -0
  32. package/dist/runtime/security-notice.js +78 -0
  33. package/dist/runtime/task-definition-provider.js +282 -0
  34. package/dist/types.js +4 -0
  35. package/examples/minimal-task.json +6 -0
  36. package/examples/navigate-example-task.json +6 -0
  37. package/node_modules/@octopus/bpmn/index.js +3 -0
  38. package/node_modules/@octopus/bpmn/lib/Context.js +245 -0
  39. package/node_modules/@octopus/bpmn/lib/Definition.js +258 -0
  40. package/node_modules/@octopus/bpmn/lib/Engine.js +275 -0
  41. package/node_modules/@octopus/bpmn/lib/PrematureStopError.js +7 -0
  42. package/node_modules/@octopus/bpmn/lib/activities/Activity.js +202 -0
  43. package/node_modules/@octopus/bpmn/lib/activities/BaseProcess.js +308 -0
  44. package/node_modules/@octopus/bpmn/lib/activities/BaseTask.js +145 -0
  45. package/node_modules/@octopus/bpmn/lib/activities/BoundaryEvent.js +12 -0
  46. package/node_modules/@octopus/bpmn/lib/activities/Dummy.js +10 -0
  47. package/node_modules/@octopus/bpmn/lib/activities/EventDefinition.js +99 -0
  48. package/node_modules/@octopus/bpmn/lib/activities/Flow.js +52 -0
  49. package/node_modules/@octopus/bpmn/lib/activities/Form.js +67 -0
  50. package/node_modules/@octopus/bpmn/lib/activities/InputOutput.js +53 -0
  51. package/node_modules/@octopus/bpmn/lib/activities/IntermediateCatchEvent.js +12 -0
  52. package/node_modules/@octopus/bpmn/lib/activities/MessageFlow.js +19 -0
  53. package/node_modules/@octopus/bpmn/lib/activities/MultiInstanceLoopCharacteristics.js +160 -0
  54. package/node_modules/@octopus/bpmn/lib/activities/Properties.js +27 -0
  55. package/node_modules/@octopus/bpmn/lib/activities/SequenceFlow.js +56 -0
  56. package/node_modules/@octopus/bpmn/lib/activities/ServiceConnector.js +71 -0
  57. package/node_modules/@octopus/bpmn/lib/context-helper.js +198 -0
  58. package/node_modules/@octopus/bpmn/lib/events/EndEvent.js +22 -0
  59. package/node_modules/@octopus/bpmn/lib/events/ErrorEvent.js +41 -0
  60. package/node_modules/@octopus/bpmn/lib/events/MessageEvent.js +19 -0
  61. package/node_modules/@octopus/bpmn/lib/events/StartEvent.js +55 -0
  62. package/node_modules/@octopus/bpmn/lib/events/TimerEvent.js +75 -0
  63. package/node_modules/@octopus/bpmn/lib/expressions.js +41 -0
  64. package/node_modules/@octopus/bpmn/lib/gateways/ExclusiveGateway.js +86 -0
  65. package/node_modules/@octopus/bpmn/lib/gateways/InclusiveGateway.js +56 -0
  66. package/node_modules/@octopus/bpmn/lib/gateways/ParallelGateway.js +195 -0
  67. package/node_modules/@octopus/bpmn/lib/getPropertyValue.js +83 -0
  68. package/node_modules/@octopus/bpmn/lib/index.js +6 -0
  69. package/node_modules/@octopus/bpmn/lib/mapper.js +55 -0
  70. package/node_modules/@octopus/bpmn/lib/parameter.js +119 -0
  71. package/node_modules/@octopus/bpmn/lib/script-helper.js +45 -0
  72. package/node_modules/@octopus/bpmn/lib/tasks/ManualTask.js +31 -0
  73. package/node_modules/@octopus/bpmn/lib/tasks/ReceiveTask.js +31 -0
  74. package/node_modules/@octopus/bpmn/lib/tasks/ScriptTask.js +35 -0
  75. package/node_modules/@octopus/bpmn/lib/tasks/SendTask.js +16 -0
  76. package/node_modules/@octopus/bpmn/lib/tasks/ServiceTask.js +68 -0
  77. package/node_modules/@octopus/bpmn/lib/tasks/SubProcess.js +17 -0
  78. package/node_modules/@octopus/bpmn/lib/tasks/Task.js +16 -0
  79. package/node_modules/@octopus/bpmn/lib/tasks/UserTask.js +47 -0
  80. package/node_modules/@octopus/bpmn/lib/transformer.js +13 -0
  81. package/node_modules/@octopus/bpmn/lib/validation.js +111 -0
  82. package/node_modules/@octopus/bpmn/package.json +17 -0
  83. package/node_modules/@octopus/bpmn/types/bpmn.d.ts +85 -0
  84. package/node_modules/@octopus/engine/README.md +370 -0
  85. package/node_modules/@octopus/engine/dist/actions/BackPreWebPageAction.d.ts +4 -0
  86. package/node_modules/@octopus/engine/dist/actions/BackPreWebPageAction.js +1 -0
  87. package/node_modules/@octopus/engine/dist/actions/BaseAction.d.ts +339 -0
  88. package/node_modules/@octopus/engine/dist/actions/BaseAction.js +1559 -0
  89. package/node_modules/@octopus/engine/dist/actions/BranchAction.d.ts +9 -0
  90. package/node_modules/@octopus/engine/dist/actions/BranchAction.js +1 -0
  91. package/node_modules/@octopus/engine/dist/actions/ClickAction.d.ts +22 -0
  92. package/node_modules/@octopus/engine/dist/actions/ClickAction.js +1 -0
  93. package/node_modules/@octopus/engine/dist/actions/ConditionAction.d.ts +4 -0
  94. package/node_modules/@octopus/engine/dist/actions/ConditionAction.js +1 -0
  95. package/node_modules/@octopus/engine/dist/actions/EmptyAction.d.ts +4 -0
  96. package/node_modules/@octopus/engine/dist/actions/EmptyAction.js +12 -0
  97. package/node_modules/@octopus/engine/dist/actions/EnterCaptchaAction.d.ts +28 -0
  98. package/node_modules/@octopus/engine/dist/actions/EnterCaptchaAction.js +1 -0
  99. package/node_modules/@octopus/engine/dist/actions/EnterTextAction.d.ts +20 -0
  100. package/node_modules/@octopus/engine/dist/actions/EnterTextAction.js +1 -0
  101. package/node_modules/@octopus/engine/dist/actions/ExtractDataAction.d.ts +40 -0
  102. package/node_modules/@octopus/engine/dist/actions/ExtractDataAction.js +1 -0
  103. package/node_modules/@octopus/engine/dist/actions/LoopAction.d.ts +41 -0
  104. package/node_modules/@octopus/engine/dist/actions/LoopAction.js +526 -0
  105. package/node_modules/@octopus/engine/dist/actions/LoopStartAction.d.ts +47 -0
  106. package/node_modules/@octopus/engine/dist/actions/LoopStartAction.js +607 -0
  107. package/node_modules/@octopus/engine/dist/actions/MouseOverAction.d.ts +8 -0
  108. package/node_modules/@octopus/engine/dist/actions/MouseOverAction.js +34 -0
  109. package/node_modules/@octopus/engine/dist/actions/NavigateAction.d.ts +38 -0
  110. package/node_modules/@octopus/engine/dist/actions/NavigateAction.js +535 -0
  111. package/node_modules/@octopus/engine/dist/actions/SwitchComboAction.d.ts +13 -0
  112. package/node_modules/@octopus/engine/dist/actions/SwitchComboAction.js +69 -0
  113. package/node_modules/@octopus/engine/dist/browser.d.ts +17 -0
  114. package/node_modules/@octopus/engine/dist/browser.js +157 -0
  115. package/node_modules/@octopus/engine/dist/browserProxy.d.ts +90 -0
  116. package/node_modules/@octopus/engine/dist/browserProxy.js +1 -0
  117. package/node_modules/@octopus/engine/dist/configs/BaseConfig.d.ts +20 -0
  118. package/node_modules/@octopus/engine/dist/configs/BaseConfig.js +88 -0
  119. package/node_modules/@octopus/engine/dist/configs/BranchConfig.d.ts +7 -0
  120. package/node_modules/@octopus/engine/dist/configs/BranchConfig.js +1 -0
  121. package/node_modules/@octopus/engine/dist/configs/ClickConfig.d.ts +36 -0
  122. package/node_modules/@octopus/engine/dist/configs/ClickConfig.js +65 -0
  123. package/node_modules/@octopus/engine/dist/configs/EnterCaptchaConfig.d.ts +19 -0
  124. package/node_modules/@octopus/engine/dist/configs/EnterCaptchaConfig.js +25 -0
  125. package/node_modules/@octopus/engine/dist/configs/EnterTextConfig.d.ts +24 -0
  126. package/node_modules/@octopus/engine/dist/configs/EnterTextConfig.js +36 -0
  127. package/node_modules/@octopus/engine/dist/configs/ExtractDataConfig.d.ts +12 -0
  128. package/node_modules/@octopus/engine/dist/configs/ExtractDataConfig.js +1 -0
  129. package/node_modules/@octopus/engine/dist/configs/LoopConfig.d.ts +25 -0
  130. package/node_modules/@octopus/engine/dist/configs/LoopConfig.js +40 -0
  131. package/node_modules/@octopus/engine/dist/configs/LoopStartConfig.d.ts +4 -0
  132. package/node_modules/@octopus/engine/dist/configs/LoopStartConfig.js +12 -0
  133. package/node_modules/@octopus/engine/dist/configs/MouseOverConfig.d.ts +8 -0
  134. package/node_modules/@octopus/engine/dist/configs/MouseOverConfig.js +15 -0
  135. package/node_modules/@octopus/engine/dist/configs/NavigateConfig.d.ts +41 -0
  136. package/node_modules/@octopus/engine/dist/configs/NavigateConfig.js +121 -0
  137. package/node_modules/@octopus/engine/dist/configs/SwitchComboConfig.d.ts +8 -0
  138. package/node_modules/@octopus/engine/dist/configs/SwitchComboConfig.js +15 -0
  139. package/node_modules/@octopus/engine/dist/enums/index.d.ts +419 -0
  140. package/node_modules/@octopus/engine/dist/enums/index.js +314 -0
  141. package/node_modules/@octopus/engine/dist/extension/BrowserWebSocketTransport-D_zAGZMQ.js +1 -0
  142. package/node_modules/@octopus/engine/dist/extension/LaunchOptions-DxvePrV4.js +6 -0
  143. package/node_modules/@octopus/engine/dist/extension/NodeWebSocketTransport-BTgRVB7Z.js +6 -0
  144. package/node_modules/@octopus/engine/dist/extension/background.js +396 -0
  145. package/node_modules/@octopus/engine/dist/extension/bidi-C_GIZ8Uz.js +131 -0
  146. package/node_modules/@octopus/engine/dist/extension/manifest.json +27 -0
  147. package/node_modules/@octopus/engine/dist/extension/src/content/anti-detection.js +1 -0
  148. package/node_modules/@octopus/engine/dist/extension-bridge/BaseExtensionBridge.d.ts +21 -0
  149. package/node_modules/@octopus/engine/dist/extension-bridge/BaseExtensionBridge.js +117 -0
  150. package/node_modules/@octopus/engine/dist/extension-bridge/SessionExtensionBridge.d.ts +17 -0
  151. package/node_modules/@octopus/engine/dist/extension-bridge/SessionExtensionBridge.js +29 -0
  152. package/node_modules/@octopus/engine/dist/extension-bridge/index.d.ts +2 -0
  153. package/node_modules/@octopus/engine/dist/extension-bridge/index.js +5 -0
  154. package/node_modules/@octopus/engine/dist/extension-bridge/types.d.ts +159 -0
  155. package/node_modules/@octopus/engine/dist/extension-bridge/types.js +5 -0
  156. package/node_modules/@octopus/engine/dist/extensions/ublock-origin/uBlock0.chromium.tar.xz +0 -0
  157. package/node_modules/@octopus/engine/dist/extensions/ublock-origin-lite/uBOLite.chromium.tar.xz +0 -0
  158. package/node_modules/@octopus/engine/dist/index.d.ts +169 -0
  159. package/node_modules/@octopus/engine/dist/index.js +1 -0
  160. package/node_modules/@octopus/engine/dist/models/actionItem.d.ts +16 -0
  161. package/node_modules/@octopus/engine/dist/models/actionItem.js +15 -0
  162. package/node_modules/@octopus/engine/dist/models/conditionCheckArgs.d.ts +11 -0
  163. package/node_modules/@octopus/engine/dist/models/conditionCheckArgs.js +11 -0
  164. package/node_modules/@octopus/engine/dist/models/customizeCookie.d.ts +14 -0
  165. package/node_modules/@octopus/engine/dist/models/customizeCookie.js +6 -0
  166. package/node_modules/@octopus/engine/dist/models/downloadFileConfig.d.ts +17 -0
  167. package/node_modules/@octopus/engine/dist/models/downloadFileConfig.js +26 -0
  168. package/node_modules/@octopus/engine/dist/models/elementNotFoundArgs.d.ts +8 -0
  169. package/node_modules/@octopus/engine/dist/models/elementNotFoundArgs.js +12 -0
  170. package/node_modules/@octopus/engine/dist/models/elementNotFoundError.d.ts +2 -0
  171. package/node_modules/@octopus/engine/dist/models/elementNotFoundError.js +6 -0
  172. package/node_modules/@octopus/engine/dist/models/extractItem.d.ts +37 -0
  173. package/node_modules/@octopus/engine/dist/models/extractItem.js +35 -0
  174. package/node_modules/@octopus/engine/dist/models/extractTemplate.d.ts +11 -0
  175. package/node_modules/@octopus/engine/dist/models/extractTemplate.js +48 -0
  176. package/node_modules/@octopus/engine/dist/models/extractTextItem.d.ts +10 -0
  177. package/node_modules/@octopus/engine/dist/models/extractTextItem.js +17 -0
  178. package/node_modules/@octopus/engine/dist/models/globalConfig.d.ts +5 -0
  179. package/node_modules/@octopus/engine/dist/models/globalConfig.js +1 -0
  180. package/node_modules/@octopus/engine/dist/models/httpHeader.d.ts +4 -0
  181. package/node_modules/@octopus/engine/dist/models/httpHeader.js +10 -0
  182. package/node_modules/@octopus/engine/dist/models/operation.d.ts +27 -0
  183. package/node_modules/@octopus/engine/dist/models/operation.js +242 -0
  184. package/node_modules/@octopus/engine/dist/models/retryCondition.d.ts +7 -0
  185. package/node_modules/@octopus/engine/dist/models/retryCondition.js +10 -0
  186. package/node_modules/@octopus/engine/dist/models/task.d.ts +89 -0
  187. package/node_modules/@octopus/engine/dist/models/task.js +120 -0
  188. package/node_modules/@octopus/engine/dist/models/trigger.d.ts +66 -0
  189. package/node_modules/@octopus/engine/dist/models/trigger.js +117 -0
  190. package/node_modules/@octopus/engine/dist/package.json +26 -0
  191. package/node_modules/@octopus/engine/dist/public-types.d.ts +13 -0
  192. package/node_modules/@octopus/engine/dist/public-types.js +2 -0
  193. package/node_modules/@octopus/engine/dist/settings.d.ts +41 -0
  194. package/node_modules/@octopus/engine/dist/settings.js +20 -0
  195. package/node_modules/@octopus/engine/dist/solvers/captcha/ClickCaptchaSolver.d.ts +6 -0
  196. package/node_modules/@octopus/engine/dist/solvers/captcha/ClickCaptchaSolver.js +1 -0
  197. package/node_modules/@octopus/engine/dist/solvers/captcha/HCaptchaSolver.d.ts +4 -0
  198. package/node_modules/@octopus/engine/dist/solvers/captcha/HCaptchaSolver.js +73 -0
  199. package/node_modules/@octopus/engine/dist/solvers/captcha/ImageCaptchaSolver.d.ts +2 -0
  200. package/node_modules/@octopus/engine/dist/solvers/captcha/ImageCaptchaSolver.js +74 -0
  201. package/node_modules/@octopus/engine/dist/solvers/captcha/RecaptchaSolver.d.ts +9 -0
  202. package/node_modules/@octopus/engine/dist/solvers/captcha/RecaptchaSolver.js +371 -0
  203. package/node_modules/@octopus/engine/dist/solvers/captcha/SliderCaptchaSolver.d.ts +6 -0
  204. package/node_modules/@octopus/engine/dist/solvers/captcha/SliderCaptchaSolver.js +184 -0
  205. package/node_modules/@octopus/engine/dist/solvers/captcha/SlidingTrajectory.d.ts +50 -0
  206. package/node_modules/@octopus/engine/dist/solvers/captcha/SlidingTrajectory.js +125 -0
  207. package/node_modules/@octopus/engine/dist/solvers/captcha/types.d.ts +68 -0
  208. package/node_modules/@octopus/engine/dist/solvers/captcha/types.js +34 -0
  209. package/node_modules/@octopus/engine/dist/solvers/captcha/utils.d.ts +2 -0
  210. package/node_modules/@octopus/engine/dist/solvers/captcha/utils.js +15 -0
  211. package/node_modules/@octopus/engine/dist/translator/actionFactory.d.ts +6 -0
  212. package/node_modules/@octopus/engine/dist/translator/actionFactory.js +1 -0
  213. package/node_modules/@octopus/engine/dist/translator/activityTypeEnum.d.ts +22 -0
  214. package/node_modules/@octopus/engine/dist/translator/activityTypeEnum.js +1 -0
  215. package/node_modules/@octopus/engine/dist/translator/backPreWebPageAction.d.ts +5 -0
  216. package/node_modules/@octopus/engine/dist/translator/backPreWebPageAction.js +1 -0
  217. package/node_modules/@octopus/engine/dist/translator/baseAction.d.ts +31 -0
  218. package/node_modules/@octopus/engine/dist/translator/baseAction.js +1 -0
  219. package/node_modules/@octopus/engine/dist/translator/breakActivity.d.ts +5 -0
  220. package/node_modules/@octopus/engine/dist/translator/breakActivity.js +1 -0
  221. package/node_modules/@octopus/engine/dist/translator/clickAction.d.ts +5 -0
  222. package/node_modules/@octopus/engine/dist/translator/clickAction.js +1 -0
  223. package/node_modules/@octopus/engine/dist/translator/completeWF.d.ts +5 -0
  224. package/node_modules/@octopus/engine/dist/translator/completeWF.js +1 -0
  225. package/node_modules/@octopus/engine/dist/translator/conditionAction.d.ts +6 -0
  226. package/node_modules/@octopus/engine/dist/translator/conditionAction.js +1 -0
  227. package/node_modules/@octopus/engine/dist/translator/emptyAction.d.ts +5 -0
  228. package/node_modules/@octopus/engine/dist/translator/emptyAction.js +1 -0
  229. package/node_modules/@octopus/engine/dist/translator/enterCapachaAction.d.ts +5 -0
  230. package/node_modules/@octopus/engine/dist/translator/enterCapachaAction.js +1 -0
  231. package/node_modules/@octopus/engine/dist/translator/enterTextAction.d.ts +5 -0
  232. package/node_modules/@octopus/engine/dist/translator/enterTextAction.js +1 -0
  233. package/node_modules/@octopus/engine/dist/translator/extractDataAction.d.ts +13 -0
  234. package/node_modules/@octopus/engine/dist/translator/extractDataAction.js +1 -0
  235. package/node_modules/@octopus/engine/dist/translator/loopAction.d.ts +5 -0
  236. package/node_modules/@octopus/engine/dist/translator/loopAction.js +1 -0
  237. package/node_modules/@octopus/engine/dist/translator/mouseOverAction.d.ts +5 -0
  238. package/node_modules/@octopus/engine/dist/translator/mouseOverAction.js +19 -0
  239. package/node_modules/@octopus/engine/dist/translator/navigateAction.d.ts +5 -0
  240. package/node_modules/@octopus/engine/dist/translator/navigateAction.js +117 -0
  241. package/node_modules/@octopus/engine/dist/translator/rootAction.d.ts +6 -0
  242. package/node_modules/@octopus/engine/dist/translator/rootAction.js +80 -0
  243. package/node_modules/@octopus/engine/dist/translator/switchCombo2Action.d.ts +5 -0
  244. package/node_modules/@octopus/engine/dist/translator/switchCombo2Action.js +19 -0
  245. package/node_modules/@octopus/engine/dist/translator/translator.d.ts +1 -0
  246. package/node_modules/@octopus/engine/dist/translator/translator.js +36 -0
  247. package/node_modules/@octopus/engine/dist/type.d.ts +25 -0
  248. package/node_modules/@octopus/engine/dist/type.js +2 -0
  249. package/node_modules/@octopus/engine/dist/types/browser.d.ts +191 -0
  250. package/node_modules/@octopus/engine/dist/types/browser.js +1 -0
  251. package/node_modules/@octopus/engine/dist/types/browserManager.d.ts +41 -0
  252. package/node_modules/@octopus/engine/dist/types/browserManager.js +1 -0
  253. package/node_modules/@octopus/engine/dist/types/index.d.ts +40 -0
  254. package/node_modules/@octopus/engine/dist/types/index.js +2 -0
  255. package/node_modules/@octopus/engine/dist/types/plugin.d.ts +29 -0
  256. package/node_modules/@octopus/engine/dist/types/plugin.js +2 -0
  257. package/node_modules/@octopus/engine/dist/utils/AsyncEmitter.d.ts +15 -0
  258. package/node_modules/@octopus/engine/dist/utils/AsyncEmitter.js +1 -0
  259. package/node_modules/@octopus/engine/dist/utils/DataStore.d.ts +58 -0
  260. package/node_modules/@octopus/engine/dist/utils/DataStore.js +1 -0
  261. package/node_modules/@octopus/engine/dist/utils/DateTimeFormatHelper.d.ts +22 -0
  262. package/node_modules/@octopus/engine/dist/utils/DateTimeFormatHelper.js +173 -0
  263. package/node_modules/@octopus/engine/dist/utils/FileDownloader.d.ts +108 -0
  264. package/node_modules/@octopus/engine/dist/utils/FileDownloader.js +1 -0
  265. package/node_modules/@octopus/engine/dist/utils/HttpRequester.d.ts +43 -0
  266. package/node_modules/@octopus/engine/dist/utils/HttpRequester.js +174 -0
  267. package/node_modules/@octopus/engine/dist/utils/JsonParser.d.ts +95 -0
  268. package/node_modules/@octopus/engine/dist/utils/JsonParser.js +439 -0
  269. package/node_modules/@octopus/engine/dist/utils/Operations.d.ts +27 -0
  270. package/node_modules/@octopus/engine/dist/utils/Operations.js +115 -0
  271. package/node_modules/@octopus/engine/dist/utils/index.d.ts +28 -0
  272. package/node_modules/@octopus/engine/dist/utils/index.js +356 -0
  273. package/node_modules/@octopus/engine/package.json +58 -0
  274. package/package.json +79 -0
  275. package/schemas/capabilities-v1.schema.json +234 -0
  276. package/schemas/detached-bootstrap-v1.schema.json +42 -0
  277. package/schemas/json-envelope-v1.schema.json +39 -0
  278. package/schemas/run-event-v1.schema.json +47 -0
@@ -0,0 +1,220 @@
1
+ import { join, resolve } from 'node:path';
2
+ import { firstPositionalArg, hasFlag, parsePositiveInt, valueAfter } from '../cli/args.js';
3
+ import { printEnvelope, printUsageError } from '../cli/output.js';
4
+ import { cloudHistory } from './cloud.js';
5
+ import { printAuthRequired } from './auth.js';
6
+ import { ApiRequestError, fetchTaskInfo } from '../runtime/api-client.js';
7
+ import { resolveAuth } from '../runtime/auth.js';
8
+ import { fetchCloudRows } from '../runtime/cloud-data.js';
9
+ import { exportRowsToFile, normalizeDataExportFormat } from '../runtime/data-exporter.js';
10
+ import { listRuns } from '../runtime/artifacts.js';
11
+ import { countRunRows, defaultRunsDir, listActiveRuns, readJsonLines } from '../runtime/local-runs.js';
12
+ import { defaultExportFileName } from '../runtime/naming.js';
13
+ import { EXIT_OK, EXIT_OPERATION_FAILED } from '../types.js';
14
+ export async function localHistory(args) {
15
+ const taskId = firstPositionalArg(args, ['--output']);
16
+ const json = hasFlag(args, '--json');
17
+ const outputDir = resolve(valueAfter(args, '--output') ?? defaultRunsDir());
18
+ if (!taskId) {
19
+ return printUsageError(json, 'Error: missing taskId', 'Usage: octoparse local history <taskId> [--output <dir>] [--json]');
20
+ }
21
+ const lots = await listLocalLots(outputDir, taskId);
22
+ if (json) {
23
+ printEnvelope(true, lots.map(localLotToPublic));
24
+ return EXIT_OK;
25
+ }
26
+ if (!lots.length) {
27
+ console.log(`No local extraction lots found: ${taskId}`);
28
+ return EXIT_OK;
29
+ }
30
+ console.log(`Local extraction lots: ${taskId}\n`);
31
+ for (const lot of lots) {
32
+ console.log(` ${lot.lotId} ${lot.status} rows=${lot.total} ${lot.startedAt}`);
33
+ }
34
+ return EXIT_OK;
35
+ }
36
+ export async function localExport(args) {
37
+ const taskId = firstPositionalArg(args, ['--file', '--lot-id', '--lot', '--output', '--format']);
38
+ const json = hasFlag(args, '--json');
39
+ const outputDir = resolve(valueAfter(args, '--output') ?? defaultRunsDir());
40
+ const lotId = valueAfter(args, '--lot-id') ?? valueAfter(args, '--lot');
41
+ const targetFile = valueAfter(args, '--file');
42
+ if (!taskId) {
43
+ return printUsageError(json, 'Error: missing taskId', 'Usage: octoparse local export <taskId> [--file <result.xlsx>] [--lot-id <lotId>] [--output <dir>] [--format xlsx|csv|html|json|xml] [--json]');
44
+ }
45
+ const format = normalizeDataExportFormat(valueAfter(args, '--format'), targetFile);
46
+ if (!format) {
47
+ return printUsageError(json, '--format supports xlsx, csv, html, json, and xml', undefined, 'UNSUPPORTED_EXPORT_FORMAT');
48
+ }
49
+ const lot = await findLocalLot(outputDir, taskId, lotId);
50
+ if (!lot) {
51
+ const message = lotId
52
+ ? `Local extraction lot not found: taskId=${taskId}, lotId=${lotId}`
53
+ : `No local extraction history found for task ${taskId}`;
54
+ if (json)
55
+ printEnvelope(false, undefined, 'LOCAL_LOT_NOT_FOUND', message);
56
+ else
57
+ console.error(message);
58
+ return EXIT_OPERATION_FAILED;
59
+ }
60
+ const runDir = join(outputDir, lot.runId);
61
+ const rows = await readJsonLines(join(runDir, 'rows.jsonl'), Number.MAX_SAFE_INTEGER);
62
+ const taskName = lot.taskName ?? await resolveTaskName(taskId);
63
+ const exportFile = targetFile ?? defaultExportFileName(taskName, format);
64
+ const exported = await exportRowsToFile(rows, exportFile, format);
65
+ const result = {
66
+ taskId,
67
+ taskName,
68
+ lotId: lot.lotId,
69
+ rows: exported.rows,
70
+ file: exported.file,
71
+ format: exported.format
72
+ };
73
+ if (json) {
74
+ printEnvelope(true, result);
75
+ }
76
+ else {
77
+ console.log(`Exported ${result.rows} rows -> ${result.file}`);
78
+ console.log(`Task: ${result.taskId}`);
79
+ console.log(`Lot: ${result.lotId}`);
80
+ console.log(`Format: ${result.format}`);
81
+ }
82
+ return EXIT_OK;
83
+ }
84
+ export async function dataHistory(args) {
85
+ const source = parseDataSource(args);
86
+ return source === 'cloud' ? cloudHistory(args) : localHistory(args);
87
+ }
88
+ export async function dataExport(args) {
89
+ const source = parseDataSource(args);
90
+ return source === 'cloud' ? cloudDataExport(args) : localExport(args);
91
+ }
92
+ async function cloudDataExport(args) {
93
+ const taskId = firstPositionalArg(args, ['--source', '--file', '--lot-id', '--lot', '--format', '--api-base-url', '--batch-size']);
94
+ const json = hasFlag(args, '--json');
95
+ const lotId = valueAfter(args, '--lot-id') ?? valueAfter(args, '--lot');
96
+ const targetFile = valueAfter(args, '--file');
97
+ if (!taskId) {
98
+ return printUsageError(json, 'Error: missing taskId', 'Usage: octoparse data export <taskId> --source cloud [--file <result.xlsx>] [--lot-id <lotId>] [--format xlsx|csv|html|json|xml] [--json]');
99
+ }
100
+ const auth = await resolveAuth();
101
+ if (!auth.authenticated || !auth.apiKey) {
102
+ return printAuthRequired(json);
103
+ }
104
+ const format = normalizeDataExportFormat(valueAfter(args, '--format'), targetFile);
105
+ if (!format) {
106
+ return printUsageError(json, '--format supports xlsx, csv, html, json, and xml', undefined, 'UNSUPPORTED_EXPORT_FORMAT');
107
+ }
108
+ try {
109
+ const rows = await fetchCloudRows({
110
+ apiKey: auth.apiKey,
111
+ taskId,
112
+ lotId,
113
+ baseUrl: valueAfter(args, '--api-base-url'),
114
+ batchSize: parsePositiveInt(valueAfter(args, '--batch-size'), 100)
115
+ });
116
+ const taskName = await resolveTaskName(taskId);
117
+ const exportFile = targetFile ?? defaultExportFileName(taskName, format);
118
+ const exported = await exportRowsToFile(rows, exportFile, format);
119
+ const result = {
120
+ taskId,
121
+ taskName,
122
+ source: 'cloud',
123
+ lotId,
124
+ rows: exported.rows,
125
+ file: exported.file,
126
+ format: exported.format
127
+ };
128
+ if (json) {
129
+ printEnvelope(true, result);
130
+ }
131
+ else {
132
+ console.log(`Exported ${result.rows} cloud rows -> ${result.file}`);
133
+ console.log(`Task: ${result.taskId}`);
134
+ if (result.lotId)
135
+ console.log(`Lot: ${result.lotId}`);
136
+ console.log(`Format: ${result.format}`);
137
+ }
138
+ return EXIT_OK;
139
+ }
140
+ catch (error) {
141
+ return printApiError(json, 'Failed to export cloud data', error);
142
+ }
143
+ }
144
+ function parseDataSource(args) {
145
+ if (hasFlag(args, '--cloud'))
146
+ return 'cloud';
147
+ if (hasFlag(args, '--local'))
148
+ return 'local';
149
+ const source = valueAfter(args, '--source');
150
+ return source === 'cloud' ? 'cloud' : 'local';
151
+ }
152
+ async function listLocalLots(outputDir, taskId) {
153
+ const runs = await listRuns(outputDir);
154
+ const activeRuns = await listActiveRuns(outputDir);
155
+ const byRunId = new Map();
156
+ for (const run of runs)
157
+ byRunId.set(run.runId, withLotId(run));
158
+ for (const run of activeRuns)
159
+ byRunId.set(run.runId, withLotId(run));
160
+ const matched = [...byRunId.values()]
161
+ .filter((run) => run.taskId === taskId)
162
+ .sort((a, b) => b.startedAt.localeCompare(a.startedAt));
163
+ return Promise.all(matched.map((run) => withActualRowCount(outputDir, run)));
164
+ }
165
+ async function findLocalLot(outputDir, taskId, lotId) {
166
+ const lots = await listLocalLots(outputDir, taskId);
167
+ if (!lots.length)
168
+ return null;
169
+ if (!lotId)
170
+ return lots[0];
171
+ return lots.find((lot) => lot.lotId === lotId) ?? null;
172
+ }
173
+ function withLotId(summary) {
174
+ return summary.lotId ? summary : { ...summary, lotId: deriveLotId(summary.runId) };
175
+ }
176
+ async function withActualRowCount(outputDir, summary) {
177
+ const total = await countRunRows(outputDir, summary.runId);
178
+ return total === summary.total ? summary : { ...summary, total };
179
+ }
180
+ function localLotToPublic(summary) {
181
+ return {
182
+ taskId: summary.taskId,
183
+ taskName: summary.taskName,
184
+ lotId: summary.lotId,
185
+ status: summary.status,
186
+ total: summary.total,
187
+ startedAt: summary.startedAt,
188
+ stoppedAt: summary.stoppedAt
189
+ };
190
+ }
191
+ function deriveLotId(runId) {
192
+ const stamp = runId.match(/_(\d{14})$/)?.[1];
193
+ return stamp ? `lot_${stamp}` : runId;
194
+ }
195
+ async function resolveTaskName(taskId) {
196
+ const auth = await resolveAuth();
197
+ if (!auth.apiKey)
198
+ return taskId;
199
+ try {
200
+ const info = await fetchTaskInfo({ apiKey: auth.apiKey, taskId });
201
+ return String(info.taskName ?? info.TaskName ?? taskId).trim() || taskId;
202
+ }
203
+ catch {
204
+ return taskId;
205
+ }
206
+ }
207
+ function printApiError(json, prefix, error) {
208
+ const code = error instanceof ApiRequestError ? error.code : 'API_REQUEST_FAILED';
209
+ const message = error instanceof Error ? error.message : String(error);
210
+ if (json) {
211
+ printEnvelope(false, undefined, code, message);
212
+ }
213
+ else {
214
+ console.error(`${code === 'AUTH_INVALID' ? 'Authentication failed' : prefix}: ${message}`);
215
+ if (error instanceof ApiRequestError && error.body && code !== 'AUTH_INVALID') {
216
+ console.error(`Response: ${error.body}`);
217
+ }
218
+ }
219
+ return EXIT_OPERATION_FAILED;
220
+ }
@@ -0,0 +1,73 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname, join } from 'node:path';
4
+ import { hasFlag, valueAfter } from '../cli/args.js';
5
+ import { printResult } from '../cli/output.js';
6
+ import { EXIT_OK, EXIT_RUNTIME_FAILED } from '../types.js';
7
+ const require = createRequire(import.meta.url);
8
+ const EngineModule = require('@octopus/engine');
9
+ const resolveChrome = EngineModule.resolveChrome;
10
+ export async function doctorCommand(args) {
11
+ const json = hasFlag(args, '--json');
12
+ const chromePath = valueAfter(args, '--chrome-path');
13
+ let chrome = { ok: false, message: 'not checked' };
14
+ if (chromePath) {
15
+ chrome = existsSync(chromePath)
16
+ ? { ok: true, message: chromePath }
17
+ : { ok: false, message: `Chrome executable not found: ${chromePath}` };
18
+ }
19
+ else {
20
+ try {
21
+ const resolved = await resolveChrome();
22
+ chrome = { ok: true, message: resolved.executablePath };
23
+ }
24
+ catch (error) {
25
+ chrome = { ok: false, message: error instanceof Error ? error.message : String(error) };
26
+ }
27
+ }
28
+ const engineDist = dirname(require.resolve('@octopus/engine'));
29
+ const extensionPath = join(engineDist, 'extension', 'manifest.json');
30
+ const ublockPath = join(engineDist, 'extensions', 'ublock-origin-lite', 'uBOLite.chromium.tar.xz');
31
+ const data = {
32
+ ok: chrome.ok && existsSync(extensionPath),
33
+ checks: [
34
+ { name: 'node', ok: true, message: process.version },
35
+ { name: 'engine', ok: true, message: require.resolve('@octopus/engine') },
36
+ { name: 'runtime-extension', ok: existsSync(extensionPath), message: extensionPath },
37
+ { name: 'adblock-extension-archive', ok: existsSync(ublockPath), message: ublockPath },
38
+ { name: 'chrome', ok: chrome.ok, message: chrome.message },
39
+ { name: 'electron-client', ok: true, message: 'not required' },
40
+ { name: 'browser-mode', ok: true, message: 'independent Chrome only' }
41
+ ]
42
+ };
43
+ printResult(json, data);
44
+ return EXIT_OK;
45
+ }
46
+ export async function browserDoctorCommand(args) {
47
+ const json = hasFlag(args, '--json');
48
+ const chromePath = valueAfter(args, '--chrome-path');
49
+ let executablePath = chromePath;
50
+ let ok = true;
51
+ let error = '';
52
+ try {
53
+ if (!executablePath) {
54
+ executablePath = (await resolveChrome()).executablePath;
55
+ }
56
+ ok = Boolean(executablePath) && existsSync(executablePath);
57
+ if (!ok)
58
+ error = `Chrome executable not found: ${executablePath}`;
59
+ }
60
+ catch (caught) {
61
+ ok = false;
62
+ error = caught instanceof Error ? caught.message : String(caught);
63
+ }
64
+ const data = {
65
+ ok,
66
+ supported: ['chrome'],
67
+ unsupported: ['kernel', 'legacy-workflow'],
68
+ executablePath,
69
+ message: ok ? 'independent Chrome is ready' : error
70
+ };
71
+ printResult(json, data);
72
+ return ok ? EXIT_OK : EXIT_RUNTIME_FAILED;
73
+ }
@@ -0,0 +1,63 @@
1
+ import { hasFlag, valueAfter } from '../cli/args.js';
2
+ import { printEnvelope } from '../cli/output.js';
3
+ import { API_BASE_URL_ENV, PROD_API_BASE_URL } from '../runtime/api-client.js';
4
+ import { configFilePath, readCliConfig, saveCliConfig } from '../runtime/config.js';
5
+ import { EXIT_OK, EXIT_OPERATION_FAILED } from '../types.js';
6
+ export async function hiddenEnvCommand(subcommand, args) {
7
+ const json = hasFlag([subcommand ?? '', ...args], '--json');
8
+ if (subcommand === 'status' || !subcommand) {
9
+ const config = await readCliConfig();
10
+ const effectiveBaseUrl = valueAfter(args, '--api-base-url') ?? process.env[API_BASE_URL_ENV] ?? config.apiBaseUrl ?? PROD_API_BASE_URL;
11
+ const data = {
12
+ apiEnv: config.apiEnv ?? 'prod',
13
+ apiBaseUrl: effectiveBaseUrl,
14
+ configFile: configFilePath(),
15
+ envOverride: Boolean(process.env[API_BASE_URL_ENV])
16
+ };
17
+ if (json)
18
+ printEnvelope(true, data);
19
+ else {
20
+ console.log(`API env: ${data.apiEnv}`);
21
+ console.log(`API base URL: ${data.apiBaseUrl}`);
22
+ console.log(`Config: ${data.configFile}`);
23
+ if (data.envOverride)
24
+ console.log(`${API_BASE_URL_ENV} is set and overrides local config.`);
25
+ }
26
+ return EXIT_OK;
27
+ }
28
+ const next = resolveEnvTarget(subcommand);
29
+ if (!next) {
30
+ const message = 'Usage: octoparse env <prod|online|status> [--json]';
31
+ if (json)
32
+ printEnvelope(false, undefined, 'INVALID_ENV', message);
33
+ else
34
+ console.error(message);
35
+ return EXIT_OPERATION_FAILED;
36
+ }
37
+ const config = await saveCliConfig({
38
+ apiEnv: next.apiEnv,
39
+ apiBaseUrl: next.apiBaseUrl
40
+ });
41
+ const data = {
42
+ apiEnv: config.apiEnv,
43
+ apiBaseUrl: config.apiBaseUrl,
44
+ configFile: configFilePath()
45
+ };
46
+ if (json)
47
+ printEnvelope(true, data);
48
+ else {
49
+ console.log(`API env switched to: ${data.apiEnv}`);
50
+ console.log(`API base URL: ${data.apiBaseUrl}`);
51
+ console.log(`Config: ${data.configFile}`);
52
+ if (process.env[API_BASE_URL_ENV]) {
53
+ console.log(`${API_BASE_URL_ENV} is currently set and will still override this config.`);
54
+ }
55
+ }
56
+ return EXIT_OK;
57
+ }
58
+ function resolveEnvTarget(value) {
59
+ if (value === 'prod' || value === 'online' || value === 'production') {
60
+ return { apiEnv: 'prod', apiBaseUrl: PROD_API_BASE_URL };
61
+ }
62
+ return undefined;
63
+ }
@@ -0,0 +1,251 @@
1
+ import { join, resolve } from 'node:path';
2
+ import { readFile, readdir } from 'node:fs/promises';
3
+ import { firstPositionalArg, hasFlag, valueAfter } from '../cli/args.js';
4
+ import { printEnvelope, printUsageError } from '../cli/output.js';
5
+ import { localExport, localHistory } from './data.js';
6
+ import { ensureRunDir, listRuns, writeRunSummary } from '../runtime/artifacts.js';
7
+ import { cleanupTaskControlState, cleanupTaskControlStates, isRunControlReachable, readTaskControlState, resolveRunControlSocketPath, sendTaskControlCommand } from '../runtime/run-control.js';
8
+ import { countRunRows, defaultRunsDir } from '../runtime/local-runs.js';
9
+ import { EXIT_OK, EXIT_OPERATION_FAILED } from '../types.js';
10
+ export async function localCommand(subcommand, args) {
11
+ const json = hasFlag([subcommand ?? '', ...args], '--json');
12
+ if (subcommand === 'status') {
13
+ return localStatus(args);
14
+ }
15
+ if (subcommand === 'history') {
16
+ return localHistory(args);
17
+ }
18
+ if (subcommand === 'export') {
19
+ return localExport(args);
20
+ }
21
+ if (subcommand === 'cleanup') {
22
+ return localCleanup(args);
23
+ }
24
+ if (subcommand === 'pause' || subcommand === 'resume' || subcommand === 'stop') {
25
+ return localControl(subcommand, args);
26
+ }
27
+ return printUsageError(json, 'Error: invalid local subcommand', 'Usage: octoparse local <status|pause|resume|stop|history|export|cleanup> <taskId> [--json]');
28
+ }
29
+ async function localStatus(args) {
30
+ const taskId = firstPositionalArg(args, ['--output']);
31
+ const json = hasFlag(args, '--json');
32
+ if (!taskId) {
33
+ return printUsageError(json, 'Error: missing taskId', 'Usage: octoparse local status <taskId> [--output <dir>] [--json]');
34
+ }
35
+ const state = await readTaskControlState(taskId);
36
+ const alive = await isRunControlReachable(state);
37
+ const actualSocketPath = state ? resolveRunControlSocketPath(state) : null;
38
+ const total = state ? await countRunRows(state.outputDir, state.runId) : 0;
39
+ const outputDir = resolve(state?.outputDir ?? valueAfter(args, '--output') ?? defaultRunsDir());
40
+ const bootstrap = !alive ? await findLatestStartingDetachedBootstrap(outputDir, taskId) : null;
41
+ const staleRun = state && !alive ? await preserveStaleRunSummary(state, total) : null;
42
+ const lastRun = staleRun ?? await findLastLocalRun(outputDir, taskId);
43
+ const currentRun = alive && state ? controlStateToPublicRun(state, total, actualSocketPath) : null;
44
+ const data = alive && state
45
+ ? {
46
+ ...state,
47
+ active: true,
48
+ status: state.status,
49
+ total,
50
+ controlSocketPath: actualSocketPath,
51
+ currentRun,
52
+ lastRun
53
+ }
54
+ : state
55
+ ? {
56
+ taskId,
57
+ active: false,
58
+ status: 'not_running',
59
+ currentRun: null,
60
+ lastRun,
61
+ cleanedStaleState: true,
62
+ lastStatus: state.status,
63
+ lastRunId: state.runId,
64
+ total
65
+ }
66
+ : bootstrap
67
+ ? {
68
+ taskId,
69
+ active: true,
70
+ status: 'starting',
71
+ currentRun: null,
72
+ lastRun,
73
+ detached: true,
74
+ pid: bootstrap.pid,
75
+ bootstrapDir: bootstrap.dir,
76
+ stdout: bootstrap.stdout,
77
+ stderr: bootstrap.stderr,
78
+ updatedAt: bootstrap.updatedAt
79
+ }
80
+ : {
81
+ taskId,
82
+ active: false,
83
+ status: 'not_running',
84
+ currentRun: null,
85
+ lastRun
86
+ };
87
+ if (state && !alive) {
88
+ await cleanupTaskControlState(taskId);
89
+ }
90
+ if (json) {
91
+ printEnvelope(true, data);
92
+ }
93
+ else if (alive && state) {
94
+ console.log(`${taskId} ${state?.status}`);
95
+ console.log(`PID: ${state?.pid}`);
96
+ console.log(`Rows: ${total}`);
97
+ console.log(`Output: ${state?.outputDir}`);
98
+ if (actualSocketPath && actualSocketPath !== state.socketPath) {
99
+ console.log(`Control socket: ${actualSocketPath}`);
100
+ }
101
+ }
102
+ else if (state) {
103
+ console.log(`${taskId} idle`);
104
+ console.log(`Cleaned stale local state from previous run: ${state.runId}`);
105
+ printLastRun(lastRun);
106
+ console.log(`Rows: ${total}`);
107
+ }
108
+ else if (bootstrap) {
109
+ console.log(`${taskId} starting`);
110
+ if (bootstrap.pid)
111
+ console.log(`PID: ${bootstrap.pid}`);
112
+ console.log(`Bootstrap: ${bootstrap.dir}`);
113
+ if (bootstrap.stdout)
114
+ console.log(`Stdout: ${bootstrap.stdout}`);
115
+ if (bootstrap.stderr)
116
+ console.log(`Stderr: ${bootstrap.stderr}`);
117
+ }
118
+ else {
119
+ console.log(`${taskId} idle`);
120
+ printLastRun(lastRun);
121
+ }
122
+ return EXIT_OK;
123
+ }
124
+ async function findLatestStartingDetachedBootstrap(outputDir, taskId) {
125
+ let entries = [];
126
+ try {
127
+ entries = await readdir(outputDir);
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ const candidates = [];
133
+ for (const entry of entries) {
134
+ if (!entry.startsWith('.detach_'))
135
+ continue;
136
+ const dir = join(outputDir, entry);
137
+ const bootstrap = await readDetachedBootstrap(dir);
138
+ if (!bootstrap || bootstrap.taskId !== taskId)
139
+ continue;
140
+ if (bootstrap.status !== 'spawning' && bootstrap.status !== 'spawned' && bootstrap.status !== 'starting')
141
+ continue;
142
+ if (!isProcessAlive(bootstrap.pid))
143
+ continue;
144
+ candidates.push(bootstrap);
145
+ }
146
+ candidates.sort((a, b) => String(b.updatedAt ?? '').localeCompare(String(a.updatedAt ?? '')));
147
+ return candidates[0] ?? null;
148
+ }
149
+ async function readDetachedBootstrap(dir) {
150
+ try {
151
+ const bootstrap = JSON.parse(await readFile(join(dir, 'bootstrap.json'), 'utf8'));
152
+ return { ...bootstrap, dir };
153
+ }
154
+ catch {
155
+ return null;
156
+ }
157
+ }
158
+ function isProcessAlive(pid) {
159
+ if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0)
160
+ return false;
161
+ try {
162
+ process.kill(pid, 0);
163
+ return true;
164
+ }
165
+ catch {
166
+ return false;
167
+ }
168
+ }
169
+ async function preserveStaleRunSummary(state, total) {
170
+ if (!state)
171
+ return null;
172
+ const runDir = await ensureRunDir(state.outputDir, state.runId);
173
+ const summary = {
174
+ runId: state.runId,
175
+ lotId: state.lotId,
176
+ taskId: state.taskId,
177
+ taskName: state.taskName,
178
+ status: 'stopped',
179
+ total,
180
+ outputDir: runDir,
181
+ startedAt: state.updatedAt,
182
+ stoppedAt: new Date().toISOString()
183
+ };
184
+ await writeRunSummary(runDir, summary).catch(() => undefined);
185
+ return summary;
186
+ }
187
+ async function findLastLocalRun(outputDir, taskId) {
188
+ const runs = await listRuns(outputDir);
189
+ return runs.find((run) => run.taskId === taskId) ?? null;
190
+ }
191
+ function controlStateToPublicRun(state, total, controlSocketPath) {
192
+ return {
193
+ runId: state.runId,
194
+ lotId: state.lotId,
195
+ taskId: state.taskId,
196
+ taskName: state.taskName,
197
+ status: state.status,
198
+ total,
199
+ outputDir: state.outputDir,
200
+ pid: state.pid,
201
+ controlSocketPath,
202
+ updatedAt: state.updatedAt
203
+ };
204
+ }
205
+ function printLastRun(lastRun) {
206
+ if (!lastRun)
207
+ return;
208
+ const lot = lastRun.lotId ? ` lot=${lastRun.lotId}` : '';
209
+ console.log(`Last run: ${lastRun.status} rows=${lastRun.total}${lot}`);
210
+ }
211
+ async function localControl(command, args) {
212
+ const taskId = firstPositionalArg(args);
213
+ const json = hasFlag(args, '--json');
214
+ if (!taskId) {
215
+ return printUsageError(json, 'Error: missing taskId', `Usage: octoparse local ${command} <taskId> [--json]`);
216
+ }
217
+ try {
218
+ const state = await sendTaskControlCommand(taskId, command);
219
+ if (json) {
220
+ printEnvelope(true, state);
221
+ }
222
+ else {
223
+ console.log(`${taskId} ${command} -> ${state.status}`);
224
+ }
225
+ return EXIT_OK;
226
+ }
227
+ catch (error) {
228
+ const message = error instanceof Error ? error.message : String(error);
229
+ if (json)
230
+ printEnvelope(false, undefined, 'LOCAL_RUN_CONTROL_FAILED', message);
231
+ else
232
+ console.error(`Local extraction control failed: ${message}`);
233
+ return EXIT_OPERATION_FAILED;
234
+ }
235
+ }
236
+ async function localCleanup(args) {
237
+ const json = hasFlag(args, '--json');
238
+ const result = await cleanupTaskControlStates();
239
+ if (json) {
240
+ printEnvelope(true, result);
241
+ }
242
+ else {
243
+ console.log(`Checked: ${result.checked}`);
244
+ console.log(`Alive: ${result.alive}`);
245
+ console.log(`Removed orphaned: ${result.removed}`);
246
+ for (const item of result.orphaned) {
247
+ console.log(` ${item.taskId ?? 'unknown-task'} ${item.runId ?? 'unknown-run'} ${item.filePath}`);
248
+ }
249
+ }
250
+ return EXIT_OK;
251
+ }