charness 3.7.0 → 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.
- package/CHANGELOG.md +30 -0
- package/DEVELOPMENT.md +6 -2
- package/README.md +5 -5
- package/SETUP.md +16 -5
- package/agent-plugin/skills/charness-manager/SKILL.md +3 -3
- package/dist/adapters/openCodeAdapter.d.ts.map +1 -1
- package/dist/adapters/openCodeAdapter.js +2 -1
- package/dist/adapters/openCodeAdapter.js.map +1 -1
- package/dist/bot.d.ts.map +1 -1
- package/dist/bot.js +50 -9
- package/dist/bot.js.map +1 -1
- package/dist/cli.js +44 -1
- package/dist/cli.js.map +1 -1
- package/dist/configDirFile.d.ts +7 -3
- package/dist/configDirFile.d.ts.map +1 -1
- package/dist/configDirFile.js +12 -5
- package/dist/configDirFile.js.map +1 -1
- package/dist/connectors/telegram/commands/botCore.d.ts +5 -1
- package/dist/connectors/telegram/commands/botCore.d.ts.map +1 -1
- package/dist/connectors/telegram/commands/runners.d.ts +52 -0
- package/dist/connectors/telegram/commands/runners.d.ts.map +1 -0
- package/dist/connectors/telegram/commands/runners.js +486 -0
- package/dist/connectors/telegram/commands/runners.js.map +1 -0
- package/dist/connectors/telegram/runnerPairingMessages.d.ts +27 -0
- package/dist/connectors/telegram/runnerPairingMessages.d.ts.map +1 -0
- package/dist/connectors/telegram/runnerPairingMessages.js +105 -0
- package/dist/connectors/telegram/runnerPairingMessages.js.map +1 -0
- package/dist/i18n/de.d.ts.map +1 -1
- package/dist/i18n/de.js +58 -0
- package/dist/i18n/de.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +58 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +58 -0
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/fr.d.ts.map +1 -1
- package/dist/i18n/fr.js +58 -0
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/hi.d.ts.map +1 -1
- package/dist/i18n/hi.js +58 -0
- package/dist/i18n/hi.js.map +1 -1
- package/dist/i18n/ja.d.ts.map +1 -1
- package/dist/i18n/ja.js +58 -0
- package/dist/i18n/ja.js.map +1 -1
- package/dist/i18n/ka.d.ts.map +1 -1
- package/dist/i18n/ka.js +58 -0
- package/dist/i18n/ka.js.map +1 -1
- package/dist/i18n/pt.d.ts.map +1 -1
- package/dist/i18n/pt.js +58 -0
- package/dist/i18n/pt.js.map +1 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +58 -0
- package/dist/i18n/ru.js.map +1 -1
- package/dist/i18n/uk.d.ts.map +1 -1
- package/dist/i18n/uk.js +58 -0
- package/dist/i18n/uk.js.map +1 -1
- package/dist/i18n/uz.d.ts.map +1 -1
- package/dist/i18n/uz.js +58 -0
- package/dist/i18n/uz.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +58 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/installManager.d.ts +57 -1
- package/dist/installManager.d.ts.map +1 -1
- package/dist/installManager.js +88 -10
- package/dist/installManager.js.map +1 -1
- package/dist/instanceSettings.d.ts +12 -2
- package/dist/instanceSettings.d.ts.map +1 -1
- package/dist/instanceSettings.js +17 -3
- package/dist/instanceSettings.js.map +1 -1
- package/dist/outputTrace.d.ts +6 -0
- package/dist/outputTrace.d.ts.map +1 -1
- package/dist/outputTrace.js +24 -1
- package/dist/outputTrace.js.map +1 -1
- package/dist/remoteRun/cliArgs.d.ts +2 -0
- package/dist/remoteRun/cliArgs.d.ts.map +1 -1
- package/dist/remoteRun/cliArgs.js +11 -2
- package/dist/remoteRun/cliArgs.js.map +1 -1
- package/dist/remoteRun/commands.d.ts +21 -0
- package/dist/remoteRun/commands.d.ts.map +1 -1
- package/dist/remoteRun/commands.js +38 -17
- package/dist/remoteRun/commands.js.map +1 -1
- package/dist/remoteRun/interrupts.d.ts +10 -0
- package/dist/remoteRun/interrupts.d.ts.map +1 -1
- package/dist/remoteRun/interrupts.js +43 -6
- package/dist/remoteRun/interrupts.js.map +1 -1
- package/dist/remoteRun/output.d.ts +2 -0
- package/dist/remoteRun/output.d.ts.map +1 -1
- package/dist/remoteRun/output.js +45 -0
- package/dist/remoteRun/output.js.map +1 -1
- package/dist/remoteRun/pairedRunners.d.ts +51 -0
- package/dist/remoteRun/pairedRunners.d.ts.map +1 -0
- package/dist/remoteRun/pairedRunners.js +165 -0
- package/dist/remoteRun/pairedRunners.js.map +1 -0
- package/dist/remoteRun/probe.d.ts +23 -1
- package/dist/remoteRun/probe.d.ts.map +1 -1
- package/dist/remoteRun/probe.js +45 -11
- package/dist/remoteRun/probe.js.map +1 -1
- package/dist/remoteRun/remoteRunCli.d.ts +13 -6
- package/dist/remoteRun/remoteRunCli.d.ts.map +1 -1
- package/dist/remoteRun/remoteRunCli.js +34 -59
- package/dist/remoteRun/remoteRunCli.js.map +1 -1
- package/dist/remoteRun/runnerClient.d.ts +2 -0
- package/dist/remoteRun/runnerClient.d.ts.map +1 -1
- package/dist/remoteRun/runnerClient.js +5 -1
- package/dist/remoteRun/runnerClient.js.map +1 -1
- package/dist/remoteRun/runnerSummary.d.ts +24 -0
- package/dist/remoteRun/runnerSummary.d.ts.map +1 -0
- package/dist/remoteRun/runnerSummary.js +86 -0
- package/dist/remoteRun/runnerSummary.js.map +1 -0
- package/dist/remoteRun/runnerWait.d.ts +68 -0
- package/dist/remoteRun/runnerWait.d.ts.map +1 -0
- package/dist/remoteRun/runnerWait.js +137 -0
- package/dist/remoteRun/runnerWait.js.map +1 -0
- package/dist/remoteRun/runnerWaitTiming.d.ts +16 -0
- package/dist/remoteRun/runnerWaitTiming.d.ts.map +1 -0
- package/dist/remoteRun/runnerWaitTiming.js +37 -0
- package/dist/remoteRun/runnerWaitTiming.js.map +1 -0
- package/dist/remoteRun/runners.d.ts +7 -7
- package/dist/remoteRun/runners.d.ts.map +1 -1
- package/dist/remoteRun/runners.js +45 -17
- package/dist/remoteRun/runners.js.map +1 -1
- package/dist/remoteRun/runnersCatalog.d.ts +14 -0
- package/dist/remoteRun/runnersCatalog.d.ts.map +1 -0
- package/dist/remoteRun/runnersCatalog.js +68 -0
- package/dist/remoteRun/runnersCatalog.js.map +1 -0
- package/dist/remoteRun/runnersConfig.d.ts +29 -20
- package/dist/remoteRun/runnersConfig.d.ts.map +1 -1
- package/dist/remoteRun/runnersConfig.js +14 -12
- package/dist/remoteRun/runnersConfig.js.map +1 -1
- package/dist/remoteRun/ssh.d.ts +22 -2
- package/dist/remoteRun/ssh.d.ts.map +1 -1
- package/dist/remoteRun/ssh.js +41 -7
- package/dist/remoteRun/ssh.js.map +1 -1
- package/dist/runnerUtility/adminCheck.d.ts +11 -0
- package/dist/runnerUtility/adminCheck.d.ts.map +1 -0
- package/dist/runnerUtility/adminCheck.js +68 -0
- package/dist/runnerUtility/adminCheck.js.map +1 -0
- package/dist/runnerUtility/appInstall.d.ts +25 -0
- package/dist/runnerUtility/appInstall.d.ts.map +1 -0
- package/dist/runnerUtility/appInstall.js +147 -0
- package/dist/runnerUtility/appInstall.js.map +1 -0
- package/dist/runnerUtility/appUpdate.d.ts +39 -0
- package/dist/runnerUtility/appUpdate.d.ts.map +1 -0
- package/dist/runnerUtility/appUpdate.js +90 -0
- package/dist/runnerUtility/appUpdate.js.map +1 -0
- package/dist/runnerUtility/execEnv.d.ts +21 -0
- package/dist/runnerUtility/execEnv.d.ts.map +1 -0
- package/dist/runnerUtility/execEnv.js +109 -0
- package/dist/runnerUtility/execEnv.js.map +1 -0
- package/dist/runnerUtility/execSession.d.ts +13 -0
- package/dist/runnerUtility/execSession.d.ts.map +1 -0
- package/dist/runnerUtility/execSession.js +58 -0
- package/dist/runnerUtility/execSession.js.map +1 -0
- package/dist/runnerUtility/hubClient.d.ts +30 -0
- package/dist/runnerUtility/hubClient.d.ts.map +1 -0
- package/dist/runnerUtility/hubClient.js +127 -0
- package/dist/runnerUtility/hubClient.js.map +1 -0
- package/dist/runnerUtility/injectStream.d.ts +9 -0
- package/dist/runnerUtility/injectStream.d.ts.map +1 -0
- package/dist/runnerUtility/injectStream.js +12 -0
- package/dist/runnerUtility/injectStream.js.map +1 -0
- package/dist/runnerUtility/iosCheck.d.ts +62 -0
- package/dist/runnerUtility/iosCheck.d.ts.map +1 -0
- package/dist/runnerUtility/iosCheck.js +200 -0
- package/dist/runnerUtility/iosCheck.js.map +1 -0
- package/dist/runnerUtility/launcher.d.ts +5 -0
- package/dist/runnerUtility/launcher.d.ts.map +1 -0
- package/dist/runnerUtility/launcher.js +85 -0
- package/dist/runnerUtility/launcher.js.map +1 -0
- package/dist/runnerUtility/linkLoop.d.ts +47 -0
- package/dist/runnerUtility/linkLoop.d.ts.map +1 -0
- package/dist/runnerUtility/linkLoop.js +281 -0
- package/dist/runnerUtility/linkLoop.js.map +1 -0
- package/dist/runnerUtility/localLinkStop.d.ts +23 -0
- package/dist/runnerUtility/localLinkStop.d.ts.map +1 -0
- package/dist/runnerUtility/localLinkStop.js +79 -0
- package/dist/runnerUtility/localLinkStop.js.map +1 -0
- package/dist/runnerUtility/machineReport.d.ts +39 -0
- package/dist/runnerUtility/machineReport.d.ts.map +1 -0
- package/dist/runnerUtility/machineReport.js +161 -0
- package/dist/runnerUtility/machineReport.js.map +1 -0
- package/dist/runnerUtility/packageId.d.ts +5 -0
- package/dist/runnerUtility/packageId.d.ts.map +1 -0
- package/dist/runnerUtility/packageId.js +64 -0
- package/dist/runnerUtility/packageId.js.map +1 -0
- package/dist/runnerUtility/pairingRecovery.d.ts +31 -0
- package/dist/runnerUtility/pairingRecovery.d.ts.map +1 -0
- package/dist/runnerUtility/pairingRecovery.js +24 -0
- package/dist/runnerUtility/pairingRecovery.js.map +1 -0
- package/dist/runnerUtility/pueueDaemon.d.ts +36 -0
- package/dist/runnerUtility/pueueDaemon.d.ts.map +1 -0
- package/dist/runnerUtility/pueueDaemon.js +165 -0
- package/dist/runnerUtility/pueueDaemon.js.map +1 -0
- package/dist/runnerUtility/runnerCli.d.ts +18 -0
- package/dist/runnerUtility/runnerCli.d.ts.map +1 -0
- package/dist/runnerUtility/runnerCli.js +184 -0
- package/dist/runnerUtility/runnerCli.js.map +1 -0
- package/dist/runnerUtility/runnerCommands.d.ts +67 -0
- package/dist/runnerUtility/runnerCommands.d.ts.map +1 -0
- package/dist/runnerUtility/runnerCommands.js +418 -0
- package/dist/runnerUtility/runnerCommands.js.map +1 -0
- package/dist/runnerUtility/runnerConfig.d.ts +63 -0
- package/dist/runnerUtility/runnerConfig.d.ts.map +1 -0
- package/dist/runnerUtility/runnerConfig.js +153 -0
- package/dist/runnerUtility/runnerConfig.js.map +1 -0
- package/dist/runnerUtility/runnerFlags.d.ts +9 -0
- package/dist/runnerUtility/runnerFlags.d.ts.map +1 -0
- package/dist/runnerUtility/runnerFlags.js +53 -0
- package/dist/runnerUtility/runnerFlags.js.map +1 -0
- package/dist/runnerUtility/runnerHome.d.ts +30 -0
- package/dist/runnerUtility/runnerHome.d.ts.map +1 -0
- package/dist/runnerUtility/runnerHome.js +82 -0
- package/dist/runnerUtility/runnerHome.js.map +1 -0
- package/dist/runnerUtility/runnerKeys.d.ts +11 -0
- package/dist/runnerUtility/runnerKeys.d.ts.map +1 -0
- package/dist/runnerUtility/runnerKeys.js +80 -0
- package/dist/runnerUtility/runnerKeys.js.map +1 -0
- package/dist/runnerUtility/runnerServers.d.ts +40 -0
- package/dist/runnerUtility/runnerServers.d.ts.map +1 -0
- package/dist/runnerUtility/runnerServers.js +79 -0
- package/dist/runnerUtility/runnerServers.js.map +1 -0
- package/dist/runnerUtility/serviceManager.d.ts +54 -0
- package/dist/runnerUtility/serviceManager.d.ts.map +1 -0
- package/dist/runnerUtility/serviceManager.js +413 -0
- package/dist/runnerUtility/serviceManager.js.map +1 -0
- package/dist/runnerUtility/serviceUnits.d.ts +36 -0
- package/dist/runnerUtility/serviceUnits.d.ts.map +1 -0
- package/dist/runnerUtility/serviceUnits.js +169 -0
- package/dist/runnerUtility/serviceUnits.js.map +1 -0
- package/dist/runnerUtility/toolInstall.d.ts +95 -0
- package/dist/runnerUtility/toolInstall.d.ts.map +1 -0
- package/dist/runnerUtility/toolInstall.js +232 -0
- package/dist/runnerUtility/toolInstall.js.map +1 -0
- package/dist/runnerUtility/versionPollTiming.d.ts +16 -0
- package/dist/runnerUtility/versionPollTiming.d.ts.map +1 -0
- package/dist/runnerUtility/versionPollTiming.js +36 -0
- package/dist/runnerUtility/versionPollTiming.js.map +1 -0
- package/dist/runners/authorizedKeys.d.ts +52 -0
- package/dist/runners/authorizedKeys.d.ts.map +1 -0
- package/dist/runners/authorizedKeys.js +153 -0
- package/dist/runners/authorizedKeys.js.map +1 -0
- package/dist/runners/bootstrapLine.d.ts +4 -0
- package/dist/runners/bootstrapLine.d.ts.map +1 -0
- package/dist/runners/bootstrapLine.js +69 -0
- package/dist/runners/bootstrapLine.js.map +1 -0
- package/dist/runners/gate.d.ts +41 -0
- package/dist/runners/gate.d.ts.map +1 -0
- package/dist/runners/gate.js +216 -0
- package/dist/runners/gate.js.map +1 -0
- package/dist/runners/gateLink.d.ts +26 -0
- package/dist/runners/gateLink.d.ts.map +1 -0
- package/dist/runners/gateLink.js +240 -0
- package/dist/runners/gateLink.js.map +1 -0
- package/dist/runners/gateProtocol.d.ts +98 -0
- package/dist/runners/gateProtocol.d.ts.map +1 -0
- package/dist/runners/gateProtocol.js +149 -0
- package/dist/runners/gateProtocol.js.map +1 -0
- package/dist/runners/gateWrapper.d.ts +6 -0
- package/dist/runners/gateWrapper.d.ts.map +1 -0
- package/dist/runners/gateWrapper.js +70 -0
- package/dist/runners/gateWrapper.js.map +1 -0
- package/dist/runners/hubAddress.d.ts +33 -0
- package/dist/runners/hubAddress.d.ts.map +1 -0
- package/dist/runners/hubAddress.js +88 -0
- package/dist/runners/hubAddress.js.map +1 -0
- package/dist/runners/hubAddressCandidates.d.ts +16 -0
- package/dist/runners/hubAddressCandidates.d.ts.map +1 -0
- package/dist/runners/hubAddressCandidates.js +96 -0
- package/dist/runners/hubAddressCandidates.js.map +1 -0
- package/dist/runners/hubIds.d.ts +16 -0
- package/dist/runners/hubIds.d.ts.map +1 -0
- package/dist/runners/hubIds.js +25 -0
- package/dist/runners/hubIds.js.map +1 -0
- package/dist/runners/hubKey.d.ts +6 -0
- package/dist/runners/hubKey.d.ts.map +1 -0
- package/dist/runners/hubKey.js +78 -0
- package/dist/runners/hubKey.js.map +1 -0
- package/dist/runners/hubLock.d.ts +7 -0
- package/dist/runners/hubLock.d.ts.map +1 -0
- package/dist/runners/hubLock.js +230 -0
- package/dist/runners/hubLock.js.map +1 -0
- package/dist/runners/hubPackage.d.ts +23 -0
- package/dist/runners/hubPackage.d.ts.map +1 -0
- package/dist/runners/hubPackage.js +184 -0
- package/dist/runners/hubPackage.js.map +1 -0
- package/dist/runners/hubPairing.d.ts +63 -0
- package/dist/runners/hubPairing.d.ts.map +1 -0
- package/dist/runners/hubPairing.js +261 -0
- package/dist/runners/hubPairing.js.map +1 -0
- package/dist/runners/hubPaths.d.ts +38 -0
- package/dist/runners/hubPaths.d.ts.map +1 -0
- package/dist/runners/hubPaths.js +94 -0
- package/dist/runners/hubPaths.js.map +1 -0
- package/dist/runners/hubRegistry.d.ts +112 -0
- package/dist/runners/hubRegistry.d.ts.map +1 -0
- package/dist/runners/hubRegistry.js +210 -0
- package/dist/runners/hubRegistry.js.map +1 -0
- package/dist/runners/linkControl.d.ts +27 -0
- package/dist/runners/linkControl.d.ts.map +1 -0
- package/dist/runners/linkControl.js +124 -0
- package/dist/runners/linkControl.js.map +1 -0
- package/dist/runners/pairCode.d.ts +30 -0
- package/dist/runners/pairCode.d.ts.map +1 -0
- package/dist/runners/pairCode.js +46 -0
- package/dist/runners/pairCode.js.map +1 -0
- package/dist/runners/pairingTiming.d.ts +18 -0
- package/dist/runners/pairingTiming.d.ts.map +1 -0
- package/dist/runners/pairingTiming.js +42 -0
- package/dist/runners/pairingTiming.js.map +1 -0
- package/dist/runners/runnerProxy.d.ts +9 -0
- package/dist/runners/runnerProxy.d.ts.map +1 -0
- package/dist/runners/runnerProxy.js +67 -0
- package/dist/runners/runnerProxy.js.map +1 -0
- package/dist/runners/runnerRemoval.d.ts +21 -0
- package/dist/runners/runnerRemoval.d.ts.map +1 -0
- package/dist/runners/runnerRemoval.js +82 -0
- package/dist/runners/runnerRemoval.js.map +1 -0
- package/dist/runners/runnersCli.d.ts +16 -0
- package/dist/runners/runnersCli.d.ts.map +1 -0
- package/dist/runners/runnersCli.js +214 -0
- package/dist/runners/runnersCli.js.map +1 -0
- package/dist/runners/runnersList.d.ts +17 -0
- package/dist/runners/runnersList.d.ts.map +1 -0
- package/dist/runners/runnersList.js +49 -0
- package/dist/runners/runnersList.js.map +1 -0
- package/dist/runners/runnersOverview.d.ts +20 -0
- package/dist/runners/runnersOverview.d.ts.map +1 -0
- package/dist/runners/runnersOverview.js +55 -0
- package/dist/runners/runnersOverview.js.map +1 -0
- package/dist/runners/sshKeys.d.ts +26 -0
- package/dist/runners/sshKeys.d.ts.map +1 -0
- package/dist/runners/sshKeys.js +60 -0
- package/dist/runners/sshKeys.js.map +1 -0
- package/dist/runners/tarArchive.d.ts +20 -0
- package/dist/runners/tarArchive.d.ts.map +1 -0
- package/dist/runners/tarArchive.js +70 -0
- package/dist/runners/tarArchive.js.map +1 -0
- package/dist/scheduler/mcpSurface.d.ts +11 -5
- package/dist/scheduler/mcpSurface.d.ts.map +1 -1
- package/dist/scheduler/mcpSurface.js +59 -26
- package/dist/scheduler/mcpSurface.js.map +1 -1
- package/dist/selfUpdate/selfCheck.js +2 -2
- package/dist/selfUpdate/selfCheck.js.map +1 -1
- package/dist/testTimings.d.ts +5 -2
- package/dist/testTimings.d.ts.map +1 -1
- package/dist/testTimings.js +10 -1
- package/dist/testTimings.js.map +1 -1
- package/dist/utils/agentEnvironment.d.ts +6 -2
- package/dist/utils/agentEnvironment.d.ts.map +1 -1
- package/dist/utils/agentEnvironment.js +13 -2
- package/dist/utils/agentEnvironment.js.map +1 -1
- package/dist/utils/runnersScreenPlan.d.ts +130 -0
- package/dist/utils/runnersScreenPlan.d.ts.map +1 -0
- package/dist/utils/runnersScreenPlan.js +257 -0
- package/dist/utils/runnersScreenPlan.js.map +1 -0
- package/package.json +5 -2
- package/remoteRun/runner.sh +81 -6
|
@@ -43,14 +43,15 @@ exports.chooseExecRunnerName = chooseExecRunnerName;
|
|
|
43
43
|
exports.findRunnerOfJob = findRunnerOfJob;
|
|
44
44
|
const fs = __importStar(require("fs"));
|
|
45
45
|
const path = __importStar(require("path"));
|
|
46
|
+
const linkControl_1 = require("../runners/linkControl");
|
|
46
47
|
const probe_1 = require("./probe");
|
|
47
48
|
const remoteRunError_1 = require("./remoteRunError");
|
|
48
49
|
const exitCodes_1 = require("./exitCodes");
|
|
49
50
|
const runnerClient_1 = require("./runnerClient");
|
|
50
51
|
/**
|
|
51
52
|
* @description Which runners exist right now: probe them (in parallel, bounded), remember each one's last answer so an offline runner
|
|
52
|
-
* still shows what it was, and pick the one a command should use — the named one, or the first in `runners.json`
|
|
53
|
-
* `--needs`, has the capability.
|
|
53
|
+
* still shows what it was, and pick the one a command should use — the named one, or the first in the catalog (`runners.json` entries,
|
|
54
|
+
* then the paired runners) that answers, is not paused and, with `--needs`, has the capability.
|
|
54
55
|
*/
|
|
55
56
|
/** The whole probing of all runners ends by then (a dead host costs its ssh connect timeout, once, in parallel with the others). */
|
|
56
57
|
exports.probeAllTimeoutMs = 20 * 1000;
|
|
@@ -82,17 +83,26 @@ function writeLastProbe(cacheDir, runnerName, text, seenAtMs) {
|
|
|
82
83
|
async function probeRunner(client, nowMs = Date.now(), signal, deadlineMs = exports.probeAllTimeoutMs) {
|
|
83
84
|
const runner = client.runner;
|
|
84
85
|
const groupLimits = { ...(runner.groups ?? {}) };
|
|
85
|
-
const
|
|
86
|
+
const isPausedByHub = runner.paired?.isPausedByHub ?? false;
|
|
87
|
+
const offline = (reason, linkLastSeenMs = null) => {
|
|
86
88
|
const last = readLastProbe(client.cacheDir, runner.name);
|
|
87
89
|
return {
|
|
88
90
|
name: runner.name,
|
|
89
91
|
status: 'offline',
|
|
90
92
|
reason: signal?.aborted === true ? `no answer within ${Math.round(deadlineMs / millisecondsPerSecond)} s` : reason,
|
|
91
|
-
|
|
93
|
+
// The link's own heartbeat is the better «last seen» of a paired runner: the last probe may be hours older than its last connection.
|
|
94
|
+
lastSeenAtMs: linkLastSeenMs ?? last?.seenAtMs ?? null,
|
|
92
95
|
lastProbe: last?.probe ?? null,
|
|
93
96
|
groupLimits,
|
|
97
|
+
isPausedByHub,
|
|
94
98
|
};
|
|
95
99
|
};
|
|
100
|
+
if (runner.paired !== undefined) {
|
|
101
|
+
// A paired runner is online when its link accepts a connection; asking is cheaper than an ssh call that fails through the proxy.
|
|
102
|
+
const link = await (0, linkControl_1.getRunnerLinkStatus)(runner.paired.hubDir, runner.paired.runnerId);
|
|
103
|
+
if (!link.isOnline)
|
|
104
|
+
return offline(runnerClient_1.notConnectedReason, link.lastSeenMs);
|
|
105
|
+
}
|
|
96
106
|
try {
|
|
97
107
|
await client.ensureScript(signal);
|
|
98
108
|
}
|
|
@@ -105,18 +115,19 @@ async function probeRunner(client, nowMs = Date.now(), signal, deadlineMs = expo
|
|
|
105
115
|
if (result.exitCode !== 0)
|
|
106
116
|
return offline((0, runnerClient_1.describeSshFailure)(result));
|
|
107
117
|
writeLastProbe(client.cacheDir, runner.name, result.stdout, nowMs);
|
|
108
|
-
return { name: runner.name, status: 'online', probe: (0, probe_1.parseProbeOutput)(result.stdout), groupLimits };
|
|
118
|
+
return { name: runner.name, status: 'online', probe: (0, probe_1.parseProbeOutput)(result.stdout), groupLimits, isPausedByHub };
|
|
109
119
|
}
|
|
110
120
|
/**
|
|
111
121
|
* @description Probe every client at once under ONE deadline (20 s): a runner that has not answered by then is reported offline with its
|
|
112
122
|
* last snapshot and its ssh calls are killed, so the view ends on time and nothing keeps the process alive afterwards. The others are not
|
|
113
|
-
* held back by it.
|
|
123
|
+
* held back by it. `stop` ends the same ssh calls from outside — the user interrupted the command that asked.
|
|
114
124
|
*/
|
|
115
|
-
async function probeRunners(clients, nowMs = Date.now(), deadlineMs = exports.probeAllTimeoutMs) {
|
|
125
|
+
async function probeRunners(clients, nowMs = Date.now(), deadlineMs = exports.probeAllTimeoutMs, stop) {
|
|
116
126
|
const deadline = new AbortController();
|
|
117
127
|
const timer = setTimeout(() => deadline.abort(), deadlineMs);
|
|
128
|
+
const signal = stop === undefined ? deadline.signal : AbortSignal.any([deadline.signal, stop]);
|
|
118
129
|
try {
|
|
119
|
-
return await Promise.all(clients.map((client) => probeRunner(client, nowMs,
|
|
130
|
+
return await Promise.all(clients.map((client) => probeRunner(client, nowMs, signal, deadlineMs)));
|
|
120
131
|
}
|
|
121
132
|
finally {
|
|
122
133
|
clearTimeout(timer);
|
|
@@ -127,23 +138,40 @@ function formatRunnersView(reports) {
|
|
|
127
138
|
return reports.map(probe_1.formatRunnerBlock).join('\n\n');
|
|
128
139
|
}
|
|
129
140
|
function checkIsUsable(report) {
|
|
130
|
-
return report.status === 'online' && report.probe.pueueVersion !== null;
|
|
141
|
+
return report.status === 'online' && report.probe.pueueVersion !== null && !(0, probe_1.checkIsRunnerPaused)(report);
|
|
142
|
+
}
|
|
143
|
+
/** Who paused a runner and the way back, for the message of a pick that refused it. */
|
|
144
|
+
function describePause(report) {
|
|
145
|
+
const probe = report.status === 'online' ? report.probe : report.lastProbe;
|
|
146
|
+
return [
|
|
147
|
+
...(report.isPausedByHub ? [`from the hub — \`charness runners resume ${report.name}\``] : []),
|
|
148
|
+
...(probe?.isPaused === true ? ['on the runner — `charness-runner resume` there'] : []),
|
|
149
|
+
].join(' and ');
|
|
150
|
+
}
|
|
151
|
+
/** « (paused, so not used: mac)» for the online runners a pick skipped only because they are paused (only those that could have done the job), else nothing. */
|
|
152
|
+
function describeSkippedPaused(reports, needs) {
|
|
153
|
+
const names = reports
|
|
154
|
+
.filter((report) => report.status === 'online' && report.probe.pueueVersion !== null && (0, probe_1.checkIsRunnerPaused)(report) && (needs === null || (0, probe_1.checkHasCapability)(report.probe, needs)))
|
|
155
|
+
.map((report) => report.name);
|
|
156
|
+
return names.length === 0 ? '' : ` (paused, so not used: ${names.join(', ')})`;
|
|
131
157
|
}
|
|
132
158
|
/**
|
|
133
|
-
* @description The runner a command should use. `wanted.runner` names one (it must exist, answer
|
|
134
|
-
* runner in `config` order that answers, has Pueue and — with `wanted.needs` — the capability. None:
|
|
135
|
-
* view, exit 125.
|
|
159
|
+
* @description The runner a command should use. `wanted.runner` names one (it must exist, answer, have Pueue and not be paused);
|
|
160
|
+
* otherwise the first runner in `config` order that answers, has Pueue, is not paused and — with `wanted.needs` — the capability. None:
|
|
161
|
+
* the reason and the `runners` view, exit 125.
|
|
136
162
|
*/
|
|
137
163
|
function pickRunner(config, reports, wanted) {
|
|
138
164
|
if (wanted.runner !== null) {
|
|
139
165
|
const report = reports.find((candidate) => candidate.name === wanted.runner);
|
|
140
166
|
if (report === undefined) {
|
|
141
|
-
return { ok: false, exitCode: exitCodes_1.remoteRunExitCodes.usage, message: `no runner named "${wanted.runner}"
|
|
167
|
+
return { ok: false, exitCode: exitCodes_1.remoteRunExitCodes.usage, message: `no runner named "${wanted.runner}" (configured: ${config.runners.map((runner) => runner.name).join(', ')})` };
|
|
142
168
|
}
|
|
143
169
|
if (report.status === 'offline')
|
|
144
170
|
return { ok: false, exitCode: exitCodes_1.remoteRunExitCodes.failed, message: `runner "${wanted.runner}" is offline (${report.reason})\n${formatRunnersView([report])}` };
|
|
145
|
-
if (
|
|
171
|
+
if (report.probe.pueueVersion === null)
|
|
146
172
|
return { ok: false, exitCode: exitCodes_1.remoteRunExitCodes.failed, message: `runner "${wanted.runner}" has no Pueue (install it there, see docs/remote-run.md)\n${formatRunnersView([report])}` };
|
|
173
|
+
if ((0, probe_1.checkIsRunnerPaused)(report))
|
|
174
|
+
return { ok: false, exitCode: exitCodes_1.remoteRunExitCodes.failed, message: `runner "${wanted.runner}" is paused ${describePause(report)}\n${formatRunnersView([report])}` };
|
|
147
175
|
if (wanted.needs !== null && !(0, probe_1.checkHasCapability)(report.probe, wanted.needs)) {
|
|
148
176
|
return { ok: false, exitCode: exitCodes_1.remoteRunExitCodes.failed, message: `runner "${wanted.runner}" cannot do "${wanted.needs}"\n${formatRunnersView([report])}` };
|
|
149
177
|
}
|
|
@@ -157,7 +185,7 @@ function pickRunner(config, reports, wanted) {
|
|
|
157
185
|
return { ok: true, runnerName: runner.name };
|
|
158
186
|
}
|
|
159
187
|
const reason = wanted.needs === null ? 'no runner is online' : `no online runner can do "${wanted.needs}"`;
|
|
160
|
-
return { ok: false, exitCode: exitCodes_1.remoteRunExitCodes.failed, message: `${reason}\n${formatRunnersView(reports)}` };
|
|
188
|
+
return { ok: false, exitCode: exitCodes_1.remoteRunExitCodes.failed, message: `${reason}${describeSkippedPaused(reports, wanted.needs)}\n${formatRunnersView(reports)}` };
|
|
161
189
|
}
|
|
162
190
|
/**
|
|
163
191
|
* @description The runner `exec` goes to. `exec` runs in the runner's copy "as the last job left it", so without `--runner` that is the
|
|
@@ -169,13 +197,13 @@ function chooseExecRunnerName(config, named, lastRunnerName) {
|
|
|
169
197
|
return { runnerName: named, note: null };
|
|
170
198
|
if (config.runners.some((runner) => runner.name === lastRunnerName))
|
|
171
199
|
return { runnerName: lastRunnerName, note: null };
|
|
172
|
-
return { runnerName: null, note: `the runner of this project's last job, "${lastRunnerName}", is not in runners.json
|
|
200
|
+
return { runnerName: null, note: `the runner of this project's last job, "${lastRunnerName}", is not a configured runner any more (not in runners.json, not paired) — using the first online runner` };
|
|
173
201
|
}
|
|
174
202
|
/** @description The runner of a job id, from the config: an id of a runner that is no longer listed is an error. */
|
|
175
203
|
function findRunnerOfJob(config, runnerName) {
|
|
176
204
|
const runner = config.runners.find((candidate) => candidate.name === runnerName);
|
|
177
205
|
if (runner === undefined)
|
|
178
|
-
throw new remoteRunError_1.RemoteRunError(`the job belongs to runner "${runnerName}", which is not in runners.json
|
|
206
|
+
throw new remoteRunError_1.RemoteRunError(`the job belongs to runner "${runnerName}", which is not a configured runner any more (not in runners.json, not paired)`);
|
|
179
207
|
return runner;
|
|
180
208
|
}
|
|
181
209
|
//# sourceMappingURL=runners.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runners.js","sourceRoot":"","sources":["../../src/remoteRun/runners.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"runners.js","sourceRoot":"","sources":["../../src/remoteRun/runners.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ConfigFileLoad } from '../configDirFile';
|
|
2
|
+
import { type RunnersConfig } from './runnersConfig';
|
|
3
|
+
/**
|
|
4
|
+
* @description Every runner `remote-run` may use: the hand-made ones of `runners.json` (plain ssh, phase 1) and the paired ones of the
|
|
5
|
+
* hub's registry (`pairedRunners.ts`), as one list with the hand-made first — the order `pickRunner` walks. A name in both is an error
|
|
6
|
+
* that names it (a runner is addressed by name, in job ids too); pairing itself avoids the clash by renaming a newcomer (`-2`), so it can
|
|
7
|
+
* only come from a hand edit made afterwards. This is the only place that knows both sources; it is kept apart from `runnersConfig.ts`
|
|
8
|
+
* because the hub registry's schemas import that file.
|
|
9
|
+
*
|
|
10
|
+
* Both loads are stamp-cached, so the bot MCP can read the runners (`runnerSummary.ts`) on every connect for the cost of two `stat` calls.
|
|
11
|
+
*/
|
|
12
|
+
/** @description The runners of the user whose home is `homeDir`. `missing` = neither a `runners.json` nor a paired runner; `invalid` carries every problem and, when there is one, the last good (or best-effort) list. */
|
|
13
|
+
export declare function loadRunnersCatalog(homeDir?: string): ConfigFileLoad<RunnersConfig>;
|
|
14
|
+
//# sourceMappingURL=runnersCatalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runnersCatalog.d.ts","sourceRoot":"","sources":["../../src/remoteRun/runnersCatalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAwC,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE3F;;;;;;;;GAQG;AAEH,2NAA2N;AAC3N,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,MAAqB,GAAG,cAAc,CAAC,aAAa,CAAC,CAgBhG"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.loadRunnersCatalog = loadRunnersCatalog;
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const pairedRunners_1 = require("./pairedRunners");
|
|
39
|
+
const runnersConfig_1 = require("./runnersConfig");
|
|
40
|
+
/**
|
|
41
|
+
* @description Every runner `remote-run` may use: the hand-made ones of `runners.json` (plain ssh, phase 1) and the paired ones of the
|
|
42
|
+
* hub's registry (`pairedRunners.ts`), as one list with the hand-made first — the order `pickRunner` walks. A name in both is an error
|
|
43
|
+
* that names it (a runner is addressed by name, in job ids too); pairing itself avoids the clash by renaming a newcomer (`-2`), so it can
|
|
44
|
+
* only come from a hand edit made afterwards. This is the only place that knows both sources; it is kept apart from `runnersConfig.ts`
|
|
45
|
+
* because the hub registry's schemas import that file.
|
|
46
|
+
*
|
|
47
|
+
* Both loads are stamp-cached, so the bot MCP can read the runners (`runnerSummary.ts`) on every connect for the cost of two `stat` calls.
|
|
48
|
+
*/
|
|
49
|
+
/** @description The runners of the user whose home is `homeDir`. `missing` = neither a `runners.json` nor a paired runner; `invalid` carries every problem and, when there is one, the last good (or best-effort) list. */
|
|
50
|
+
function loadRunnersCatalog(homeDir = os.homedir()) {
|
|
51
|
+
const handMade = (0, runnersConfig_1.loadRunnersConfig)(homeDir);
|
|
52
|
+
const paired = (0, pairedRunners_1.loadPairedRunners)(homeDir);
|
|
53
|
+
const handMadeRunners = handMade.config?.runners ?? [];
|
|
54
|
+
const pairedRunners = paired.config?.runners ?? [];
|
|
55
|
+
const handMadeNames = new Set(handMadeRunners.map((runner) => runner.name));
|
|
56
|
+
const clashes = pairedRunners.filter((runner) => handMadeNames.has(runner.name));
|
|
57
|
+
const errors = [
|
|
58
|
+
...(handMade.status === 'invalid' ? handMade.errors : []),
|
|
59
|
+
...(paired.status === 'invalid' ? paired.errors : []),
|
|
60
|
+
...clashes.map((runner) => `runner "${runner.name}" is in runners.json and also paired — rename the runners.json entry (or remove the pairing)`),
|
|
61
|
+
];
|
|
62
|
+
const clashingNames = new Set(clashes.map((runner) => runner.name));
|
|
63
|
+
const config = { runners: [...handMadeRunners, ...pairedRunners.filter((runner) => !clashingNames.has(runner.name))] };
|
|
64
|
+
if (errors.length > 0)
|
|
65
|
+
return { status: 'invalid', config: config.runners.length > 0 ? config : null, errors };
|
|
66
|
+
return { status: handMade.status === 'missing' && paired.status === 'missing' ? 'missing' : 'ok', config };
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=runnersCatalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runnersCatalog.js","sourceRoot":"","sources":["../../src/remoteRun/runnersCatalog.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,gDAgBC;AAhCD,uCAAyB;AAEzB,mDAAoD;AACpD,mDAA2F;AAE3F;;;;;;;;GAQG;AAEH,2NAA2N;AAC3N,SAAgB,kBAAkB,CAAC,UAAkB,EAAE,CAAC,OAAO,EAAE;IAC/D,MAAM,QAAQ,GAAG,IAAA,iCAAiB,EAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,IAAA,iCAAiB,EAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,eAAe,GAAmB,QAAQ,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC;IACvE,MAAM,aAAa,GAAmB,MAAM,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC;IACnE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5E,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACjF,MAAM,MAAM,GAAG;QACb,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,MAAM,CAAC,IAAI,8FAA8F,CAAC;KACjJ,CAAC;IACF,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,MAAM,MAAM,GAAkB,EAAE,OAAO,EAAE,CAAC,GAAG,eAAe,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACtI,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAC/G,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;AAC7G,CAAC"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { type ConfigFileLoad } from '../configDirFile';
|
|
3
|
+
/** @description What a runner's name may be: lowercase letters and digits, single dashes between. */
|
|
4
|
+
export declare const runnerNamePattern: RegExp;
|
|
3
5
|
/** The group every runner has; its parallel limit may be set, it never needs `group add`. */
|
|
4
6
|
export declare const defaultPueueGroup = "default";
|
|
7
|
+
/** @description A runner's work folder: an absolute path on the runner, or one the runner's shell expands from `~`. */
|
|
8
|
+
export declare const runnerWorkDirSchema: z.ZodString;
|
|
5
9
|
declare const runnerSchema: z.ZodObject<{
|
|
6
10
|
name: z.ZodString;
|
|
7
11
|
ssh: z.ZodString;
|
|
@@ -13,31 +17,36 @@ declare const runnerSchema: z.ZodObject<{
|
|
|
13
17
|
}, z.core.$strict>>;
|
|
14
18
|
groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
15
19
|
}, z.core.$strict>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @name PairedRunnerLink
|
|
22
|
+
* @description What only a PAIRED runner has (`pairedRunners.ts` fills it; the file never can): where the hub keeps its link on this
|
|
23
|
+
* machine and whether the hub paused it. A runner without it is a hand-made one, reached by plain ssh.
|
|
24
|
+
*/
|
|
25
|
+
export interface PairedRunnerLink {
|
|
26
|
+
runnerId: string;
|
|
27
|
+
/** The registry folder (`~/.config/charness/runners`): `link/<id>.sock` / `link/<id>.json` say whether the runner is connected. */
|
|
28
|
+
hubDir: string;
|
|
29
|
+
/** Paused from the hub (`charness runners pause`, `/runners`): the runner stays attached but is never picked for a job. */
|
|
30
|
+
isPausedByHub: boolean;
|
|
31
|
+
/** The OS the runner reported at pairing (`macOS 15.1`) — text from the runner machine, to be cleaned before it is shown to an agent. */
|
|
32
|
+
os: string;
|
|
33
|
+
}
|
|
34
|
+
/** @name RunnerConfig @description One runner: an entry of the file, or a paired runner built from the hub's registry. */
|
|
35
|
+
export type RunnerConfig = z.infer<typeof runnerSchema> & {
|
|
36
|
+
paired?: PairedRunnerLink;
|
|
37
|
+
};
|
|
38
|
+
/** @name RunnersConfig @description Every runner `remote-run` may use (see `runnersCatalog.ts`); the file alone holds only the hand-made ones. */
|
|
39
|
+
export interface RunnersConfig {
|
|
40
|
+
runners: RunnerConfig[];
|
|
41
|
+
}
|
|
33
42
|
/** @description Where the file lives: beside the user's global charness env file, whatever `ENV_FILE` says (it is per OS user, not per instance). */
|
|
34
43
|
export declare function getRunnersConfigPath(homeDir?: string): string;
|
|
35
|
-
/** @description The file
|
|
44
|
+
/** @description The content of the runners file at `filePath` (stamp-cached): missing = no runners, invalid = its errors and the last good config of this process, if any. */
|
|
45
|
+
export declare function loadRunnersConfigAt(filePath: string): ConfigFileLoad<RunnersConfig>;
|
|
46
|
+
/** @description The user's `runners.json` right now. */
|
|
36
47
|
export declare function loadRunnersConfig(homeDir?: string): ConfigFileLoad<RunnersConfig>;
|
|
37
48
|
/** @description Forget the cached loads (tests that swap files under one path). */
|
|
38
49
|
export declare function resetRunnersConfigCache(): void;
|
|
39
|
-
/** @description Does any runner exist right now? The bot MCP's instructions mention remote-run only while one does. */
|
|
40
|
-
export declare function checkIsRemoteRunOffered(homeDir?: string): boolean;
|
|
41
50
|
/** @description The parallel limits of a runner's groups as the `name=n,name=n` string the runner script applies, in the order of the file (the runner applies each pair on its own, so the order carries no meaning). */
|
|
42
51
|
export declare function formatGroupLimits(groups: RunnerConfig['groups']): string;
|
|
43
52
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runnersConfig.d.ts","sourceRoot":"","sources":["../../src/remoteRun/runnersConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"runnersConfig.d.ts","sourceRoot":"","sources":["../../src/remoteRun/runnersConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAkB/E,qGAAqG;AACrG,eAAO,MAAM,iBAAiB,QAA+B,CAAC;AAE9D,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C,uHAAuH;AACvH,eAAO,MAAM,mBAAmB,aAA2K,CAAC;AAE5M,QAAA,MAAM,YAAY;;;;;;;;;;kBAOhB,CAAC;AAMH;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,mIAAmI;IACnI,MAAM,EAAE,MAAM,CAAC;IACf,2HAA2H;IAC3H,aAAa,EAAE,OAAO,CAAC;IACvB,yIAAyI;IACzI,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,0HAA0H;AAC1H,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAC;AACxF,kJAAkJ;AAClJ,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,qJAAqJ;AACrJ,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,MAAqB,GAAG,MAAM,CAE3E;AAYD,8KAA8K;AAC9K,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,aAAa,CAAC,CAEnF;AAED,wDAAwD;AACxD,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAqB,GAAG,cAAc,CAAC,aAAa,CAAC,CAE/F;AAED,mFAAmF;AACnF,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED,0NAA0N;AAC1N,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,MAAM,CAExE"}
|
|
@@ -33,11 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.defaultPueueGroup = void 0;
|
|
36
|
+
exports.runnerWorkDirSchema = exports.defaultPueueGroup = exports.runnerNamePattern = void 0;
|
|
37
37
|
exports.getRunnersConfigPath = getRunnersConfigPath;
|
|
38
|
+
exports.loadRunnersConfigAt = loadRunnersConfigAt;
|
|
38
39
|
exports.loadRunnersConfig = loadRunnersConfig;
|
|
39
40
|
exports.resetRunnersConfigCache = resetRunnersConfigCache;
|
|
40
|
-
exports.checkIsRemoteRunOffered = checkIsRemoteRunOffered;
|
|
41
41
|
exports.formatGroupLimits = formatGroupLimits;
|
|
42
42
|
const os = __importStar(require("os"));
|
|
43
43
|
const path = __importStar(require("path"));
|
|
@@ -58,15 +58,18 @@ const legacyNames_1 = require("../legacyNames");
|
|
|
58
58
|
* the shared stamp-cached one, so the bot's MCP can ask "is any runner listed?" on every connect for the cost of one `stat`.
|
|
59
59
|
*/
|
|
60
60
|
const configFileName = 'runners.json';
|
|
61
|
-
|
|
61
|
+
/** @description What a runner's name may be: lowercase letters and digits, single dashes between. */
|
|
62
|
+
exports.runnerNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
62
63
|
const groupNamePattern = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
63
64
|
/** The group every runner has; its parallel limit may be set, it never needs `group add`. */
|
|
64
65
|
exports.defaultPueueGroup = 'default';
|
|
66
|
+
/** @description A runner's work folder: an absolute path on the runner, or one the runner's shell expands from `~`. */
|
|
67
|
+
exports.runnerWorkDirSchema = zod_1.z.string().min(1).refine((workDir) => workDir.startsWith('/') || workDir === '~' || workDir.startsWith('~/'), 'must be an absolute path on the runner or start with ~/');
|
|
65
68
|
const runnerSchema = zod_1.z.strictObject({
|
|
66
|
-
name: zod_1.z.string().regex(runnerNamePattern, 'must be lowercase letters, digits and single dashes'),
|
|
69
|
+
name: zod_1.z.string().regex(exports.runnerNamePattern, 'must be lowercase letters, digits and single dashes'),
|
|
67
70
|
ssh: zod_1.z.string().min(1).refine((destination) => !destination.startsWith('-') && !/\s/.test(destination), 'must be an ssh destination (user@host or a host alias): no spaces, not an option'),
|
|
68
71
|
sshOptions: zod_1.z.array(zod_1.z.string()).optional(),
|
|
69
|
-
workDir:
|
|
72
|
+
workDir: exports.runnerWorkDirSchema,
|
|
70
73
|
pueue: zod_1.z.strictObject({ bin: zod_1.z.string().min(1).optional(), config: zod_1.z.string().min(1).optional() }).optional(),
|
|
71
74
|
groups: zod_1.z.record(zod_1.z.string().regex(groupNamePattern, 'is not a valid group name'), zod_1.z.number().int().positive()).optional(),
|
|
72
75
|
});
|
|
@@ -87,19 +90,18 @@ const loader = (0, configDirFile_1.createConfigFileLoader)({
|
|
|
87
90
|
return { ok: false, errors: parsed.error.issues.map((issue) => `${configFileName} ${issue.path.join('.') || '(root)'}: ${issue.message}`) };
|
|
88
91
|
},
|
|
89
92
|
});
|
|
90
|
-
/** @description The file
|
|
93
|
+
/** @description The content of the runners file at `filePath` (stamp-cached): missing = no runners, invalid = its errors and the last good config of this process, if any. */
|
|
94
|
+
function loadRunnersConfigAt(filePath) {
|
|
95
|
+
return loader.load(filePath);
|
|
96
|
+
}
|
|
97
|
+
/** @description The user's `runners.json` right now. */
|
|
91
98
|
function loadRunnersConfig(homeDir = os.homedir()) {
|
|
92
|
-
return
|
|
99
|
+
return loadRunnersConfigAt(getRunnersConfigPath(homeDir));
|
|
93
100
|
}
|
|
94
101
|
/** @description Forget the cached loads (tests that swap files under one path). */
|
|
95
102
|
function resetRunnersConfigCache() {
|
|
96
103
|
loader.resetCache();
|
|
97
104
|
}
|
|
98
|
-
/** @description Does any runner exist right now? The bot MCP's instructions mention remote-run only while one does. */
|
|
99
|
-
function checkIsRemoteRunOffered(homeDir = os.homedir()) {
|
|
100
|
-
const load = loadRunnersConfig(homeDir);
|
|
101
|
-
return (load.config?.runners.length ?? 0) > 0;
|
|
102
|
-
}
|
|
103
105
|
/** @description The parallel limits of a runner's groups as the `name=n,name=n` string the runner script applies, in the order of the file (the runner applies each pair on its own, so the order carries no meaning). */
|
|
104
106
|
function formatGroupLimits(groups) {
|
|
105
107
|
return Object.entries(groups ?? {}).map(([groupName, parallel]) => `${groupName}=${parallel}`).join(',');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runnersConfig.js","sourceRoot":"","sources":["../../src/remoteRun/runnersConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"runnersConfig.js","sourceRoot":"","sources":["../../src/remoteRun/runnersConfig.ts"],"names":[],"mappings":"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"}
|
package/dist/remoteRun/ssh.d.ts
CHANGED
|
@@ -4,8 +4,25 @@ import type { RunnerConfig } from './runnersConfig';
|
|
|
4
4
|
export declare function getRemoteRunCacheDir(env?: NodeJS.ProcessEnv, homeDir?: string): string;
|
|
5
5
|
/** @description Create the cache dir (mode 0700) and its ssh folder. */
|
|
6
6
|
export declare function ensureRemoteRunCacheDir(cacheDir: string): void;
|
|
7
|
-
/** @description
|
|
7
|
+
/** @description Can `value` be put into an ssh option or a command line that ssh and rsync's `-e` both read back as one word? (Whitespace can; a quote, a backslash, a `$`, a backtick or a line break cannot.) */
|
|
8
|
+
export declare function checkIsSshPathQuotable(value: string): boolean;
|
|
9
|
+
/** @description ssh expands `%`-tokens in a ProxyCommand, an identity file, a known-hosts path and a ControlPath: a literal `%` is written `%%`. */
|
|
10
|
+
export declare function escapeSshTokens(value: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* @description A path as the VALUE of an `-o` option. ssh reads `-o` like a line of its config file: `UserKnownHostsFile` there is a LIST of
|
|
13
|
+
* files separated by whitespace and `ControlPath` takes one word followed by nothing, so `/home/my user/…` would be two words and ssh would
|
|
14
|
+
* fail or look in the wrong place. Double quotes keep one path one word (ssh_config(5): «arguments may optionally be enclosed in double
|
|
15
|
+
* quotes»); `-i` takes its path as a plain argument and needs none.
|
|
16
|
+
*/
|
|
17
|
+
export declare function quoteSshOptionPath(value: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* @description The ssh options of every call to `runner` (without the destination): the operator's own first — ssh keeps the first value of an
|
|
20
|
+
* option — then ours. The shared connection is an optimisation: a cache path that is too long for a unix socket, or holds a character that
|
|
21
|
+
* cannot be quoted, turns it off instead of failing the call.
|
|
22
|
+
*/
|
|
8
23
|
export declare function buildSshArgs(runner: Pick<RunnerConfig, 'sshOptions'>, cacheDir: string): string[];
|
|
24
|
+
/** @description Is `word` one that needs no quoting for a POSIX shell (and for ssh's and rsync's own parsers)? */
|
|
25
|
+
export declare function checkIsPlainShellWord(word: string): boolean;
|
|
9
26
|
/** @description One word quoted for a POSIX shell (`shellSingleQuote`; an already plain word stays as it is, so the commands read well in a log). */
|
|
10
27
|
export declare function quoteShellWord(word: string): string;
|
|
11
28
|
/** @description The words of an argument list as one string rsync's `-e` and a shell both read back as the same words. */
|
|
@@ -36,7 +53,10 @@ export declare const sshFailureExitCode = 255;
|
|
|
36
53
|
*/
|
|
37
54
|
export declare function checkIsConnectionLost(exitCode: number | null): boolean;
|
|
38
55
|
export interface SshRunResult {
|
|
39
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* `null` when the call was cut off (a timeout, or the caller's own stop) — also when the ssh client had already exited by itself: its pipes are let go
|
|
58
|
+
* before `close` then, the output can be a part of what a master connection was still sending, and the client's own code must not vouch for it.
|
|
59
|
+
*/
|
|
40
60
|
exitCode: number | null;
|
|
41
61
|
stdout: string;
|
|
42
62
|
stderr: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../../src/remoteRun/ssh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,8BAA8B,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../../src/remoteRun/ssh.ts"],"names":[],"mappings":"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"}
|
package/dist/remoteRun/ssh.js
CHANGED
|
@@ -36,7 +36,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.sshFailureExitCode = void 0;
|
|
37
37
|
exports.getRemoteRunCacheDir = getRemoteRunCacheDir;
|
|
38
38
|
exports.ensureRemoteRunCacheDir = ensureRemoteRunCacheDir;
|
|
39
|
+
exports.checkIsSshPathQuotable = checkIsSshPathQuotable;
|
|
40
|
+
exports.escapeSshTokens = escapeSshTokens;
|
|
41
|
+
exports.quoteSshOptionPath = quoteSshOptionPath;
|
|
39
42
|
exports.buildSshArgs = buildSshArgs;
|
|
43
|
+
exports.checkIsPlainShellWord = checkIsPlainShellWord;
|
|
40
44
|
exports.quoteShellWord = quoteShellWord;
|
|
41
45
|
exports.joinShellWords = joinShellWords;
|
|
42
46
|
exports.buildRsyncSshCommand = buildRsyncSshCommand;
|
|
@@ -53,6 +57,7 @@ const fs = __importStar(require("fs"));
|
|
|
53
57
|
const os = __importStar(require("os"));
|
|
54
58
|
const path = __importStar(require("path"));
|
|
55
59
|
const tmuxExec_1 = require("../utils/tmuxExec");
|
|
60
|
+
const interrupts_1 = require("./interrupts");
|
|
56
61
|
/**
|
|
57
62
|
* @description Everything that touches ssh: the argument lists (pure, unit-tested), the quoting for the remote shell, and the process
|
|
58
63
|
* helpers. Always non-interactive (`BatchMode`: a password or an unknown host key is an error, never a prompt), host keys checked as the
|
|
@@ -79,25 +84,52 @@ function ensureRemoteRunCacheDir(cacheDir) {
|
|
|
79
84
|
fs.chmodSync(cacheDir, cacheDirectoryMode);
|
|
80
85
|
fs.chmodSync(path.join(cacheDir, 'ssh'), cacheDirectoryMode);
|
|
81
86
|
}
|
|
82
|
-
/**
|
|
87
|
+
/** A path that holds one of these cannot reach ssh's option parser AND rsync's `-e` parser as the same single word. */
|
|
88
|
+
const unquotablePathPattern = /["'\\$`\r\n]/;
|
|
89
|
+
/** @description Can `value` be put into an ssh option or a command line that ssh and rsync's `-e` both read back as one word? (Whitespace can; a quote, a backslash, a `$`, a backtick or a line break cannot.) */
|
|
90
|
+
function checkIsSshPathQuotable(value) {
|
|
91
|
+
return !unquotablePathPattern.test(value);
|
|
92
|
+
}
|
|
93
|
+
/** @description ssh expands `%`-tokens in a ProxyCommand, an identity file, a known-hosts path and a ControlPath: a literal `%` is written `%%`. */
|
|
94
|
+
function escapeSshTokens(value) {
|
|
95
|
+
return value.replace(/%/g, '%%');
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @description A path as the VALUE of an `-o` option. ssh reads `-o` like a line of its config file: `UserKnownHostsFile` there is a LIST of
|
|
99
|
+
* files separated by whitespace and `ControlPath` takes one word followed by nothing, so `/home/my user/…` would be two words and ssh would
|
|
100
|
+
* fail or look in the wrong place. Double quotes keep one path one word (ssh_config(5): «arguments may optionally be enclosed in double
|
|
101
|
+
* quotes»); `-i` takes its path as a plain argument and needs none.
|
|
102
|
+
*/
|
|
103
|
+
function quoteSshOptionPath(value) {
|
|
104
|
+
return /\s/.test(value) ? `"${value}"` : value;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* @description The ssh options of every call to `runner` (without the destination): the operator's own first — ssh keeps the first value of an
|
|
108
|
+
* option — then ours. The shared connection is an optimisation: a cache path that is too long for a unix socket, or holds a character that
|
|
109
|
+
* cannot be quoted, turns it off instead of failing the call.
|
|
110
|
+
*/
|
|
83
111
|
function buildSshArgs(runner, cacheDir) {
|
|
84
112
|
const controlDirectory = path.join(cacheDir, 'ssh');
|
|
85
|
-
const
|
|
113
|
+
const isControlPathUsable = controlDirectory.length + 1 + controlPathHashLength <= unixSocketPathLimit && checkIsSshPathQuotable(controlDirectory);
|
|
86
114
|
return [
|
|
87
115
|
...(runner.sshOptions ?? []),
|
|
88
116
|
'-o', 'BatchMode=yes',
|
|
89
117
|
'-o', `ConnectTimeout=${connectTimeoutSeconds}`,
|
|
90
118
|
'-o', `ServerAliveInterval=${serverAliveIntervalSeconds}`,
|
|
91
119
|
'-o', `ServerAliveCountMax=${serverAliveCountMax}`,
|
|
92
|
-
...(
|
|
93
|
-
? ['-o', 'ControlMaster=auto', '-o', `ControlPath=${path.join(controlDirectory, '%C')}`, '-o', `ControlPersist=${controlPersistSeconds}`]
|
|
120
|
+
...(isControlPathUsable
|
|
121
|
+
? ['-o', 'ControlMaster=auto', '-o', `ControlPath=${quoteSshOptionPath(path.join(escapeSshTokens(controlDirectory), '%C'))}`, '-o', `ControlPersist=${controlPersistSeconds}`]
|
|
94
122
|
: ['-o', 'ControlMaster=no']),
|
|
95
123
|
];
|
|
96
124
|
}
|
|
97
125
|
const safeWordPattern = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
126
|
+
/** @description Is `word` one that needs no quoting for a POSIX shell (and for ssh's and rsync's own parsers)? */
|
|
127
|
+
function checkIsPlainShellWord(word) {
|
|
128
|
+
return safeWordPattern.test(word);
|
|
129
|
+
}
|
|
98
130
|
/** @description One word quoted for a POSIX shell (`shellSingleQuote`; an already plain word stays as it is, so the commands read well in a log). */
|
|
99
131
|
function quoteShellWord(word) {
|
|
100
|
-
return word !== '' &&
|
|
132
|
+
return word !== '' && checkIsPlainShellWord(word) ? word : (0, tmuxExec_1.shellSingleQuote)(word);
|
|
101
133
|
}
|
|
102
134
|
/** @description The words of an argument list as one string rsync's `-e` and a shell both read back as the same words. */
|
|
103
135
|
function joinShellWords(words) {
|
|
@@ -173,9 +205,11 @@ function runSshCommand(params) {
|
|
|
173
205
|
settle();
|
|
174
206
|
resolve({ exitCode: exports.sshFailureExitCode, stdout, stderr: `${stderr}${error.message}\n`, isTimedOut });
|
|
175
207
|
});
|
|
176
|
-
|
|
208
|
+
// Once cut off, its `exit` is enough: after login this ssh is only a client of a ControlPersist master that holds its pipes, so `close` would
|
|
209
|
+
// come when the remote command ends (or the master gives up), not when the client is gone — the call, a probe's deadline or a Ctrl-C, would hang.
|
|
210
|
+
void (0, interrupts_1.waitForChildEnd)(child, () => isTimedOut).then((exitCode) => {
|
|
177
211
|
settle();
|
|
178
|
-
resolve({ exitCode, stdout, stderr, isTimedOut });
|
|
212
|
+
resolve({ exitCode: isTimedOut ? null : exitCode, stdout, stderr, isTimedOut });
|
|
179
213
|
});
|
|
180
214
|
child.stdin.end(params.input ?? '');
|
|
181
215
|
});
|