Skip to content
Merged
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
4 changes: 4 additions & 0 deletions apps/server/src/git/Layers/GitManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,9 @@ export const makeGitManager = Effect.gen(function* () {

const status: GitManagerShape["status"] = Effect.fnUntraced(function* (input) {
const details = yield* gitCore.statusDetails(input.cwd);
const repositoryUrl = yield* gitCore
.readConfigValue(input.cwd, "remote.origin.url")
.pipe(Effect.catch(() => Effect.succeed(null)));

const pr =
details.branch !== null
Expand All @@ -436,6 +439,7 @@ export const makeGitManager = Effect.gen(function* () {

return {
branch: details.branch,
...(repositoryUrl ? { repositoryUrl } : {}),
hasWorkingTreeChanges: details.hasWorkingTreeChanges,
workingTree: details.workingTree,
hasUpstream: details.hasUpstream,
Expand Down
59 changes: 58 additions & 1 deletion apps/server/src/terminal/Layers/Manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
type PtyProcess,
type PtySpawnInput,
} from "../Services/PTY";
import { TerminalManagerRuntime } from "./Manager";
import { __terminalManagerInternals, TerminalManagerRuntime } from "./Manager";
import { Effect, Encoding } from "effect";

class FakePtyProcess implements PtyProcess {
Expand Down Expand Up @@ -172,6 +172,20 @@ describe("TerminalManager", () => {
options: {
shellResolver?: () => string;
subprocessChecker?: (terminalPid: number) => Promise<boolean>;
externalServerDiscoverer?: (
filter: { projectRoot?: string; cwd?: string },
) => Promise<
Array<{
pid: number;
port: number;
address: string;
name: string | null;
commandLine: string | null;
parentPid: number | null;
createdAt: string | null;
}>
>;
externalProcessKiller?: (pid: number) => Promise<void>;
subprocessPollIntervalMs?: number;
processKillGraceMs?: number;
maxRetainedInactiveSessions?: number;
Expand All @@ -187,6 +201,10 @@ describe("TerminalManager", () => {
historyLineLimit,
shellResolver: options.shellResolver ?? (() => "/bin/bash"),
...(options.subprocessChecker ? { subprocessChecker: options.subprocessChecker } : {}),
...(options.externalServerDiscoverer
? { externalServerDiscoverer: options.externalServerDiscoverer }
: {}),
...(options.externalProcessKiller ? { externalProcessKiller: options.externalProcessKiller } : {}),
...(options.subprocessPollIntervalMs
? { subprocessPollIntervalMs: options.subprocessPollIntervalMs }
: {}),
Expand All @@ -198,6 +216,45 @@ describe("TerminalManager", () => {
return { logsDir, ptyAdapter, manager };
}

it("matches command lines for project roots that contain spaces", () => {
const commandLine =
'node "C:\\Users\\First Last\\source\\repos\\t3code-main\\apps\\web\\node_modules\\vite\\bin\\vite.js"';
const result = __terminalManagerInternals.commandMatchesProjectRoot(commandLine, [
"C:\\Users\\First Last\\source\\repos\\t3code-main",
]);

expect(result).toBe(true);
});

it("does not match project roots by substring collision", () => {
const commandLine =
'node "C:\\Users\\Addis\\source\\repos\\t3code-main-2\\apps\\web\\node_modules\\vite\\bin\\vite.js"';
const result = __terminalManagerInternals.commandMatchesProjectRoot(commandLine, [
"C:\\Users\\Addis\\source\\repos\\t3code-main",
]);

expect(result).toBe(false);
});

it("rejects stopping external pids that were not registered for the thread", async () => {
const killedPids: number[] = [];
const { manager } = makeManager(5, {
externalProcessKiller: async (pid) => {
killedPids.push(pid);
},
});

await expect(
manager.close({
threadId: "thread-1",
terminalId: "external:51515",
}),
).rejects.toThrow(/not registered for thread/i);
expect(killedPids).toEqual([]);

manager.dispose();
});

it("spawns lazily and reuses running terminal per thread", async () => {
const { manager, ptyAdapter } = makeManager();
const [first, second] = await Promise.all([
Expand Down
Loading
Loading