charness 3.6.1 → 3.8.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 (828) hide show
  1. package/CHANGELOG.md +297 -0
  2. package/DEVELOPMENT.md +35 -5
  3. package/MIGRATION_RENAME_TO_CHARNESS.md +3 -2
  4. package/README.md +84 -782
  5. package/SETUP.md +48 -10
  6. package/agent-plugin/.claude-plugin/plugin.json +7 -0
  7. package/agent-plugin/README.md +35 -0
  8. package/agent-plugin/skills/charness-delegate-claude/SKILL.md +85 -0
  9. package/agent-plugin/skills/charness-delegate-claude/summarize-events.py +90 -0
  10. package/agent-plugin/skills/charness-delegate-opencode/SKILL.md +68 -0
  11. package/agent-plugin/skills/charness-manager/SKILL.md +152 -0
  12. package/dist/adapters/adapterNames.d.ts +6 -0
  13. package/dist/adapters/adapterNames.d.ts.map +1 -1
  14. package/dist/adapters/adapterNames.js +8 -2
  15. package/dist/adapters/adapterNames.js.map +1 -1
  16. package/dist/adapters/claudeCliAdapter.d.ts +8 -0
  17. package/dist/adapters/claudeCliAdapter.d.ts.map +1 -1
  18. package/dist/adapters/claudeCliAdapter.js +42 -5
  19. package/dist/adapters/claudeCliAdapter.js.map +1 -1
  20. package/dist/adapters/claudeJsonStreamAdapter.d.ts +10 -9
  21. package/dist/adapters/claudeJsonStreamAdapter.d.ts.map +1 -1
  22. package/dist/adapters/claudeJsonStreamAdapter.js +53 -55
  23. package/dist/adapters/claudeJsonStreamAdapter.js.map +1 -1
  24. package/dist/adapters/createAdapter.d.ts +8 -0
  25. package/dist/adapters/createAdapter.d.ts.map +1 -1
  26. package/dist/adapters/createAdapter.js +14 -2
  27. package/dist/adapters/createAdapter.js.map +1 -1
  28. package/dist/adapters/openCodeAdapter.d.ts +43 -6
  29. package/dist/adapters/openCodeAdapter.d.ts.map +1 -1
  30. package/dist/adapters/openCodeAdapter.js +97 -7
  31. package/dist/adapters/openCodeAdapter.js.map +1 -1
  32. package/dist/agentLogin/agentLogin.d.ts +1 -1
  33. package/dist/agentLogin/agentLogin.d.ts.map +1 -1
  34. package/dist/apiRetryKick.d.ts +4 -3
  35. package/dist/apiRetryKick.d.ts.map +1 -1
  36. package/dist/apiRetryKick.js +16 -9
  37. package/dist/apiRetryKick.js.map +1 -1
  38. package/dist/bot.d.ts.map +1 -1
  39. package/dist/bot.js +1360 -387
  40. package/dist/bot.js.map +1 -1
  41. package/dist/cli/claudeProfiles.d.ts +11 -6
  42. package/dist/cli/claudeProfiles.d.ts.map +1 -1
  43. package/dist/cli/claudeProfiles.js +50 -22
  44. package/dist/cli/claudeProfiles.js.map +1 -1
  45. package/dist/cli/hot.d.ts +5 -0
  46. package/dist/cli/hot.d.ts.map +1 -1
  47. package/dist/cli/hot.js +71 -25
  48. package/dist/cli/hot.js.map +1 -1
  49. package/dist/cli/tscWatchGuard.d.ts +95 -0
  50. package/dist/cli/tscWatchGuard.d.ts.map +1 -0
  51. package/dist/cli/tscWatchGuard.js +295 -0
  52. package/dist/cli/tscWatchGuard.js.map +1 -0
  53. package/dist/cli.js +55 -1
  54. package/dist/cli.js.map +1 -1
  55. package/dist/configDirFile.d.ts +7 -3
  56. package/dist/configDirFile.d.ts.map +1 -1
  57. package/dist/configDirFile.js +12 -5
  58. package/dist/configDirFile.js.map +1 -1
  59. package/dist/connectors/jira/answerSink.d.ts +10 -0
  60. package/dist/connectors/jira/answerSink.d.ts.map +1 -1
  61. package/dist/connectors/jira/answerSink.js +13 -0
  62. package/dist/connectors/jira/answerSink.js.map +1 -1
  63. package/dist/connectors/jira/config.d.ts +2 -0
  64. package/dist/connectors/jira/config.d.ts.map +1 -1
  65. package/dist/connectors/jira/config.js +9 -1
  66. package/dist/connectors/jira/config.js.map +1 -1
  67. package/dist/connectors/jira/connector.d.ts +11 -1
  68. package/dist/connectors/jira/connector.d.ts.map +1 -1
  69. package/dist/connectors/jira/connector.js +7 -0
  70. package/dist/connectors/jira/connector.js.map +1 -1
  71. package/dist/connectors/jira/inbound.d.ts +27 -4
  72. package/dist/connectors/jira/inbound.d.ts.map +1 -1
  73. package/dist/connectors/jira/inbound.js +69 -8
  74. package/dist/connectors/jira/inbound.js.map +1 -1
  75. package/dist/connectors/jira/issueBlocks.d.ts +5 -0
  76. package/dist/connectors/jira/issueBlocks.d.ts.map +1 -1
  77. package/dist/connectors/jira/issueBlocks.js +11 -0
  78. package/dist/connectors/jira/issueBlocks.js.map +1 -1
  79. package/dist/connectors/telegram/bindPicker.d.ts +5 -0
  80. package/dist/connectors/telegram/bindPicker.d.ts.map +1 -1
  81. package/dist/connectors/telegram/bindPicker.js +18 -1
  82. package/dist/connectors/telegram/bindPicker.js.map +1 -1
  83. package/dist/connectors/telegram/bindPickerClaims.d.ts +46 -0
  84. package/dist/connectors/telegram/bindPickerClaims.d.ts.map +1 -0
  85. package/dist/connectors/telegram/bindPickerClaims.js +83 -0
  86. package/dist/connectors/telegram/bindPickerClaims.js.map +1 -0
  87. package/dist/connectors/telegram/bindSweep.d.ts +32 -0
  88. package/dist/connectors/telegram/bindSweep.d.ts.map +1 -0
  89. package/dist/connectors/telegram/bindSweep.js +47 -0
  90. package/dist/connectors/telegram/bindSweep.js.map +1 -0
  91. package/dist/connectors/telegram/bindingWelcome.d.ts +23 -0
  92. package/dist/connectors/telegram/bindingWelcome.d.ts.map +1 -0
  93. package/dist/connectors/telegram/bindingWelcome.js +26 -0
  94. package/dist/connectors/telegram/bindingWelcome.js.map +1 -0
  95. package/dist/connectors/telegram/botApiConnection.d.ts +74 -0
  96. package/dist/connectors/telegram/botApiConnection.d.ts.map +1 -0
  97. package/dist/connectors/telegram/botApiConnection.js +201 -0
  98. package/dist/connectors/telegram/botApiConnection.js.map +1 -0
  99. package/dist/connectors/telegram/commands/botCore.d.ts +13 -3
  100. package/dist/connectors/telegram/commands/botCore.d.ts.map +1 -1
  101. package/dist/connectors/telegram/commands/botSkills.d.ts +21 -0
  102. package/dist/connectors/telegram/commands/botSkills.d.ts.map +1 -0
  103. package/dist/connectors/telegram/commands/botSkills.js +133 -0
  104. package/dist/connectors/telegram/commands/botSkills.js.map +1 -0
  105. package/dist/connectors/telegram/commands/callbackMessage.d.ts +5 -0
  106. package/dist/connectors/telegram/commands/callbackMessage.d.ts.map +1 -1
  107. package/dist/connectors/telegram/commands/callbackMessage.js +9 -0
  108. package/dist/connectors/telegram/commands/callbackMessage.js.map +1 -1
  109. package/dist/connectors/telegram/commands/compaction.d.ts +65 -3
  110. package/dist/connectors/telegram/commands/compaction.d.ts.map +1 -1
  111. package/dist/connectors/telegram/commands/compaction.js +512 -94
  112. package/dist/connectors/telegram/commands/compaction.js.map +1 -1
  113. package/dist/connectors/telegram/commands/displayModes.d.ts +1 -1
  114. package/dist/connectors/telegram/commands/displayModes.d.ts.map +1 -1
  115. package/dist/connectors/telegram/commands/displayModes.js +6 -18
  116. package/dist/connectors/telegram/commands/displayModes.js.map +1 -1
  117. package/dist/connectors/telegram/commands/fallbackChain.js +5 -5
  118. package/dist/connectors/telegram/commands/fallbackChain.js.map +1 -1
  119. package/dist/connectors/telegram/commands/reminders.d.ts +1 -1
  120. package/dist/connectors/telegram/commands/reminders.d.ts.map +1 -1
  121. package/dist/connectors/telegram/commands/reminders.js +4 -15
  122. package/dist/connectors/telegram/commands/reminders.js.map +1 -1
  123. package/dist/connectors/telegram/commands/runners.d.ts +52 -0
  124. package/dist/connectors/telegram/commands/runners.d.ts.map +1 -0
  125. package/dist/connectors/telegram/commands/runners.js +486 -0
  126. package/dist/connectors/telegram/commands/runners.js.map +1 -0
  127. package/dist/connectors/telegram/commands/selfUpdate.d.ts.map +1 -1
  128. package/dist/connectors/telegram/commands/selfUpdate.js +1 -11
  129. package/dist/connectors/telegram/commands/selfUpdate.js.map +1 -1
  130. package/dist/connectors/telegram/commands/taskAgents.d.ts +22 -0
  131. package/dist/connectors/telegram/commands/taskAgents.d.ts.map +1 -0
  132. package/dist/connectors/telegram/commands/taskAgents.js +104 -0
  133. package/dist/connectors/telegram/commands/taskAgents.js.map +1 -0
  134. package/dist/connectors/telegram/commands/todo.d.ts +82 -0
  135. package/dist/connectors/telegram/commands/todo.d.ts.map +1 -0
  136. package/dist/connectors/telegram/commands/todo.js +384 -0
  137. package/dist/connectors/telegram/commands/todo.js.map +1 -0
  138. package/dist/connectors/telegram/commands/topicCommand.d.ts +6 -2
  139. package/dist/connectors/telegram/commands/topicCommand.d.ts.map +1 -1
  140. package/dist/connectors/telegram/commands/topicCommand.js +6 -9
  141. package/dist/connectors/telegram/commands/topicCommand.js.map +1 -1
  142. package/dist/connectors/telegram/openBindMessages.d.ts +45 -0
  143. package/dist/connectors/telegram/openBindMessages.d.ts.map +1 -0
  144. package/dist/connectors/telegram/openBindMessages.js +67 -0
  145. package/dist/connectors/telegram/openBindMessages.js.map +1 -0
  146. package/dist/connectors/telegram/outputFlushPlan.d.ts +49 -0
  147. package/dist/connectors/telegram/outputFlushPlan.d.ts.map +1 -1
  148. package/dist/connectors/telegram/outputFlushPlan.js +46 -0
  149. package/dist/connectors/telegram/outputFlushPlan.js.map +1 -1
  150. package/dist/connectors/telegram/pendingTopicName.d.ts +26 -0
  151. package/dist/connectors/telegram/pendingTopicName.d.ts.map +1 -0
  152. package/dist/connectors/telegram/pendingTopicName.js +27 -0
  153. package/dist/connectors/telegram/pendingTopicName.js.map +1 -0
  154. package/dist/connectors/telegram/runnerPairingMessages.d.ts +27 -0
  155. package/dist/connectors/telegram/runnerPairingMessages.d.ts.map +1 -0
  156. package/dist/connectors/telegram/runnerPairingMessages.js +105 -0
  157. package/dist/connectors/telegram/runnerPairingMessages.js.map +1 -0
  158. package/dist/connectors/test/connector.d.ts.map +1 -1
  159. package/dist/connectors/test/connector.js +6 -1
  160. package/dist/connectors/test/connector.js.map +1 -1
  161. package/dist/i18n/de.d.ts.map +1 -1
  162. package/dist/i18n/de.js +148 -7
  163. package/dist/i18n/de.js.map +1 -1
  164. package/dist/i18n/en.d.ts.map +1 -1
  165. package/dist/i18n/en.js +148 -7
  166. package/dist/i18n/en.js.map +1 -1
  167. package/dist/i18n/es.d.ts.map +1 -1
  168. package/dist/i18n/es.js +148 -7
  169. package/dist/i18n/es.js.map +1 -1
  170. package/dist/i18n/fr.d.ts.map +1 -1
  171. package/dist/i18n/fr.js +148 -7
  172. package/dist/i18n/fr.js.map +1 -1
  173. package/dist/i18n/hi.d.ts.map +1 -1
  174. package/dist/i18n/hi.js +148 -7
  175. package/dist/i18n/hi.js.map +1 -1
  176. package/dist/i18n/ja.d.ts.map +1 -1
  177. package/dist/i18n/ja.js +148 -7
  178. package/dist/i18n/ja.js.map +1 -1
  179. package/dist/i18n/ka.d.ts.map +1 -1
  180. package/dist/i18n/ka.js +148 -7
  181. package/dist/i18n/ka.js.map +1 -1
  182. package/dist/i18n/pt.d.ts.map +1 -1
  183. package/dist/i18n/pt.js +148 -7
  184. package/dist/i18n/pt.js.map +1 -1
  185. package/dist/i18n/ru.d.ts.map +1 -1
  186. package/dist/i18n/ru.js +148 -7
  187. package/dist/i18n/ru.js.map +1 -1
  188. package/dist/i18n/uk.d.ts.map +1 -1
  189. package/dist/i18n/uk.js +148 -7
  190. package/dist/i18n/uk.js.map +1 -1
  191. package/dist/i18n/uz.d.ts.map +1 -1
  192. package/dist/i18n/uz.js +148 -7
  193. package/dist/i18n/uz.js.map +1 -1
  194. package/dist/i18n/zh.d.ts.map +1 -1
  195. package/dist/i18n/zh.js +148 -7
  196. package/dist/i18n/zh.js.map +1 -1
  197. package/dist/i18n.d.ts +1 -1
  198. package/dist/i18n.js +1 -1
  199. package/dist/installManager.d.ts +70 -1
  200. package/dist/installManager.d.ts.map +1 -1
  201. package/dist/installManager.js +125 -13
  202. package/dist/installManager.js.map +1 -1
  203. package/dist/instanceSettings.d.ts +50 -1
  204. package/dist/instanceSettings.d.ts.map +1 -1
  205. package/dist/instanceSettings.js +119 -26
  206. package/dist/instanceSettings.js.map +1 -1
  207. package/dist/openCodeQuestionFlow.d.ts +30 -0
  208. package/dist/openCodeQuestionFlow.d.ts.map +1 -1
  209. package/dist/openCodeQuestionFlow.js +43 -0
  210. package/dist/openCodeQuestionFlow.js.map +1 -1
  211. package/dist/outputTrace.d.ts +6 -0
  212. package/dist/outputTrace.d.ts.map +1 -1
  213. package/dist/outputTrace.js +24 -1
  214. package/dist/outputTrace.js.map +1 -1
  215. package/dist/postToSession.d.ts +20 -5
  216. package/dist/postToSession.d.ts.map +1 -1
  217. package/dist/postToSession.js +38 -7
  218. package/dist/postToSession.js.map +1 -1
  219. package/dist/remoteRun/cliArgs.d.ts +75 -0
  220. package/dist/remoteRun/cliArgs.d.ts.map +1 -0
  221. package/dist/remoteRun/cliArgs.js +206 -0
  222. package/dist/remoteRun/cliArgs.js.map +1 -0
  223. package/dist/remoteRun/commands.d.ts +77 -0
  224. package/dist/remoteRun/commands.d.ts.map +1 -0
  225. package/dist/remoteRun/commands.js +327 -0
  226. package/dist/remoteRun/commands.js.map +1 -0
  227. package/dist/remoteRun/depsPlan.d.ts +16 -0
  228. package/dist/remoteRun/depsPlan.d.ts.map +1 -0
  229. package/dist/remoteRun/depsPlan.js +82 -0
  230. package/dist/remoteRun/depsPlan.js.map +1 -0
  231. package/dist/remoteRun/exitCodes.d.ts +34 -0
  232. package/dist/remoteRun/exitCodes.d.ts.map +1 -0
  233. package/dist/remoteRun/exitCodes.js +38 -0
  234. package/dist/remoteRun/exitCodes.js.map +1 -0
  235. package/dist/remoteRun/foreground.d.ts +29 -0
  236. package/dist/remoteRun/foreground.d.ts.map +1 -0
  237. package/dist/remoteRun/foreground.js +159 -0
  238. package/dist/remoteRun/foreground.js.map +1 -0
  239. package/dist/remoteRun/interrupts.d.ts +27 -0
  240. package/dist/remoteRun/interrupts.d.ts.map +1 -0
  241. package/dist/remoteRun/interrupts.js +78 -0
  242. package/dist/remoteRun/interrupts.js.map +1 -0
  243. package/dist/remoteRun/jobFiles.d.ts +31 -0
  244. package/dist/remoteRun/jobFiles.d.ts.map +1 -0
  245. package/dist/remoteRun/jobFiles.js +84 -0
  246. package/dist/remoteRun/jobFiles.js.map +1 -0
  247. package/dist/remoteRun/jobFormat.d.ts +16 -0
  248. package/dist/remoteRun/jobFormat.d.ts.map +1 -0
  249. package/dist/remoteRun/jobFormat.js +130 -0
  250. package/dist/remoteRun/jobFormat.js.map +1 -0
  251. package/dist/remoteRun/jobId.d.ts +15 -0
  252. package/dist/remoteRun/jobId.d.ts.map +1 -0
  253. package/dist/remoteRun/jobId.js +85 -0
  254. package/dist/remoteRun/jobId.js.map +1 -0
  255. package/dist/remoteRun/jobMeta.d.ts +18 -0
  256. package/dist/remoteRun/jobMeta.d.ts.map +1 -0
  257. package/dist/remoteRun/jobMeta.js +33 -0
  258. package/dist/remoteRun/jobMeta.js.map +1 -0
  259. package/dist/remoteRun/jobQuery.d.ts +12 -0
  260. package/dist/remoteRun/jobQuery.d.ts.map +1 -0
  261. package/dist/remoteRun/jobQuery.js +22 -0
  262. package/dist/remoteRun/jobQuery.js.map +1 -0
  263. package/dist/remoteRun/jobRecords.d.ts +22 -0
  264. package/dist/remoteRun/jobRecords.d.ts.map +1 -0
  265. package/dist/remoteRun/jobRecords.js +145 -0
  266. package/dist/remoteRun/jobRecords.js.map +1 -0
  267. package/dist/remoteRun/jobState.d.ts +62 -0
  268. package/dist/remoteRun/jobState.d.ts.map +1 -0
  269. package/dist/remoteRun/jobState.js +132 -0
  270. package/dist/remoteRun/jobState.js.map +1 -0
  271. package/dist/remoteRun/leaseTiming.d.ts +18 -0
  272. package/dist/remoteRun/leaseTiming.d.ts.map +1 -0
  273. package/dist/remoteRun/leaseTiming.js +43 -0
  274. package/dist/remoteRun/leaseTiming.js.map +1 -0
  275. package/dist/remoteRun/output.d.ts +37 -0
  276. package/dist/remoteRun/output.d.ts.map +1 -0
  277. package/dist/remoteRun/output.js +127 -0
  278. package/dist/remoteRun/output.js.map +1 -0
  279. package/dist/remoteRun/pairedRunners.d.ts +51 -0
  280. package/dist/remoteRun/pairedRunners.d.ts.map +1 -0
  281. package/dist/remoteRun/pairedRunners.js +165 -0
  282. package/dist/remoteRun/pairedRunners.js.map +1 -0
  283. package/dist/remoteRun/pathPatterns.d.ts +33 -0
  284. package/dist/remoteRun/pathPatterns.d.ts.map +1 -0
  285. package/dist/remoteRun/pathPatterns.js +115 -0
  286. package/dist/remoteRun/pathPatterns.js.map +1 -0
  287. package/dist/remoteRun/probe.d.ts +78 -0
  288. package/dist/remoteRun/probe.d.ts.map +1 -0
  289. package/dist/remoteRun/probe.js +157 -0
  290. package/dist/remoteRun/probe.js.map +1 -0
  291. package/dist/remoteRun/project.d.ts +26 -0
  292. package/dist/remoteRun/project.d.ts.map +1 -0
  293. package/dist/remoteRun/project.js +159 -0
  294. package/dist/remoteRun/project.js.map +1 -0
  295. package/dist/remoteRun/projectConfig.d.ts +38 -0
  296. package/dist/remoteRun/projectConfig.d.ts.map +1 -0
  297. package/dist/remoteRun/projectConfig.js +87 -0
  298. package/dist/remoteRun/projectConfig.js.map +1 -0
  299. package/dist/remoteRun/remoteRunCli.d.ts +22 -0
  300. package/dist/remoteRun/remoteRunCli.d.ts.map +1 -0
  301. package/dist/remoteRun/remoteRunCli.js +119 -0
  302. package/dist/remoteRun/remoteRunCli.js.map +1 -0
  303. package/dist/remoteRun/remoteRunError.d.ts +14 -0
  304. package/dist/remoteRun/remoteRunError.d.ts.map +1 -0
  305. package/dist/remoteRun/remoteRunError.js +28 -0
  306. package/dist/remoteRun/remoteRunError.js.map +1 -0
  307. package/dist/remoteRun/rsync.d.ts +17 -0
  308. package/dist/remoteRun/rsync.d.ts.map +1 -0
  309. package/dist/remoteRun/rsync.js +39 -0
  310. package/dist/remoteRun/rsync.js.map +1 -0
  311. package/dist/remoteRun/runnerClient.d.ts +52 -0
  312. package/dist/remoteRun/runnerClient.d.ts.map +1 -0
  313. package/dist/remoteRun/runnerClient.js +143 -0
  314. package/dist/remoteRun/runnerClient.js.map +1 -0
  315. package/dist/remoteRun/runnerSummary.d.ts +24 -0
  316. package/dist/remoteRun/runnerSummary.d.ts.map +1 -0
  317. package/dist/remoteRun/runnerSummary.js +86 -0
  318. package/dist/remoteRun/runnerSummary.js.map +1 -0
  319. package/dist/remoteRun/runnerWait.d.ts +68 -0
  320. package/dist/remoteRun/runnerWait.d.ts.map +1 -0
  321. package/dist/remoteRun/runnerWait.js +137 -0
  322. package/dist/remoteRun/runnerWait.js.map +1 -0
  323. package/dist/remoteRun/runnerWaitTiming.d.ts +16 -0
  324. package/dist/remoteRun/runnerWaitTiming.d.ts.map +1 -0
  325. package/dist/remoteRun/runnerWaitTiming.js +37 -0
  326. package/dist/remoteRun/runnerWaitTiming.js.map +1 -0
  327. package/dist/remoteRun/runners.d.ts +57 -0
  328. package/dist/remoteRun/runners.d.ts.map +1 -0
  329. package/dist/remoteRun/runners.js +209 -0
  330. package/dist/remoteRun/runners.js.map +1 -0
  331. package/dist/remoteRun/runnersCatalog.d.ts +14 -0
  332. package/dist/remoteRun/runnersCatalog.d.ts.map +1 -0
  333. package/dist/remoteRun/runnersCatalog.js +68 -0
  334. package/dist/remoteRun/runnersCatalog.js.map +1 -0
  335. package/dist/remoteRun/runnersConfig.d.ts +53 -0
  336. package/dist/remoteRun/runnersConfig.d.ts.map +1 -0
  337. package/dist/remoteRun/runnersConfig.js +109 -0
  338. package/dist/remoteRun/runnersConfig.js.map +1 -0
  339. package/dist/remoteRun/safeCopy.d.ts +31 -0
  340. package/dist/remoteRun/safeCopy.d.ts.map +1 -0
  341. package/dist/remoteRun/safeCopy.js +137 -0
  342. package/dist/remoteRun/safeCopy.js.map +1 -0
  343. package/dist/remoteRun/sendPlan.d.ts +67 -0
  344. package/dist/remoteRun/sendPlan.d.ts.map +1 -0
  345. package/dist/remoteRun/sendPlan.js +139 -0
  346. package/dist/remoteRun/sendPlan.js.map +1 -0
  347. package/dist/remoteRun/ssh.d.ts +82 -0
  348. package/dist/remoteRun/ssh.d.ts.map +1 -0
  349. package/dist/remoteRun/ssh.js +236 -0
  350. package/dist/remoteRun/ssh.js.map +1 -0
  351. package/dist/remoteRun/submit.d.ts +29 -0
  352. package/dist/remoteRun/submit.d.ts.map +1 -0
  353. package/dist/remoteRun/submit.js +142 -0
  354. package/dist/remoteRun/submit.js.map +1 -0
  355. package/dist/requests/answerRequest.d.ts +5 -0
  356. package/dist/requests/answerRequest.d.ts.map +1 -1
  357. package/dist/requests/answerRequest.js +16 -1
  358. package/dist/requests/answerRequest.js.map +1 -1
  359. package/dist/requests/requestLedger.d.ts +13 -0
  360. package/dist/requests/requestLedger.d.ts.map +1 -1
  361. package/dist/requests/requestLedger.js +9 -0
  362. package/dist/requests/requestLedger.js.map +1 -1
  363. package/dist/requests/topicRequestSettle.d.ts +30 -0
  364. package/dist/requests/topicRequestSettle.d.ts.map +1 -0
  365. package/dist/requests/topicRequestSettle.js +23 -0
  366. package/dist/requests/topicRequestSettle.js.map +1 -0
  367. package/dist/requests/wakeUpEngine.d.ts +4 -0
  368. package/dist/requests/wakeUpEngine.d.ts.map +1 -1
  369. package/dist/requests/wakeUpEngine.js +5 -1
  370. package/dist/requests/wakeUpEngine.js.map +1 -1
  371. package/dist/runnerUtility/adminCheck.d.ts +11 -0
  372. package/dist/runnerUtility/adminCheck.d.ts.map +1 -0
  373. package/dist/runnerUtility/adminCheck.js +68 -0
  374. package/dist/runnerUtility/adminCheck.js.map +1 -0
  375. package/dist/runnerUtility/appInstall.d.ts +25 -0
  376. package/dist/runnerUtility/appInstall.d.ts.map +1 -0
  377. package/dist/runnerUtility/appInstall.js +147 -0
  378. package/dist/runnerUtility/appInstall.js.map +1 -0
  379. package/dist/runnerUtility/appUpdate.d.ts +39 -0
  380. package/dist/runnerUtility/appUpdate.d.ts.map +1 -0
  381. package/dist/runnerUtility/appUpdate.js +90 -0
  382. package/dist/runnerUtility/appUpdate.js.map +1 -0
  383. package/dist/runnerUtility/execEnv.d.ts +21 -0
  384. package/dist/runnerUtility/execEnv.d.ts.map +1 -0
  385. package/dist/runnerUtility/execEnv.js +109 -0
  386. package/dist/runnerUtility/execEnv.js.map +1 -0
  387. package/dist/runnerUtility/execSession.d.ts +13 -0
  388. package/dist/runnerUtility/execSession.d.ts.map +1 -0
  389. package/dist/runnerUtility/execSession.js +58 -0
  390. package/dist/runnerUtility/execSession.js.map +1 -0
  391. package/dist/runnerUtility/hubClient.d.ts +30 -0
  392. package/dist/runnerUtility/hubClient.d.ts.map +1 -0
  393. package/dist/runnerUtility/hubClient.js +127 -0
  394. package/dist/runnerUtility/hubClient.js.map +1 -0
  395. package/dist/runnerUtility/injectStream.d.ts +9 -0
  396. package/dist/runnerUtility/injectStream.d.ts.map +1 -0
  397. package/dist/runnerUtility/injectStream.js +12 -0
  398. package/dist/runnerUtility/injectStream.js.map +1 -0
  399. package/dist/runnerUtility/iosCheck.d.ts +62 -0
  400. package/dist/runnerUtility/iosCheck.d.ts.map +1 -0
  401. package/dist/runnerUtility/iosCheck.js +200 -0
  402. package/dist/runnerUtility/iosCheck.js.map +1 -0
  403. package/dist/runnerUtility/launcher.d.ts +5 -0
  404. package/dist/runnerUtility/launcher.d.ts.map +1 -0
  405. package/dist/runnerUtility/launcher.js +85 -0
  406. package/dist/runnerUtility/launcher.js.map +1 -0
  407. package/dist/runnerUtility/linkLoop.d.ts +47 -0
  408. package/dist/runnerUtility/linkLoop.d.ts.map +1 -0
  409. package/dist/runnerUtility/linkLoop.js +281 -0
  410. package/dist/runnerUtility/linkLoop.js.map +1 -0
  411. package/dist/runnerUtility/localLinkStop.d.ts +23 -0
  412. package/dist/runnerUtility/localLinkStop.d.ts.map +1 -0
  413. package/dist/runnerUtility/localLinkStop.js +79 -0
  414. package/dist/runnerUtility/localLinkStop.js.map +1 -0
  415. package/dist/runnerUtility/machineReport.d.ts +39 -0
  416. package/dist/runnerUtility/machineReport.d.ts.map +1 -0
  417. package/dist/runnerUtility/machineReport.js +161 -0
  418. package/dist/runnerUtility/machineReport.js.map +1 -0
  419. package/dist/runnerUtility/packageId.d.ts +5 -0
  420. package/dist/runnerUtility/packageId.d.ts.map +1 -0
  421. package/dist/runnerUtility/packageId.js +64 -0
  422. package/dist/runnerUtility/packageId.js.map +1 -0
  423. package/dist/runnerUtility/pairingRecovery.d.ts +31 -0
  424. package/dist/runnerUtility/pairingRecovery.d.ts.map +1 -0
  425. package/dist/runnerUtility/pairingRecovery.js +24 -0
  426. package/dist/runnerUtility/pairingRecovery.js.map +1 -0
  427. package/dist/runnerUtility/pueueDaemon.d.ts +36 -0
  428. package/dist/runnerUtility/pueueDaemon.d.ts.map +1 -0
  429. package/dist/runnerUtility/pueueDaemon.js +165 -0
  430. package/dist/runnerUtility/pueueDaemon.js.map +1 -0
  431. package/dist/runnerUtility/runnerCli.d.ts +18 -0
  432. package/dist/runnerUtility/runnerCli.d.ts.map +1 -0
  433. package/dist/runnerUtility/runnerCli.js +184 -0
  434. package/dist/runnerUtility/runnerCli.js.map +1 -0
  435. package/dist/runnerUtility/runnerCommands.d.ts +67 -0
  436. package/dist/runnerUtility/runnerCommands.d.ts.map +1 -0
  437. package/dist/runnerUtility/runnerCommands.js +418 -0
  438. package/dist/runnerUtility/runnerCommands.js.map +1 -0
  439. package/dist/runnerUtility/runnerConfig.d.ts +63 -0
  440. package/dist/runnerUtility/runnerConfig.d.ts.map +1 -0
  441. package/dist/runnerUtility/runnerConfig.js +153 -0
  442. package/dist/runnerUtility/runnerConfig.js.map +1 -0
  443. package/dist/runnerUtility/runnerFlags.d.ts +9 -0
  444. package/dist/runnerUtility/runnerFlags.d.ts.map +1 -0
  445. package/dist/runnerUtility/runnerFlags.js +53 -0
  446. package/dist/runnerUtility/runnerFlags.js.map +1 -0
  447. package/dist/runnerUtility/runnerHome.d.ts +30 -0
  448. package/dist/runnerUtility/runnerHome.d.ts.map +1 -0
  449. package/dist/runnerUtility/runnerHome.js +82 -0
  450. package/dist/runnerUtility/runnerHome.js.map +1 -0
  451. package/dist/runnerUtility/runnerKeys.d.ts +11 -0
  452. package/dist/runnerUtility/runnerKeys.d.ts.map +1 -0
  453. package/dist/runnerUtility/runnerKeys.js +80 -0
  454. package/dist/runnerUtility/runnerKeys.js.map +1 -0
  455. package/dist/runnerUtility/runnerServers.d.ts +40 -0
  456. package/dist/runnerUtility/runnerServers.d.ts.map +1 -0
  457. package/dist/runnerUtility/runnerServers.js +79 -0
  458. package/dist/runnerUtility/runnerServers.js.map +1 -0
  459. package/dist/runnerUtility/serviceManager.d.ts +54 -0
  460. package/dist/runnerUtility/serviceManager.d.ts.map +1 -0
  461. package/dist/runnerUtility/serviceManager.js +413 -0
  462. package/dist/runnerUtility/serviceManager.js.map +1 -0
  463. package/dist/runnerUtility/serviceUnits.d.ts +36 -0
  464. package/dist/runnerUtility/serviceUnits.d.ts.map +1 -0
  465. package/dist/runnerUtility/serviceUnits.js +169 -0
  466. package/dist/runnerUtility/serviceUnits.js.map +1 -0
  467. package/dist/runnerUtility/toolInstall.d.ts +95 -0
  468. package/dist/runnerUtility/toolInstall.d.ts.map +1 -0
  469. package/dist/runnerUtility/toolInstall.js +232 -0
  470. package/dist/runnerUtility/toolInstall.js.map +1 -0
  471. package/dist/runnerUtility/versionPollTiming.d.ts +16 -0
  472. package/dist/runnerUtility/versionPollTiming.d.ts.map +1 -0
  473. package/dist/runnerUtility/versionPollTiming.js +36 -0
  474. package/dist/runnerUtility/versionPollTiming.js.map +1 -0
  475. package/dist/runners/authorizedKeys.d.ts +52 -0
  476. package/dist/runners/authorizedKeys.d.ts.map +1 -0
  477. package/dist/runners/authorizedKeys.js +153 -0
  478. package/dist/runners/authorizedKeys.js.map +1 -0
  479. package/dist/runners/bootstrapLine.d.ts +4 -0
  480. package/dist/runners/bootstrapLine.d.ts.map +1 -0
  481. package/dist/runners/bootstrapLine.js +69 -0
  482. package/dist/runners/bootstrapLine.js.map +1 -0
  483. package/dist/runners/gate.d.ts +41 -0
  484. package/dist/runners/gate.d.ts.map +1 -0
  485. package/dist/runners/gate.js +216 -0
  486. package/dist/runners/gate.js.map +1 -0
  487. package/dist/runners/gateLink.d.ts +26 -0
  488. package/dist/runners/gateLink.d.ts.map +1 -0
  489. package/dist/runners/gateLink.js +240 -0
  490. package/dist/runners/gateLink.js.map +1 -0
  491. package/dist/runners/gateProtocol.d.ts +98 -0
  492. package/dist/runners/gateProtocol.d.ts.map +1 -0
  493. package/dist/runners/gateProtocol.js +149 -0
  494. package/dist/runners/gateProtocol.js.map +1 -0
  495. package/dist/runners/gateWrapper.d.ts +6 -0
  496. package/dist/runners/gateWrapper.d.ts.map +1 -0
  497. package/dist/runners/gateWrapper.js +70 -0
  498. package/dist/runners/gateWrapper.js.map +1 -0
  499. package/dist/runners/hubAddress.d.ts +33 -0
  500. package/dist/runners/hubAddress.d.ts.map +1 -0
  501. package/dist/runners/hubAddress.js +88 -0
  502. package/dist/runners/hubAddress.js.map +1 -0
  503. package/dist/runners/hubAddressCandidates.d.ts +16 -0
  504. package/dist/runners/hubAddressCandidates.d.ts.map +1 -0
  505. package/dist/runners/hubAddressCandidates.js +96 -0
  506. package/dist/runners/hubAddressCandidates.js.map +1 -0
  507. package/dist/runners/hubIds.d.ts +16 -0
  508. package/dist/runners/hubIds.d.ts.map +1 -0
  509. package/dist/runners/hubIds.js +25 -0
  510. package/dist/runners/hubIds.js.map +1 -0
  511. package/dist/runners/hubKey.d.ts +6 -0
  512. package/dist/runners/hubKey.d.ts.map +1 -0
  513. package/dist/runners/hubKey.js +78 -0
  514. package/dist/runners/hubKey.js.map +1 -0
  515. package/dist/runners/hubLock.d.ts +7 -0
  516. package/dist/runners/hubLock.d.ts.map +1 -0
  517. package/dist/runners/hubLock.js +230 -0
  518. package/dist/runners/hubLock.js.map +1 -0
  519. package/dist/runners/hubPackage.d.ts +23 -0
  520. package/dist/runners/hubPackage.d.ts.map +1 -0
  521. package/dist/runners/hubPackage.js +184 -0
  522. package/dist/runners/hubPackage.js.map +1 -0
  523. package/dist/runners/hubPairing.d.ts +63 -0
  524. package/dist/runners/hubPairing.d.ts.map +1 -0
  525. package/dist/runners/hubPairing.js +261 -0
  526. package/dist/runners/hubPairing.js.map +1 -0
  527. package/dist/runners/hubPaths.d.ts +38 -0
  528. package/dist/runners/hubPaths.d.ts.map +1 -0
  529. package/dist/runners/hubPaths.js +94 -0
  530. package/dist/runners/hubPaths.js.map +1 -0
  531. package/dist/runners/hubRegistry.d.ts +112 -0
  532. package/dist/runners/hubRegistry.d.ts.map +1 -0
  533. package/dist/runners/hubRegistry.js +210 -0
  534. package/dist/runners/hubRegistry.js.map +1 -0
  535. package/dist/runners/linkControl.d.ts +27 -0
  536. package/dist/runners/linkControl.d.ts.map +1 -0
  537. package/dist/runners/linkControl.js +124 -0
  538. package/dist/runners/linkControl.js.map +1 -0
  539. package/dist/runners/pairCode.d.ts +30 -0
  540. package/dist/runners/pairCode.d.ts.map +1 -0
  541. package/dist/runners/pairCode.js +46 -0
  542. package/dist/runners/pairCode.js.map +1 -0
  543. package/dist/runners/pairingTiming.d.ts +18 -0
  544. package/dist/runners/pairingTiming.d.ts.map +1 -0
  545. package/dist/runners/pairingTiming.js +42 -0
  546. package/dist/runners/pairingTiming.js.map +1 -0
  547. package/dist/runners/runnerProxy.d.ts +9 -0
  548. package/dist/runners/runnerProxy.d.ts.map +1 -0
  549. package/dist/runners/runnerProxy.js +67 -0
  550. package/dist/runners/runnerProxy.js.map +1 -0
  551. package/dist/runners/runnerRemoval.d.ts +21 -0
  552. package/dist/runners/runnerRemoval.d.ts.map +1 -0
  553. package/dist/runners/runnerRemoval.js +82 -0
  554. package/dist/runners/runnerRemoval.js.map +1 -0
  555. package/dist/runners/runnersCli.d.ts +16 -0
  556. package/dist/runners/runnersCli.d.ts.map +1 -0
  557. package/dist/runners/runnersCli.js +214 -0
  558. package/dist/runners/runnersCli.js.map +1 -0
  559. package/dist/runners/runnersList.d.ts +17 -0
  560. package/dist/runners/runnersList.d.ts.map +1 -0
  561. package/dist/runners/runnersList.js +49 -0
  562. package/dist/runners/runnersList.js.map +1 -0
  563. package/dist/runners/runnersOverview.d.ts +20 -0
  564. package/dist/runners/runnersOverview.d.ts.map +1 -0
  565. package/dist/runners/runnersOverview.js +55 -0
  566. package/dist/runners/runnersOverview.js.map +1 -0
  567. package/dist/runners/sshKeys.d.ts +26 -0
  568. package/dist/runners/sshKeys.d.ts.map +1 -0
  569. package/dist/runners/sshKeys.js +60 -0
  570. package/dist/runners/sshKeys.js.map +1 -0
  571. package/dist/runners/tarArchive.d.ts +20 -0
  572. package/dist/runners/tarArchive.d.ts.map +1 -0
  573. package/dist/runners/tarArchive.js +70 -0
  574. package/dist/runners/tarArchive.js.map +1 -0
  575. package/dist/scheduler/delivery.d.ts +2 -0
  576. package/dist/scheduler/delivery.d.ts.map +1 -1
  577. package/dist/scheduler/delivery.js +8 -4
  578. package/dist/scheduler/delivery.js.map +1 -1
  579. package/dist/scheduler/mcpSurface.d.ts +65 -10
  580. package/dist/scheduler/mcpSurface.d.ts.map +1 -1
  581. package/dist/scheduler/mcpSurface.js +319 -89
  582. package/dist/scheduler/mcpSurface.js.map +1 -1
  583. package/dist/scheduler/recurrence.d.ts +5 -0
  584. package/dist/scheduler/recurrence.d.ts.map +1 -1
  585. package/dist/scheduler/recurrence.js +12 -2
  586. package/dist/scheduler/recurrence.js.map +1 -1
  587. package/dist/scheduler/strictToolInput.d.ts +45 -0
  588. package/dist/scheduler/strictToolInput.d.ts.map +1 -0
  589. package/dist/scheduler/strictToolInput.js +155 -0
  590. package/dist/scheduler/strictToolInput.js.map +1 -0
  591. package/dist/scheduler/types.d.ts +5 -0
  592. package/dist/scheduler/types.d.ts.map +1 -1
  593. package/dist/scheduler/workerWatch.d.ts +23 -0
  594. package/dist/scheduler/workerWatch.d.ts.map +1 -0
  595. package/dist/scheduler/workerWatch.js +26 -0
  596. package/dist/scheduler/workerWatch.js.map +1 -0
  597. package/dist/selfUpdate/emittedSource.d.ts +11 -0
  598. package/dist/selfUpdate/emittedSource.d.ts.map +1 -0
  599. package/dist/selfUpdate/emittedSource.js +24 -0
  600. package/dist/selfUpdate/emittedSource.js.map +1 -0
  601. package/dist/selfUpdate/gitSource.d.ts +0 -6
  602. package/dist/selfUpdate/gitSource.d.ts.map +1 -1
  603. package/dist/selfUpdate/gitSource.js +2 -18
  604. package/dist/selfUpdate/gitSource.js.map +1 -1
  605. package/dist/selfUpdate/selfCheck.js +2 -2
  606. package/dist/selfUpdate/selfCheck.js.map +1 -1
  607. package/dist/selfUpdate/updatePending.d.ts +1 -1
  608. package/dist/sendErrorClassifier.d.ts +10 -0
  609. package/dist/sendErrorClassifier.d.ts.map +1 -1
  610. package/dist/sendErrorClassifier.js +22 -0
  611. package/dist/sendErrorClassifier.js.map +1 -1
  612. package/dist/state.d.ts +78 -7
  613. package/dist/state.d.ts.map +1 -1
  614. package/dist/state.js +125 -8
  615. package/dist/state.js.map +1 -1
  616. package/dist/stateFile.d.ts +9 -0
  617. package/dist/stateFile.d.ts.map +1 -0
  618. package/dist/stateFile.js +57 -0
  619. package/dist/stateFile.js.map +1 -0
  620. package/dist/stt/sttInstall.d.ts +7 -3
  621. package/dist/stt/sttInstall.d.ts.map +1 -1
  622. package/dist/stt/sttInstall.js +8 -4
  623. package/dist/stt/sttInstall.js.map +1 -1
  624. package/dist/stt/sttSettings.d.ts +2 -1
  625. package/dist/stt/sttSettings.d.ts.map +1 -1
  626. package/dist/stt/sttSettings.js +3 -2
  627. package/dist/stt/sttSettings.js.map +1 -1
  628. package/dist/stt/transcribeChain.js +2 -0
  629. package/dist/stt/transcribeChain.js.map +1 -1
  630. package/dist/testTimings.d.ts +16 -3
  631. package/dist/testTimings.d.ts.map +1 -1
  632. package/dist/testTimings.js +35 -2
  633. package/dist/testTimings.js.map +1 -1
  634. package/dist/todos/board.d.ts +83 -0
  635. package/dist/todos/board.d.ts.map +1 -0
  636. package/dist/todos/board.js +227 -0
  637. package/dist/todos/board.js.map +1 -0
  638. package/dist/todos/format.d.ts +33 -0
  639. package/dist/todos/format.d.ts.map +1 -0
  640. package/dist/todos/format.js +90 -0
  641. package/dist/todos/format.js.map +1 -0
  642. package/dist/todos/merge.d.ts +46 -0
  643. package/dist/todos/merge.d.ts.map +1 -0
  644. package/dist/todos/merge.js +137 -0
  645. package/dist/todos/merge.js.map +1 -0
  646. package/dist/todos/midTurn.d.ts +75 -0
  647. package/dist/todos/midTurn.d.ts.map +1 -0
  648. package/dist/todos/midTurn.js +214 -0
  649. package/dist/todos/midTurn.js.map +1 -0
  650. package/dist/todos/nag.d.ts +85 -0
  651. package/dist/todos/nag.d.ts.map +1 -0
  652. package/dist/todos/nag.js +103 -0
  653. package/dist/todos/nag.js.map +1 -0
  654. package/dist/todos/order.d.ts +25 -0
  655. package/dist/todos/order.d.ts.map +1 -0
  656. package/dist/todos/order.js +61 -0
  657. package/dist/todos/order.js.map +1 -0
  658. package/dist/todos/projects.d.ts +102 -0
  659. package/dist/todos/projects.d.ts.map +1 -0
  660. package/dist/todos/projects.js +342 -0
  661. package/dist/todos/projects.js.map +1 -0
  662. package/dist/todos/registry.d.ts +64 -0
  663. package/dist/todos/registry.d.ts.map +1 -0
  664. package/dist/todos/registry.js +116 -0
  665. package/dist/todos/registry.js.map +1 -0
  666. package/dist/todos/reminderEngine.d.ts +78 -0
  667. package/dist/todos/reminderEngine.d.ts.map +1 -0
  668. package/dist/todos/reminderEngine.js +162 -0
  669. package/dist/todos/reminderEngine.js.map +1 -0
  670. package/dist/todos/store.d.ts +34 -0
  671. package/dist/todos/store.d.ts.map +1 -0
  672. package/dist/todos/store.js +208 -0
  673. package/dist/todos/store.js.map +1 -0
  674. package/dist/todos/tools.d.ts +26 -0
  675. package/dist/todos/tools.d.ts.map +1 -0
  676. package/dist/todos/tools.js +25 -0
  677. package/dist/todos/tools.js.map +1 -0
  678. package/dist/todos/types.d.ts +85 -0
  679. package/dist/todos/types.d.ts.map +1 -0
  680. package/dist/todos/types.js +30 -0
  681. package/dist/todos/types.js.map +1 -0
  682. package/dist/trackers/taskAgentLimit.d.ts +58 -0
  683. package/dist/trackers/taskAgentLimit.d.ts.map +1 -0
  684. package/dist/trackers/taskAgentLimit.js +62 -0
  685. package/dist/trackers/taskAgentLimit.js.map +1 -0
  686. package/dist/trackers/taskAgentSlots.d.ts +109 -0
  687. package/dist/trackers/taskAgentSlots.d.ts.map +1 -0
  688. package/dist/trackers/taskAgentSlots.js +227 -0
  689. package/dist/trackers/taskAgentSlots.js.map +1 -0
  690. package/dist/trackers/types.d.ts +14 -0
  691. package/dist/trackers/types.d.ts.map +1 -0
  692. package/dist/trackers/types.js +3 -0
  693. package/dist/trackers/types.js.map +1 -0
  694. package/dist/types.d.ts +80 -4
  695. package/dist/types.d.ts.map +1 -1
  696. package/dist/utils/adoptReplayGate.d.ts +16 -0
  697. package/dist/utils/adoptReplayGate.d.ts.map +1 -0
  698. package/dist/utils/adoptReplayGate.js +35 -0
  699. package/dist/utils/adoptReplayGate.js.map +1 -0
  700. package/dist/utils/agentEnvironment.d.ts +6 -2
  701. package/dist/utils/agentEnvironment.d.ts.map +1 -1
  702. package/dist/utils/agentEnvironment.js +13 -2
  703. package/dist/utils/agentEnvironment.js.map +1 -1
  704. package/dist/utils/agentPlugin.d.ts +113 -0
  705. package/dist/utils/agentPlugin.d.ts.map +1 -0
  706. package/dist/utils/agentPlugin.js +192 -0
  707. package/dist/utils/agentPlugin.js.map +1 -0
  708. package/dist/utils/autoContinueOnLimit.d.ts +7 -4
  709. package/dist/utils/autoContinueOnLimit.d.ts.map +1 -1
  710. package/dist/utils/autoContinueOnLimit.js +8 -5
  711. package/dist/utils/autoContinueOnLimit.js.map +1 -1
  712. package/dist/utils/botSkills.d.ts +62 -0
  713. package/dist/utils/botSkills.d.ts.map +1 -0
  714. package/dist/utils/botSkills.js +53 -0
  715. package/dist/utils/botSkills.js.map +1 -0
  716. package/dist/utils/botSkillsSetting.d.ts +7 -0
  717. package/dist/utils/botSkillsSetting.d.ts.map +1 -0
  718. package/dist/utils/botSkillsSetting.js +9 -0
  719. package/dist/utils/botSkillsSetting.js.map +1 -0
  720. package/dist/utils/claudeCodeVersion.d.ts +7 -0
  721. package/dist/utils/claudeCodeVersion.d.ts.map +1 -1
  722. package/dist/utils/claudeCodeVersion.js +14 -1
  723. package/dist/utils/claudeCodeVersion.js.map +1 -1
  724. package/dist/utils/claudeCompactHook.d.ts +33 -15
  725. package/dist/utils/claudeCompactHook.d.ts.map +1 -1
  726. package/dist/utils/claudeCompactHook.js +36 -17
  727. package/dist/utils/claudeCompactHook.js.map +1 -1
  728. package/dist/utils/claudeCompactSummary.d.ts +15 -0
  729. package/dist/utils/claudeCompactSummary.d.ts.map +1 -0
  730. package/dist/utils/claudeCompactSummary.js +96 -0
  731. package/dist/utils/claudeCompactSummary.js.map +1 -0
  732. package/dist/utils/claudeLimitSwitch.d.ts +11 -9
  733. package/dist/utils/claudeLimitSwitch.d.ts.map +1 -1
  734. package/dist/utils/claudeLimitSwitch.js +18 -16
  735. package/dist/utils/claudeLimitSwitch.js.map +1 -1
  736. package/dist/utils/claudeProfiles.d.ts +5 -0
  737. package/dist/utils/claudeProfiles.d.ts.map +1 -1
  738. package/dist/utils/claudeProfiles.js +14 -0
  739. package/dist/utils/claudeProfiles.js.map +1 -1
  740. package/dist/utils/claudeSessionTitle.d.ts +2 -1
  741. package/dist/utils/claudeSessionTitle.d.ts.map +1 -1
  742. package/dist/utils/claudeSessionTitle.js +14 -14
  743. package/dist/utils/claudeSessionTitle.js.map +1 -1
  744. package/dist/utils/claudeUsage.d.ts +95 -15
  745. package/dist/utils/claudeUsage.d.ts.map +1 -1
  746. package/dist/utils/claudeUsage.js +241 -41
  747. package/dist/utils/claudeUsage.js.map +1 -1
  748. package/dist/utils/claudeUsageRefresher.d.ts +10 -0
  749. package/dist/utils/claudeUsageRefresher.d.ts.map +1 -1
  750. package/dist/utils/claudeUsageRefresher.js +11 -0
  751. package/dist/utils/claudeUsageRefresher.js.map +1 -1
  752. package/dist/utils/claudeUserEntry.d.ts +50 -0
  753. package/dist/utils/claudeUserEntry.d.ts.map +1 -0
  754. package/dist/utils/claudeUserEntry.js +120 -0
  755. package/dist/utils/claudeUserEntry.js.map +1 -0
  756. package/dist/utils/clearThreadOutputQueues.d.ts +4 -3
  757. package/dist/utils/clearThreadOutputQueues.d.ts.map +1 -1
  758. package/dist/utils/clearThreadOutputQueues.js +1 -1
  759. package/dist/utils/clearThreadOutputQueues.js.map +1 -1
  760. package/dist/utils/compactOnIdle.d.ts +33 -13
  761. package/dist/utils/compactOnIdle.d.ts.map +1 -1
  762. package/dist/utils/compactOnIdle.js +35 -15
  763. package/dist/utils/compactOnIdle.js.map +1 -1
  764. package/dist/utils/fallbackBridge.d.ts +5 -0
  765. package/dist/utils/fallbackBridge.d.ts.map +1 -1
  766. package/dist/utils/fallbackBridge.js +19 -3
  767. package/dist/utils/fallbackBridge.js.map +1 -1
  768. package/dist/utils/fallbackChain.d.ts +9 -3
  769. package/dist/utils/fallbackChain.d.ts.map +1 -1
  770. package/dist/utils/fallbackChain.js +21 -3
  771. package/dist/utils/fallbackChain.js.map +1 -1
  772. package/dist/utils/fileSendPlan.d.ts +6 -0
  773. package/dist/utils/fileSendPlan.d.ts.map +1 -1
  774. package/dist/utils/fileSendPlan.js +11 -0
  775. package/dist/utils/fileSendPlan.js.map +1 -1
  776. package/dist/utils/fileSendService.d.ts +7 -1
  777. package/dist/utils/fileSendService.d.ts.map +1 -1
  778. package/dist/utils/fileSendService.js +6 -2
  779. package/dist/utils/fileSendService.js.map +1 -1
  780. package/dist/utils/groupFinalizePlan.d.ts +8 -10
  781. package/dist/utils/groupFinalizePlan.d.ts.map +1 -1
  782. package/dist/utils/groupFinalizePlan.js +8 -12
  783. package/dist/utils/groupFinalizePlan.js.map +1 -1
  784. package/dist/utils/jsonStreamHost.d.ts +13 -0
  785. package/dist/utils/jsonStreamHost.d.ts.map +1 -1
  786. package/dist/utils/jsonStreamHost.js +13 -0
  787. package/dist/utils/jsonStreamHost.js.map +1 -1
  788. package/dist/utils/messageSendService.d.ts +5 -3
  789. package/dist/utils/messageSendService.d.ts.map +1 -1
  790. package/dist/utils/messageSendService.js +12 -5
  791. package/dist/utils/messageSendService.js.map +1 -1
  792. package/dist/utils/openCodeCompactPlugin.d.ts +39 -14
  793. package/dist/utils/openCodeCompactPlugin.d.ts.map +1 -1
  794. package/dist/utils/openCodeCompactPlugin.js +106 -38
  795. package/dist/utils/openCodeCompactPlugin.js.map +1 -1
  796. package/dist/utils/promptDelivery.d.ts +29 -12
  797. package/dist/utils/promptDelivery.d.ts.map +1 -1
  798. package/dist/utils/promptDelivery.js +13 -5
  799. package/dist/utils/promptDelivery.js.map +1 -1
  800. package/dist/utils/runnersScreenPlan.d.ts +130 -0
  801. package/dist/utils/runnersScreenPlan.d.ts.map +1 -0
  802. package/dist/utils/runnersScreenPlan.js +257 -0
  803. package/dist/utils/runnersScreenPlan.js.map +1 -0
  804. package/dist/utils/sentKindsSummary.d.ts +10 -0
  805. package/dist/utils/sentKindsSummary.d.ts.map +1 -0
  806. package/dist/utils/sentKindsSummary.js +18 -0
  807. package/dist/utils/sentKindsSummary.js.map +1 -0
  808. package/dist/utils/telegramError.d.ts +6 -0
  809. package/dist/utils/telegramError.d.ts.map +1 -1
  810. package/dist/utils/telegramError.js +12 -0
  811. package/dist/utils/telegramError.js.map +1 -1
  812. package/dist/utils/topicView.d.ts +1 -1
  813. package/dist/utils/topicView.js +1 -1
  814. package/dist/utils/transcribeAudio.d.ts +10 -0
  815. package/dist/utils/transcribeAudio.d.ts.map +1 -1
  816. package/dist/utils/transcribeAudio.js +22 -3
  817. package/dist/utils/transcribeAudio.js.map +1 -1
  818. package/dist/utils/transcriptErrorTime.d.ts +8 -0
  819. package/dist/utils/transcriptErrorTime.d.ts.map +1 -0
  820. package/dist/utils/transcriptErrorTime.js +56 -0
  821. package/dist/utils/transcriptErrorTime.js.map +1 -0
  822. package/dist/utils/wedgeRecovery.d.ts +2 -0
  823. package/dist/utils/wedgeRecovery.d.ts.map +1 -1
  824. package/dist/utils/wedgeRecovery.js +1 -1
  825. package/dist/utils/wedgeRecovery.js.map +1 -1
  826. package/package.json +15 -4
  827. package/remoteRun/runner.sh +1317 -0
  828. package/stt/gigaam_sidecar.py +555 -50
