mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
fix(desktop): create control-socket dir before opening the SSH master
OpenSSH does not create intermediate directories for ControlPath, so on a fresh box (no prior hermes-desktop-ssh dir under \$TMPDIR) the very first connect failed when ssh tried to create the master socket. Unit tests mock spawn and never hit real fs, so this was invisible. open() now mkdir -p (mode 0700 — the socket grants command execution on the master) the control-socket directory before spawning ssh. Mirrors ssh.py. Added a test that open() creates a non-existent control dir.
This commit is contained in:
parent
28d7472fb4
commit
cc96ac617a
2 changed files with 30 additions and 0 deletions
|
|
@ -36,6 +36,7 @@ const crypto = require('node:crypto')
|
|||
const net = require('node:net')
|
||||
const os = require('node:os')
|
||||
const path = require('node:path')
|
||||
const fs = require('node:fs')
|
||||
|
||||
const DEFAULT_CONNECT_TIMEOUT_MS = 15_000
|
||||
const DEFAULT_EXEC_TIMEOUT_MS = 20_000
|
||||
|
|
@ -354,6 +355,15 @@ class SshConnection {
|
|||
this._opened = true
|
||||
return
|
||||
}
|
||||
// Ensure the control-socket directory exists — OpenSSH will not create
|
||||
// intermediate dirs for ControlPath, so a fresh box (no prior hermes-ssh
|
||||
// socket dir under $TMPDIR) would otherwise fail before the first connect.
|
||||
// 0o700: the socket grants command execution on the master; keep it private.
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(this.controlPath), { recursive: true, mode: 0o700 })
|
||||
} catch {
|
||||
// best effort — a pre-existing dir or a races-with-another-conn mkdir is fine
|
||||
}
|
||||
const args = buildMasterArgs(this, this._connectTimeoutMs)
|
||||
this._logLine(`opening control master to ${target(this.user, this.host)}:${this.port}`)
|
||||
let result
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
const test = require('node:test')
|
||||
const assert = require('node:assert/strict')
|
||||
const { EventEmitter } = require('node:events')
|
||||
const fs = require('node:fs')
|
||||
const os = require('node:os')
|
||||
const path = require('node:path')
|
||||
|
||||
const {
|
||||
SSH_ERROR,
|
||||
|
|
@ -247,6 +250,23 @@ test('open() is a no-op when the master is already alive', async () => {
|
|||
assert.deepEqual(ops, ['check'], 'alive master → no second spawn to open it')
|
||||
})
|
||||
|
||||
test('open() creates the control-socket directory if it does not exist', async () => {
|
||||
const dir = path.join(os.tmpdir(), `hermes-ssh-test-${process.pid}-${Date.now()}`)
|
||||
assert.ok(!fs.existsSync(dir), 'precondition: control dir absent')
|
||||
const spawnFn = scriptedSpawn(args => (args.includes('check') ? { code: 255 } : { code: 0 }))
|
||||
const conn = new SshConnection({ host: 'box', user: 'me' }, { spawnFn, controlDir: dir })
|
||||
try {
|
||||
await conn.open()
|
||||
assert.ok(fs.existsSync(dir), 'open() created the control-socket directory before spawning ssh')
|
||||
} finally {
|
||||
try {
|
||||
fs.rmSync(dir, { recursive: true, force: true })
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test('open() surfaces a classified auth error', async () => {
|
||||
const spawnFn = scriptedSpawn(args => {
|
||||
if (args.includes('check')) return { code: 255 }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue