charness 3.7.0 → 3.9.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 +36 -0
- package/DEVELOPMENT.md +8 -2
- package/README.md +5 -5
- package/SETUP.md +17 -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.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +56 -6
- 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 +524 -0
- package/dist/connectors/telegram/commands/runners.js.map +1 -0
- package/dist/connectors/telegram/runnerPairingMessages.d.ts +29 -0
- package/dist/connectors/telegram/runnerPairingMessages.d.ts.map +1 -0
- package/dist/connectors/telegram/runnerPairingMessages.js +112 -0
- package/dist/connectors/telegram/runnerPairingMessages.js.map +1 -0
- package/dist/i18n/de.d.ts.map +1 -1
- package/dist/i18n/de.js +60 -0
- package/dist/i18n/de.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +60 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +60 -0
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/fr.d.ts.map +1 -1
- package/dist/i18n/fr.js +60 -0
- package/dist/i18n/fr.js.map +1 -1
- package/dist/i18n/hi.d.ts.map +1 -1
- package/dist/i18n/hi.js +60 -0
- package/dist/i18n/hi.js.map +1 -1
- package/dist/i18n/ja.d.ts.map +1 -1
- package/dist/i18n/ja.js +60 -0
- package/dist/i18n/ja.js.map +1 -1
- package/dist/i18n/ka.d.ts.map +1 -1
- package/dist/i18n/ka.js +60 -0
- package/dist/i18n/ka.js.map +1 -1
- package/dist/i18n/pt.d.ts.map +1 -1
- package/dist/i18n/pt.js +60 -0
- package/dist/i18n/pt.js.map +1 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +60 -0
- package/dist/i18n/ru.js.map +1 -1
- package/dist/i18n/uk.d.ts.map +1 -1
- package/dist/i18n/uk.js +60 -0
- package/dist/i18n/uk.js.map +1 -1
- package/dist/i18n/uz.d.ts.map +1 -1
- package/dist/i18n/uz.js +60 -0
- package/dist/i18n/uz.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +60 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.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 +15 -8
- package/dist/remoteRun/runners.d.ts.map +1 -1
- package/dist/remoteRun/runners.js +59 -19
- 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 +31 -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 +18 -0
- package/dist/runnerUtility/adminCheck.d.ts.map +1 -0
- package/dist/runnerUtility/adminCheck.js +69 -0
- package/dist/runnerUtility/adminCheck.js.map +1 -0
- package/dist/runnerUtility/adminService.d.ts +22 -0
- package/dist/runnerUtility/adminService.d.ts.map +1 -0
- package/dist/runnerUtility/adminService.js +145 -0
- package/dist/runnerUtility/adminService.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 +39 -0
- package/dist/runnerUtility/linkLoop.d.ts.map +1 -0
- package/dist/runnerUtility/linkLoop.js +274 -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/pairArgs.d.ts +16 -0
- package/dist/runnerUtility/pairArgs.d.ts.map +1 -0
- package/dist/runnerUtility/pairArgs.js +17 -0
- package/dist/runnerUtility/pairArgs.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/rootServiceCli.d.ts +8 -0
- package/dist/runnerUtility/rootServiceCli.d.ts.map +1 -0
- package/dist/runnerUtility/rootServiceCli.js +31 -0
- package/dist/runnerUtility/rootServiceCli.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 +190 -0
- package/dist/runnerUtility/runnerCli.js.map +1 -0
- package/dist/runnerUtility/runnerCommands.d.ts +76 -0
- package/dist/runnerUtility/runnerCommands.d.ts.map +1 -0
- package/dist/runnerUtility/runnerCommands.js +464 -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/serviceCommon.d.ts +97 -0
- package/dist/runnerUtility/serviceCommon.d.ts.map +1 -0
- package/dist/runnerUtility/serviceCommon.js +209 -0
- package/dist/runnerUtility/serviceCommon.js.map +1 -0
- package/dist/runnerUtility/serviceManager.d.ts +51 -0
- package/dist/runnerUtility/serviceManager.d.ts.map +1 -0
- package/dist/runnerUtility/serviceManager.js +344 -0
- package/dist/runnerUtility/serviceManager.js.map +1 -0
- package/dist/runnerUtility/serviceUnits.d.ts +38 -0
- package/dist/runnerUtility/serviceUnits.d.ts.map +1 -0
- package/dist/runnerUtility/serviceUnits.js +189 -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 +13 -0
- package/dist/runners/bootstrapLine.d.ts.map +1 -0
- package/dist/runners/bootstrapLine.js +154 -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 +225 -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 +104 -0
- package/dist/runners/gateProtocol.d.ts.map +1 -0
- package/dist/runners/gateProtocol.js +151 -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 +70 -0
- package/dist/runners/hubPairing.d.ts.map +1 -0
- package/dist/runners/hubPairing.js +277 -0
- package/dist/runners/hubPairing.js.map +1 -0
- package/dist/runners/hubPaths.d.ts +42 -0
- package/dist/runners/hubPaths.d.ts.map +1 -0
- package/dist/runners/hubPaths.js +96 -0
- package/dist/runners/hubPaths.js.map +1 -0
- package/dist/runners/hubRegistry.d.ts +134 -0
- package/dist/runners/hubRegistry.d.ts.map +1 -0
- package/dist/runners/hubRegistry.js +256 -0
- package/dist/runners/hubRegistry.js.map +1 -0
- package/dist/runners/linkControl.d.ts +38 -0
- package/dist/runners/linkControl.d.ts.map +1 -0
- package/dist/runners/linkControl.js +171 -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/removalTiming.d.ts +13 -0
- package/dist/runners/removalTiming.d.ts.map +1 -0
- package/dist/runners/removalTiming.js +17 -0
- package/dist/runners/removalTiming.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 +94 -0
- package/dist/runners/runnerRemoval.js.map +1 -0
- package/dist/runners/runnerServiceKind.d.ts +10 -0
- package/dist/runners/runnerServiceKind.d.ts.map +1 -0
- package/dist/runners/runnerServiceKind.js +13 -0
- package/dist/runners/runnerServiceKind.js.map +1 -0
- package/dist/runners/runnersCli.d.ts +25 -0
- package/dist/runners/runnersCli.d.ts.map +1 -0
- package/dist/runners/runnersCli.js +239 -0
- package/dist/runners/runnersCli.js.map +1 -0
- package/dist/runners/runnersList.d.ts +14 -0
- package/dist/runners/runnersList.d.ts.map +1 -0
- package/dist/runners/runnersList.js +51 -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 +56 -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.d.ts +13 -0
- package/dist/selfUpdate/selfCheck.d.ts.map +1 -1
- package/dist/selfUpdate/selfCheck.js +9 -3
- package/dist/selfUpdate/selfCheck.js.map +1 -1
- package/dist/stt/sttInstallEntry.js +7 -4
- package/dist/stt/sttInstallEntry.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 +141 -0
- package/dist/utils/runnersScreenPlan.d.ts.map +1 -0
- package/dist/utils/runnersScreenPlan.js +272 -0
- package/dist/utils/runnersScreenPlan.js.map +1 -0
- package/package.json +5 -2
- package/remoteRun/runner.sh +81 -6
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { connectToHub } from './hubClient';
|
|
2
|
+
import { type RunnerConfig } from './runnerConfig';
|
|
3
|
+
import type { RunnerHomePaths } from './runnerHome';
|
|
4
|
+
/**
|
|
5
|
+
* @description Updating the runner to the hub's current package: the hub's `link` answer names the package id; when it is not the one
|
|
6
|
+
* installed, an IDLE runner fetches the package with the `package` request (a connection of its own), checks that the archive hashes to
|
|
7
|
+
* the id the hub announced, installs it next to the running copy, points `current` at it and records the id. The caller then exits with
|
|
8
|
+
* the restart code and the launcher starts the new copy. Nothing is changed in the running copy.
|
|
9
|
+
*/
|
|
10
|
+
export type UpdateOutcome = {
|
|
11
|
+
ok: true;
|
|
12
|
+
packageId: string;
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
reason: string;
|
|
16
|
+
};
|
|
17
|
+
export interface UpdateParams {
|
|
18
|
+
paths: RunnerHomePaths;
|
|
19
|
+
config: RunnerConfig;
|
|
20
|
+
clientPrivateKey: string;
|
|
21
|
+
connect?: typeof connectToHub;
|
|
22
|
+
}
|
|
23
|
+
/** @description Fetch, verify and install the hub's package; on success `current` and `config.json` point at it. */
|
|
24
|
+
export declare function updateFromHub(params: UpdateParams): Promise<UpdateOutcome>;
|
|
25
|
+
export interface UpdateDecisionInputs {
|
|
26
|
+
/** What the hub named in the `link` answer (`none`: it could not pack one). */
|
|
27
|
+
hubPackageId: string | undefined;
|
|
28
|
+
installedPackageId: string | null;
|
|
29
|
+
activeExecCount: number;
|
|
30
|
+
/** The id of an update that already failed, and when — it is not tried again for {@link updateRetryDelayMs}. */
|
|
31
|
+
failedPackageId: string | null;
|
|
32
|
+
failedAtMs: number;
|
|
33
|
+
nowMs: number;
|
|
34
|
+
}
|
|
35
|
+
export declare const updateRetryDelayMs: number;
|
|
36
|
+
export type UpdateDecision = 'none' | 'wait-until-idle' | 'update-now';
|
|
37
|
+
/** @description Update only to a package the hub named and the runner does not have; only while no command runs; not again right after a failure. */
|
|
38
|
+
export declare function decideUpdate(inputs: UpdateDecisionInputs): UpdateDecision;
|
|
39
|
+
//# sourceMappingURL=appUpdate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appUpdate.d.ts","sourceRoot":"","sources":["../../src/runnerUtility/appUpdate.ts"],"names":[],"mappings":"AAGA,OAAO,EAAY,YAAY,EAAoB,MAAM,aAAa,CAAC;AAGvE,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;GAKG;AAEH,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5F,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,eAAe,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,YAAY,CAAC;CAC/B;AAED,oHAAoH;AACpH,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CA2BhF;AAED,MAAM,WAAW,oBAAoB;IACnC,+EAA+E;IAC/E,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,gHAAgH;IAChH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AAEjD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,iBAAiB,GAAG,YAAY,CAAC;AAEvE,qJAAqJ;AACrJ,wBAAgB,YAAY,CAAC,MAAM,EAAE,oBAAoB,GAAG,cAAc,CAIzE"}
|
|
@@ -0,0 +1,90 @@
|
|
|
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.updateRetryDelayMs = void 0;
|
|
37
|
+
exports.updateFromHub = updateFromHub;
|
|
38
|
+
exports.decideUpdate = decideUpdate;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const gateProtocol_1 = require("../runners/gateProtocol");
|
|
42
|
+
const hubClient_1 = require("./hubClient");
|
|
43
|
+
const packageId_1 = require("./packageId");
|
|
44
|
+
const appInstall_1 = require("./appInstall");
|
|
45
|
+
const runnerConfig_1 = require("./runnerConfig");
|
|
46
|
+
/** @description Fetch, verify and install the hub's package; on success `current` and `config.json` point at it. */
|
|
47
|
+
async function updateFromHub(params) {
|
|
48
|
+
const endpoint = { host: params.config.hub.host, port: params.config.hub.port, user: params.config.hub.user, hostKey: params.config.hub.hostKey };
|
|
49
|
+
const archivePath = path.join(params.paths.root, `package.${process.pid}.tgz`);
|
|
50
|
+
try {
|
|
51
|
+
const client = await (params.connect ?? hubClient_1.connectToHub)(endpoint, params.clientPrivateKey);
|
|
52
|
+
try {
|
|
53
|
+
const answer = await (0, hubClient_1.callGate)(client, 'package', null);
|
|
54
|
+
const expectedId = answer?.marker.fields.id;
|
|
55
|
+
if (answer === null || expectedId === undefined)
|
|
56
|
+
return { ok: false, reason: 'the hub did not send the package' };
|
|
57
|
+
const chunks = [];
|
|
58
|
+
for await (const chunk of answer.channel)
|
|
59
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
60
|
+
const archive = Buffer.concat(chunks);
|
|
61
|
+
if ((0, packageId_1.getPackageIdOfArchive)(archive) !== expectedId)
|
|
62
|
+
return { ok: false, reason: 'the package the hub sent does not match the id it announced' };
|
|
63
|
+
fs.writeFileSync(archivePath, archive, { mode: 0o600 });
|
|
64
|
+
const appDir = (0, appInstall_1.installAppFromArchive)(archivePath, params.paths);
|
|
65
|
+
(0, appInstall_1.switchCurrentApp)(params.paths, appDir);
|
|
66
|
+
(0, runnerConfig_1.writeRunnerConfig)(params.paths, { ...params.config, packageId: expectedId });
|
|
67
|
+
(0, appInstall_1.removeOldApps)(params.paths);
|
|
68
|
+
return { ok: true, packageId: expectedId };
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
client.end();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return { ok: false, reason: error instanceof Error ? error.message : 'the update failed' };
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
fs.rmSync(archivePath, { force: true });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.updateRetryDelayMs = 10 * 60 * 1000;
|
|
82
|
+
/** @description Update only to a package the hub named and the runner does not have; only while no command runs; not again right after a failure. */
|
|
83
|
+
function decideUpdate(inputs) {
|
|
84
|
+
if (inputs.hubPackageId === undefined || inputs.hubPackageId === gateProtocol_1.noPackageMarker || inputs.hubPackageId === inputs.installedPackageId)
|
|
85
|
+
return 'none';
|
|
86
|
+
if (inputs.failedPackageId === inputs.hubPackageId && inputs.nowMs - inputs.failedAtMs < exports.updateRetryDelayMs)
|
|
87
|
+
return 'none';
|
|
88
|
+
return inputs.activeExecCount === 0 ? 'update-now' : 'wait-until-idle';
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=appUpdate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appUpdate.js","sourceRoot":"","sources":["../../src/runnerUtility/appUpdate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RunnerHomePaths } from './runnerHome';
|
|
2
|
+
/** @description `KEY=VALUE` lines; blank lines and `#` comments are skipped, `export ` is allowed, one pair of quotes around a value is removed. A line that is not a pair is returned in `rejected` (its number, never its text — a value may be a secret). */
|
|
3
|
+
export declare function parseEnvFile(text: string): {
|
|
4
|
+
values: Record<string, string>;
|
|
5
|
+
rejected: number[];
|
|
6
|
+
};
|
|
7
|
+
/** @description The Android SDK folder if one is where the installers put it (or the environment says), else `null`. */
|
|
8
|
+
export declare function findAndroidSdk(homeDir: string, environment: NodeJS.ProcessEnv, checkExists?: (candidate: string) => boolean): string | null;
|
|
9
|
+
export interface ExecEnvInputs {
|
|
10
|
+
paths: RunnerHomePaths;
|
|
11
|
+
accountName: string;
|
|
12
|
+
/** The utility's own environment: only LANG and TMPDIR are taken from it. */
|
|
13
|
+
environment: NodeJS.ProcessEnv;
|
|
14
|
+
nodeDir: string;
|
|
15
|
+
operatorEnv: Readonly<Record<string, string>>;
|
|
16
|
+
androidSdk: string | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildExecEnv(inputs: ExecEnvInputs): NodeJS.ProcessEnv;
|
|
19
|
+
/** @description The environment for the next command: the `env` file is read fresh each time, so an edit applies to the next job without a restart. */
|
|
20
|
+
export declare function readExecEnv(paths: RunnerHomePaths, environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
21
|
+
//# sourceMappingURL=execEnv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execEnv.d.ts","sourceRoot":"","sources":["../../src/runnerUtility/execEnv.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAapD,gQAAgQ;AAChQ,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAiBjG;AAED,wHAAwH;AACxH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,WAAW,GAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAuB,GAAG,MAAM,GAAG,IAAI,CAG1J;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,eAAe,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,UAAU,CAYrE;AAED,uJAAuJ;AACvJ,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,UAAU,CAenH"}
|
|
@@ -0,0 +1,109 @@
|
|
|
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.parseEnvFile = parseEnvFile;
|
|
37
|
+
exports.findAndroidSdk = findAndroidSdk;
|
|
38
|
+
exports.buildExecEnv = buildExecEnv;
|
|
39
|
+
exports.readExecEnv = readExecEnv;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* @description The environment of a command that runs over the link — CLEAN, like a login by a service: the runner's own environment
|
|
45
|
+
* (a launchd job's, a terminal's) never leaks into the agents' jobs. HOME, USER, LOGNAME, LANG, TMPDIR; a PATH of `~/.charness-runner/bin`
|
|
46
|
+
* (pueue, rsync), the folder of the `node` that runs the utility, the Homebrew and system folders; the Android SDK when it is where
|
|
47
|
+
* the usual installers put it; then the operator's own `KEY=VALUE` lines from `~/.charness-runner/env`.
|
|
48
|
+
*/
|
|
49
|
+
const systemPathDirs = ['/opt/homebrew/bin', '/opt/homebrew/sbin', '/usr/local/bin', '/usr/local/sbin', '/usr/bin', '/bin', '/usr/sbin', '/sbin'];
|
|
50
|
+
const defaultLang = 'en_US.UTF-8';
|
|
51
|
+
const envNamePattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
52
|
+
/** @description `KEY=VALUE` lines; blank lines and `#` comments are skipped, `export ` is allowed, one pair of quotes around a value is removed. A line that is not a pair is returned in `rejected` (its number, never its text — a value may be a secret). */
|
|
53
|
+
function parseEnvFile(text) {
|
|
54
|
+
const values = {};
|
|
55
|
+
const rejected = [];
|
|
56
|
+
text.split('\n').forEach((rawLine, index) => {
|
|
57
|
+
const line = rawLine.trim().replace(/^export\s+/, '');
|
|
58
|
+
if (line === '' || line.startsWith('#'))
|
|
59
|
+
return;
|
|
60
|
+
const separator = line.indexOf('=');
|
|
61
|
+
const name = separator === -1 ? '' : line.slice(0, separator).trim();
|
|
62
|
+
if (!envNamePattern.test(name)) {
|
|
63
|
+
rejected.push(index + 1);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
let value = line.slice(separator + 1).trim();
|
|
67
|
+
if (value.length >= 2 && (value.startsWith('"') || value.startsWith("'")) && value.endsWith(value[0]))
|
|
68
|
+
value = value.slice(1, -1);
|
|
69
|
+
values[name] = value;
|
|
70
|
+
});
|
|
71
|
+
return { values, rejected };
|
|
72
|
+
}
|
|
73
|
+
/** @description The Android SDK folder if one is where the installers put it (or the environment says), else `null`. */
|
|
74
|
+
function findAndroidSdk(homeDir, environment, checkExists = fs.existsSync) {
|
|
75
|
+
const candidates = [environment.ANDROID_HOME, environment.ANDROID_SDK_ROOT, path.join(homeDir, 'Library', 'Android', 'sdk'), path.join(homeDir, 'Android', 'Sdk'), '/opt/android-sdk'];
|
|
76
|
+
return candidates.find((candidate) => candidate !== undefined && candidate !== '' && checkExists(candidate)) ?? null;
|
|
77
|
+
}
|
|
78
|
+
function buildExecEnv(inputs) {
|
|
79
|
+
const pathDirs = [inputs.paths.binDir, inputs.nodeDir, ...systemPathDirs];
|
|
80
|
+
return {
|
|
81
|
+
HOME: inputs.paths.homeDir,
|
|
82
|
+
USER: inputs.accountName,
|
|
83
|
+
LOGNAME: inputs.accountName,
|
|
84
|
+
LANG: inputs.environment.LANG ?? defaultLang,
|
|
85
|
+
TMPDIR: inputs.environment.TMPDIR ?? os.tmpdir(),
|
|
86
|
+
PATH: [...new Set(pathDirs)].join(path.delimiter),
|
|
87
|
+
...(inputs.androidSdk === null ? {} : { ANDROID_HOME: inputs.androidSdk, ANDROID_SDK_ROOT: inputs.androidSdk }),
|
|
88
|
+
...inputs.operatorEnv,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** @description The environment for the next command: the `env` file is read fresh each time, so an edit applies to the next job without a restart. */
|
|
92
|
+
function readExecEnv(paths, environment = process.env) {
|
|
93
|
+
let operatorEnv = {};
|
|
94
|
+
try {
|
|
95
|
+
operatorEnv = parseEnvFile(fs.readFileSync(paths.envFile, 'utf8')).values;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
operatorEnv = {};
|
|
99
|
+
}
|
|
100
|
+
return buildExecEnv({
|
|
101
|
+
paths,
|
|
102
|
+
accountName: os.userInfo().username,
|
|
103
|
+
environment,
|
|
104
|
+
nodeDir: path.dirname(process.execPath),
|
|
105
|
+
operatorEnv,
|
|
106
|
+
androidSdk: findAndroidSdk(paths.homeDir, environment),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=execEnv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execEnv.js","sourceRoot":"","sources":["../../src/runnerUtility/execEnv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ServerChannel } from 'ssh2';
|
|
2
|
+
export interface ExecTracker {
|
|
3
|
+
started: () => void;
|
|
4
|
+
ended: () => void;
|
|
5
|
+
}
|
|
6
|
+
export interface ExecOptions {
|
|
7
|
+
env: NodeJS.ProcessEnv;
|
|
8
|
+
cwd: string;
|
|
9
|
+
tracker: ExecTracker;
|
|
10
|
+
}
|
|
11
|
+
/** @description Run `command` for `channel`. */
|
|
12
|
+
export declare function serveExec(channel: ServerChannel, command: string, options: ExecOptions): void;
|
|
13
|
+
//# sourceMappingURL=execSession.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execSession.d.ts","sourceRoot":"","sources":["../../src/runnerUtility/execSession.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAe1C,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,WAAW,CAAC;CACtB;AAYD,gDAAgD;AAChD,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CA2B7F"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serveExec = serveExec;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
/**
|
|
6
|
+
* @description One `exec` request of a session: the command runs under `bash -c` in a clean environment, its stdin is the channel's
|
|
7
|
+
* (the client's EOF is the command's EOF — phase 1's lease relies on it), its stdout and stderr go back on the channel's two streams,
|
|
8
|
+
* and its end is reported as an exit status or a signal.
|
|
9
|
+
*
|
|
10
|
+
* Sshd's semantics for a channel that is CLOSED by the client: the pipes close, the command is NOT killed. Here: stdout and stderr are
|
|
11
|
+
* destroyed (the command's next write fails, as with a closed pipe), its stdin is closed, and the process runs on. Only the real end
|
|
12
|
+
* of the process closes the books on it ({@link ExecTracker.ended}), which is what «the runner is busy» means.
|
|
13
|
+
*/
|
|
14
|
+
const commandNotFoundStatus = 127;
|
|
15
|
+
const signalPrefix = /^SIG/;
|
|
16
|
+
function sendExitStatus(channel, exitCode, signal) {
|
|
17
|
+
try {
|
|
18
|
+
if (signal !== null)
|
|
19
|
+
channel.exit(signal.replace(signalPrefix, ''), false, '');
|
|
20
|
+
else
|
|
21
|
+
channel.exit(exitCode ?? commandNotFoundStatus);
|
|
22
|
+
channel.end();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// The client closed the channel first: nobody is waiting for the status.
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** @description Run `command` for `channel`. */
|
|
29
|
+
function serveExec(channel, command, options) {
|
|
30
|
+
options.tracker.started();
|
|
31
|
+
const child = (0, child_process_1.spawn)('bash', ['-c', command], { env: options.env, cwd: options.cwd, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
32
|
+
child.stdin.on('error', () => undefined);
|
|
33
|
+
channel.on('error', () => undefined);
|
|
34
|
+
channel.stderr.on('error', () => undefined);
|
|
35
|
+
channel.pipe(child.stdin);
|
|
36
|
+
child.stdout.pipe(channel, { end: false });
|
|
37
|
+
child.stderr.pipe(channel.stderr, { end: false });
|
|
38
|
+
channel.once('close', () => {
|
|
39
|
+
child.stdin.destroy();
|
|
40
|
+
child.stdout.destroy();
|
|
41
|
+
child.stderr.destroy();
|
|
42
|
+
});
|
|
43
|
+
// A failed start emits `error` and then `close`: the command is finished exactly once, or the busy count would go negative.
|
|
44
|
+
let isFinished = false;
|
|
45
|
+
const finish = (exitCode, signal) => {
|
|
46
|
+
if (isFinished)
|
|
47
|
+
return;
|
|
48
|
+
isFinished = true;
|
|
49
|
+
sendExitStatus(channel, exitCode, signal);
|
|
50
|
+
options.tracker.ended();
|
|
51
|
+
};
|
|
52
|
+
child.once('error', (error) => {
|
|
53
|
+
channel.stderr.write(`charness-runner: cannot start the command (${error.message})\n`);
|
|
54
|
+
finish(commandNotFoundStatus, null);
|
|
55
|
+
});
|
|
56
|
+
child.once('close', (exitCode, signal) => finish(exitCode, signal));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=execSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execSession.js","sourceRoot":"","sources":["../../src/runnerUtility/execSession.ts"],"names":[],"mappings":";;AAsCA,8BA2BC;AAjED,iDAAsC;AAGtC;;;;;;;;GAQG;AAEH,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,YAAY,GAAG,MAAM,CAAC;AAa5B,SAAS,cAAc,CAAC,OAAsB,EAAE,QAAuB,EAAE,MAA6B;IACpG,IAAI,CAAC;QACH,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;;YAC1E,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,qBAAqB,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,SAAgB,SAAS,CAAC,OAAsB,EAAE,OAAe,EAAE,OAAoB;IACrF,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,IAAA,qBAAK,EAAC,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACtH,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACrC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1B,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3C,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;QACzB,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACtB,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IACH,4HAA4H;IAC5H,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,MAAM,MAAM,GAAG,CAAC,QAAuB,EAAE,MAA6B,EAAQ,EAAE;QAC9E,IAAI,UAAU;YAAE,OAAO;QACvB,UAAU,GAAG,IAAI,CAAC;QAClB,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC1C,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC;QACvF,MAAM,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Client, type ClientChannel } from 'ssh2';
|
|
2
|
+
import type { z } from 'zod';
|
|
3
|
+
import { type GateMarker, type GateRequest } from '../runners/gateProtocol';
|
|
4
|
+
export interface HubEndpoint {
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
user: string;
|
|
8
|
+
/** `<type> <base64>`: the only host key that is accepted. */
|
|
9
|
+
hostKey: string;
|
|
10
|
+
}
|
|
11
|
+
export type HubConnectionFailure = 'auth' | 'hostKey' | 'network';
|
|
12
|
+
/** @description Why the hub could not be reached: the key was refused (`auth` — for a paired runner, it was removed), the host key is not the pinned one, or the network/sshd. */
|
|
13
|
+
export declare class HubConnectionError extends Error {
|
|
14
|
+
readonly failure: HubConnectionFailure;
|
|
15
|
+
constructor(failure: HubConnectionFailure, message: string);
|
|
16
|
+
}
|
|
17
|
+
/** @description Connect and log in; resolves with the ready client. The caller ends it. */
|
|
18
|
+
export declare function connectToHub(endpoint: HubEndpoint, privateKey: string): Promise<Client>;
|
|
19
|
+
export interface GateAnswer {
|
|
20
|
+
marker: GateMarker;
|
|
21
|
+
/** The channel, positioned right after the marker line (what came with it is put back). */
|
|
22
|
+
channel: ClientChannel;
|
|
23
|
+
}
|
|
24
|
+
/** @description Run the gate request `request` and read its marker line; `null` when the answer has none (the hub refused, or the key line is gone). `body` is sent as stdin and ends it; without one stdin stays open (`link`). */
|
|
25
|
+
export declare function callGate(client: Client, request: GateRequest, body: string | null): Promise<GateAnswer | null>;
|
|
26
|
+
/** @description Everything the channel still sends, as text. */
|
|
27
|
+
export declare function readChannelText(channel: ClientChannel): Promise<string>;
|
|
28
|
+
/** @description A gate request whose answer is one JSON line (`redeem`, `cancel`, `identity`, `unpair`), parsed with `schema`. */
|
|
29
|
+
export declare function requestGateJson<TReply>(client: Client, request: 'redeem' | 'cancel' | 'unpair' | 'identity', body: string | null, schema: z.ZodType<TReply>): Promise<TReply>;
|
|
30
|
+
//# sourceMappingURL=hubClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubClient.d.ts","sourceRoot":"","sources":["../../src/runnerUtility/hubClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAA+B,MAAM,MAAM,CAAC;AAC/E,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAuB,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAejG,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;AAElE,kLAAkL;AAClL,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;gBAE3B,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM;CAK3D;AAiBD,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BvF;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,2FAA2F;IAC3F,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,oOAAoO;AACpO,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAkB9G;AAED,gEAAgE;AAChE,wBAAsB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAI7E;AAED,kIAAkI;AAClI,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAanL"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HubConnectionError = void 0;
|
|
4
|
+
exports.connectToHub = connectToHub;
|
|
5
|
+
exports.callGate = callGate;
|
|
6
|
+
exports.readChannelText = readChannelText;
|
|
7
|
+
exports.requestGateJson = requestGateJson;
|
|
8
|
+
const ssh2_1 = require("ssh2");
|
|
9
|
+
const gateProtocol_1 = require("../runners/gateProtocol");
|
|
10
|
+
const sshKeys_1 = require("../runners/sshKeys");
|
|
11
|
+
/**
|
|
12
|
+
* @description The runner's side of every conversation with the hub's sshd: an ssh2 client that pins the hub's host key (the one in the
|
|
13
|
+
* pairing code, then in `config.json`), logs in with a key, and runs one of the gate's request words (`redeem`, `package`, `link`,
|
|
14
|
+
* `unpair`). Whatever the hub's shell printed before the gate ran is skipped up to the marker line (`readUntilGateMarker`).
|
|
15
|
+
*/
|
|
16
|
+
const connectReadyTimeoutMs = 20 * 1000;
|
|
17
|
+
const keepaliveIntervalMs = 15 * 1000;
|
|
18
|
+
const keepaliveCountMax = 3;
|
|
19
|
+
const clientAuthenticationLevel = 'client-authentication';
|
|
20
|
+
const hostDeniedText = 'Host denied';
|
|
21
|
+
/** @description Why the hub could not be reached: the key was refused (`auth` — for a paired runner, it was removed), the host key is not the pinned one, or the network/sshd. */
|
|
22
|
+
class HubConnectionError extends Error {
|
|
23
|
+
failure;
|
|
24
|
+
constructor(failure, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'HubConnectionError';
|
|
27
|
+
this.failure = failure;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.HubConnectionError = HubConnectionError;
|
|
31
|
+
/** The signature algorithms the client offers for each key type: only those of the pinned key, so a hub with several host keys cannot answer with another one. */
|
|
32
|
+
const hostKeyAlgorithms = {
|
|
33
|
+
'ssh-ed25519': ['ssh-ed25519'],
|
|
34
|
+
'ecdsa-sha2-nistp256': ['ecdsa-sha2-nistp256'],
|
|
35
|
+
'ecdsa-sha2-nistp384': ['ecdsa-sha2-nistp384'],
|
|
36
|
+
'ecdsa-sha2-nistp521': ['ecdsa-sha2-nistp521'],
|
|
37
|
+
'ssh-rsa': ['rsa-sha2-512', 'rsa-sha2-256', 'ssh-rsa'],
|
|
38
|
+
};
|
|
39
|
+
function classifyFailure(error) {
|
|
40
|
+
if (error.level === clientAuthenticationLevel)
|
|
41
|
+
return new HubConnectionError('auth', 'the hub refused the runner\'s key');
|
|
42
|
+
if (error.message.includes(hostDeniedText))
|
|
43
|
+
return new HubConnectionError('hostKey', 'the hub\'s ssh host key is not the one pinned at pairing');
|
|
44
|
+
return new HubConnectionError('network', error.message);
|
|
45
|
+
}
|
|
46
|
+
/** @description Connect and log in; resolves with the ready client. The caller ends it. */
|
|
47
|
+
function connectToHub(endpoint, privateKey) {
|
|
48
|
+
const pinned = (0, sshKeys_1.parsePublicKey)(endpoint.hostKey);
|
|
49
|
+
const serverHostKey = pinned === null ? undefined : hostKeyAlgorithms[pinned.type];
|
|
50
|
+
if (pinned === null || serverHostKey === undefined)
|
|
51
|
+
return Promise.reject(new HubConnectionError('hostKey', 'the pinned hub host key does not read'));
|
|
52
|
+
const pinnedBlob = pinned.blob;
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const client = new ssh2_1.Client();
|
|
55
|
+
let isReady = false;
|
|
56
|
+
client.once('ready', () => {
|
|
57
|
+
isReady = true;
|
|
58
|
+
resolve(client);
|
|
59
|
+
});
|
|
60
|
+
// Kept for the life of the client: an error after `ready` (a reset connection) must never be an unhandled event; whoever uses the
|
|
61
|
+
// client listens for `close` / `error` itself.
|
|
62
|
+
client.on('error', (error) => {
|
|
63
|
+
if (isReady)
|
|
64
|
+
return;
|
|
65
|
+
client.end();
|
|
66
|
+
reject(classifyFailure(error));
|
|
67
|
+
});
|
|
68
|
+
client.connect({
|
|
69
|
+
host: endpoint.host,
|
|
70
|
+
port: endpoint.port,
|
|
71
|
+
username: endpoint.user,
|
|
72
|
+
privateKey,
|
|
73
|
+
algorithms: { serverHostKey },
|
|
74
|
+
hostVerifier: (key) => Buffer.isBuffer(key) && key.equals(pinnedBlob),
|
|
75
|
+
keepaliveInterval: keepaliveIntervalMs,
|
|
76
|
+
keepaliveCountMax,
|
|
77
|
+
readyTimeout: connectReadyTimeoutMs,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/** @description Run the gate request `request` and read its marker line; `null` when the answer has none (the hub refused, or the key line is gone). `body` is sent as stdin and ends it; without one stdin stays open (`link`). */
|
|
82
|
+
function callGate(client, request, body) {
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
client.exec(request, async (error, channel) => {
|
|
85
|
+
if (error !== undefined) {
|
|
86
|
+
reject(new HubConnectionError('network', error.message));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (body !== null)
|
|
90
|
+
channel.end(body);
|
|
91
|
+
const read = await (0, gateProtocol_1.readUntilGateMarker)(channel);
|
|
92
|
+
if (read === null || read.marker.request !== request) {
|
|
93
|
+
channel.destroy();
|
|
94
|
+
resolve(null);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
channel.unshift(read.rest);
|
|
98
|
+
resolve({ marker: read.marker, channel });
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** @description Everything the channel still sends, as text. */
|
|
103
|
+
async function readChannelText(channel) {
|
|
104
|
+
const chunks = [];
|
|
105
|
+
for await (const chunk of channel)
|
|
106
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
107
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
108
|
+
}
|
|
109
|
+
/** @description A gate request whose answer is one JSON line (`redeem`, `cancel`, `identity`, `unpair`), parsed with `schema`. */
|
|
110
|
+
async function requestGateJson(client, request, body, schema) {
|
|
111
|
+
const answer = await callGate(client, request, body ?? '');
|
|
112
|
+
if (answer === null)
|
|
113
|
+
throw new HubConnectionError('network', 'the hub did not answer the request');
|
|
114
|
+
const text = await readChannelText(answer.channel);
|
|
115
|
+
let parsedJson;
|
|
116
|
+
try {
|
|
117
|
+
parsedJson = JSON.parse(text);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
throw new HubConnectionError('network', 'the hub\'s answer is not valid JSON');
|
|
121
|
+
}
|
|
122
|
+
const parsed = schema.safeParse(parsedJson);
|
|
123
|
+
if (!parsed.success)
|
|
124
|
+
throw new HubConnectionError('network', 'the hub\'s answer is not what this version expects');
|
|
125
|
+
return parsed.data;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=hubClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubClient.js","sourceRoot":"","sources":["../../src/runnerUtility/hubClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Duplex } from 'stream';
|
|
2
|
+
import type { Server } from 'ssh2';
|
|
3
|
+
/**
|
|
4
|
+
* @description ssh2's `Server.injectSocket` is typed for a `net.Socket` but takes any bidirectional stream «as though it were a TCP
|
|
5
|
+
* socket connection» — the channel the hub's gate speaks over, and a `direct-tcpip` channel of that session, are such streams; this is
|
|
6
|
+
* how the runner serves ssh over a link. The one cast the library's typing needs is here.
|
|
7
|
+
*/
|
|
8
|
+
export declare function injectStreamIntoSshServer(server: Server, stream: Duplex): void;
|
|
9
|
+
//# sourceMappingURL=injectStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injectStream.d.ts","sourceRoot":"","sources":["../../src/runnerUtility/injectStream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9E"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.injectStreamIntoSshServer = injectStreamIntoSshServer;
|
|
4
|
+
/**
|
|
5
|
+
* @description ssh2's `Server.injectSocket` is typed for a `net.Socket` but takes any bidirectional stream «as though it were a TCP
|
|
6
|
+
* socket connection» — the channel the hub's gate speaks over, and a `direct-tcpip` channel of that session, are such streams; this is
|
|
7
|
+
* how the runner serves ssh over a link. The one cast the library's typing needs is here.
|
|
8
|
+
*/
|
|
9
|
+
function injectStreamIntoSshServer(server, stream) {
|
|
10
|
+
server.injectSocket(stream);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=injectStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injectStream.js","sourceRoot":"","sources":["../../src/runnerUtility/injectStream.ts"],"names":[],"mappings":";;AASA,8DAEC;AAPD;;;;GAIG;AACH,SAAgB,yBAAyB,CAAC,MAAc,EAAE,MAAc;IACtE,MAAM,CAAC,YAAY,CAAC,MAAgB,CAAC,CAAC;AACxC,CAAC"}
|