Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
19 changes: 15 additions & 4 deletions apps/desktop/src/lib/trpc/routers/external/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from "node:path";
import { clipboard, shell } from "electron";
import { db } from "main/lib/db";
import { EXTERNAL_APPS, type ExternalApp } from "main/lib/db/schemas";
import { terminalManager } from "main/lib/terminal";
import { z } from "zod";
import { publicProcedure, router } from "../..";

Expand Down Expand Up @@ -151,7 +152,7 @@ export const createExternalRouter = () => {
path: z.string(),
line: z.number().optional(),
column: z.number().optional(),
cwd: z.string().optional(),
paneId: z.string().optional(),
}),
)
.mutation(async ({ input }) => {
Expand All @@ -166,10 +167,20 @@ export const createExternalRouter = () => {
}

// Convert to absolute path - required for editor commands to work reliably
// Query actual cwd from the terminal's pty process if paneId is provided
if (!path.isAbsolute(filePath)) {
filePath = input.cwd
? path.resolve(input.cwd, filePath)
: path.resolve(filePath);
const cwd = input.paneId
? terminalManager.getCwd(input.paneId)
: null;
if (!cwd) {
console.warn(
`[openFileInEditor] Cannot resolve relative path "${filePath}": no cwd available for paneId "${input.paneId}"`,
);
throw new Error(
`Cannot open relative path "${filePath}" - terminal session not found`,
);
}
filePath = path.resolve(cwd, filePath);
}

// Build the file location string (file:line:column format for URL schemes)
Expand Down
22 changes: 22 additions & 0 deletions apps/desktop/src/main/lib/terminal/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,28 @@ export class TerminalManager extends EventEmitter {
};
}

/**
* Get the current working directory of a terminal session.
* Uses cwd tracked from OSC 7 sequences emitted by the shell.
* Falls back to the initial cwd if no OSC 7 has been received.
*/
getCwd(paneId: string): string | null {
const session = this.sessions.get(paneId);
if (!session || !session.isAlive) {
console.warn(
`[TerminalManager.getCwd] Session not found or not alive for paneId: ${paneId}`,
);
return null;
}

// Use tracked cwd from OSC 7 if available, otherwise fall back to initial cwd
const cwd = session.trackedCwd ?? session.cwd;
console.log(
`[TerminalManager.getCwd] paneId: ${paneId}, trackedCwd: ${session.trackedCwd}, initialCwd: ${session.cwd}, returning: ${cwd}`,
);
return cwd;
}
Comment on lines +266 to +286
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Checking package.json for build targets ==="
if [ -f "package.json" ]; then
  cat package.json | jq '.build' 2>/dev/null || grep -A 20 '"build"' package.json | head -30
fi

echo -e "\n=== Checking for Windows platform references in desktop/src ==="
rg -i "win32|windows" apps/desktop/src --max-count=20

echo -e "\n=== Checking manager.ts file ==="
wc -l apps/desktop/src/main/lib/terminal/manager.ts

echo -e "\n=== Examining getCwd method and surrounding code ==="
sed -n '265,315p' apps/desktop/src/main/lib/terminal/manager.ts

Repository: superset-sh/superset

Length of output: 5213


Add Windows support to getCwd method.

The getCwd method correctly implements platform-specific cwd detection for macOS and Linux, but Windows is missing. Since the app targets Windows (evidenced by platform checks throughout the codebase), add a Windows implementation. Windows lacks /proc/[pid]/cwd symlinks and lsof, so consider using Windows-specific APIs or the wmic command as a fallback approach. Ensure the method maintains its current defensive behavior with timeout protection and graceful fallback to session.cwd.

🤖 Prompt for AI Agents
In apps/desktop/src/main/lib/terminal/manager.ts around lines 268-313, add
Windows handling in getCwd: detect process.platform === "win32" and run a
short-timeout child command (PowerShell or wmic) to retrieve the target process
executable path or command-line, derive the directory (path.dirname) and check
existsSync before returning it; keep the exec in try/catch with the same timeout
protection and fall back to session.cwd on any failure so behavior remains
defensive and consistent with macOS/Linux branches.


async killByWorkspaceId(
workspaceId: string,
): Promise<{ killed: number; failed: number }> {
Expand Down
36 changes: 36 additions & 0 deletions apps/desktop/src/main/lib/terminal/parse-cwd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Parse OSC 7 escape sequences to extract the current working directory.
* OSC 7 format: ESC]7;file://hostname/path BEL (or ESC\)
*
* This is emitted by shells when the directory changes.
*/

const ESC = "\x1b";
const BEL = "\x07";

// Match OSC 7 sequences: ESC]7;file://hostname/path followed by BEL or ST (ESC\)
const OSC7_PATTERN = new RegExp(
`${ESC}\\]7;file://[^/]*((?:/[^${BEL}${ESC}]*)*)(?:${BEL}|${ESC}\\\\)`,
"g",
);

/**
* Parse terminal output data for OSC 7 directory sequences.
* Returns the last (most recent) directory found, or null if none.
*/
export function parseCwd(data: string): string | null {
let lastMatch: string | null = null;

for (const match of data.matchAll(OSC7_PATTERN)) {
const path = match[1];
if (path) {
try {
lastMatch = decodeURIComponent(path);
} catch {
lastMatch = path;
}
}
}

return lastMatch;
}
7 changes: 7 additions & 0 deletions apps/desktop/src/main/lib/terminal/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from "../terminal-escape-filter";
import { HistoryReader, HistoryWriter } from "../terminal-history";
import { buildTerminalEnv, FALLBACK_SHELL, getDefaultShell } from "./env";
import { parseCwd } from "./parse-cwd";
import type { InternalCreateSessionParams, TerminalSession } from "./types";

const DEFAULT_COLS = 80;
Expand Down Expand Up @@ -153,6 +154,12 @@ export function setupDataHandler(
dataToStore = extractContentAfterClear(data);
}

// Track cwd from OSC 7 sequences
const cwdFromData = parseCwd(data);
if (cwdFromData) {
session.trackedCwd = cwdFromData;
}

const filteredData = session.escapeFilter.filter(dataToStore);
session.scrollback += filteredData;
session.historyWriter?.write(filteredData);
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/main/lib/terminal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export interface TerminalSession {
paneId: string;
workspaceId: string;
cwd: string;
/** Current working directory tracked from OSC 7 sequences */
trackedCwd?: string;
cols: number;
rows: number;
lastActive: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,7 @@ export const Terminal = ({ tabId, workspaceId }: TerminalProps) => {
xterm,
fitAddon,
cleanup: cleanupQuerySuppression,
} = createTerminalInstance(
container,
workspaceCwd,
initialThemeRef.current,
);
} = createTerminalInstance(container, paneId, initialThemeRef.current);
xtermRef.current = xterm;
fitAddonRef.current = fitAddon;
isExitedRef.current = false;
Expand Down Expand Up @@ -397,7 +393,7 @@ export const Terminal = ({ tabId, workspaceId }: TerminalProps) => {
xtermRef.current = null;
searchAddonRef.current = null;
};
}, [paneId, workspaceId, workspaceCwd]);
}, [paneId, workspaceId]);

useEffect(() => {
const xterm = xtermRef.current;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function loadRenderer(xterm: XTerm): { dispose: () => void } {

export function createTerminalInstance(
container: HTMLDivElement,
cwd?: string,
paneId: string,
initialTheme?: ITheme | null,
): {
xterm: XTerm;
Expand Down Expand Up @@ -148,7 +148,7 @@ export function createTerminalInstance(
path,
line,
column,
cwd,
paneId,
})
.catch((error) => {
console.error(
Expand Down