Skip to content
This repository was archived by the owner on Apr 13, 2026. It is now read-only.

Commit 658d5e1

Browse files
authored
fix: resolve extension host reconnection causing editor file open to fail (#145)
- Disable vscode.git/git-base/github via extensions.experimental.disabled - Add killProcessOnPort with port-free wait before starting bridge/code-server - Add EADDRINUSE error handling and already-running guard to bridge server - Support __ASSETS_PATH__ vite define injection for cross-project asset resolution - Change extension activationEvents from onStartupFinished to * for earlier activation - Add error handler for extension bridge socket connection - Re-throw error in codeServerStarter catch block instead of silently swallowing
1 parent 5b18b8b commit 658d5e1

9 files changed

Lines changed: 103 additions & 39 deletions

File tree

27 Bytes
Binary file not shown.

src/main/code-server/bridge.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ interface IBridgeRequestParams {
1111
params: Record<string, any>;
1212
}
1313

14+
export const BRIDGE_SERVER_PORT = 45000;
15+
1416
class ExtensionBridgeServer extends EventEmitter {
1517
private server: net.Server | null = null;
1618
private clients = new Map<string, net.Socket>();
@@ -36,8 +38,16 @@ class ExtensionBridgeServer extends EventEmitter {
3638
this.webContents = webContents;
3739
}
3840

39-
start(port: number = 45000) {
40-
return new Promise((resolve) => {
41+
start(port: number = BRIDGE_SERVER_PORT) {
42+
console.log(`[ExtensionBridgeServer] Starting on port ${port}...`);
43+
return new Promise((resolve, reject) => {
44+
// If already running on this port, reuse it
45+
if (this.server && this.server.listening) {
46+
console.log(`[ExtensionBridgeServer] Already running on port ${port}`);
47+
resolve('');
48+
return;
49+
}
50+
4151
this.server = net.createServer((socket) => {
4252
let currentCwd: string | null = null;
4353

@@ -121,9 +131,24 @@ class ExtensionBridgeServer extends EventEmitter {
121131
}
122132
});
123133
});
134+
135+
// Handle server errors (e.g., EADDRINUSE)
136+
this.server.on('error', (error: NodeJS.ErrnoException) => {
137+
console.error(`[ExtensionBridgeServer] Server error:`, error);
138+
if (error.code === 'EADDRINUSE') {
139+
reject(
140+
new Error(
141+
`Port ${port} is already in use. Please close other instances or restart the application.`,
142+
),
143+
);
144+
} else {
145+
reject(error);
146+
}
147+
});
148+
124149
this.server.listen(port, () => {
125150
console.log(
126-
`Extension Bridge server started on port ${port}`,
151+
`[ExtensionBridgeServer] ✓ Started on port ${port}`,
127152
Date.now(),
128153
);
129154
resolve('');
Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,30 @@
11
import { app } from 'electron';
22
import path from 'path';
33

4+
declare const __ASSETS_PATH__: string | undefined;
5+
46
const EXTENSION_DIR = 'extensions';
57
const VSIX_FILENAME = 'neovate-code-extension-0.0.7.vsix';
68

79
/**
810
* 获取扩展目录的统一路径
9-
* 开发环境:项目根目录/assets/extensions
11+
* 开发环境:由各自 vite config 的 define 注入 __ASSETS_PATH__,默认 __dirname/../../assets
1012
* 打包环境:应用根目录/assets/extensions
1113
*/
1214
export function getExtensionsBasePath(): string {
13-
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
14-
15-
if (isDev) {
16-
// 开发环境:项目根目录/assets/extensions
17-
return path.join(
18-
__dirname,
19-
'..',
20-
'..',
21-
'assets',
22-
EXTENSION_DIR,
23-
VSIX_FILENAME,
24-
);
25-
} else {
26-
// 打包环境:应用根目录/assets/extensions(asar 内)
27-
// __dirname = app.asar/dist/main/code-server,需要回到 app.asar/dist/
28-
return path.join(
29-
__dirname,
30-
'..',
31-
'..',
32-
'assets',
33-
EXTENSION_DIR,
34-
VSIX_FILENAME,
35-
);
15+
if (!app.isPackaged) {
16+
const assetsPath =
17+
typeof __ASSETS_PATH__ !== 'undefined'
18+
? __ASSETS_PATH__
19+
: path.join(__dirname, '..', '..', 'assets');
20+
return path.join(assetsPath, EXTENSION_DIR, VSIX_FILENAME);
3621
}
22+
return path.join(
23+
__dirname,
24+
'..',
25+
'..',
26+
'assets',
27+
EXTENSION_DIR,
28+
VSIX_FILENAME,
29+
);
3730
}

src/main/code-server/index.ts

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { execSync } from 'node:child_process';
2-
import { bridgeServer } from './bridge';
2+
import { bridgeServer, BRIDGE_SERVER_PORT } from './bridge';
33
import { CODE_SERVER_PORT, DATA_DIR, EXTENSIONS_DIR } from './constants';
44
import {
55
downloadCodeServer,
@@ -27,9 +27,9 @@ export interface CodeServerInstance {
2727
}
2828

2929
/**
30-
* Kill any process running on the specified port
30+
* Kill any process running on the specified port and wait for it to be freed
3131
*/
32-
function killProcessOnPort(port: number): void {
32+
async function killProcessOnPort(port: number): Promise<void> {
3333
try {
3434
if (process.platform === 'win32') {
3535
// Windows
@@ -54,6 +54,19 @@ function killProcessOnPort(port: number): void {
5454
stdio: 'ignore',
5555
});
5656
}
57+
58+
// Wait for port to be fully released (max 2 seconds)
59+
const maxWaitMs = 2000;
60+
const startTime = Date.now();
61+
while (Date.now() - startTime < maxWaitMs) {
62+
try {
63+
execSync(`lsof -ti:${port}`, { stdio: 'ignore' });
64+
await new Promise((resolve) => setTimeout(resolve, 100));
65+
} catch {
66+
// Port is free
67+
return;
68+
}
69+
}
5770
} catch {
5871
// No process on port, or kill failed - that's fine
5972
}
@@ -103,32 +116,52 @@ class CodeServerManager {
103116
if (!installed) {
104117
await downloadCodeServer(onProgress);
105118
}
119+
106120
// 2. Override settings for minimal UI
107121
await overrideCodeServerSettings();
108122

109-
// 3. Kill any existing process on the port
110-
killProcessOnPort(CODE_SERVER_PORT);
123+
// 3. Kill any existing process on the ports
124+
await killProcessOnPort(CODE_SERVER_PORT);
125+
await killProcessOnPort(BRIDGE_SERVER_PORT);
126+
127+
// 4a. Start bridge server (REQUIRED for editor functionality)
111128
try {
112-
// code server extension bridge server
113129
await bridgeServer.start();
114-
// preset extension
130+
} catch (e) {
131+
throw new CodeServerStartError(
132+
`Bridge server failed to start: ${e instanceof Error ? e.message : String(e)}`,
133+
e instanceof Error ? e : new Error(String(e)),
134+
);
135+
}
136+
137+
// 4b. Install extensions (non-critical, can fail gracefully)
138+
try {
115139
await installExtension();
116-
// overwrite vscode dist style
140+
} catch (e) {
141+
console.warn(
142+
'[CodeServerManager] Extension installation failed (non-critical):',
143+
e,
144+
);
145+
}
146+
147+
// 4c. Inject styles (non-critical, can fail gracefully)
148+
try {
117149
injectStyle();
118150
} catch (e) {
119-
console.warn(`Extension Service failed`, e);
151+
console.warn(
152+
'[CodeServerManager] Style injection failed (non-critical):',
153+
e,
154+
);
120155
}
121156

122157
try {
123-
// start code server
124158
await codeServerStarter({
125159
port: CODE_SERVER_PORT,
126160
extDir: EXTENSIONS_DIR,
127161
dataDir: DATA_DIR,
128162
});
129163

130164
const url = `http://127.0.0.1:${CODE_SERVER_PORT}`;
131-
132165
return {
133166
url,
134167
stop: () => {

src/main/code-server/settings.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ const OVERRIDE_SETTINGS = {
2828
'security.workspace.trust.enabled': false,
2929
/** 禁用空入口时的欢迎特性 */
3030
'workbench.startupEditor': 'none',
31+
/** 禁用不必要的内置扩展,避免其 activate() 阻塞 Eager extensions activated */
32+
'extensions.experimental.disabled': [
33+
'vscode.git',
34+
'vscode.git-base',
35+
'vscode.github',
36+
'vscode.github-authentication',
37+
'vscode.npm',
38+
],
3139
/** 行号前面的空间,用于小灯泡 */
3240
'editor.glyphMargin': false,
3341
/** 代码折叠功能 */

src/main/code-server/starter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export async function codeServerStarter(opts: {
3232
return await delay(1000); // FIXME: 插件关闭侧边栏有延迟,先这样处理,后面可能用魔改产物的方式强制屏蔽
3333
} catch (e) {
3434
console.log(`Code Server Starter Failed: ${e}`);
35+
throw e;
3536
}
3637
}
3738

27 Bytes
Binary file not shown.

vscode-extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
],
1313
"main": "./dist/extension.js",
1414
"activationEvents": [
15-
"onStartupFinished"
15+
"*"
1616
],
1717
"contributes": {},
1818
"scripts": {

vscode-extension/src/communication.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ export function runServer() {
5454
send('ping', {});
5555
});
5656

57+
client.on('error', (err) => {
58+
console.error('Extension bridge connection error:', err.message);
59+
});
60+
5761
client.on('data', async (data) => {
5862
try {
5963
const request = JSON.parse(data.toString());

0 commit comments

Comments
 (0)