Skip to content

Commit aecf3a9

Browse files
committed
Address rest of comments
1 parent 4bec022 commit aecf3a9

File tree

2 files changed

+47
-17
lines changed

2 files changed

+47
-17
lines changed

src/remote/sshProcess.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ export interface SshProcessMonitorOptions {
2828
networkInfoPath: string;
2929
proxyLogDir?: string;
3030
logger: Logger;
31-
// Poll interval for SSH process and file discovery
32-
pollInterval?: number;
31+
// Initial poll interval for SSH process and file discovery (ms)
32+
discoveryPollIntervalMs?: number;
33+
// Maximum backoff interval for process and file discovery (ms)
34+
maxDiscoveryBackoffMs?: number;
3335
// Poll interval for network info updates
3436
networkPollInterval?: number;
3537
// For port-based SSH process discovery
@@ -72,7 +74,8 @@ export class SshProcessMonitor implements vscode.Disposable {
7274
this.options = {
7375
...options,
7476
proxyLogDir: options.proxyLogDir,
75-
pollInterval: options.pollInterval ?? 1000,
77+
discoveryPollIntervalMs: options.discoveryPollIntervalMs ?? 1000,
78+
maxDiscoveryBackoffMs: options.maxDiscoveryBackoffMs ?? 30_000,
7679
// Matches the SSH update interval
7780
networkPollInterval: options.networkPollInterval ?? 3000,
7881
};
@@ -138,8 +141,10 @@ export class SshProcessMonitor implements vscode.Disposable {
138141
* Starts monitoring when it finds the process through the port.
139142
*/
140143
private async searchForProcess(): Promise<void> {
141-
const { pollInterval, logger, sshHost } = this.options;
144+
const { discoveryPollIntervalMs, maxDiscoveryBackoffMs, logger, sshHost } =
145+
this.options;
142146
let attempt = 0;
147+
let currentBackoff = discoveryPollIntervalMs;
143148

144149
while (!this.disposed) {
145150
attempt++;
@@ -157,7 +162,8 @@ export class SshProcessMonitor implements vscode.Disposable {
157162
return;
158163
}
159164

160-
await this.delay(pollInterval);
165+
await this.delay(currentBackoff);
166+
currentBackoff = Math.min(currentBackoff * 2, maxDiscoveryBackoffMs);
161167
}
162168
}
163169

@@ -172,13 +178,14 @@ export class SshProcessMonitor implements vscode.Disposable {
172178
const logPath = await findRemoteSshLogPath(
173179
codeLogDir,
174180
remoteSshExtensionId,
181+
logger,
175182
);
176183
if (!logPath) {
177184
return undefined;
178185
}
179186

180187
const logContent = await fs.readFile(logPath, "utf8");
181-
this.options.logger.debug(`Read Remote SSH log file: ${logPath}`);
188+
this.options.logger.debug(`Read Remote SSH log file:`, logPath);
182189

183190
const port = findPort(logContent);
184191
if (!port) {
@@ -234,11 +241,18 @@ export class SshProcessMonitor implements vscode.Disposable {
234241
* Polls until found or PID changes.
235242
*/
236243
private async searchForLogFile(): Promise<void> {
237-
const { proxyLogDir: logDir, logger, pollInterval } = this.options;
244+
const {
245+
proxyLogDir: logDir,
246+
logger,
247+
discoveryPollIntervalMs,
248+
maxDiscoveryBackoffMs,
249+
} = this.options;
238250
if (!logDir) {
239251
return;
240252
}
241253

254+
let currentBackoff = discoveryPollIntervalMs;
255+
242256
const targetPid = this.currentPid;
243257
while (!this.disposed && this.currentPid === targetPid) {
244258
try {
@@ -262,7 +276,8 @@ export class SshProcessMonitor implements vscode.Disposable {
262276
logger.debug(`Could not read log directory: ${logDir}`);
263277
}
264278

265-
await this.delay(pollInterval);
279+
await this.delay(currentBackoff);
280+
currentBackoff = Math.min(currentBackoff * 2, maxDiscoveryBackoffMs);
266281
}
267282
}
268283

@@ -376,10 +391,13 @@ export class SshProcessMonitor implements vscode.Disposable {
376391

377392
/**
378393
* Finds the Remote SSH extension's log file path.
394+
* Tries extension-specific folder first (Cursor, Windsurf, Antigravity),
395+
* then output_logging_ fallback (MS VS Code).
379396
*/
380397
async function findRemoteSshLogPath(
381398
codeLogDir: string,
382399
extensionId: string,
400+
logger: Logger,
383401
): Promise<string | undefined> {
384402
const logsParentDir = path.dirname(codeLogDir);
385403

@@ -394,8 +412,12 @@ async function findRemoteSshLogPath(
394412
if (remoteSsh) {
395413
return path.join(extensionLogDir, remoteSsh);
396414
}
415+
// Folder exists but no Remote SSH log yet
416+
logger.debug(
417+
`Extension log folder exists but no Remote SSH log found: ${extensionLogDir}`,
418+
);
397419
} catch {
398-
// Extension-specific folder doesn't exist, try fallback
420+
// Extension-specific folder doesn't exist - expected for MS VS Code, try fallback
399421
}
400422

401423
try {
@@ -411,9 +433,14 @@ async function findRemoteSshLogPath(
411433
if (remoteSSHLog) {
412434
return path.join(outputPath, remoteSSHLog);
413435
}
436+
logger.debug(
437+
`Output logging folder exists but no Remote SSH log found: ${outputPath}`,
438+
);
439+
} else {
440+
logger.debug(`No output_logging_ folders found in: ${logsParentDir}`);
414441
}
415442
} catch {
416-
// output_logging folder doesn't exist
443+
logger.debug(`Could not read logs parent directory: ${logsParentDir}`);
417444
}
418445

419446
return undefined;

test/unit/remote/sshProcess.test.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe("SshProcessMonitor", () => {
3030

3131
// Default: process found immediately
3232
vi.mocked(find).mockResolvedValue([
33-
{ pid: 999, name: "ssh", cmd: "ssh host" },
33+
{ pid: 999, ppid: 1, name: "ssh", cmd: "ssh host" },
3434
]);
3535
});
3636

@@ -66,7 +66,9 @@ describe("SshProcessMonitor", () => {
6666
vi.mocked(find)
6767
.mockResolvedValueOnce([])
6868
.mockResolvedValueOnce([])
69-
.mockResolvedValueOnce([{ pid: 888, name: "ssh", cmd: "ssh host" }]);
69+
.mockResolvedValueOnce([
70+
{ pid: 888, ppid: 1, name: "ssh", cmd: "ssh host" },
71+
]);
7072

7173
const monitor = createMonitor({ codeLogDir: "/logs/window1" });
7274
const pid = await waitForEvent(monitor.onPidChange);
@@ -80,7 +82,7 @@ describe("SshProcessMonitor", () => {
8082
vol.fromJSON({});
8183

8284
vi.mocked(find).mockResolvedValue([
83-
{ pid: 777, name: "ssh", cmd: "ssh host" },
85+
{ pid: 777, ppid: 1, name: "ssh", cmd: "ssh host" },
8486
]);
8587

8688
const monitor = createMonitor({ codeLogDir: "/logs/window1" });
@@ -116,8 +118,8 @@ describe("SshProcessMonitor", () => {
116118

117119
// First search finds PID 999, after reconnect finds PID 888
118120
vi.mocked(find)
119-
.mockResolvedValueOnce([{ pid: 999, name: "ssh", cmd: "ssh" }])
120-
.mockResolvedValue([{ pid: 888, name: "ssh", cmd: "ssh" }]);
121+
.mockResolvedValueOnce([{ pid: 999, ppid: 1, name: "ssh", cmd: "ssh" }])
122+
.mockResolvedValue([{ pid: 888, ppid: 1, name: "ssh", cmd: "ssh" }]);
121123

122124
const monitor = createMonitor({
123125
codeLogDir: "/logs/window1",
@@ -158,7 +160,7 @@ describe("SshProcessMonitor", () => {
158160

159161
// Always returns the same PID
160162
vi.mocked(find).mockResolvedValue([
161-
{ pid: 999, name: "ssh", cmd: "ssh" },
163+
{ pid: 999, ppid: 1, name: "ssh", cmd: "ssh" },
162164
]);
163165

164166
const monitor = createMonitor({
@@ -395,7 +397,8 @@ describe("SshProcessMonitor", () => {
395397
codeLogDir: "/logs/window1",
396398
remoteSshExtensionId: "ms-vscode-remote.remote-ssh",
397399
logger: createMockLogger(),
398-
pollInterval: 10,
400+
discoveryPollIntervalMs: 10,
401+
maxDiscoveryBackoffMs: 100,
399402
networkPollInterval: 10,
400403
...overrides,
401404
});

0 commit comments

Comments
 (0)