Skip to content

Commit 33d047c

Browse files
authored
🤖 Fix: Use tilde path in SSH runtime to avoid process.env in renderer (#458)
Fixes regression from #451 where was using `process.env.USER` in renderer code, causing "process is not defined" error during workspace creation. ## Problem After merging #451, workspace creation fails with: ``` ReferenceError: process is not defined at parseRuntimeString (chatCommands.ts:56:66) ``` The issue: `chatCommands.ts` is renderer code that was trying to access `process.env.USER` to construct SSH default paths. The `process` global is not available in Electron renderer processes. ## Solution **Use tilde paths instead** - Now that we have tilde resolution from #451, we can simply use `~/cmux` as the default SSH `srcBaseDir`. The backend will resolve it via `runtime.resolvePath()`. Before: ```typescript const user = atIndex > 0 ? hostPart.substring(0, atIndex) : (process.env.USER ?? "user"); const homeDir = user === "root" ? "/root" : `/home/${user}`; return { type: "ssh", host: hostPart, srcBaseDir: `${homeDir}/cmux`, }; ``` After: ```typescript return { type: "ssh", host: hostPart, srcBaseDir: "~/cmux", // Backend resolves via runtime.resolvePath() }; ``` ## ESLint Rule Added `no-restricted-globals` and `no-restricted-syntax` rules to prevent future `process.env` usage in renderer code: - Applies to: `src/**/*.ts(x)` - Excludes: main, preload, services, runtime, telemetry, utils/main, utils/providers, tests - Error message guides developers to use IPC or constants instead This catches the issue at development time rather than runtime. ## Testing - Unit tests updated to expect `~/cmux` instead of computed paths - ESLint passes with new rules - Workspace creation now works without process.env _Generated with `cmux`_
1 parent f36f7a4 commit 33d047c

File tree

3 files changed

+48
-22
lines changed

3 files changed

+48
-22
lines changed

‎eslint.config.mjs‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,42 @@ export default defineConfig([
391391
],
392392
},
393393
},
394+
{
395+
// Renderer process (frontend) architectural boundary - prevent Node.js API usage
396+
files: ["src/**/*.ts", "src/**/*.tsx"],
397+
ignores: [
398+
"src/main*.ts",
399+
"src/preload.ts",
400+
"src/services/**",
401+
"src/runtime/**",
402+
"src/utils/main/**",
403+
"src/utils/providers/**",
404+
"src/telemetry/**",
405+
"src/git.ts",
406+
"src/config.ts",
407+
"src/debug/**",
408+
"**/*.test.ts",
409+
"**/*.test.tsx",
410+
],
411+
rules: {
412+
"no-restricted-globals": [
413+
"error",
414+
{
415+
name: "process",
416+
message:
417+
"Renderer code cannot access 'process' global (not available in renderer). Use IPC to communicate with main process or use constants for environment-agnostic values.",
418+
},
419+
],
420+
"no-restricted-syntax": [
421+
"error",
422+
{
423+
selector: "MemberExpression[object.name='process'][property.name='env']",
424+
message:
425+
"Renderer code cannot access process.env (not available in renderer). Use IPC to get environment variables from main process or use constants.",
426+
},
427+
],
428+
},
429+
},
394430
{
395431
// Test file configuration
396432
files: ["**/*.test.ts", "**/*.test.tsx"],

‎src/utils/chatCommands.test.ts‎

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe("parseRuntimeString", () => {
1818
expect(result).toEqual({
1919
type: "ssh",
2020
host: "user@host",
21-
srcBaseDir: "/home/user/cmux",
21+
srcBaseDir: "~/cmux",
2222
});
2323
});
2424

@@ -27,7 +27,7 @@ describe("parseRuntimeString", () => {
2727
expect(result).toEqual({
2828
type: "ssh",
2929
30-
srcBaseDir: "/home/User/cmux",
30+
srcBaseDir: "~/cmux",
3131
});
3232
});
3333

@@ -36,7 +36,7 @@ describe("parseRuntimeString", () => {
3636
expect(result).toEqual({
3737
type: "ssh",
3838
host: "user@host",
39-
srcBaseDir: "/home/user/cmux",
39+
srcBaseDir: "~/cmux",
4040
});
4141
});
4242

@@ -47,34 +47,31 @@ describe("parseRuntimeString", () => {
4747

4848
test("accepts SSH with hostname only (user will be inferred)", () => {
4949
const result = parseRuntimeString("ssh hostname", workspaceName);
50-
// When no user is specified, uses current user (process.env.USER)
51-
const expectedUser = process.env.USER ?? "user";
52-
const expectedHomeDir = expectedUser === "root" ? "/root" : `/home/${expectedUser}`;
50+
// Uses tilde path - backend will resolve it via runtime.resolvePath()
5351
expect(result).toEqual({
5452
type: "ssh",
5553
host: "hostname",
56-
srcBaseDir: `${expectedHomeDir}/cmux`,
54+
srcBaseDir: "~/cmux",
5755
});
5856
});
5957

6058
test("accepts SSH with hostname.domain only", () => {
6159
const result = parseRuntimeString("ssh dev.example.com", workspaceName);
62-
// When no user is specified, uses current user (process.env.USER)
63-
const expectedUser = process.env.USER ?? "user";
64-
const expectedHomeDir = expectedUser === "root" ? "/root" : `/home/${expectedUser}`;
60+
// Uses tilde path - backend will resolve it via runtime.resolvePath()
6561
expect(result).toEqual({
6662
type: "ssh",
6763
host: "dev.example.com",
68-
srcBaseDir: `${expectedHomeDir}/cmux`,
64+
srcBaseDir: "~/cmux",
6965
});
7066
});
7167

72-
test("uses /root for root user", () => {
68+
test("uses tilde path for root user too", () => {
7369
const result = parseRuntimeString("ssh root@hostname", workspaceName);
70+
// Backend will resolve ~ to /root for root user
7471
expect(result).toEqual({
7572
type: "ssh",
7673
host: "root@hostname",
77-
srcBaseDir: "/root/cmux",
74+
srcBaseDir: "~/cmux",
7875
});
7976
});
8077

‎src/utils/chatCommands.ts‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,13 @@ export function parseRuntimeString(
5151
throw new Error("SSH runtime requires host (e.g., 'ssh hostname' or 'ssh user@host')");
5252
}
5353

54-
// Extract username from user@host format, or default to current user
55-
const atIndex = hostPart.indexOf("@");
56-
const user = atIndex > 0 ? hostPart.substring(0, atIndex) : (process.env.USER ?? "user");
57-
58-
// Determine home directory path based on user
59-
// root user's home is /root, all others are /home/<user>
60-
const homeDir = user === "root" ? "/root" : `/home/${user}`;
61-
6254
// Accept both "hostname" and "user@hostname" formats
6355
// SSH will use current user or ~/.ssh/config if user not specified
56+
// Use tilde path - backend will resolve it via runtime.resolvePath()
6457
return {
6558
type: RUNTIME_MODE.SSH,
6659
host: hostPart,
67-
srcBaseDir: `${homeDir}/cmux`, // Default remote base directory (NOT including workspace name)
60+
srcBaseDir: "~/cmux", // Default remote base directory (tilde will be resolved by backend)
6861
};
6962
}
7063

0 commit comments

Comments
 (0)