90 lines
2.8 KiB
TypeScript
90 lines
2.8 KiB
TypeScript
import { existsSync } from "node:fs";
|
|
import { homedir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { PROJECT_ROOT } from "../constants.ts";
|
|
import type { RegistryEntry } from "../types.ts";
|
|
import { registerPortlessAliases } from "./portless.ts";
|
|
import { portlessHttpsUrl } from "./ports.ts";
|
|
import { fatal, log } from "./shell.ts";
|
|
import { spawnDevInTmux, tmuxSessionName } from "./tmux.ts";
|
|
import { rewriteDbEnv } from "./url.ts";
|
|
|
|
export function buildDevEnvAndArgs(entry: RegistryEntry): {
|
|
env: Record<string, string>;
|
|
args: string[];
|
|
} {
|
|
const { worktreeNum, databaseUrl } = entry;
|
|
let env: Record<string, string> = {
|
|
...(process.env as Record<string, string>),
|
|
};
|
|
if (worktreeNum > 1) {
|
|
if (!databaseUrl) fatal("agent worktree missing databaseUrl");
|
|
env = rewriteDbEnv(env, databaseUrl);
|
|
if (!env.EMULATE_GOOGLE_URL) {
|
|
env.EMULATE_GOOGLE_URL = portlessHttpsUrl("google.emulate.localhost");
|
|
}
|
|
const portlessCa = join(homedir(), ".portless", "ca.pem");
|
|
if (existsSync(portlessCa) && !env.NODE_EXTRA_CA_CERTS) {
|
|
env.NODE_EXTRA_CA_CERTS = portlessCa;
|
|
}
|
|
const aliases = registerPortlessAliases(worktreeNum);
|
|
env.BETTER_AUTH_URL = aliases.apiUrl;
|
|
env.CLIENT_URL = aliases.viteUrl;
|
|
env.VITE_BACKEND_URL = aliases.apiUrl;
|
|
env.VITE_FRONTEND_URL = aliases.viteUrl;
|
|
// Hand the worktree's tunnel to dev.ts, which derives MCP_SERVER_URL /
|
|
// CHAT_URL / SLACK_BOT_URL from NGROK_URL. Set at `bun dw setup`; reused
|
|
// here on a plain `bun dw run`.
|
|
if (entry.ngrokUrl && !env.NGROK_URL) {
|
|
env.NGROK_URL = entry.ngrokUrl;
|
|
}
|
|
}
|
|
|
|
const args = [
|
|
"bun",
|
|
"scripts/dev.ts",
|
|
"--worktree",
|
|
String(worktreeNum),
|
|
...process.argv.slice(3),
|
|
];
|
|
return { env, args };
|
|
}
|
|
|
|
export function startDev(
|
|
entry: RegistryEntry,
|
|
opts?: { allowTmux?: boolean },
|
|
): never {
|
|
const { worktreeNum, branchName } = entry;
|
|
const { env, args } = buildDevEnvAndArgs(entry);
|
|
|
|
// Agent worktrees (N > 1) in a non-TTY invocation: wrap in detached tmux
|
|
// so the calling agent doesn't block. Canonical (N=1) stays inline always.
|
|
// Node/Bun sets isTTY to true when stdout is a TTY and undefined otherwise.
|
|
const useTmux =
|
|
(opts?.allowTmux ?? true) && worktreeNum > 1 && !process.stdout.isTTY;
|
|
if (useTmux) {
|
|
log(
|
|
`starting dev in tmux (worktree=${worktreeNum}${branchName ? `, branch=${branchName}` : ""}, non-TTY)`,
|
|
);
|
|
spawnDevInTmux(tmuxSessionName(worktreeNum), env, args, PROJECT_ROOT);
|
|
process.exit(0);
|
|
}
|
|
|
|
log(
|
|
`starting dev (worktree=${worktreeNum}${branchName ? `, branch=${branchName}` : ""})`,
|
|
);
|
|
const proc = Bun.spawn(args, {
|
|
cwd: PROJECT_ROOT,
|
|
env,
|
|
stdout: "inherit",
|
|
stderr: "inherit",
|
|
});
|
|
|
|
const forward = (sig: NodeJS.Signals) => () => proc.kill(sig);
|
|
process.on("SIGINT", forward("SIGINT"));
|
|
process.on("SIGTERM", forward("SIGTERM"));
|
|
|
|
proc.exited.then((code) => process.exit(code ?? 0));
|
|
return undefined as never;
|
|
}
|