Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix log file finding for non ms vs code#665

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
EhabY wants to merge2 commits intocoder:main
base:main
Choose a base branch
Loading
fromEhabY:fix-log-file-finding-for-non-ms-vs-code
Open
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletionssrc/extension.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,8 @@

import axios, { isAxiosError } from "axios";
import { getErrorMessage } from "coder/site/src/api/errors";
import * as module from "module";
import { createRequire } from "node:module";
import * as path from "node:path";
import * as vscode from "vscode";

import { errToStr } from "./api/api-helper";
Expand All@@ -14,6 +15,7 @@ import { AuthAction } from "./core/secretsManager";
import { CertificateError, getErrorDetail } from "./error";
import { maybeAskUrl } from "./promptUtils";
import { Remote } from "./remote/remote";
import { getRemoteSshExtension } from "./remote/sshExtension";
import { toSafeHost } from "./util";
import {
WorkspaceProvider,
Expand All@@ -33,30 +35,21 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
// Cursor and VSCode are covered by ms remote, and the only other is windsurf for now
// Means that vscodium is not supported by this for now

const remoteSSHExtension =
vscode.extensions.getExtension("jeanp413.open-remote-ssh") ||
vscode.extensions.getExtension("codeium.windsurf-remote-openssh") ||
vscode.extensions.getExtension("anysphere.remote-ssh") ||
vscode.extensions.getExtension("ms-vscode-remote.remote-ssh") ||
vscode.extensions.getExtension("google.antigravity-remote-openssh");
const remoteSshExtension = getRemoteSshExtension();

let vscodeProposed: typeof vscode = vscode;

if (!remoteSSHExtension) {
if (remoteSshExtension) {
const extensionRequire = createRequire(
path.join(remoteSshExtension.extensionPath, "package.json"),
);
vscodeProposed = extensionRequire("vscode");
} else {
vscode.window.showErrorMessage(
"Remote SSH extension not found, this may not work as expected.\n" +
// NB should we link to documentation or marketplace?
"Please install your choice of Remote SSH extension from the VS Code Marketplace.",
);
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
vscodeProposed = (module as any)._load(
"vscode",
{
filename: remoteSSHExtension.extensionPath,
},
false,
);
}

const serviceContainer = new ServiceContainer(ctx, vscodeProposed);
Expand DownExpand Up@@ -366,11 +359,12 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
// after the Coder extension is installed, instead of throwing a fatal error
// (this would require the user to uninstall the Coder extension and
// reinstall after installing the remote SSH extension, which is annoying)
if (remoteSSHExtension && vscodeProposed.env.remoteAuthority) {
if (remoteSshExtension && vscodeProposed.env.remoteAuthority) {
try {
const details = await remote.setup(
vscodeProposed.env.remoteAuthority,
isFirstConnect,
remoteSshExtension.id,
);
if (details) {
ctx.subscriptions.push(details);
Expand Down
232 changes: 41 additions & 191 deletionssrc/remote/remote.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,12 +4,10 @@ import {
type Workspace,
type WorkspaceAgent,
} from "coder/site/src/api/typesGenerated";
import find from "find-process";
import * as jsonc from "jsonc-parser";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import prettyBytes from "pretty-bytes";
import * as semver from "semver";
import * as vscode from "vscode";

Expand All@@ -36,12 +34,12 @@ import {
AuthorityPrefix,
escapeCommandArg,
expandPath,
findPort,
parseRemoteAuthority,
} from "../util";
import { WorkspaceMonitor } from "../workspace/workspaceMonitor";

import { SSHConfig, type SSHValues, mergeSSHConfigValues } from "./sshConfig";
import { SshProcessMonitor } from "./sshProcess";
import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport";
import { WorkspaceStateMachine } from "./workspaceStateMachine";

Expand DownExpand Up@@ -109,6 +107,7 @@ export class Remote {
public async setup(
remoteAuthority: string,
firstConnect: boolean,
remoteSshExtensionId: string,
): Promise<RemoteDetails | undefined> {
const parts = parseRemoteAuthority(remoteAuthority);
if (!parts) {
Expand DownExpand Up@@ -148,15 +147,15 @@ export class Remote {
]);

if (result.type === "login") {
return this.setup(remoteAuthority, firstConnect);
return this.setup(remoteAuthority, firstConnect, remoteSshExtensionId);
} else if (!result.userChoice) {
// User declined to log in.
await this.closeRemote();
return;
} else {
// Log in then try again.
await this.commands.login({ url: baseUrlRaw, label: parts.label });
return this.setup(remoteAuthority, firstConnect);
return this.setup(remoteAuthority, firstConnect, remoteSshExtensionId);
}
};

Expand DownExpand Up@@ -485,30 +484,26 @@ export class Remote {
throw error;
}

// TODO: This needs to be reworked; it fails to pick up reconnects.
this.findSSHProcessID().then(async (pid) => {
if (!pid) {
// TODO: Show an error here!
return;
}
disposables.push(this.showNetworkUpdates(pid));
if (logDir) {
const logFiles = await fs.readdir(logDir);
const logFileName = logFiles
.reverse()
.find(
(file) => file === `${pid}.log` || file.endsWith(`-${pid}.log`),
);
this.commands.workspaceLogPath = logFileName
? path.join(logDir, logFileName)
: undefined;
} else {
this.commands.workspaceLogPath = undefined;
}
// Monitor SSH process and display network status
const sshMonitor = SshProcessMonitor.start({
sshHost: parts.host,
networkInfoPath: this.pathResolver.getNetworkInfoPath(),
proxyLogDir: logDir || undefined,
logger: this.logger,
codeLogDir: this.pathResolver.getCodeLogDir(),
remoteSshExtensionId,
});
disposables.push(sshMonitor);

this.commands.workspaceLogPath = sshMonitor.getLogFilePath();

// Register the label formatter again because SSH overrides it!
disposables.push(
sshMonitor.onLogFilePathChange((newPath) => {
this.commands.workspaceLogPath = newPath;
}),
// Watch for logDir configuration changes
this.watchLogDirSetting(logDir, featureSet),
// Register the label formatter again because SSH overrides it!
vscode.extensions.onDidChange(() => {
// Dispose previous label formatter
labelFormatterDisposable.dispose();
Expand DownExpand Up@@ -741,172 +736,27 @@ export class Remote {
return ` ${args.join(" ")}`;
}

// showNetworkUpdates finds the SSH process ID that is being used by this
// workspace and reads the file being created by the Coder CLI.
private showNetworkUpdates(sshPid: number): vscode.Disposable {
const networkStatus = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Left,
1000,
);
const networkInfoFile = path.join(
this.pathResolver.getNetworkInfoPath(),
`${sshPid}.json`,
);

const updateStatus = (network: {
p2p: boolean;
latency: number;
preferred_derp: string;
derp_latency: { [key: string]: number };
upload_bytes_sec: number;
download_bytes_sec: number;
using_coder_connect: boolean;
}) => {
let statusText = "$(globe) ";

// Coder Connect doesn't populate any other stats
if (network.using_coder_connect) {
networkStatus.text = statusText + "Coder Connect ";
networkStatus.tooltip = "You're connected using Coder Connect.";
networkStatus.show();
return;
}

if (network.p2p) {
statusText += "Direct ";
networkStatus.tooltip = "You're connected peer-to-peer ✨.";
} else {
statusText += network.preferred_derp + " ";
networkStatus.tooltip =
"You're connected through a relay 🕵.\nWe'll switch over to peer-to-peer when available.";
}
networkStatus.tooltip +=
"\n\nDownload ↓ " +
prettyBytes(network.download_bytes_sec, {
bits: true,
}) +
"/s • Upload ↑ " +
prettyBytes(network.upload_bytes_sec, {
bits: true,
}) +
"/s\n";

if (!network.p2p) {
const derpLatency = network.derp_latency[network.preferred_derp];

networkStatus.tooltip += `You ↔ ${derpLatency.toFixed(2)}ms ↔ ${network.preferred_derp} ↔ ${(network.latency - derpLatency).toFixed(2)}ms ↔ Workspace`;

let first = true;
Object.keys(network.derp_latency).forEach((region) => {
if (region === network.preferred_derp) {
return;
}
if (first) {
networkStatus.tooltip += `\n\nOther regions:`;
first = false;
}
networkStatus.tooltip += `\n${region}: ${Math.round(network.derp_latency[region] * 100) / 100}ms`;
});
}

statusText += "(" + network.latency.toFixed(2) + "ms)";
networkStatus.text = statusText;
networkStatus.show();
};
let disposed = false;
const periodicRefresh = () => {
if (disposed) {
return;
}
fs.readFile(networkInfoFile, "utf8")
.then((content) => {
return JSON.parse(content);
})
.then((parsed) => {
try {
updateStatus(parsed);
} catch {
// Ignore
}
})
.catch(() => {
// TODO: Log a failure here!
})
.finally(() => {
// This matches the write interval of `coder vscodessh`.
setTimeout(periodicRefresh, 3000);
});
};
periodicRefresh();

return {
dispose: () => {
disposed = true;
networkStatus.dispose();
},
};
}

// findSSHProcessID returns the currently active SSH process ID that is
// powering the remote SSH connection.
private async findSSHProcessID(timeout = 15000): Promise<number | undefined> {
const search = async (logPath: string): Promise<number | undefined> => {
// This searches for the socksPort that Remote SSH is connecting to. We do
// this to find the SSH process that is powering this connection. That SSH
// process will be logging network information periodically to a file.
const text = await fs.readFile(logPath, "utf8");
const port = findPort(text);
if (!port) {
return;
}
const processes = await find("port", port);
if (processes.length < 1) {
return;
}
const process = processes[0];
return process.pid;
};
const start = Date.now();
const loop = async (): Promise<number | undefined> => {
if (Date.now() - start > timeout) {
return undefined;
}
// Loop until we find the remote SSH log for this window.
const filePath = await this.getRemoteSSHLogPath();
if (!filePath) {
return new Promise((resolve) => setTimeout(() => resolve(loop()), 500));
}
// Then we search the remote SSH log until we find the port.
const result = await search(filePath);
if (!result) {
return new Promise((resolve) => setTimeout(() => resolve(loop()), 500));
private watchLogDirSetting(
currentLogDir: string,
featureSet: FeatureSet,
): vscode.Disposable {
return vscode.workspace.onDidChangeConfiguration((e) => {
if (e.affectsConfiguration("coder.proxyLogDirectory")) {
const newLogDir = this.getLogDir(featureSet);
if (newLogDir !== currentLogDir) {
vscode.window
.showInformationMessage(
"Log directory configuration changed. Reload window to apply.",
"Reload",
)
.then((action) => {
if (action === "Reload") {
vscode.commands.executeCommand("workbench.action.reloadWindow");
}
});
}
}
return result;
};
return loop();
}

/**
* Returns the log path for the "Remote - SSH" output panel. There is no VS
* Code API to get the contents of an output panel. We use this to get the
* active port so we can display network information.
*/
private async getRemoteSSHLogPath(): Promise<string | undefined> {
const upperDir = path.dirname(this.pathResolver.getCodeLogDir());
// Node returns these directories sorted already!
const dirs = await fs.readdir(upperDir);
const latestOutput = dirs
.reverse()
.filter((dir) => dir.startsWith("output_logging_"));
if (latestOutput.length === 0) {
return undefined;
}
const dir = await fs.readdir(path.join(upperDir, latestOutput[0]));
const remoteSSH = dir.filter((file) => file.indexOf("Remote - SSH") !== -1);
if (remoteSSH.length === 0) {
return undefined;
}
return path.join(upperDir, latestOutput[0], remoteSSH[0]);
});
}

/**
Expand Down
25 changes: 25 additions & 0 deletionssrc/remote/sshExtension.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
import * as vscode from "vscode";

export const REMOTE_SSH_EXTENSION_IDS = [
"jeanp413.open-remote-ssh",
"codeium.windsurf-remote-openssh",
"anysphere.remote-ssh",
"ms-vscode-remote.remote-ssh",
"google.antigravity-remote-openssh",
] as const;

export type RemoteSshExtensionId = (typeof REMOTE_SSH_EXTENSION_IDS)[number];

type RemoteSshExtension = vscode.Extension<unknown> & {
id: RemoteSshExtensionId;
};

export function getRemoteSshExtension(): RemoteSshExtension | undefined {
for (const id of REMOTE_SSH_EXTENSION_IDS) {
const extension = vscode.extensions.getExtension(id);
if (extension) {
return extension as RemoteSshExtension;
}
}
return undefined;
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp