mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 00:03:20 +00:00
fix(ssh): frame Windows runtime transfer completion
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
+10
-15
@@ -9,21 +9,16 @@ keeps commands, hashes, runner identities, timings, and failure details.
|
||||
A checked box means the work has evidence in the detailed ledger. Design approval alone does not
|
||||
complete a box.
|
||||
|
||||
Active checkpoint: **Milestone 6 / Work Package 5 bounded runtime transfer — explicit remote-command
|
||||
dialect correction locally green, exact-head native rerun next, 2026-07-17, Codex implementation
|
||||
owner.** Exact head
|
||||
`34b51eb12640795d1cdd99a17c6fbadda79c2f60`, run `29558649829`, and Windows x64/ARM64 jobs
|
||||
`87816177268`/`87816177284` prove pinned launcher control in 734.8928/681.1723 ms and advance the
|
||||
injected connection beyond its probe. Both then fail the first Windows staging command because the
|
||||
generic system-SSH file channel applies the POSIX `exec /bin/sh -c` wrapper to an already complete
|
||||
PowerShell command; no payload byte or transfer metric runs. All four primary non-Windows jobs and
|
||||
both Linux oldest-userland supplements pass. Add only an explicit `posix`/`powershell` command
|
||||
dialect at the file-channel boundary, preserving the exact POSIX call and using `wrapCommand: false`
|
||||
only for PowerShell. `E-M6-WINDOWS-COMMAND-DIALECT-CORRECTION-LOCAL-001` records 40 focused cases,
|
||||
285 release contracts, typecheck, full lint/reliability/max-lines, formatting, exact call-site audit,
|
||||
diff, and resolver isolation green. Commit/push this exact package and require native full-size,
|
||||
cancellation, concurrency, reuse, and exact teardown proof. No product/default caller, Beta,
|
||||
fallback, tuple, publication, or signing behavior is authorized.
|
||||
Active checkpoint: **Milestone 6 / Work Package 5 bounded runtime transfer — explicit Windows
|
||||
completion framing locally green, exact-head native rerun next, 2026-07-17, Codex implementation
|
||||
owner.** Exact head `ad3c3975c099f0e197661091b1cec9b0a0d6d553`, run `29559577952`, and Windows
|
||||
x64/ARM64 jobs `87818965702`/`87818965757` independently time out after 1,200,018/1,200,015 ms with
|
||||
one fixture-user command chain retained. The isolated correction replaces final stdin EOF reads with
|
||||
an exact `ORCAEND1` frame while preserving local size and remote frame validation. Local evidence is
|
||||
green through 54 focused cases, 697 broad relay cases, 285 release contracts, typecheck, full
|
||||
lint/reliability/max-lines, formatting, diff, and resolver isolation. Commit/push only this package
|
||||
and require fresh x64/ARM64 full-size, cancellation, concurrency, reuse, and exact teardown proof.
|
||||
No product/default caller, Beta, fallback, tuple, publication, or signing behavior is authorized.
|
||||
`E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-PORTABLE-FIXTURE-LOCAL-001` records the portable-fixture
|
||||
correction locally green: the no-Orca-publication workflow oracle permits the one exact pinned
|
||||
Microsoft fixture release URL while Orca release URLs, `gh release`, and `contents: write` remain
|
||||
|
||||
+79
-6
@@ -8,8 +8,8 @@ work; keep exact commands, runner identities, hashes, metrics, and residual gaps
|
||||
|
||||
Date created: 2026-07-14<br>
|
||||
Last updated: 2026-07-17<br>
|
||||
Current phase: Milestone 6 / Work Package 5 bounded runtime transfer — **In progress — explicit remote-command dialect correction exact-head native qualification, 2026-07-17, Codex implementation owner**. Exact head `34b51eb12640795d1cdd99a17c6fbadda79c2f60`, run `29558649829`, and x64/ARM64 jobs `87816177268`/`87816177284` select a required `posix`/`powershell` file-channel dialect. `E-M6-WINDOWS-COMMAND-DIALECT-CORRECTION-LOCAL-001` records the correction locally green: POSIX retains `connection.exec(command)` exactly and PowerShell alone uses `{ wrapCommand: false }`. Commit/push only this package and require fresh native x64/ARM64 proof. Legacy remains the sole production path; real Apple/SignPath rehearsals remain deferred.<br>
|
||||
Session checkpoint: **COMMAND DIALECT CORRECTION LOCAL GREEN / NATIVE RERUN NEXT — 2026-07-17, Codex implementation owner** — 40 focused cases with two declared live skips, 285 release contracts with eight declared skips, typecheck, lint/reliability/max-lines, formatting, exact call-site audit, diff, and protected-resolver isolation pass. Full-size transfer, cancellation, concurrency, reuse, and exact profile teardown remain unproven. No product caller, upload, default/fallback, Beta, tuple publication, or signing behavior changed.<br>
|
||||
Current phase: Milestone 6 / Work Package 5 bounded runtime transfer — **In progress — explicit Windows completion framing locally green, exact-head native qualification next, 2026-07-17, Codex implementation owner**. Exact correction head `ad3c3975c099f0e197661091b1cec9b0a0d6d553`, run `29559577952`, and Windows x64/ARM64 jobs `87818965702`/`87818965757` independently time out after 1,200,018/1,200,015 ms with a fixture-user command chain still waiting. `E-M6-WINDOWS-FRAMED-COMPLETION-LOCAL-001` replaces the receiver's EOF dependency with an exact `ORCAEND1` frame while retaining local byte-count and remote frame validation; focused, broad, release, typecheck, lint, formatting, diff, and resolver-isolation gates pass. Commit/push only this package and require fresh native full-size/cancellation/concurrency/reuse/teardown proof. Legacy remains the sole production path; real Apple/SignPath rehearsals remain deferred.<br>
|
||||
Session checkpoint: **WINDOWS FRAMED COMPLETION LOCAL GREEN / NATIVE RERUN NEXT — 2026-07-17, Codex implementation owner** — 54 focused cases with three declared skips, 697 broad relay cases with ten declared skips, 285 release contracts with eight declared skips, typecheck, lint/reliability/max-lines, formatting, diff, and protected-resolver isolation pass. Both PowerShell receivers are now length-delimited and completion-framed, with no stdin-EOF read; local short/long payload mismatches abort boundedly. Native proof remains required. No product caller, upload, default/fallback, Beta, tuple publication, or signing behavior changed.<br>
|
||||
Primary design: [SSH relay GitHub Release plan](./2026-07-14-ssh-relay-github-release-plan.html)<br>
|
||||
Motivating issues: [#8450](https://github.com/stablyai/orca/issues/8450), [#1693](https://github.com/stablyai/orca/issues/1693)
|
||||
|
||||
@@ -20346,6 +20346,79 @@ lint` passes in 18.59 seconds real with the 26 existing warnings, all 41 reliabi
|
||||
require exact-head x64/ARM64 full-size serial/concurrent/collision/cancellation metrics,
|
||||
connection reuse, and exact fixture profile/directory teardown before advancing.
|
||||
|
||||
### E-M6-WINDOWS-COMMAND-DIALECT-CI-RED-001 — both architectures retain an EOF-dependent receiver
|
||||
|
||||
- Date/owner/source/run: 2026-07-17, Codex implementation owner; exact head
|
||||
`ad3c3975c099f0e197661091b1cec9b0a0d6d553`, artifact run
|
||||
[29559577952](https://github.com/stablyai/orca/actions/runs/29559577952), Windows x64 job
|
||||
[87818965702](https://github.com/stablyai/orca/actions/runs/29559577952/job/87818965702) on
|
||||
`windows-2022`, and native ARM64 job
|
||||
[87818965757](https://github.com/stablyai/orca/actions/runs/29559577952/job/87818965757) on
|
||||
`windows-11-arm`; both use Node 24.18.0 and official OpenSSH 10.0p2. X64 uses PowerShell
|
||||
5.1.20348.4294 and ARM64 uses 5.1.26100.8655.
|
||||
- Preserved gates: each Windows job passes 101 artifact files, 864 runnable cases, and 19 declared
|
||||
skips before the live step, including launcher compilation/lifecycle, exact clean-build identity,
|
||||
runtime smoke, and source/cache boundaries. X64 measures 42 files / 96,527,161 bytes; ARM64
|
||||
measures 42 / 85,213,511. Linux x64/ARM64 jobs `87818965677`/`87818965726`, Darwin x64/ARM64 jobs
|
||||
`87818965712`/`87818965737`, and Linux oldest-userland supplements
|
||||
`87819839680`/`87819839678` pass at the same exact head.
|
||||
- Pinned trust: the standalone strict-trust launcher succeeds with sentinel, ended stdout, natural
|
||||
process exit/channel close 0, and zero stderr in 400.4242 ms on x64 and 719.5839 ms on ARM64.
|
||||
This independently preserves the earlier launcher/trust correction before the full-size test.
|
||||
- Exact RED: the full-size test times out at its 1,200,000 ms ceiling after 1,200,018 ms on x64 and
|
||||
1,200,015 ms on ARM64. Neither emits serial/concurrent/collision/cancellation metrics. X64
|
||||
teardown finds fixture-owned `sshd-session.exe`, `cmd.exe`, `conhost.exe`, and `powershell.exe`;
|
||||
ARM64 independently finds `cmd.exe`, `conhost.exe`, and `powershell.exe`. Both then correctly
|
||||
report the still-locked `UsrClass.dat` instead of hiding fixture residue.
|
||||
- Classification: command routing now reaches a complete PowerShell receiver, but both control and
|
||||
file protocols still demand a final stdin EOF after already length-delimited bytes. The retained
|
||||
command chains select that shared EOF-dependent completion boundary as the next isolated
|
||||
hypothesis; they do not prove which transfer phase was active, so no full-size or teardown item is
|
||||
credited. Replace only the final EOF check with an explicit fixed completion frame, retain exact
|
||||
local byte-count and remote frame validation, and require fresh x64/ARM64 proof.
|
||||
|
||||
### E-M6-WINDOWS-FRAMED-COMPLETION-LOCAL-001 — explicit completion framing is locally green
|
||||
|
||||
- Date/owner/base/runner: 2026-07-17, Codex implementation owner; uncommitted correction atop exact
|
||||
native RED head `ad3c3975c099f0e197661091b1cec9b0a0d6d553` on Apple arm64, macOS 26.2 build
|
||||
25C56, Node 26.5.0, and pnpm 10.24.0. The repository requests Node 24; native jobs remain
|
||||
authoritative.
|
||||
- Implementation boundary: Windows staging-control requests and file streams now end with the exact
|
||||
eight-byte ASCII frame `ORCAEND1`; their PowerShell 5.1 receivers read and verify that frame after
|
||||
their already bounded length fields and never wait for stdin EOF. The file destination counts
|
||||
accepted payload bytes locally, aborts boundedly on short/long input, and writes the completion
|
||||
frame only after the declared size is exact. The remote receiver still uses `CreateNew`, exact
|
||||
payload length, fixed 64 KiB buffering, and failure cleanup. POSIX/SFTP, SSH authentication,
|
||||
connection reuse, launcher/trust, product/default, Beta, fallback, tuple, publication, and signing
|
||||
behavior are unchanged.
|
||||
- Focused evidence: an initial focused run exposed two outdated test assumptions (closing without
|
||||
the declared payload and asserting close synchronously); all product cases passed. After correcting
|
||||
those framing oracles, `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-runtime-command-file-destination.test.ts
|
||||
src/main/ssh/ssh-relay-runtime-system-ssh-file-channel.test.ts
|
||||
src/main/ssh/ssh-relay-runtime-windows-file-destination.test.ts
|
||||
src/main/ssh/ssh-relay-runtime-windows-staging-control.test.ts
|
||||
src/main/ssh/ssh-relay-runtime-windows-tree-transfer.test.ts
|
||||
src/main/ssh/ssh-relay-runtime-windows-system-ssh-openssh-full-size.test.ts` exits 0 with four
|
||||
files passed, one live file skipped, 54 runnable cases passed, and three declared skips in 3.26
|
||||
seconds real; maximum RSS is 146,571,264 bytes and peak footprint is 97,474,416 bytes.
|
||||
- Broad/release evidence: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 src/main/ssh/ssh-relay-*.test.ts` exits 0 with 53 files passed, six declared skipped,
|
||||
697 runnable cases passed, and ten declared skips in 23.32 seconds real; maximum RSS is
|
||||
301,826,048 bytes and peak footprint is 96,818,888 bytes. The exact release-contract command exits
|
||||
0 with 51 files, 285 runnable cases, and eight declared skips in 12.44 seconds real; maximum RSS is
|
||||
195,035,136 bytes and peak footprint is 97,294,144 bytes.
|
||||
- Static/isolation evidence: `/usr/bin/time -l pnpm typecheck` exits 0 in 2.86 seconds real with
|
||||
1,242,497,024-byte maximum RSS and 97,031,904-byte peak footprint. `/usr/bin/time -l pnpm lint`
|
||||
exits 0 in 13.52 seconds real with the existing 26 warnings, all 41 reliability gates, and the
|
||||
355-entry max-lines ratchet; maximum RSS is 2,110,308,352 bytes and peak footprint is 97,441,720
|
||||
bytes. Targeted `oxfmt --check`, `git diff --check`, exact no-`ReadByte()`/frame source audit, and
|
||||
protected-resolver comparison pass.
|
||||
- Residual gate: local Darwin declares the PowerShell/live cases skipped. Commit/push this exact
|
||||
package and require fresh native x64/ARM64 receiver syntax, full-size serial/concurrent/collision/
|
||||
cancellation metrics, connection reuse, and exact fixture profile/directory teardown before
|
||||
advancing.
|
||||
|
||||
## Accepted Gaps
|
||||
|
||||
No product gap is accepted merely because it appears in this list. Each entry requires explicit
|
||||
@@ -20404,10 +20477,10 @@ The project is not complete until every applicable item below is checked with ev
|
||||
|
||||
## Next Required Action
|
||||
|
||||
Commit and push only the explicit command-dialect correction recorded by
|
||||
`E-M6-WINDOWS-COMMAND-DIALECT-CORRECTION-LOCAL-001`, then require exact-head Windows x64/ARM64
|
||||
full-size transfer, cancellation, concurrency, connection reuse, and exact profile/directory
|
||||
teardown evidence. Do not check the broad Milestone 6 Windows system-SSH item or add an
|
||||
Commit and push only the explicit Windows completion-framing correction recorded by
|
||||
`E-M6-WINDOWS-FRAMED-COMPLETION-LOCAL-001`, then require exact-head Windows x64/ARM64 full-size
|
||||
transfer, cancellation, concurrency, connection reuse, and exact profile/directory teardown
|
||||
evidence. Do not check the broad Milestone 6 Windows system-SSH item or add an
|
||||
Electron/startup/product importer, per-target mode wiring, fallback, tuple
|
||||
enablement, release publication, or default behavior.
|
||||
Keep Node upstream `.tar.xz` inputs, Windows ZIP, `ORCA_RELAY_PATH`, existing desktop required-assets
|
||||
|
||||
@@ -38,6 +38,8 @@ function decodeHeader(frame: Buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
const completionFrame = Buffer.from('ORCAEND1', 'ascii')
|
||||
|
||||
function decodePowerShellCommand(command: string): string {
|
||||
const encoded = command.match(/-EncodedCommand\s+([A-Za-z0-9+/=]+)$/u)?.[1]
|
||||
if (!encoded) {
|
||||
@@ -89,7 +91,8 @@ describe('SSH relay runtime Windows system-SSH file destination', () => {
|
||||
expect(script).toContain('[IO.FileMode]::CreateNew')
|
||||
expect(script).toContain('[IO.FileShare]::None')
|
||||
expect(script).toContain('New-Object byte[] 65536')
|
||||
expect(script).toContain('$inputStream.ReadByte() -ne -1')
|
||||
expect(script).toContain("-ne 'ORCAEND1'")
|
||||
expect(script).not.toContain('$inputStream.ReadByte()')
|
||||
for (const forbidden of ['ReadToEnd', 'CopyTo(', 'FromBase64String', 'ConvertFrom-Json']) {
|
||||
expect(script).not.toContain(forbidden)
|
||||
}
|
||||
@@ -108,7 +111,10 @@ describe('SSH relay runtime Windows system-SSH file destination', () => {
|
||||
|
||||
headerCallback?.()
|
||||
const destination = await opening
|
||||
await destination.write(Buffer.alloc(65_537, 7))
|
||||
const closing = destination.close()
|
||||
await vi.waitFor(() => expect(channel.write).toHaveBeenCalledTimes(3))
|
||||
expect(channel.write.mock.calls[2]?.[0]).toEqual(completionFrame)
|
||||
resolve()
|
||||
await closing
|
||||
})
|
||||
@@ -161,14 +167,33 @@ describe('SSH relay runtime Windows system-SSH file destination', () => {
|
||||
expect(channel.forceClose).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('propagates receiver failure only after EOF and remote settlement', async () => {
|
||||
it('propagates receiver failure only after framed completion and remote settlement', async () => {
|
||||
const { channel, reject } = createChannel()
|
||||
const destination = await openDestination(channel, { expectedSize: 0 })
|
||||
const closing = destination.close()
|
||||
|
||||
expect(channel.end).toHaveBeenCalledOnce()
|
||||
reject(new Error('receiver rejected extra byte'))
|
||||
await expect(closing).rejects.toThrow('receiver rejected extra byte')
|
||||
await vi.waitFor(() => expect(channel.end).toHaveBeenCalledOnce())
|
||||
expect(channel.write.mock.calls[1]?.[0]).toEqual(completionFrame)
|
||||
reject(new Error('receiver rejected completion frame'))
|
||||
await expect(closing).rejects.toThrow('receiver rejected completion frame')
|
||||
})
|
||||
|
||||
it.each([
|
||||
['short', 2, Buffer.from([1])],
|
||||
['long', 1, Buffer.from([1, 2])]
|
||||
])('rejects a %s payload locally and aborts its channel', async (_kind, expectedSize, bytes) => {
|
||||
const { channel, resolve } = createChannel()
|
||||
channel.requestClose.mockImplementation(() => resolve())
|
||||
const destination = await openDestination(channel, { expectedSize })
|
||||
|
||||
if (bytes.length > expectedSize) {
|
||||
await expect(destination.write(bytes)).rejects.toThrow(/payload size mismatch/i)
|
||||
} else {
|
||||
await destination.write(bytes)
|
||||
await expect(destination.close()).rejects.toThrow(/payload size mismatch/i)
|
||||
}
|
||||
expect(channel.requestClose).toHaveBeenCalledOnce()
|
||||
expect(channel.forceClose).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it.skipIf(process.platform !== 'win32')(
|
||||
@@ -211,8 +236,12 @@ describe('SSH relay runtime Windows system-SSH file destination', () => {
|
||||
signal: new AbortController().signal,
|
||||
openChannel: openPowerShellChannel
|
||||
})
|
||||
await destination.write(bytes)
|
||||
await expect(destination.close()).rejects.toThrow(/PowerShell receiver failed/i)
|
||||
if (bytes.length > expectedSize) {
|
||||
await expect(destination.write(bytes)).rejects.toThrow(/payload size mismatch/i)
|
||||
} else {
|
||||
await destination.write(bytes)
|
||||
await expect(destination.close()).rejects.toThrow(/payload size mismatch/i)
|
||||
}
|
||||
await expect(readFile(remotePath)).rejects.toMatchObject({ code: 'ENOENT' })
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -14,12 +14,14 @@ export type OpenSshRelayRuntimeWindowsFileDestinationOptions = Readonly<{
|
||||
}>
|
||||
|
||||
const HEADER_MAGIC = Buffer.from('ORCARLY1', 'ascii')
|
||||
const COMPLETION_MAGIC = Buffer.from('ORCAEND1', 'ascii')
|
||||
const FIXED_HEADER_BYTES = 20
|
||||
const MAXIMUM_PATH_BYTES = 32 * 1024
|
||||
const PAYLOAD_BUFFER_BYTES = 64 * 1024
|
||||
|
||||
export const SSH_RELAY_RUNTIME_WINDOWS_FILE_DESTINATION_LIMITS = Object.freeze({
|
||||
maximumHeaderBytes: FIXED_HEADER_BYTES + MAXIMUM_PATH_BYTES,
|
||||
completionBytes: COMPLETION_MAGIC.length,
|
||||
maximumPathBytes: MAXIMUM_PATH_BYTES,
|
||||
payloadBufferBytes: PAYLOAD_BUFFER_BYTES
|
||||
})
|
||||
@@ -61,7 +63,9 @@ try {
|
||||
$outputStream.Write($buffer, 0, $read)
|
||||
$remaining -= $read
|
||||
}
|
||||
if ($inputStream.ReadByte() -ne -1) { throw 'SSH relay runtime Windows receiver extra byte' }
|
||||
[byte[]]$completion = New-Object byte[] ${COMPLETION_MAGIC.length}
|
||||
Read-Exact $inputStream $completion ${COMPLETION_MAGIC.length}
|
||||
if ([Text.Encoding]::ASCII.GetString($completion) -ne 'ORCAEND1') { throw 'bad completion' }
|
||||
$outputStream.Flush()
|
||||
$outputStream.Dispose()
|
||||
$outputStream = $null
|
||||
@@ -142,7 +146,6 @@ export async function openSshRelayRuntimeWindowsFileDestination(
|
||||
try {
|
||||
// Why: the receiver must authenticate framing before any source buffer can be borrowed.
|
||||
await destination.write(header)
|
||||
return destination
|
||||
} catch (error) {
|
||||
try {
|
||||
await destination.abort(error)
|
||||
@@ -154,4 +157,51 @@ export async function openSshRelayRuntimeWindowsFileDestination(
|
||||
}
|
||||
throw error
|
||||
}
|
||||
|
||||
let payloadBytes = 0
|
||||
const abortForSizeMismatch = async (): Promise<never> => {
|
||||
const error = new Error('SSH relay runtime Windows file destination payload size mismatch')
|
||||
try {
|
||||
await destination.abort(error)
|
||||
} catch (cleanupError) {
|
||||
throw new AggregateError(
|
||||
[error, cleanupError],
|
||||
'SSH relay runtime Windows file size cleanup failed'
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
return Object.freeze({
|
||||
write: async (chunk: Buffer): Promise<void> => {
|
||||
if (!Buffer.isBuffer(chunk) || chunk.length === 0) {
|
||||
return destination.write(chunk)
|
||||
}
|
||||
if (payloadBytes + chunk.length > options.expectedSize) {
|
||||
return abortForSizeMismatch()
|
||||
}
|
||||
await destination.write(chunk)
|
||||
payloadBytes += chunk.length
|
||||
},
|
||||
close: async (): Promise<void> => {
|
||||
if (payloadBytes !== options.expectedSize) {
|
||||
return abortForSizeMismatch()
|
||||
}
|
||||
try {
|
||||
// Why: Win32-OpenSSH may retain stdin EOF; an explicit frame ends verified bytes instead.
|
||||
await destination.write(COMPLETION_MAGIC)
|
||||
await destination.close()
|
||||
} catch (error) {
|
||||
try {
|
||||
await destination.abort(error)
|
||||
} catch (cleanupError) {
|
||||
throw new AggregateError(
|
||||
[error, cleanupError],
|
||||
'SSH relay runtime Windows file completion cleanup failed'
|
||||
)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
},
|
||||
abort: (reason: unknown): Promise<void> => destination.abort(reason)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ function decodeRequest(frame: Buffer) {
|
||||
operation: frame.readUInt8(8),
|
||||
reserved: frame.subarray(9, 12),
|
||||
remoteRoot: frame.subarray(rootStart, pathStart).toString('utf8'),
|
||||
remotePath: frame.subarray(pathStart, pathStart + pathLength).toString('utf8')
|
||||
remotePath: frame.subarray(pathStart, pathStart + pathLength).toString('utf8'),
|
||||
completion: frame.subarray(pathStart + pathLength).toString('ascii')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +97,8 @@ describe('SSH relay runtime Windows system-SSH staging control', () => {
|
||||
expect(script).toContain('New-Item -ItemType Directory -Path')
|
||||
expect(script).toContain('[IO.Path]::GetDirectoryName')
|
||||
expect(script).toContain('[IO.Directory]::Delete')
|
||||
expect(script).toContain('$inputStream.ReadByte() -ne -1')
|
||||
expect(script).toContain("-ne 'ORCAEND1'")
|
||||
expect(script).not.toContain('$inputStream.ReadByte()')
|
||||
for (const forbidden of ['ReadToEnd', 'FromBase64String', 'ConvertFrom-Json']) {
|
||||
expect(script).not.toContain(forbidden)
|
||||
}
|
||||
@@ -111,7 +113,8 @@ describe('SSH relay runtime Windows system-SSH staging control', () => {
|
||||
operation: opcode,
|
||||
reserved: Buffer.alloc(3),
|
||||
remoteRoot: 'C:/Users/测试/.orca-remote/stage',
|
||||
remotePath
|
||||
remotePath,
|
||||
completion: 'ORCAEND1'
|
||||
})
|
||||
fixture.resolve()
|
||||
await expect(running).resolves.toBeUndefined()
|
||||
|
||||
@@ -19,6 +19,7 @@ export type RunSshRelayRuntimeWindowsStagingControlOptions = Readonly<{
|
||||
}>
|
||||
|
||||
const REQUEST_MAGIC = Buffer.from('ORCACTL1', 'ascii')
|
||||
const COMPLETION_MAGIC = Buffer.from('ORCAEND1', 'ascii')
|
||||
const FIXED_REQUEST_BYTES = 20
|
||||
const MAXIMUM_PATH_BYTES = 32 * 1024
|
||||
const COMMAND_TIMEOUT_MS = 30_000
|
||||
@@ -32,7 +33,8 @@ const OPERATION_CODES: Record<SshRelayRuntimeWindowsStagingOperation, number> =
|
||||
export const SSH_RELAY_RUNTIME_WINDOWS_STAGING_CONTROL_LIMITS = Object.freeze({
|
||||
commandTimeoutMs: COMMAND_TIMEOUT_MS,
|
||||
maximumPathBytes: MAXIMUM_PATH_BYTES,
|
||||
maximumRequestBytes: FIXED_REQUEST_BYTES + MAXIMUM_PATH_BYTES * 2
|
||||
maximumRequestBytes: FIXED_REQUEST_BYTES + MAXIMUM_PATH_BYTES * 2 + COMPLETION_MAGIC.length,
|
||||
completionBytes: COMPLETION_MAGIC.length
|
||||
})
|
||||
|
||||
const RECEIVER_SCRIPT = `
|
||||
@@ -62,7 +64,9 @@ try {
|
||||
[byte[]]$pathBytes = New-Object byte[] ([int]$pathLength)
|
||||
Read-Exact $inputStream $rootBytes ([int]$rootLength)
|
||||
Read-Exact $inputStream $pathBytes ([int]$pathLength)
|
||||
if ($inputStream.ReadByte() -ne -1) { throw 'extra byte' }
|
||||
[byte[]]$completion = New-Object byte[] ${COMPLETION_MAGIC.length}
|
||||
Read-Exact $inputStream $completion ${COMPLETION_MAGIC.length}
|
||||
if ([Text.Encoding]::ASCII.GetString($completion) -ne 'ORCAEND1') { throw 'bad completion' }
|
||||
$utf8 = New-Object Text.UTF8Encoding($false, $true)
|
||||
$root = $utf8.GetString($rootBytes)
|
||||
$path = if ($pathLength -eq 0) { '' } else { $utf8.GetString($pathBytes) }
|
||||
@@ -159,13 +163,16 @@ function buildRequest(
|
||||
): Buffer {
|
||||
const rootBytes = Buffer.from(remoteRoot, 'utf8')
|
||||
const pathBytes = Buffer.from(remotePath, 'utf8')
|
||||
const request = Buffer.alloc(FIXED_REQUEST_BYTES + rootBytes.length + pathBytes.length)
|
||||
const request = Buffer.alloc(
|
||||
FIXED_REQUEST_BYTES + rootBytes.length + pathBytes.length + COMPLETION_MAGIC.length
|
||||
)
|
||||
REQUEST_MAGIC.copy(request, 0)
|
||||
request.writeUInt8(OPERATION_CODES[operation], 8)
|
||||
request.writeUInt32LE(rootBytes.length, 12)
|
||||
request.writeUInt32LE(pathBytes.length, 16)
|
||||
rootBytes.copy(request, FIXED_REQUEST_BYTES)
|
||||
pathBytes.copy(request, FIXED_REQUEST_BYTES + rootBytes.length)
|
||||
COMPLETION_MAGIC.copy(request, FIXED_REQUEST_BYTES + rootBytes.length + pathBytes.length)
|
||||
return request
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user