@@ -0,0 +1,1317 @@
1
+ #!/usr/bin/env bash
2
+ # charness remote-run — the script that runs ON a runner (never on the main box).
3
+ #
4
+ # `charness remote-run` uploads this file to <workDir>/bin/runner-<sha8>.sh and calls it over ssh. Every subcommand takes
5
+ # the runner's work dir first: runner.sh <subcommand> <workDir> [arguments…]
6
+ # Pueue is reached through PUEUE_BIN / PUEUE_CONFIG (both optional), set by the caller on every call.
7
+ #
8
+ # A PAIRED runner (`charness-runner`, src/runnerUtility) keeps its work dir as <state>/work in its own state folder; `probe` reads three
9
+ # files there and reports them — nothing else in this script knows about pairing. A hand-made runner has none of them:
10
+ # <state>/paused exists → paused=yes (written by `charness-runner pause`; the main box never sends a job to a paused runner)
11
+ # <state>/service one word: user-service | system-service (no file = run by hand: service=foreground)
12
+ # <state>/ios-check.json the runner's iOS Simulator boot check (one JSON line): "status": "pass"|"fail"|"skipped" and "reason": "…"|null
13
+ # <state> counts only when it holds config.json and keys/ (a work dir that merely is called «work» is not one).
14
+ #
15
+ # bash 3.2-compatible on purpose (the stock bash of macOS): no associative arrays, no `wait -n`, no `mapfile`, no
16
+ # `timeout(1)`, no `read -t` (its timeout status differs between 3.2 and 4+), no GNU-only flags. Time limits are done by background watchdogs.
17
+ #
18
+ # Subcommands (the contract is in src/remoteRun/README.md):
19
+ # probe key=value lines: OS, CPU, RAM, disk, jobs, capabilities (paused / service / iOS boot check of a paired runner)
20
+ # prepare layout for a job, Pueue daemon + groups, prune
21
+ # lease add the job to Pueue, stream its log, kill it when the caller's heartbeat stops (foreground run)
22
+ # submit add the job to Pueue and return (--bg)
23
+ # job the Pueue task itself: lock → apply the staged tree → deps → command → result files
24
+ # follow stream a job's log until it ends (logs -f)
25
+ # wait block until the job ends
26
+ # result key=value facts of a job (exit code, phase, markers)
27
+ # meta the job's meta.json
28
+ # tail the last N lines of a job's log
29
+ # kill stop a job: SIGTERM, SIGKILL after 10 s
30
+ # exec one-off command in the project's copy, leased like a foreground job
31
+ # workdir the work dir as an absolute path
32
+ # pueue run Pueue itself with the configured binary and config
33
+ # jobs the meta.json of every job of a project (or of all), as a JSON array
34
+ # prune remove old jobs, stale stage dirs and unused project copies
35
+ # sync-tree the apply step alone (tests)
36
+
37
+ LC_ALL=C
38
+ export LC_ALL
39
+ set -u
40
+
41
+ # ─────────────────────────── constants ───────────────────────────
42
+
43
+ kill_grace_sec=10
44
+ pueue_start_wait_sec=8
45
+ default_timeout_sec=10800
46
+ job_keep_days=3
47
+ copy_keep_days=30
48
+ stage_keep_days=1
49
+ drain_interval_sec=0.2
50
+ probe_step_sec=4
51
+ exit_file_wait_sec=5
52
+ task_id_wait_sec=5
53
+ heartbeat_poll_sec=1
54
+ # How long a job waiting for its project's lock sleeps between two attempts (a test shortens it to race many waiters quickly).
55
+ lock_poll_sec=1
56
+
57
+ # ─────────────────────────── helpers ───────────────────────────
58
+
59
+ die() {
60
+ printf 'remote-run-runner: %s\n' "$*" >&2
61
+ exit 125
62
+ }
63
+
64
+ # Own progress lines of a job, written into its log; the caller shows lines with this prefix on stderr.
65
+ say() {
66
+ printf 'remote-run: %s\n' "$*"
67
+ }
68
+
69
+ pq() {
70
+ "${PUEUE_BIN:-pueue}" ${PUEUE_CONFIG:+-c "$PUEUE_CONFIG"} "$@"
71
+ }
72
+
73
+ pueued_bin() {
74
+ if [ -n "${PUEUE_BIN:-}" ] && [ -x "$(dirname "$PUEUE_BIN")/pueued" ]; then
75
+ printf '%s\n' "$(dirname "$PUEUE_BIN")/pueued"
76
+ else
77
+ printf 'pueued\n'
78
+ fi
79
+ }
80
+
81
+ # A name that is safe to use as one path component.
82
+ check_name() {
83
+ case $1 in
84
+ '' | *[!A-Za-z0-9._-]* | .*) die "bad name: $1" ;;
85
+ esac
86
+ }
87
+
88
+ # A relative path inside a project: no leading slash, no ".." component.
89
+ check_relative_path() {
90
+ case $1 in
91
+ /*) return 1 ;;
92
+ esac
93
+ case "/$1/" in
94
+ */../*) return 1 ;;
95
+ esac
96
+ return 0
97
+ }
98
+
99
+ # Sets work, slug, job and every directory derived from them. A slug of "-" means "the project this job id belongs to" (the commands
100
+ # that take only a job id): found by looking for the job's folder.
101
+ set_paths() {
102
+ work=$1
103
+ slug=${2:-}
104
+ job=${3:-}
105
+ if [ "$slug" = "-" ] && [ -n "$job" ]; then
106
+ slug=
107
+ local found
108
+ for found in "$work"/p/*/jobs/"$job"; do
109
+ [ -d "$found" ] && slug=$(basename "$(dirname "$(dirname "$found")")") && break
110
+ done
111
+ [ -n "$slug" ] || die "no such job: $job"
112
+ fi
113
+ check_name "$slug"
114
+ proj="$work/p/$slug"
115
+ tree="$proj/tree"
116
+ jobs_dir="$proj/jobs"
117
+ lock="$proj/lock"
118
+ if [ -n "$job" ]; then
119
+ case $job in *[!a-z0-9-]* | -*) die "bad job id: $job" ;; esac
120
+ jd="$jobs_dir/$job"
121
+ stage="$proj/stage/$job"
122
+ fi
123
+ }
124
+
125
+ set_phase() {
126
+ printf '%s\n' "$1" > "$jd/phase"
127
+ }
128
+
129
+ read_file() {
130
+ cat "$1" 2>/dev/null
131
+ }
132
+
133
+ # A Mac that idle-sleeps in the middle of a job drops the link and stalls the build: while the process $1 runs, `caffeinate -i -w` holds
134
+ # the idle-sleep assertion and ends by itself with it. A sibling, not a wrapper: the command keeps its own signals and exit status.
135
+ # Nothing on other systems, or where caffeinate is missing.
136
+ keep_awake_while() {
137
+ [ "$(uname -s)" = Darwin ] && command -v caffeinate > /dev/null 2>&1 || return 0
138
+ caffeinate -i -w "$1" > /dev/null 2>&1 < /dev/null &
139
+ }
140
+
141
+ # ─────────────────────────── probe ───────────────────────────
142
+
143
+ kb_to_mb() {
144
+ awk -v kb="$1" 'BEGIN { printf "%d", kb / 1024 }'
145
+ }
146
+
147
+ probe_linux() {
148
+ local os_name os_version
149
+ os_name=Linux
150
+ os_version=
151
+ if [ -r /etc/os-release ]; then
152
+ os_name=$(. /etc/os-release; printf '%s' "${NAME:-Linux}")
153
+ os_version=$(. /etc/os-release; printf '%s' "${VERSION_ID:-}")
154
+ fi
155
+ echo "os_name=$os_name"
156
+ echo "os_version=$os_version"
157
+ echo "cpu_model=$(sed -n 's/^model name[[:space:]]*:[[:space:]]*//p' /proc/cpuinfo | head -n 1)"
158
+ local threads cores
159
+ threads=$(grep -c '^processor' /proc/cpuinfo)
160
+ cores=$(awk -F: '/^physical id/ { p = $2 } /^core id/ { print p ":" $2 }' /proc/cpuinfo | sort -u | wc -l | tr -d ' ')
161
+ [ "$cores" -gt 0 ] 2> /dev/null || cores=$threads
162
+ echo "cpu_cores=$cores"
163
+ echo "cpu_threads=$threads"
164
+ echo "load=$(cut -d' ' -f1 /proc/loadavg)"
165
+ echo "ram_total_mb=$(kb_to_mb "$(sed -n 's/^MemTotal:[[:space:]]*\([0-9]*\).*/\1/p' /proc/meminfo)")"
166
+ echo "ram_free_mb=$(kb_to_mb "$(sed -n 's/^MemAvailable:[[:space:]]*\([0-9]*\).*/\1/p' /proc/meminfo)")"
167
+ }
168
+
169
+ probe_darwin() {
170
+ echo "os_name=$(sw_vers -productName 2> /dev/null || echo macOS)"
171
+ echo "os_version=$(sw_vers -productVersion 2> /dev/null)"
172
+ echo "cpu_model=$(sysctl -n machdep.cpu.brand_string 2> /dev/null)"
173
+ echo "cpu_cores=$(sysctl -n hw.physicalcpu 2> /dev/null)"
174
+ echo "cpu_threads=$(sysctl -n hw.logicalcpu 2> /dev/null)"
175
+ echo "load=$(sysctl -n vm.loadavg 2> /dev/null | tr -d '{}' | awk '{ print $1 }')"
176
+ local mem_bytes page_size free_pages
177
+ mem_bytes=$(sysctl -n hw.memsize 2> /dev/null)
178
+ page_size=$(sysctl -n hw.pagesize 2> /dev/null)
179
+ free_pages=$(vm_stat 2> /dev/null | awk '/^Pages (free|inactive|speculative):/ { gsub("\\.", "", $NF); sum += $NF } END { print sum + 0 }')
180
+ echo "ram_total_mb=$(awk -v b="$mem_bytes" 'BEGIN { printf "%d", b / 1048576 }')"
181
+ echo "ram_free_mb=$(awk -v p="$free_pages" -v s="${page_size:-4096}" 'BEGIN { printf "%d", p * s / 1048576 }')"
182
+ }
183
+
184
+ # capability lines: cap_<name>=yes|no and cap_<name>_detail=<words>
185
+ probe_android() {
186
+ local acceleration=
187
+ case $(uname -s) in
188
+ Linux) [ -r /dev/kvm ] && [ -w /dev/kvm ] && acceleration=KVM ;;
189
+ Darwin) [ "$(sysctl -n kern.hv_support 2> /dev/null)" = 1 ] && acceleration=HVF ;;
190
+ esac
191
+ local emulator_path=
192
+ local sdk_root
193
+ for sdk_root in "${ANDROID_HOME:-}" "${ANDROID_SDK_ROOT:-}"; do
194
+ [ -n "$sdk_root" ] && [ -x "$sdk_root/emulator/emulator" ] && emulator_path="$sdk_root/emulator/emulator" && break
195
+ done
196
+ [ -z "$emulator_path" ] && emulator_path=$(command -v emulator 2> /dev/null || true)
197
+ if [ -n "$acceleration" ] && [ -n "$emulator_path" ]; then
198
+ echo "cap_android=yes"
199
+ echo "cap_android_detail=$acceleration, emulator"
200
+ else
201
+ echo "cap_android=no"
202
+ if [ -z "$acceleration" ] && [ -z "$emulator_path" ]; then
203
+ echo "cap_android_detail=no hardware acceleration, no emulator binary"
204
+ elif [ -z "$acceleration" ]; then
205
+ echo "cap_android_detail=no hardware acceleration (KVM / HVF)"
206
+ else
207
+ echo "cap_android_detail=no emulator binary"
208
+ fi
209
+ fi
210
+ }
211
+
212
+ probe_playwright() {
213
+ local browsers_dir=${PLAYWRIGHT_BROWSERS_PATH:-}
214
+ if [ -z "$browsers_dir" ]; then
215
+ case $(uname -s) in
216
+ Darwin) browsers_dir="$HOME/Library/Caches/ms-playwright" ;;
217
+ *) browsers_dir="$HOME/.cache/ms-playwright" ;;
218
+ esac
219
+ fi
220
+ local found=
221
+ local kind
222
+ for kind in chromium firefox webkit; do
223
+ if ls -d "$browsers_dir/$kind-"* > /dev/null 2>&1; then
224
+ found="${found:+$found, }$kind"
225
+ fi
226
+ done
227
+ if [ -n "$found" ]; then
228
+ echo "cap_playwright=yes"
229
+ echo "cap_playwright_detail=$found"
230
+ else
231
+ echo "cap_playwright=no"
232
+ echo "cap_playwright_detail=no browsers installed"
233
+ fi
234
+ }
235
+
236
+ # Jobs by their files: running = a live job process, queued = submitted and not started, finished = has an exit file.
237
+ probe_jobs() {
238
+ local running=0 queued=0
239
+ local active_slugs=
240
+ local job_dir slug pid
241
+ for job_dir in "$work"/p/*/jobs/*/; do
242
+ [ -d "$job_dir" ] || continue
243
+ [ -f "$job_dir/exit" ] && continue
244
+ [ -f "$job_dir/task-id" ] || continue
245
+ slug=$(basename "$(dirname "$(dirname "$job_dir")")")
246
+ pid=$(read_file "$job_dir/pid")
247
+ if [ -n "$pid" ] && kill -0 "$pid" 2> /dev/null; then
248
+ running=$((running + 1))
249
+ elif [ -z "$pid" ]; then
250
+ queued=$((queued + 1))
251
+ else
252
+ continue
253
+ fi
254
+ case " $active_slugs " in
255
+ *" $slug "*) ;;
256
+ *) active_slugs="$active_slugs $slug" ;;
257
+ esac
258
+ done
259
+ local project_count=0
260
+ local counted
261
+ for counted in $active_slugs; do project_count=$((project_count + 1)); done
262
+ echo "jobs_running=$running"
263
+ echo "jobs_queued=$queued"
264
+ echo "jobs_projects=$project_count"
265
+ }
266
+
267
+ # The probe's slow questions — Pueue, the disk, Xcode and its simulators, Docker — are asked at once, each bounded by probe_step_sec, so
268
+ # a hung one costs that much in all, not that much each: the caller gives the whole probe 20 s, and a Mac with a slow simctl, a stalled
269
+ # mount and a sleeping Docker would otherwise read as offline. One step: output in $probe_dir/<name>.out, its status in <name>.status
270
+ # (137: it was cut off; no file: it never finished).
271
+ start_probe_step() {
272
+ local name=$1
273
+ local limit=$2
274
+ shift 2
275
+ (
276
+ "$@" > "$probe_dir/$name.out" 2> /dev/null < /dev/null &
277
+ local command_pid=$!
278
+ ( sleep "$limit"; kill -9 "$command_pid" ) > /dev/null 2>&1 &
279
+ local watchdog_pid=$!
280
+ wait "$command_pid" 2> /dev/null
281
+ echo "$?" > "$probe_dir/$name.status"
282
+ kill "$watchdog_pid" > /dev/null 2>&1
283
+ ) &
284
+ }
285
+
286
+ probe_step_status() {
287
+ read_file "$probe_dir/$1.status"
288
+ }
289
+
290
+ probe_step_output() {
291
+ read_file "$probe_dir/$1.out"
292
+ }
293
+
294
+ probe_slow() {
295
+ mkdir -p "$work/tmp"
296
+ probe_dir="$work/tmp/probe.$$.$RANDOM"
297
+ mkdir -p "$probe_dir" || return 1
298
+ local is_darwin=0
299
+ [ "$(uname -s)" = Darwin ] && is_darwin=1
300
+ local has_docker=0
301
+ command -v docker > /dev/null 2>&1 && has_docker=1
302
+ start_probe_step pueue "$probe_step_sec" "${PUEUE_BIN:-pueue}" ${PUEUE_CONFIG:+-c "$PUEUE_CONFIG"} --version
303
+ start_probe_step disk "$probe_step_sec" df -Pk "$work"
304
+ if [ "$is_darwin" -eq 1 ]; then
305
+ start_probe_step xcode "$probe_step_sec" xcodebuild -version
306
+ start_probe_step simctl "$probe_step_sec" xcrun simctl list devices available
307
+ fi
308
+ [ "$has_docker" -eq 1 ] && start_probe_step docker "$probe_step_sec" docker info
309
+ wait
310
+
311
+ echo "disk_free_mb=$(kb_to_mb "$(probe_step_output disk | awk 'NR == 2 { print $4 }')")"
312
+ if [ "$(probe_step_status pueue)" = 0 ]; then
313
+ echo "pueue=$(probe_step_output pueue | awk '{ print $NF }')"
314
+ else
315
+ echo "pueue=missing"
316
+ fi
317
+ if [ "$is_darwin" -eq 0 ]; then
318
+ echo "cap_ios=no"
319
+ echo "cap_ios_detail=$(uname -s)"
320
+ elif [ "$(probe_step_status xcode)" = 0 ]; then
321
+ local xcode_version
322
+ xcode_version=$(probe_step_output xcode | head -n 1 | sed 's/^Xcode //')
323
+ if [ "${ios_boot_status:-}" = failed ]; then
324
+ # Xcode is there, but the runner's own check failed. The Simulator refusing to boot is Apple's rule (none is available outside a login
325
+ # session); any other reason (no runtime installed, the device could not be created) is shown as it is — the login session would be a
326
+ # wrong cure. «the Simulator did not» is `iosBootFailureReasonPrefix` in runnerUtility/iosCheck.ts.
327
+ echo "cap_ios=no"
328
+ case "$ios_boot_detail" in
329
+ "the Simulator did not"*) echo "cap_ios_detail=needs the runner user's login session" ;;
330
+ ?*) echo "cap_ios_detail=the iOS Simulator check failed: $ios_boot_detail" ;;
331
+ *) echo "cap_ios_detail=the iOS Simulator check failed" ;;
332
+ esac
333
+ elif [ -n "$runner_state" ] && [ -z "$ios_boot_status" ]; then
334
+ # A paired runner writes its record every time its link starts: none yet means the check of THIS start is still running, and an
335
+ # answer from an earlier start (another account, another kind of service) must not be offered meanwhile.
336
+ echo "cap_ios=no"
337
+ echo "cap_ios_detail=the iOS Simulator is being checked"
338
+ else
339
+ echo "cap_ios=yes"
340
+ if [ "$(probe_step_status simctl)" = 0 ]; then
341
+ echo "cap_ios_detail=Xcode $xcode_version, $(probe_step_output simctl | grep -c '(Shutdown)\|(Booted)') simulators"
342
+ else
343
+ # A slow simctl is no reason to say there is no Xcode, nor to invent a simulator count.
344
+ echo "cap_ios_detail=Xcode $xcode_version"
345
+ fi
346
+ fi
347
+ else
348
+ echo "cap_ios=no"
349
+ if [ "$(probe_step_status xcode)" = 137 ] || [ -z "$(probe_step_status xcode)" ]; then
350
+ echo "cap_ios_detail=Xcode did not answer in time"
351
+ else
352
+ echo "cap_ios_detail=Xcode not found"
353
+ fi
354
+ fi
355
+ if [ "$has_docker" -eq 0 ]; then
356
+ echo "cap_docker=no"
357
+ echo "cap_docker_detail=docker not installed"
358
+ elif [ "$(probe_step_status docker)" = 0 ]; then
359
+ echo "cap_docker=yes"
360
+ echo "cap_docker_detail="
361
+ else
362
+ echo "cap_docker=no"
363
+ echo "cap_docker_detail=daemon not reachable"
364
+ fi
365
+ rm -rf "$probe_dir"
366
+ }
367
+
368
+ # The state folder of the charness-runner this work dir belongs to (sets runner_state; empty for a hand-made runner), and what it says
369
+ # about the machine: sets ios_boot_status (ok | failed | unchecked, empty off a Mac or without a check) and ios_boot_detail.
370
+ read_runner_state() {
371
+ runner_state=
372
+ ios_boot_status=
373
+ ios_boot_detail=
374
+ local candidate
375
+ candidate=$(dirname "$work")
376
+ [ "$(basename "$work")" = work ] && [ -f "$candidate/config.json" ] && [ -d "$candidate/keys" ] || return 0
377
+ runner_state=$candidate
378
+ local check="$runner_state/ios-check.json"
379
+ [ -f "$check" ] || return 0
380
+ if grep -q '"status"[[:space:]]*:[[:space:]]*"pass"' "$check"; then
381
+ ios_boot_status=ok
382
+ elif grep -q '"status"[[:space:]]*:[[:space:]]*"fail"' "$check"; then
383
+ ios_boot_status=failed
384
+ else
385
+ ios_boot_status=unchecked
386
+ fi
387
+ ios_boot_detail=$(sed -n 's/.*"reason"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$check" | head -n 1)
388
+ }
389
+
390
+ # paused= / service= / runner_version= / ios_boot= of a paired runner (nothing for a hand-made one).
391
+ probe_runner_state() {
392
+ [ -n "$runner_state" ] || return 0
393
+ if [ -f "$runner_state/paused" ]; then echo "paused=yes"; else echo "paused=no"; fi
394
+ local service
395
+ service=$(head -n 1 "$runner_state/service" 2> /dev/null)
396
+ case $service in
397
+ user-service | system-service) echo "service=$service" ;;
398
+ '') echo "service=foreground" ;;
399
+ *) echo "service=unknown" ;;
400
+ esac
401
+ echo "runner_version=$(sed -n 's/^[[:space:]]*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$runner_state/current/package.json" 2> /dev/null | head -n 1)"
402
+ [ -n "$ios_boot_status" ] && echo "ios_boot=$ios_boot_status"
403
+ [ -n "$ios_boot_detail" ] && echo "ios_boot_detail=$ios_boot_detail"
404
+ return 0
405
+ }
406
+
407
+ cmd_probe() {
408
+ work=$1
409
+ mkdir -p "$work" 2> /dev/null
410
+ read_runner_state
411
+ echo "os=$(uname -s)"
412
+ echo "arch=$(uname -m)"
413
+ echo "kernel=$(uname -r)"
414
+ case $(uname -s) in
415
+ Linux) probe_linux ;;
416
+ Darwin) probe_darwin ;;
417
+ esac
418
+ probe_slow
419
+ probe_jobs
420
+ probe_android
421
+ probe_playwright
422
+ probe_runner_state
423
+ }
424
+
425
+ # ─────────────────────────── pueue ───────────────────────────
426
+
427
+ # Start the daemon once when it does not answer, with its output away from the ssh channel (it would keep the call open).
428
+ ensure_pueued() {
429
+ if pq status > /dev/null 2>&1; then
430
+ return 0
431
+ fi
432
+ "$(pueued_bin)" ${PUEUE_CONFIG:+-c "$PUEUE_CONFIG"} -d > /dev/null 2>&1 < /dev/null
433
+ local started=$SECONDS
434
+ while [ $((SECONDS - started)) -lt "$pueue_start_wait_sec" ]; do
435
+ pq status > /dev/null 2>&1 && return 0
436
+ sleep 0.2
437
+ done
438
+ return 1
439
+ }
440
+
441
+ # groups = "name=parallel,name=parallel"
442
+ apply_groups() {
443
+ printf '%s\n' "$1" | tr ',' '\n' | while IFS= read -r pair; do
444
+ name=${pair%%=*}
445
+ parallel=${pair#*=}
446
+ [ -n "$name" ] || continue
447
+ [ "$name" = default ] || pq group add "$name" > /dev/null 2>&1
448
+ pq parallel -g "$name" "$parallel" > /dev/null 2>&1
449
+ done
450
+ }
451
+
452
+ # ─────────────────────────── prepare / prune ───────────────────────────
453
+
454
+ cmd_prepare() {
455
+ set_paths "$1" "$2" "$3"
456
+ local groups=${4:-}
457
+ # Ids are 30 random bits, made on the caller's side: a clash would make two jobs share one folder.
458
+ if [ -e "$jd/meta.json" ] || [ -e "$jd/task-id" ]; then
459
+ die "job id already in use: $job"
460
+ fi
461
+ mkdir -p "$work/bin" "$tree" "$proj/stage" "$jobs_dir" "$stage" "$jd" || die "cannot create the layout under $work"
462
+ : > "$jd/log"
463
+ touch "$proj/used"
464
+ if ! ensure_pueued; then
465
+ die "Pueue daemon is not reachable and could not be started (check PUEUE_BIN / the pueue settings of this runner)"
466
+ fi
467
+ apply_groups "$groups"
468
+ prune_work "$work" "$slug"
469
+ echo "work=$(cd "$work" && pwd -P)"
470
+ }
471
+
472
+ # Has the job in folder $1 ended? It wrote its exit file; or it never got a Pueue task (its submit failed half-way); or its process is
473
+ # gone (a runner script of an earlier version SIGKILLed it without recording that). One that waits in the queue, or runs, has not.
474
+ check_job_dir_ended() {
475
+ [ -f "$1/exit" ] && return 0
476
+ [ -f "$1/task-id" ] || return 0
477
+ local recorded_pid
478
+ recorded_pid=$(read_file "$1/pid")
479
+ [ -n "$recorded_pid" ] && ! kill -0 "$recorded_pid" 2> /dev/null
480
+ }
481
+
482
+ # Is Pueue's task number $1 the task of job $2? Pueue hands a number it has freed (a task that was removed, or cleaned) to the next task
483
+ # added, and `pueue wait` / `pueue remove` of a number act on whoever holds it — so a job's recorded number is only good while the task
484
+ # under it carries the job's own label. The label filter is Pueue's (`status --json label=…`: an exact match), the answer the task's id.
485
+ check_task_belongs_to_job() {
486
+ pq status --json "label=rr-$2" 2> /dev/null | grep -Eq "\"$1\": ?\{ ?\"id\": ?$1[,}]"
487
+ }
488
+
489
+ # Remove: jobs older than the keep time (and their Pueue tasks), leftover stage dirs, project copies unused for long.
490
+ prune_work() {
491
+ local work_dir=$1
492
+ local keep_slug=${2:-}
493
+ [ -d "$work_dir/p" ] || return 0
494
+ find "$work_dir/p" -mindepth 3 -maxdepth 3 -path '*/jobs/*' -type d -mtime +"$job_keep_days" 2> /dev/null | while IFS= read -r old_job; do
495
+ check_job_dir_ended "$old_job" || continue
496
+ task=$(read_file "$old_job/task-id")
497
+ if [ -n "$task" ] && [ ! -f "$old_job/task-removed" ] && check_task_belongs_to_job "$task" "$(basename "$old_job")"; then
498
+ pq remove "$task" > /dev/null 2>&1
499
+ fi
500
+ rm -rf "$old_job"
501
+ done
502
+ find "$work_dir/p" -mindepth 3 -maxdepth 3 -path '*/stage/*' -type d -mtime +"$stage_keep_days" 2> /dev/null | while IFS= read -r old_stage; do
503
+ # A job that is queued in Pueue (a task, no exit yet) has not applied its stage: behind a busy group it can wait for days.
504
+ check_job_dir_ended "$(dirname "$(dirname "$old_stage")")/jobs/$(basename "$old_stage")" || continue
505
+ rm -rf "$old_stage"
506
+ done
507
+ # Lease state of a watcher that was killed hard (a live one rewrites its beat every few seconds, so its folder is never this old).
508
+ find "$work_dir/tmp" -mindepth 1 -maxdepth 1 -mtime +"$stage_keep_days" 2> /dev/null | while IFS= read -r old_lease; do
509
+ rm -rf "$old_lease"
510
+ done
511
+ find "$work_dir/p" -mindepth 2 -maxdepth 2 -name used -type f -mtime +"$copy_keep_days" 2> /dev/null | while IFS= read -r used_file; do
512
+ old_copy=$(dirname "$used_file")
513
+ [ "$(basename "$old_copy")" = "$keep_slug" ] && continue
514
+ [ -e "$old_copy/lock" ] && continue
515
+ rm -rf "$old_copy"
516
+ done
517
+ }
518
+
519
+ cmd_prune() {
520
+ prune_work "$1" ""
521
+ }
522
+
523
+ # ─────────────────────────── adding the job to Pueue ───────────────────────────
524
+
525
+ # Quote one word for a shell (single quotes).
526
+ quote_word() {
527
+ printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"
528
+ }
529
+
530
+ add_job() {
531
+ local group=$1
532
+ local script_path
533
+ script_path=$(cd "$(dirname "$0")" && pwd)/$(basename "$0")
534
+ local command_line
535
+ command_line="exec bash $(quote_word "$script_path") job $(quote_word "$work") $(quote_word "$slug") $(quote_word "$job")"
536
+ local task
537
+ # The job's time limit counts from here (a foreground run's own timer does too): a job that waited its whole limit in the queue is
538
+ # over when it starts (cmd_job), not given the limit again.
539
+ date +%s > "$jd/queued-at"
540
+ task=$(pq add --print-task-id -g "$group" -l "rr-$job" -w "$proj" -- "$command_line") || return 1
541
+ printf '%s\n' "$task" > "$jd/task-id"
542
+ }
543
+
544
+ cmd_submit() {
545
+ set_paths "$1" "$2" "$3"
546
+ add_job "${4:-default}" || die "pueue add failed (does the group \"${4:-default}\" exist on this runner?)"
547
+ echo queued
548
+ }
549
+
550
+ # ─────────────────────────── killing ───────────────────────────
551
+
552
+ # Signal the whole process group of the job (the script, its command and everything the command started). Pueue runs a task as
553
+ # `sh -c <command>` in a process group of its own, and the command line of add_job starts with `exec`, so the job script IS that
554
+ # group's leader: a group-wide signal has no wrapper to end first (which made Pueue call the task done while the job still ran).
555
+ # The group is read from the LIVE job process at the moment of use and signalled only while the job leads its own group: a stale id,
556
+ # or a Pueue that stopped isolating its tasks, can never make this hit another group (pueued's, the caller's).
557
+ signal_job_group() {
558
+ local job_pid group_id
559
+ job_pid=$(read_file "$jd/pid")
560
+ [ -n "$job_pid" ] && kill -0 "$job_pid" 2> /dev/null || return 1
561
+ group_id=$(ps -o pgid= -p "$job_pid" 2> /dev/null | tr -d ' ')
562
+ [ -n "$group_id" ] && [ "$group_id" = "$job_pid" ] || return 1
563
+ kill "-$1" -- "-$group_id" 2> /dev/null
564
+ }
565
+
566
+ # Stop a job: a queued one is removed, a running one gets SIGTERM (its whole group) and, after the grace time, Pueue's own kill of the
567
+ # whole task tree. $1 says why: `timeout` when the caller's own time limit ran out (recorded like the job's own watchdog does it — 124,
568
+ # "timed out" — whichever of the two gets there first), anything else is a kill (137).
569
+ kill_job() {
570
+ local task
571
+ task=$(read_file "$jd/task-id")
572
+ [ -n "$task" ] || return 1
573
+ [ -f "$jd/exit" ] && return 0
574
+ if [ "${1:-}" = timeout ]; then
575
+ touch "$jd/timeout"
576
+ else
577
+ touch "$jd/killed"
578
+ fi
579
+ if [ ! -f "$jd/pid" ] && pq remove "$task" > /dev/null 2>&1; then
580
+ # Pueue gives a number it has freed to the next task it is asked to add: from here on `task-id` is a number that may belong to another
581
+ # job, so nothing may act on it any more (`prune_work` skips such a job's task; waiting is decided by the exit file below).
582
+ touch "$jd/task-removed"
583
+ finish_killed_job
584
+ return 0
585
+ fi
586
+ signal_job_group TERM || pq kill "$task" > /dev/null 2>&1
587
+ local started=$SECONDS
588
+ while [ $((SECONDS - started)) -lt "$kill_grace_sec" ]; do
589
+ check_job_ended && return 0
590
+ sleep 0.2
591
+ done
592
+ hard_kill_job "$task"
593
+ sleep 1
594
+ return 0
595
+ }
596
+
597
+ # SIGKILL of the whole group; Pueue's own kill follows only so that its task list agrees. Without a verifiable group, Pueue's kill
598
+ # is all there is. A job that is SIGKILLed cannot write its own result, so the result is recorded FIRST: when the caller is the job's own
599
+ # watchdog the group-wide SIGKILL takes the watchdog down with the job.
600
+ hard_kill_job() {
601
+ finish_killed_job
602
+ signal_job_group KILL || pq kill "$1" > /dev/null 2>&1
603
+ pq kill "$1" > /dev/null 2>&1
604
+ return 0
605
+ }
606
+
607
+ # A job that was killed (never started and removed from the queue, or SIGKILLed after it ignored SIGTERM): its result is written the way a
608
+ # finished job writes it — 124 when its time limit did it, 137 otherwise — so it is an ENDED job: `wait` and `ls` read a code, the next job
609
+ # of the project takes over its lock at once, and prune removes it after 3 days like any other.
610
+ finish_killed_job() {
611
+ [ -f "$jd/exit" ] && return 0
612
+ local code=137
613
+ [ -f "$jd/timeout" ] && code=124
614
+ rm -rf "$stage"
615
+ printf '%s\n' done > "$jd/phase"
616
+ date +%s > "$jd/finished-at"
617
+ printf '%s\n' "$code" > "$jd/exit.tmp" && mv "$jd/exit.tmp" "$jd/exit"
618
+ }
619
+
620
+ # Ended = it wrote its exit file, or its process is gone.
621
+ check_job_ended() {
622
+ [ -f "$jd/exit" ] && return 0
623
+ local pid
624
+ pid=$(read_file "$jd/pid")
625
+ [ -n "$pid" ] && ! kill -0 "$pid" 2> /dev/null && return 0
626
+ return 1
627
+ }
628
+
629
+ # Prints `killed`, or `already-ended <code>` for a job that had finished before the kill came (nothing is killed then, and the caller
630
+ # must not be told it was).
631
+ cmd_kill() {
632
+ set_paths "$1" "$2" "$3"
633
+ if [ -n "$(read_file "$jd/task-id")" ] && [ -f "$jd/exit" ]; then
634
+ echo "already-ended $(read_file "$jd/exit")"
635
+ return 0
636
+ fi
637
+ kill_job "${4:-}" || die "no such job: $job"
638
+ echo killed
639
+ }
640
+
641
+ # ─────────────────────────── log streaming ───────────────────────────
642
+
643
+ # Print what the log gained since $log_offset, and move the offset.
644
+ drain_log() {
645
+ local size
646
+ size=$(wc -c < "$jd/log" 2> /dev/null | tr -d ' ')
647
+ [ -n "$size" ] || return 0
648
+ if [ "$size" -gt "$log_offset" ]; then
649
+ tail -c +"$((log_offset + 1))" "$jd/log" | head -c "$((size - log_offset))"
650
+ log_offset=$size
651
+ fi
652
+ }
653
+
654
+ # Stream the log until the file $jd/drain-stop appears, then print the rest. Runs as a background job.
655
+ stream_log() {
656
+ log_offset=${1:-0}
657
+ while [ ! -f "$jd/drain-stop" ]; do
658
+ drain_log
659
+ sleep "$drain_interval_sec"
660
+ done
661
+ drain_log
662
+ }
663
+
664
+ # Return when the job is over: Pueue says its task is done, or the job's exit file is there — whichever comes first, and at once if it
665
+ # already is. The exit file is the job's own record. The number of a task that was removed (a queued job that was killed) can be given to
666
+ # another job, and `pueue wait` on it would then follow THAT job for as long as it runs (hours, behind a busy group), or return at once
667
+ # for a number nobody holds: so the wait is cut as soon as the exit file appears, and the number is never trusted alone.
668
+ #
669
+ # The Pueue wait runs in a process group of its own (`set -m`, as `exec` does) and the cut signals the GROUP. `pq` is a function, so the
670
+ # background job is a subshell and `pueue wait` its child — unless the bash happens to run it in place of the subshell (5.3 can; 5.2, the
671
+ # bash of Ubuntu 24.04, does not). A kill of the subshell alone left `pueue wait` running for as long as the task, however many times
672
+ # the exit file won the race. Under job control bash no longer gives a background job /dev/null as stdin, so it is given here: in a
673
+ # lease the caller's stdin is the heartbeat stream, which only the lease watcher (fd 3) may read.
674
+ wait_for_task() {
675
+ [ -f "$jd/exit" ] && return 0
676
+ set -m
677
+ pq wait "$1" < /dev/null > /dev/null 2>&1 &
678
+ local waiter_pid=$!
679
+ set +m
680
+ while kill -0 "$waiter_pid" 2> /dev/null; do
681
+ if [ -f "$jd/exit" ]; then
682
+ # The group, or the process alone when it did not get a group of its own: a group id that is not a live group is harmless.
683
+ kill -- "-$waiter_pid" > /dev/null 2>&1 || kill "$waiter_pid" > /dev/null 2>&1
684
+ break
685
+ fi
686
+ sleep 0.2
687
+ done
688
+ { wait "$waiter_pid"; } 2> /dev/null
689
+ return 0
690
+ }
691
+
692
+ # Stream the log of the task until Pueue reports it done.
693
+ follow_until_done() {
694
+ rm -f "$jd/drain-stop"
695
+ stream_log "$1" &
696
+ local streamer_pid=$!
697
+ local task
698
+ task=$(read_file "$jd/task-id")
699
+ [ -n "$task" ] && wait_for_task "$task"
700
+ touch "$jd/drain-stop"
701
+ wait "$streamer_pid" 2> /dev/null
702
+ }
703
+
704
+ # Where `follow` starts in the log: the beginning, the end, or the last N lines.
705
+ resolve_follow_offset() {
706
+ local size
707
+ size=$(wc -c < "$jd/log" 2> /dev/null | tr -d ' ')
708
+ size=${size:-0}
709
+ case ${1:-start} in
710
+ end) echo "$size" ;;
711
+ tail:*)
712
+ local tail_bytes
713
+ tail_bytes=$(tail -n "${1#tail:}" "$jd/log" 2> /dev/null | wc -c | tr -d ' ')
714
+ echo $((size - tail_bytes))
715
+ ;;
716
+ *) echo 0 ;;
717
+ esac
718
+ }
719
+
720
+ cmd_follow() {
721
+ set_paths "$1" "$2" "$3"
722
+ local from_offset
723
+ from_offset=$(resolve_follow_offset "${4:-start}")
724
+ follow_until_done "$from_offset"
725
+ }
726
+
727
+ # After Pueue says a task is done, its own last file may still be a moment away: wait for the exit file, or for the job process to
728
+ # be gone (killed with SIGKILL, it never writes one), at most five seconds.
729
+ wait_for_exit_file() {
730
+ local started=$SECONDS
731
+ while [ ! -f "$jd/exit" ] && [ $((SECONDS - started)) -lt "$exit_file_wait_sec" ]; do
732
+ check_job_ended && return 0
733
+ sleep 0.2
734
+ done
735
+ }
736
+
737
+ cmd_wait() {
738
+ set_paths "$1" "$2" "$3"
739
+ local task
740
+ task=$(read_file "$jd/task-id")
741
+ [ -n "$task" ] || die "no such job: $job"
742
+ wait_for_task "$task"
743
+ wait_for_exit_file
744
+ echo done
745
+ }
746
+
747
+ # The facts of a job, one key=value per line (parsed by jobState.ts).
748
+ print_job_facts() {
749
+ echo "slug=$slug"
750
+ echo "exit=$(read_file "$jd/exit")"
751
+ echo "phase=$(read_file "$jd/phase")"
752
+ echo "task=$(read_file "$jd/task-id")"
753
+ echo "started=$(read_file "$jd/started-at")"
754
+ echo "finished=$(read_file "$jd/finished-at")"
755
+ [ -f "$jd/timeout" ] && echo "timeout=1" || echo "timeout=0"
756
+ [ -f "$jd/killed" ] && echo "killed=1" || echo "killed=0"
757
+ [ -f "$jd/pid" ] && echo "has_pid=1" || echo "has_pid=0"
758
+ [ -d "$jd/artifacts" ] && echo "artifacts=1" || echo "artifacts=0"
759
+ echo "failed_step=$(read_file "$jd/failed-step")"
760
+ }
761
+
762
+ cmd_result() {
763
+ set_paths "$1" "$2" "$3"
764
+ [ -d "$jd" ] || die "no such job: $job"
765
+ print_job_facts
766
+ }
767
+
768
+ cmd_meta() {
769
+ set_paths "$1" "$2" "$3"
770
+ cat "$jd/meta.json" 2> /dev/null || die "no such job: $job"
771
+ }
772
+
773
+ # The last N lines of a job's log (all of it when N is "all").
774
+ cmd_tail() {
775
+ set_paths "$1" "$2" "$3"
776
+ if [ "${4:-all}" = all ]; then cat "$jd/log" 2> /dev/null; else tail -n "$4" "$jd/log" 2> /dev/null; fi
777
+ }
778
+
779
+ # ─────────────────────────── the lease ───────────────────────────
780
+
781
+ # The caller writes one byte every few seconds. No byte for the lease time, or the end of its stream, means the caller is gone: the job
782
+ # is killed. A caller that finishes normally has no use for the lease any more because the job it guarded is over (the lease script
783
+ # stops its watcher when the job ends), and one that wants the job stopped says so with `kill` (an interrupted `exec` ends its stream on purpose, to have its command stopped).
784
+ # A stock sshd never notices a half-open connection, so this is the only thing that stops an orphaned job. The stream is the caller's,
785
+ # on fd 3 (a background job gets /dev/null as stdin, so the callers keep theirs there). A reader process does plain blocking reads and
786
+ # records each byte's arrival time; this loop compares the clock with it once a second — no `read -t`, whose exit status for a timeout
787
+ # is not the same in bash 3.2 (macOS) and bash 4+, and which would make a quiet second look like a closed connection.
788
+ watch_lease() {
789
+ local lease_sec=$1
790
+ lease_state_dir=
791
+ lease_reader_pid=
792
+ # The traps first: a command that ends at once gets the watcher a SIGTERM before it has done anything, and whatever it created by
793
+ # then must go with it. The name is made here, not by a command substitution, so there is no instant with a dir nobody knows of.
794
+ trap 'end_lease_watch' EXIT
795
+ trap 'exit 143' TERM
796
+ lease_state_dir="$work/tmp/lease.$$.$RANDOM"
797
+ mkdir -p "$lease_state_dir" || return 1
798
+ write_lease_beat
799
+ (
800
+ byte=
801
+ while IFS= read -r -n 1 byte; do
802
+ write_lease_beat
803
+ done
804
+ : > "$lease_state_dir/eof"
805
+ ) <&3 &
806
+ lease_reader_pid=$!
807
+ local verdict=0
808
+ local last_beat
809
+ while :; do
810
+ if [ -f "$lease_state_dir/eof" ]; then
811
+ verdict=1
812
+ break
813
+ fi
814
+ last_beat=$(cat "$lease_state_dir/beat" 2> /dev/null)
815
+ case $last_beat in
816
+ '' | *[!0-9]*) ;;
817
+ *) [ $(($(date +%s) - last_beat)) -ge "$lease_sec" ] && { verdict=1; break; } ;;
818
+ esac
819
+ sleep "$heartbeat_poll_sec"
820
+ done
821
+ end_lease_watch
822
+ return "$verdict"
823
+ }
824
+
825
+ # The time of the latest heartbeat, replaced atomically: a plain `> beat` truncates the file first, and a watcher that read it in
826
+ # that instant got an empty string, whose arithmetic is a syntax error that ended the watcher without a verdict.
827
+ write_lease_beat() {
828
+ date +%s > "$lease_state_dir/beat.new" && mv -f "$lease_state_dir/beat.new" "$lease_state_dir/beat"
829
+ }
830
+
831
+ end_lease_watch() {
832
+ [ -n "${lease_reader_pid:-}" ] && kill "$lease_reader_pid" > /dev/null 2>&1
833
+ [ -n "${lease_state_dir:-}" ] && rm -rf "$lease_state_dir"
834
+ }
835
+
836
+ ms_to_sec() {
837
+ awk -v ms="$1" 'BEGIN { s = int((ms + 999) / 1000); if (s < 2) s = 2; print s }'
838
+ }
839
+
840
+ cmd_lease() {
841
+ set_paths "$1" "$2" "$3"
842
+ local group=$4
843
+ local lease_sec
844
+ lease_sec=$(ms_to_sec "$5")
845
+ # A background job gets /dev/null as stdin unless job control is on: keep the caller's stream on fd 3 for the watcher.
846
+ exec 3<&0
847
+ add_job "$group" || die "pueue add failed (does the group \"$group\" exist on this runner?)"
848
+ rm -f "$jd/drain-stop"
849
+ stream_log 0 &
850
+ local streamer_pid=$!
851
+ (
852
+ watch_lease "$lease_sec" || kill_job
853
+ ) &
854
+ local watcher_pid=$!
855
+ local task
856
+ task=$(read_file "$jd/task-id")
857
+ wait_for_task "$task"
858
+ kill "$watcher_pid" > /dev/null 2>&1
859
+ wait_for_exit_file
860
+ touch "$jd/drain-stop"
861
+ wait "$streamer_pid" 2> /dev/null
862
+ }
863
+
864
+ # ─────────────────────────── the job (the Pueue task) ───────────────────────────
865
+
866
+ lock_held=0
867
+
868
+ # The project lock is a FILE made with the shell's own exclusive create (`set -C`, noclobber: open(O_EXCL)) — never with `mkdir`. The uutils
869
+ # coreutils that current Ubuntu ships as /usr/bin/mkdir let several simultaneous `mkdir` of one path ALL succeed (measured here: 265 races
870
+ # of 1200 with five contenders), which gave two jobs of one project the lock together. The file holds the holder's pid and job, two lines,
871
+ # written by the very redirection that creates it. Succeeds only for the one that created the file.
872
+ create_lock_file() {
873
+ ( set -C; printf '%s\n%s\n' "$$" "$job" > "$1" ) 2> /dev/null
874
+ }
875
+
876
+ # Read the lock file $1 into holder_pid and holder_job (empty when it is missing or has no holder written yet).
877
+ read_lock_holder() {
878
+ holder_pid=
879
+ holder_job=
880
+ { IFS= read -r holder_pid; IFS= read -r holder_job; } 2> /dev/null < "$1"
881
+ return 0
882
+ }
883
+
884
+ # Is the lock held by nobody any more? Its holder's process is gone, or its job has an exit record, or no holder was ever written and it
885
+ # is over a minute old (its creator died between creating the file and writing it).
886
+ check_lock_is_stale() {
887
+ local holder_pid holder_job
888
+ read_lock_holder "$lock"
889
+ if [ -n "$holder_pid" ] && ! kill -0 "$holder_pid" 2> /dev/null; then
890
+ return 0
891
+ fi
892
+ if [ -n "$holder_job" ] && [ -f "$jobs_dir/$holder_job/exit" ]; then
893
+ return 0
894
+ fi
895
+ [ -z "$holder_pid" ] && [ -n "$(find "$lock" -maxdepth 0 -mmin +1 2> /dev/null)" ]
896
+ }
897
+
898
+ # Break a stale lock — by ONE waiter at a time, and only the lock of the very holder that was seen dead. Several waiters can see the same
899
+ # dead holder at once; if each simply removed the lock, a slow one could remove the lock a faster one had already re-created, and two jobs
900
+ # of a project would run on one tree. So the RIGHT to break the lock of holder (pid, job) is a file named for that holder, made with the
901
+ # exclusive create: of the waiters that saw it, one wins and the others yield. That holder can never hold the lock again (its process is
902
+ # gone, its job id is unique), so once the winner has seen the lock still carries that holder, nothing but the winner can remove it, and a
903
+ # lock some other job has taken since is not that holder's and is left alone. Returns 1 when another waiter is at it.
904
+ break_stale_lock() {
905
+ local holder_pid holder_job
906
+ read_lock_holder "$lock"
907
+ check_lock_is_stale || return 0
908
+ break_lock_of_holder "$holder_pid" "$holder_job"
909
+ }
910
+
911
+ # The right to break the lock of holder $1/$2 (pid, job): the file `$lock.breaking.<pid>.<job>.<generation>`. A winner that died leaves its
912
+ # file behind, held by a process that is gone: the next waiter takes the next generation of the name — and again only one of them wins it.
913
+ # A name is made of the holder's own words only (a lock file with something else in it must not name a path).
914
+ break_lock_of_holder() {
915
+ local stale_pid=$1 stale_job=$2
916
+ case $stale_pid in *[!0-9]*) stale_pid= ;; esac
917
+ case $stale_job in *[!A-Za-z0-9._-]*) stale_job= ;; esac
918
+ local generation=0 right
919
+ while :; do
920
+ right="$lock.breaking.${stale_pid:-none}.${stale_job:-none}.$generation"
921
+ create_lock_file "$right" && break
922
+ check_breaker_is_alive "$right" && return 1
923
+ generation=$((generation + 1))
924
+ done
925
+ # The lock must carry that holder AND still be stale. For a named holder the second test adds nothing (it is dead for good). For a lock
926
+ # with no holder written it is what matters: every fresh lock is empty for a moment between its creation and the write of its pid, so
927
+ # "no holder" names no one in particular — only the over-a-minute test tells a creator that died from one that is about to write.
928
+ local holder_pid holder_job
929
+ read_lock_holder "$lock"
930
+ if [ "$holder_pid" = "$1" ] && [ "$holder_job" = "$2" ] && check_lock_is_stale; then
931
+ rm -f "$lock"
932
+ fi
933
+ return 0
934
+ }
935
+
936
+ # Is the breaker that made the right file $1 still at it? Its process is alive — the test the lock itself uses, so a breaker that is merely
937
+ # slow (a busy disk) keeps its right however long it takes — and its job has no exit record (the lock's second test: a breaker that died
938
+ # and whose pid went to some long-lived process would otherwise hold its right for ever, and the project's queue would never drain). A file
939
+ # with no process written yet is a breaker between creating it and writing it: it counts until it is over a minute old.
940
+ check_breaker_is_alive() {
941
+ local holder_pid holder_job
942
+ read_lock_holder "$1"
943
+ if [ -n "$holder_job" ] && [ -f "$jobs_dir/$holder_job/exit" ]; then
944
+ return 1
945
+ fi
946
+ if [ -n "$holder_pid" ]; then
947
+ kill -0 "$holder_pid" 2> /dev/null
948
+ return
949
+ fi
950
+ [ -z "$(find "$1" -maxdepth 0 -mmin +1 2> /dev/null)" ]
951
+ }
952
+
953
+ acquire_lock() {
954
+ local noticed=0
955
+ local holder_pid holder_job
956
+ while ! create_lock_file "$lock"; do
957
+ [ "$killed" -eq 1 ] && return 1
958
+ if check_lock_is_stale && break_stale_lock; then
959
+ continue
960
+ fi
961
+ if [ "$noticed" -eq 0 ]; then
962
+ read_lock_holder "$lock"
963
+ say "waiting for job ${holder_job:-another job} of this project"
964
+ noticed=1
965
+ fi
966
+ nap "$lock_poll_sec"
967
+ done
968
+ lock_held=1
969
+ # The rights to break the lock name holders that are gone and can never hold it again: nothing needs them any more.
970
+ rm -f "$lock".breaking.* 2> /dev/null
971
+ return 0
972
+ }
973
+
974
+ # Give the lock back — only if it is still ours (a lock that was broken as stale and taken by another job is not ours to remove).
975
+ release_lock() {
976
+ if [ "$lock_held" -eq 1 ]; then
977
+ local holder_pid holder_job
978
+ read_lock_holder "$lock"
979
+ if [ "$holder_pid" = "$$" ] && [ "$holder_job" = "$job" ]; then
980
+ rm -f "$lock"
981
+ fi
982
+ fi
983
+ lock_held=0
984
+ }
985
+
986
+ remove_tree_file() {
987
+ check_relative_path "$1" || return 0
988
+ rm -f "$tree/$1"
989
+ local dir
990
+ dir=$(dirname "$1")
991
+ while [ "$dir" != "." ] && rmdir "$tree/$dir" 2> /dev/null; do
992
+ dir=$(dirname "$dir")
993
+ done
994
+ }
995
+
996
+ # The apply step: delete what the previous submit had and this one has not, copy the staged files over, keep this submit's list.
997
+ sync_tree() {
998
+ mkdir -p "$tree"
999
+ if [ -f "$proj/manifest" ]; then
1000
+ sort "$proj/manifest" > "$jd/manifest.old.sorted"
1001
+ sort "$jd/manifest" > "$jd/manifest.new.sorted"
1002
+ comm -23 "$jd/manifest.old.sorted" "$jd/manifest.new.sorted" | while IFS= read -r removed_file; do
1003
+ remove_tree_file "$removed_file"
1004
+ done
1005
+ fi
1006
+ # --modify-window=-1: compare the mtimes to the nanosecond too — a same-length edit within one second of the previous version is
1007
+ # otherwise "unchanged" (rsync's quick check) and the copy would keep the old file.
1008
+ rsync -a --modify-window=-1 --exclude=/.git "$stage/" "$tree/" || return 1
1009
+ if [ -d "$stage/.git" ]; then
1010
+ mkdir -p "$tree/.git"
1011
+ rsync -a --modify-window=-1 --delete "$stage/.git/" "$tree/.git/" || return 1
1012
+ else
1013
+ # `.git` only with --with-git: a copy that got one from an earlier run must not keep serving its stale history.
1014
+ rm -rf "$tree/.git"
1015
+ fi
1016
+ cp "$jd/manifest" "$proj/manifest"
1017
+ rm -rf "$stage"
1018
+ }
1019
+
1020
+ cmd_sync_tree() {
1021
+ set_paths "$1" "$2" "$3"
1022
+ sync_tree || die "apply failed"
1023
+ }
1024
+
1025
+ # bash -c <command> in <directory>, with the job's -e variables, output into the log; sets step_code. Runs as a child so a
1026
+ # SIGTERM to the job (the `killed` flag) does not abandon it half-way.
1027
+ run_step() {
1028
+ local directory=$1
1029
+ local command_text=$2
1030
+ (
1031
+ cd "$directory" || exit 126
1032
+ if [ -s "$jd/env" ]; then
1033
+ while IFS= read -r env_line; do export "$env_line"; done < "$jd/env"
1034
+ fi
1035
+ exec bash -c "$command_text"
1036
+ ) < /dev/null &
1037
+ local step_pid=$!
1038
+ keep_awake_while "$step_pid"
1039
+ # The notices bash prints when it reaps a signalled child ("Terminated") are not the job's output.
1040
+ { wait "$step_pid"; } 2> /dev/null
1041
+ step_code=$?
1042
+ while kill -0 "$step_pid" 2> /dev/null; do
1043
+ { wait "$step_pid"; } 2> /dev/null
1044
+ step_code=$?
1045
+ done
1046
+ }
1047
+
1048
+ # A sleep that a group-wide SIGTERM does not end (and bash does not report as "Terminated").
1049
+ nap() {
1050
+ ( trap '' TERM; exec sleep "$1" )
1051
+ }
1052
+
1053
+ # The timeout: from the job's start, covering the lock wait, the install and the command. SIGTERM to the job's group, SIGKILL after the
1054
+ # grace time (a process that ignores SIGTERM). It polls once a second for the cancel file instead of sleeping the whole limit: a
1055
+ # sleeper that a group-wide SIGTERM (a kill) ends would look like an expired limit, and a finished job needs no process to reap.
1056
+ start_watchdog() {
1057
+ local limit=$1
1058
+ local task
1059
+ task=$(read_file "$jd/task-id")
1060
+ (
1061
+ trap '' TERM
1062
+ local started=$SECONDS
1063
+ while [ $((SECONDS - started)) -lt "$limit" ]; do
1064
+ [ -f "$jd/watchdog-cancel" ] && exit 0
1065
+ nap 1
1066
+ done
1067
+ [ -f "$jd/watchdog-cancel" ] && exit 0
1068
+ touch "$jd/timeout"
1069
+ signal_job_group TERM || pq kill "$task" > /dev/null 2>&1
1070
+ local grace_started=$SECONDS
1071
+ while [ $((SECONDS - grace_started)) -lt "$kill_grace_sec" ]; do
1072
+ [ -f "$jd/watchdog-cancel" ] && exit 0
1073
+ nap 1
1074
+ done
1075
+ hard_kill_job "$task"
1076
+ ) > /dev/null 2>&1 &
1077
+ }
1078
+
1079
+ stop_watchdog() {
1080
+ touch "$jd/watchdog-cancel"
1081
+ }
1082
+
1083
+ # True when "$tree/$1" has no symlink in it, so a walk that starts there stays inside the copy (the walk of the whole copy never followed a
1084
+ # link either).
1085
+ check_has_no_symlink() {
1086
+ local parent physical
1087
+ [ ! -L "$tree/$1" ] || return 1
1088
+ parent=$(dirname "$1")
1089
+ [ "$parent" = . ] && return 0
1090
+ physical=$(cd "$tree/$parent" 2> /dev/null && pwd -P) || return 1
1091
+ [ "$physical" = "$(cd "$tree" && pwd -P)/$parent" ]
1092
+ }
1093
+
1094
+ # Files the command created or changed that match pull.ere (one extended regular expression per line), kept per job. Only the folders in
1095
+ # pull.roots are walked (the fixed prefixes of the patterns, `e2e/artifacts`): the copy also holds node_modules and every build folder,
1096
+ # which a Gradle or Metro build fills with hundreds of thousands of files. `.` there (a pattern that starts with a wildcard) or no
1097
+ # pull.roots at all (an older main box) walks the whole copy.
1098
+ collect_artifacts() {
1099
+ [ -s "$jd/pull.ere" ] || return 0
1100
+ [ -f "$jd/start-marker" ] || return 0
1101
+ local roots=()
1102
+ local root
1103
+ if [ -s "$jd/pull.roots" ]; then
1104
+ while IFS= read -r root; do
1105
+ [ -n "$root" ] || continue
1106
+ if [ "$root" = . ]; then
1107
+ roots=(.)
1108
+ break
1109
+ fi
1110
+ check_relative_path "$root" && [ -e "$tree/$root" ] && check_has_no_symlink "$root" && roots+=("$root")
1111
+ done < "$jd/pull.roots"
1112
+ [ "${#roots[@]}" -gt 0 ] || return 0
1113
+ else
1114
+ roots=(.)
1115
+ fi
1116
+ local patterns=()
1117
+ local pattern
1118
+ while IFS= read -r pattern; do
1119
+ [ -n "$pattern" ] && patterns+=("$pattern")
1120
+ done < "$jd/pull.ere"
1121
+ [ "${#patterns[@]}" -gt 0 ] || return 0
1122
+ local candidate relative
1123
+ while IFS= read -r -d '' candidate; do
1124
+ relative=${candidate#./}
1125
+ for pattern in "${patterns[@]}"; do
1126
+ if [[ $relative =~ $pattern ]]; then
1127
+ mkdir -p "$jd/artifacts/$(dirname "$relative")" && cp -p "$tree/$relative" "$jd/artifacts/$relative"
1128
+ break
1129
+ fi
1130
+ done
1131
+ done < <(cd "$tree" && find "${roots[@]}" -name .git -prune -o -type f -newer "$jd/start-marker" -print0 2> /dev/null)
1132
+ }
1133
+
1134
+ finish_job() {
1135
+ local code=$1
1136
+ if [ -f "$jd/timeout" ]; then
1137
+ code=124
1138
+ say "timed out after ${timeout_sec}s"
1139
+ elif [ "$killed" -eq 1 ]; then
1140
+ code=137
1141
+ say "job killed"
1142
+ fi
1143
+ stop_watchdog
1144
+ set_phase collecting
1145
+ collect_artifacts
1146
+ release_lock
1147
+ rm -rf "$stage"
1148
+ set_phase done
1149
+ date +%s > "$jd/finished-at"
1150
+ printf '%s\n' "$code" > "$jd/exit.tmp" && mv "$jd/exit.tmp" "$jd/exit"
1151
+ exit "$code"
1152
+ }
1153
+
1154
+ cmd_job() {
1155
+ set_paths "$1" "$2" "$3"
1156
+ exec >> "$jd/log" 2>&1
1157
+ printf '%s\n' "$$" > "$jd/pid"
1158
+ date +%s > "$jd/started-at"
1159
+ set_phase starting
1160
+ killed=0
1161
+ trap 'killed=1' TERM INT HUP
1162
+ # Pueue starts the task before the adder has written its id down.
1163
+ local id_wait_started=$SECONDS
1164
+ while [ ! -s "$jd/task-id" ] && [ $((SECONDS - id_wait_started)) -lt "$task_id_wait_sec" ]; do
1165
+ sleep 0.1
1166
+ done
1167
+ timeout_sec=$(read_file "$jd/timeout-sec")
1168
+ [ -n "$timeout_sec" ] || timeout_sec=$default_timeout_sec
1169
+ local limit_sec=$timeout_sec
1170
+ local queued_at
1171
+ queued_at=$(read_file "$jd/queued-at")
1172
+ case $queued_at in
1173
+ '' | *[!0-9]*) ;;
1174
+ *) limit_sec=$((timeout_sec - ($(date +%s) - queued_at))) ;;
1175
+ esac
1176
+ if [ "$limit_sec" -le 0 ]; then
1177
+ touch "$jd/timeout"
1178
+ say "its time limit ran out while it was queued"
1179
+ finish_job 124
1180
+ fi
1181
+ start_watchdog "$limit_sec"
1182
+
1183
+ set_phase waiting-lock
1184
+ acquire_lock || finish_job 137
1185
+
1186
+ set_phase syncing
1187
+ sync_tree || { say "applying the files failed"; finish_job 125; }
1188
+
1189
+ local subfolder
1190
+ subfolder=$(read_file "$jd/subfolder")
1191
+ check_relative_path "$subfolder" || { say "bad subfolder"; finish_job 125; }
1192
+ [ "$killed" -eq 1 ] && finish_job 137
1193
+
1194
+ local install_command install_key
1195
+ install_command=$(read_file "$jd/install-command")
1196
+ install_key=$(read_file "$jd/install-key")
1197
+ if [ -n "$install_command" ] && [ "$(read_file "$proj/deps.hash")" != "$install_key" ]; then
1198
+ set_phase deps
1199
+ say "installing dependencies: $install_command"
1200
+ run_step "$tree" "$install_command"
1201
+ if [ "$step_code" -ne 0 ]; then
1202
+ rm -f "$proj/deps.hash"
1203
+ echo deps > "$jd/failed-step"
1204
+ say "the dependencies step failed with exit code $step_code"
1205
+ finish_job 125
1206
+ fi
1207
+ printf '%s\n' "$install_key" > "$proj/deps.hash"
1208
+ fi
1209
+ [ "$killed" -eq 1 ] && finish_job 137
1210
+
1211
+ touch "$jd/start-marker"
1212
+ set_phase running
1213
+ run_step "$tree/$subfolder" "$(read_file "$jd/cmd")"
1214
+ finish_job "$step_code"
1215
+ }
1216
+
1217
+ # ─────────────────────────── exec ───────────────────────────
1218
+
1219
+ cmd_exec() {
1220
+ set_paths "$1" "$2" ""
1221
+ local subfolder=$3
1222
+ local lease_sec
1223
+ lease_sec=$(ms_to_sec "$4")
1224
+ shift 4
1225
+ check_relative_path "$subfolder" || die "bad subfolder"
1226
+ [ -d "$tree/$subfolder" ] || die "the runner has no copy of this project yet (run a command first)"
1227
+ exec 3<&0
1228
+ # Own process group, so the lease can stop the command and everything it started.
1229
+ set -m
1230
+ ( cd "$tree/$subfolder" && exec bash -c "$*" ) < /dev/null &
1231
+ local exec_pid=$!
1232
+ keep_awake_while "$exec_pid"
1233
+ set +m
1234
+ (
1235
+ if ! watch_lease "$lease_sec"; then
1236
+ kill -TERM -- "-$exec_pid" 2> /dev/null
1237
+ local grace_started=$SECONDS
1238
+ while kill -0 "$exec_pid" 2> /dev/null && [ $((SECONDS - grace_started)) -lt "$kill_grace_sec" ]; do
1239
+ sleep 0.2
1240
+ done
1241
+ kill -KILL -- "-$exec_pid" 2> /dev/null
1242
+ fi
1243
+ ) &
1244
+ local watcher_pid=$!
1245
+ wait "$exec_pid"
1246
+ local exec_code=$?
1247
+ kill "$watcher_pid" > /dev/null 2>&1
1248
+ # The command's own code, said out loud: ssh reports 255 for ITS failures, and a command that really exits 255 (ssh and adb do) would
1249
+ # look like a lost connection. The marker (RS, the name, the code, a newline) is the last thing on stderr; the CLI takes it out of what it
1250
+ # shows. A session that dies before it has none, which is how a lost connection is told from a command that exited 255.
1251
+ printf '\036remote-run-exit:%s\n' "$exec_code" >&2
1252
+ exit "$exec_code"
1253
+ }
1254
+
1255
+ # ─────────────────────────── jobs / pueue passthrough ───────────────────────────
1256
+
1257
+ # One JSON object per job: {"id", "facts": "k=v;k=v", "meta": <meta.json>} — everything `ls` and `status` show, in one call.
1258
+ cmd_jobs() {
1259
+ local work_dir=$1
1260
+ local only_slug=${2:-}
1261
+ local first=1
1262
+ local meta job_dir
1263
+ printf '['
1264
+ for meta in "$work_dir"/p/${only_slug:-*}/jobs/*/meta.json; do
1265
+ [ -f "$meta" ] || continue
1266
+ job_dir=$(dirname "$meta")
1267
+ set_paths "$work_dir" "$(basename "$(dirname "$(dirname "$job_dir")")")" "$(basename "$job_dir")"
1268
+ [ "$first" -eq 1 ] || printf ','
1269
+ first=0
1270
+ printf '{"id":"%s","facts":"%s","meta":' "$job" "$(print_job_facts | tr '\n' ';')"
1271
+ cat "$meta"
1272
+ printf '}'
1273
+ done
1274
+ printf ']\n'
1275
+ }
1276
+
1277
+ # The work dir as an absolute path (a `~/…` the caller configured is expanded by the shell before it gets here).
1278
+ cmd_workdir() {
1279
+ mkdir -p "$1" && (cd "$1" && pwd -P)
1280
+ }
1281
+
1282
+ cmd_pueue() {
1283
+ shift
1284
+ pq "$@"
1285
+ }
1286
+
1287
+ # ─────────────────────────── dispatch ───────────────────────────
1288
+
1289
+ main() {
1290
+ local subcommand=${1:-}
1291
+ [ $# -gt 0 ] && shift
1292
+ case $subcommand in
1293
+ probe) cmd_probe "$@" ;;
1294
+ prepare) cmd_prepare "$@" ;;
1295
+ lease) cmd_lease "$@" ;;
1296
+ submit) cmd_submit "$@" ;;
1297
+ job) cmd_job "$@" ;;
1298
+ follow) cmd_follow "$@" ;;
1299
+ wait) cmd_wait "$@" ;;
1300
+ result) cmd_result "$@" ;;
1301
+ meta) cmd_meta "$@" ;;
1302
+ tail) cmd_tail "$@" ;;
1303
+ kill) cmd_kill "$@" ;;
1304
+ exec) cmd_exec "$@" ;;
1305
+ pueue) cmd_pueue "$@" ;;
1306
+ workdir) cmd_workdir "$@" ;;
1307
+ jobs) cmd_jobs "$@" ;;
1308
+ prune) cmd_prune "$@" ;;
1309
+ sync-tree) cmd_sync_tree "$@" ;;
1310
+ *) die "unknown subcommand: $subcommand" ;;
1311
+ esac
1312
+ }
1313
+
1314
+ # Run as a script it dispatches; sourced (the unit tests call the functions one by one) it only defines them.
1315
+ if [ "${BASH_SOURCE[0]}" = "$0" ]; then
1316
+ main "$@"
1317
+ fi