Skip to content

Commit f6d9cf4

Browse files
committed
review comments
1 parent 3cd7f25 commit f6d9cf4

6 files changed

Lines changed: 220 additions & 220 deletions

File tree

src/commands/devbox/pty.ts

Lines changed: 44 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import WebSocket from "ws";
22
import { cliStatus } from "../../utils/cliStatus.js";
33
import { outputError } from "../../utils/output.js";
4-
import { processUtils } from "../../utils/processUtils.js";
54
import { waitForReady } from "../../utils/ssh.js";
65
import {
76
getPtyBaseUrl,
8-
ptyControl,
97
createPtySessionReleaser,
108
resolvePtyWebSocketUrl,
119
createPtyTunnel,
1210
getPtyTunnelBaseUrl,
1311
isLocalPtyOverride,
14-
buildWsHeaders,
1512
settleAfterPtyTunnel,
1613
refreshPtySessionAfterAttach,
14+
startPtyIoSession,
1715
} from "../../lib/pty-client.js";
1816
import { openPtyWebSocket } from "../../lib/pty-ws.js";
1917

@@ -41,7 +39,7 @@ function registerPtyInterruptHandlers(
4139
return dispose;
4240
}
4341

44-
function writePtyStreamToStdout(data: WebSocket.Data): void {
42+
function writePtyStreamToStdout(data: WebSocket.RawData): void {
4543
if (Buffer.isBuffer(data)) {
4644
process.stdout.write(data);
4745
} else if (data instanceof ArrayBuffer) {
@@ -101,6 +99,11 @@ export async function ptyDevbox(devboxId: string, options: PtyOptions = {}) {
10199
}
102100
}
103101

102+
const PTY_EXEC_TIMEOUT_MS = (() => {
103+
const v = parseInt(process.env.RUNLOOP_PTY_EXEC_TIMEOUT_MS || "0", 10);
104+
return isNaN(v) || v < 0 ? 0 : v;
105+
})();
106+
104107
async function execCommand(
105108
baseUrl: string,
106109
sessionName: string,
@@ -112,41 +115,39 @@ async function execCommand(
112115
rows: 24,
113116
authToken,
114117
});
115-
const ws = await openPtyWebSocket(wsUrl, buildWsHeaders(authToken));
116-
await refreshPtySessionAfterAttach(
117-
ws,
118-
baseUrl,
119-
sessionName,
120-
80,
121-
24,
122-
authToken,
123-
);
118+
const ws = await openPtyWebSocket(wsUrl, authToken);
119+
await refreshPtySessionAfterAttach(ws, baseUrl, sessionName, 80, 24, authToken);
124120
ws.send(command + "\n");
125121

126122
return new Promise<void>((resolve, reject) => {
127-
const releaseOnce = createPtySessionReleaser(
128-
baseUrl,
129-
sessionName,
130-
authToken,
131-
);
132-
const disposeInterruptSignals = registerPtyInterruptHandlers(
133-
ws,
134-
releaseOnce,
135-
);
136-
137-
ws.on("message", (data: WebSocket.Data) => {
123+
const releaseOnce = createPtySessionReleaser(baseUrl, sessionName, authToken);
124+
const disposeInterruptSignals = registerPtyInterruptHandlers(ws, releaseOnce);
125+
126+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
127+
if (PTY_EXEC_TIMEOUT_MS > 0) {
128+
timeoutId = setTimeout(() => {
129+
releaseOnce();
130+
ws.close();
131+
}, PTY_EXEC_TIMEOUT_MS);
132+
}
133+
134+
const finish = () => {
135+
if (timeoutId !== undefined) clearTimeout(timeoutId);
136+
releaseOnce();
137+
disposeInterruptSignals();
138+
};
139+
140+
ws.on("message", (data: WebSocket.RawData) => {
138141
writePtyStreamToStdout(data);
139142
});
140143

141144
ws.on("close", () => {
142-
releaseOnce();
143-
disposeInterruptSignals();
145+
finish();
144146
resolve();
145147
});
146148

147149
ws.on("error", (err: Error) => {
148-
releaseOnce();
149-
disposeInterruptSignals();
150+
finish();
150151
reject(err);
151152
});
152153
});
@@ -165,79 +166,21 @@ async function interactiveSession(
165166
rows,
166167
authToken,
167168
});
168-
const ws = await openPtyWebSocket(wsUrl, buildWsHeaders(authToken));
169-
await refreshPtySessionAfterAttach(
170-
ws,
171-
baseUrl,
172-
sessionName,
173-
cols,
174-
rows,
175-
authToken,
176-
);
177-
178-
return new Promise<void>((resolve, reject) => {
179-
const releaseOnce = createPtySessionReleaser(
180-
baseUrl,
181-
sessionName,
182-
authToken,
183-
);
184-
const disposeInterruptSignals = registerPtyInterruptHandlers(
185-
ws,
186-
releaseOnce,
187-
);
188-
189-
const cleanup = () => {
190-
disposeInterruptSignals();
191-
process.stdin.removeListener("data", onStdinData);
192-
process.removeListener("SIGWINCH", onResize);
193-
if (processUtils.stdin.isTTY && processUtils.stdin.setRawMode) {
194-
processUtils.stdin.setRawMode(false);
195-
}
196-
process.stdin.pause();
197-
};
198-
199-
const onStdinData = (data: Buffer) => {
200-
if (ws.readyState === WebSocket.OPEN) {
201-
ws.send(data);
202-
}
203-
};
204-
205-
const onResize = () => {
206-
const newCols = process.stdout.columns || 80;
207-
const newRows = process.stdout.rows || 24;
208-
ptyControl(
209-
baseUrl,
210-
sessionName,
211-
{
212-
action: "resize",
213-
cols: newCols,
214-
rows: newRows,
215-
},
216-
authToken,
217-
).catch(() => {});
218-
};
169+
const ws = await openPtyWebSocket(wsUrl, authToken);
170+
await refreshPtySessionAfterAttach(ws, baseUrl, sessionName, cols, rows, authToken);
219171

220-
if (processUtils.stdin.isTTY && processUtils.stdin.setRawMode) {
221-
processUtils.stdin.setRawMode(true);
222-
}
223-
process.stdin.resume();
224-
process.stdin.on("data", onStdinData);
225-
process.on("SIGWINCH", onResize);
172+
const releaseOnce = createPtySessionReleaser(baseUrl, sessionName, authToken);
173+
const { dispose, done } = startPtyIoSession(ws, baseUrl, sessionName, authToken);
174+
const disposeSignals = registerPtyInterruptHandlers(ws, releaseOnce);
226175

227-
ws.on("message", (data: WebSocket.Data) => {
228-
writePtyStreamToStdout(data);
229-
});
230-
231-
ws.on("close", () => {
232-
releaseOnce();
233-
cleanup();
234-
resolve();
235-
});
236-
237-
ws.on("error", (err: Error) => {
238-
releaseOnce();
239-
cleanup();
240-
reject(err);
241-
});
242-
});
176+
try {
177+
await done;
178+
releaseOnce();
179+
} catch (err) {
180+
releaseOnce();
181+
throw err;
182+
} finally {
183+
dispose();
184+
disposeSignals();
185+
}
243186
}

src/commands/object/upload.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ async function readStdinBuffer(): Promise<Buffer> {
173173
export async function uploadObject(options: UploadObjectOptions) {
174174
try {
175175
const client = getClient();
176-
const { paths, name, contentType, output: outputFormat } = options;
176+
const { paths: rawPaths, name, contentType, output: outputFormat } = options;
177+
const paths = [...rawPaths];
177178

178179
if (paths.length === 0) {
179180
if (!processUtils.stdin.isTTY) {

src/components/DevboxActionsMenu.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
getPtyTunnelBaseUrl,
3535
settleAfterPtyTunnel,
3636
} from "../lib/pty-client.js";
37+
import { waitForReady } from "../utils/ssh.js";
3738

3839
type Operation =
3940
| "exec"
@@ -697,6 +698,8 @@ export const DevboxActionsMenu = ({
697698
break;
698699

699700
case "pty": {
701+
await waitForReady(devbox.id, 180, 3);
702+
700703
let ptyBaseUrl: string;
701704
let ptyAuthToken: string | undefined;
702705

0 commit comments

Comments
 (0)