dev-prism 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -11
- package/bin/dev-prism.js +20 -233
- package/dist/chunk-3ATDGV4Y.js +22 -0
- package/dist/chunk-3MSC3CGG.js +78 -0
- package/dist/chunk-6YMQTISJ.js +84 -0
- package/dist/chunk-7YGOMAJG.js +106 -0
- package/dist/chunk-AW2FJGXA.js +38 -0
- package/dist/chunk-C4WLIOBR.js +67 -0
- package/dist/chunk-D6QWWXZD.js +49 -0
- package/dist/chunk-HCCZKLC4.js +64 -0
- package/dist/chunk-HZUN6NRB.js +70 -0
- package/dist/chunk-J36LRUXM.js +60 -0
- package/dist/chunk-JHR4WADC.js +200 -0
- package/dist/chunk-JIU574KX.js +41 -0
- package/dist/chunk-LOVO4P3Y.js +41 -0
- package/dist/chunk-P3ETW2KK.js +166 -0
- package/dist/chunk-QUMZI5KK.js +98 -0
- package/dist/chunk-SSQ7XBY2.js +30 -0
- package/dist/chunk-UHI2QJFI.js +200 -0
- package/dist/chunk-X5A6H4Q7.js +70 -0
- package/dist/chunk-Y3GR6XK7.js +71 -0
- package/dist/commands/claude.js +3 -102
- package/dist/commands/create.js +6 -5
- package/dist/commands/destroy.js +4 -3
- package/dist/commands/info.js +7 -0
- package/dist/commands/list.js +4 -4
- package/dist/commands/logs.js +8 -0
- package/dist/commands/prune.js +10 -0
- package/dist/commands/start.js +9 -0
- package/dist/commands/stop-all.js +9 -0
- package/dist/commands/stop.js +7 -0
- package/dist/index.js +60 -10
- package/dist/lib/env.js +3 -1
- package/dist/lib/ports.js +1 -1
- package/dist/lib/store.js +6 -0
- package/dist/lib/worktree.js +1 -5
- package/package.json +14 -4
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
up
|
|
3
|
+
} from "./chunk-GBN67HYD.js";
|
|
4
|
+
import {
|
|
5
|
+
loadConfig
|
|
6
|
+
} from "./chunk-25WQHUYW.js";
|
|
7
|
+
import {
|
|
8
|
+
SessionStore
|
|
9
|
+
} from "./chunk-6YMQTISJ.js";
|
|
10
|
+
|
|
11
|
+
// src/commands/start.ts
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
async function startSession(projectRoot, sessionId, options) {
|
|
14
|
+
const config = await loadConfig(projectRoot);
|
|
15
|
+
const store = new SessionStore();
|
|
16
|
+
let sessionDir;
|
|
17
|
+
try {
|
|
18
|
+
const session = store.findSession(projectRoot, sessionId);
|
|
19
|
+
if (!session) {
|
|
20
|
+
console.error(chalk.red(`Error: Session ${sessionId} not found.`));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
sessionDir = session.session_dir;
|
|
24
|
+
} finally {
|
|
25
|
+
store.close();
|
|
26
|
+
}
|
|
27
|
+
let profiles;
|
|
28
|
+
if (options.mode === "docker") {
|
|
29
|
+
const allApps = config.apps ?? [];
|
|
30
|
+
const excludeApps = options.without ?? [];
|
|
31
|
+
profiles = allApps.filter((app) => !excludeApps.includes(app));
|
|
32
|
+
}
|
|
33
|
+
await up({ cwd: sessionDir, profiles });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
startSession
|
|
38
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRunning
|
|
3
|
+
} from "./chunk-GBN67HYD.js";
|
|
4
|
+
import {
|
|
5
|
+
loadConfig
|
|
6
|
+
} from "./chunk-25WQHUYW.js";
|
|
7
|
+
import {
|
|
8
|
+
SessionStore
|
|
9
|
+
} from "./chunk-6YMQTISJ.js";
|
|
10
|
+
|
|
11
|
+
// src/commands/stop-all.ts
|
|
12
|
+
import { existsSync } from "fs";
|
|
13
|
+
import { resolve } from "path";
|
|
14
|
+
import chalk from "chalk";
|
|
15
|
+
import { execa } from "execa";
|
|
16
|
+
async function stopAllSessions(projectRoot) {
|
|
17
|
+
const config = await loadConfig(projectRoot);
|
|
18
|
+
const store = new SessionStore();
|
|
19
|
+
let sessions;
|
|
20
|
+
try {
|
|
21
|
+
sessions = store.listByProject(projectRoot);
|
|
22
|
+
} finally {
|
|
23
|
+
store.close();
|
|
24
|
+
}
|
|
25
|
+
if (sessions.length === 0) {
|
|
26
|
+
console.log(chalk.gray("No sessions found."));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const runningSessions = [];
|
|
30
|
+
for (const session of sessions) {
|
|
31
|
+
const envFile = resolve(session.session_dir, ".env.session");
|
|
32
|
+
if (existsSync(envFile)) {
|
|
33
|
+
const running = await isRunning({ cwd: session.session_dir });
|
|
34
|
+
if (running) {
|
|
35
|
+
runningSessions.push({ sessionId: session.session_id, path: session.session_dir });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (runningSessions.length === 0) {
|
|
40
|
+
console.log(chalk.gray("No running sessions found."));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log(chalk.blue(`Stopping ${runningSessions.length} running session(s)...
|
|
44
|
+
`));
|
|
45
|
+
const allApps = config.apps ?? [];
|
|
46
|
+
const profileFlags = allApps.flatMap((p) => ["--profile", p]);
|
|
47
|
+
const allServices = ["postgres", "mailpit", ...allApps];
|
|
48
|
+
for (const session of runningSessions) {
|
|
49
|
+
console.log(chalk.gray(` Stopping session ${session.sessionId}...`));
|
|
50
|
+
try {
|
|
51
|
+
await execa(
|
|
52
|
+
"docker",
|
|
53
|
+
["compose", "-f", "docker-compose.session.yml", "--env-file", ".env.session", ...profileFlags, "stop", ...allServices],
|
|
54
|
+
{ cwd: session.path, stdio: "pipe" }
|
|
55
|
+
);
|
|
56
|
+
console.log(chalk.green(` Session ${session.sessionId} stopped.`));
|
|
57
|
+
} catch {
|
|
58
|
+
console.log(chalk.yellow(` Warning: Could not stop session ${session.sessionId}`));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
console.log(chalk.green(`
|
|
62
|
+
Stopped ${runningSessions.length} session(s).`));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
stopAllSessions
|
|
67
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadConfig
|
|
3
|
+
} from "./chunk-25WQHUYW.js";
|
|
4
|
+
import {
|
|
5
|
+
SessionStore
|
|
6
|
+
} from "./chunk-6YMQTISJ.js";
|
|
7
|
+
|
|
8
|
+
// src/commands/logs.ts
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { execa } from "execa";
|
|
11
|
+
async function streamLogs(projectRoot, sessionId, options) {
|
|
12
|
+
const config = await loadConfig(projectRoot);
|
|
13
|
+
const store = new SessionStore();
|
|
14
|
+
let sessionDir;
|
|
15
|
+
try {
|
|
16
|
+
const session = store.findSession(projectRoot, sessionId);
|
|
17
|
+
if (!session) {
|
|
18
|
+
console.error(chalk.red(`Error: Session ${sessionId} not found.`));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
sessionDir = session.session_dir;
|
|
22
|
+
} finally {
|
|
23
|
+
store.close();
|
|
24
|
+
}
|
|
25
|
+
let profileFlags = [];
|
|
26
|
+
if (options.mode === "docker") {
|
|
27
|
+
const allApps = config.apps ?? [];
|
|
28
|
+
const excludeApps = options.without ?? [];
|
|
29
|
+
const profiles = allApps.filter((app) => !excludeApps.includes(app));
|
|
30
|
+
profileFlags = profiles.flatMap((p) => ["--profile", p]);
|
|
31
|
+
}
|
|
32
|
+
const args = [
|
|
33
|
+
"compose",
|
|
34
|
+
"-f",
|
|
35
|
+
"docker-compose.session.yml",
|
|
36
|
+
"--env-file",
|
|
37
|
+
".env.session",
|
|
38
|
+
...profileFlags,
|
|
39
|
+
"logs",
|
|
40
|
+
"-f",
|
|
41
|
+
"--tail",
|
|
42
|
+
options.tail ?? "50"
|
|
43
|
+
];
|
|
44
|
+
await execa("docker", args, { cwd: sessionDir, stdio: "inherit" });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
streamLogs
|
|
49
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
calculatePorts
|
|
3
|
+
} from "./chunk-PJKUD2N2.js";
|
|
4
|
+
import {
|
|
5
|
+
getSessionWorktrees
|
|
6
|
+
} from "./chunk-GWDGC2OE.js";
|
|
7
|
+
import {
|
|
8
|
+
loadConfig
|
|
9
|
+
} from "./chunk-25WQHUYW.js";
|
|
10
|
+
import {
|
|
11
|
+
isRunning
|
|
12
|
+
} from "./chunk-GBN67HYD.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/list.ts
|
|
15
|
+
import { existsSync } from "fs";
|
|
16
|
+
import { resolve } from "path";
|
|
17
|
+
import chalk from "chalk";
|
|
18
|
+
async function listSessions(projectRoot) {
|
|
19
|
+
const config = await loadConfig(projectRoot);
|
|
20
|
+
const sessions = await getSessionWorktrees(projectRoot);
|
|
21
|
+
if (sessions.length === 0) {
|
|
22
|
+
console.log(chalk.gray("No active sessions found."));
|
|
23
|
+
console.log(chalk.gray("\nTo create a session:"));
|
|
24
|
+
console.log(chalk.cyan(" dev-prism create"));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.log(chalk.blue("Active Sessions:"));
|
|
28
|
+
console.log(chalk.gray("================\n"));
|
|
29
|
+
for (const session of sessions) {
|
|
30
|
+
const status = await getSessionStatus(session.sessionId, session.path, session.branch, config);
|
|
31
|
+
printSessionStatus(status);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function getSessionStatus(sessionId, path, branch, config) {
|
|
35
|
+
const ports = calculatePorts(config, sessionId);
|
|
36
|
+
let running = false;
|
|
37
|
+
const envFile = resolve(path, ".env.session");
|
|
38
|
+
if (existsSync(envFile)) {
|
|
39
|
+
running = await isRunning({ cwd: path });
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
sessionId,
|
|
43
|
+
path,
|
|
44
|
+
branch,
|
|
45
|
+
running,
|
|
46
|
+
ports
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function printSessionStatus(status) {
|
|
50
|
+
const statusIcon = status.running ? chalk.green("\u25CF") : chalk.red("\u25CB");
|
|
51
|
+
const statusText = status.running ? chalk.green("running") : chalk.gray("stopped");
|
|
52
|
+
console.log(`${statusIcon} Session ${chalk.bold(status.sessionId)} ${statusText}`);
|
|
53
|
+
console.log(chalk.gray(` Path: ${status.path}`));
|
|
54
|
+
console.log(chalk.gray(` Branch: ${status.branch}`));
|
|
55
|
+
console.log(chalk.gray(" Ports:"));
|
|
56
|
+
for (const [name, port] of Object.entries(status.ports)) {
|
|
57
|
+
console.log(chalk.gray(` ${name}: ${port}`));
|
|
58
|
+
}
|
|
59
|
+
console.log("");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
listSessions
|
|
64
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
calculatePorts
|
|
3
|
+
} from "./chunk-3ATDGV4Y.js";
|
|
4
|
+
import {
|
|
5
|
+
isRunning
|
|
6
|
+
} from "./chunk-GBN67HYD.js";
|
|
7
|
+
import {
|
|
8
|
+
loadConfig
|
|
9
|
+
} from "./chunk-25WQHUYW.js";
|
|
10
|
+
import {
|
|
11
|
+
SessionStore
|
|
12
|
+
} from "./chunk-6YMQTISJ.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/list.ts
|
|
15
|
+
import { existsSync } from "fs";
|
|
16
|
+
import { resolve } from "path";
|
|
17
|
+
import chalk from "chalk";
|
|
18
|
+
async function listSessions(projectRoot) {
|
|
19
|
+
const config = await loadConfig(projectRoot);
|
|
20
|
+
const store = new SessionStore();
|
|
21
|
+
let sessions;
|
|
22
|
+
try {
|
|
23
|
+
sessions = store.listByProject(projectRoot);
|
|
24
|
+
} finally {
|
|
25
|
+
store.close();
|
|
26
|
+
}
|
|
27
|
+
if (sessions.length === 0) {
|
|
28
|
+
console.log(chalk.gray("No active sessions found."));
|
|
29
|
+
console.log(chalk.gray("\nTo create a session:"));
|
|
30
|
+
console.log(chalk.cyan(" dev-prism create"));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log(chalk.blue("Active Sessions:"));
|
|
34
|
+
console.log(chalk.gray("================\n"));
|
|
35
|
+
for (const session of sessions) {
|
|
36
|
+
const status = await getSessionStatus(session.session_id, session.session_dir, session.branch, config);
|
|
37
|
+
printSessionStatus(status);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function getSessionStatus(sessionId, path, branch, config) {
|
|
41
|
+
const ports = calculatePorts(config, sessionId);
|
|
42
|
+
let running = false;
|
|
43
|
+
const envFile = resolve(path, ".env.session");
|
|
44
|
+
if (existsSync(envFile)) {
|
|
45
|
+
running = await isRunning({ cwd: path });
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
sessionId,
|
|
49
|
+
path,
|
|
50
|
+
branch,
|
|
51
|
+
running,
|
|
52
|
+
ports
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function printSessionStatus(status) {
|
|
56
|
+
const statusIcon = status.running ? chalk.green("\u25CF") : chalk.red("\u25CB");
|
|
57
|
+
const statusText = status.running ? chalk.green("running") : chalk.gray("stopped");
|
|
58
|
+
console.log(`${statusIcon} Session ${chalk.bold(status.sessionId)} ${statusText}`);
|
|
59
|
+
console.log(chalk.gray(` Path: ${status.path}`));
|
|
60
|
+
console.log(chalk.gray(` Branch: ${status.branch}`));
|
|
61
|
+
console.log(chalk.gray(" Ports:"));
|
|
62
|
+
for (const [name, port] of Object.entries(status.ports)) {
|
|
63
|
+
console.log(chalk.cyan(` ${name}: http://localhost:${port}`));
|
|
64
|
+
}
|
|
65
|
+
console.log("");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
listSessions
|
|
70
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/lib/env.ts
|
|
2
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
3
|
+
import { resolve, dirname } from "path";
|
|
4
|
+
function generateEnvContent(sessionId, ports, projectName) {
|
|
5
|
+
const lines = [
|
|
6
|
+
`# Auto-generated session environment`,
|
|
7
|
+
`SESSION_ID=${sessionId}`,
|
|
8
|
+
`COMPOSE_PROJECT_NAME=${projectName}-${sessionId}`,
|
|
9
|
+
"",
|
|
10
|
+
"# Ports (used by docker-compose.session.yml)"
|
|
11
|
+
];
|
|
12
|
+
for (const [name, port] of Object.entries(ports)) {
|
|
13
|
+
lines.push(`${name}=${port}`);
|
|
14
|
+
}
|
|
15
|
+
return lines.join("\n") + "\n";
|
|
16
|
+
}
|
|
17
|
+
function writeEnvFile(sessionDir, sessionId, ports, projectName) {
|
|
18
|
+
const content = generateEnvContent(sessionId, ports, projectName);
|
|
19
|
+
const filePath = resolve(sessionDir, ".env.session");
|
|
20
|
+
writeFileSync(filePath, content, "utf-8");
|
|
21
|
+
return filePath;
|
|
22
|
+
}
|
|
23
|
+
function renderAppEnv(template, ports) {
|
|
24
|
+
const result = {};
|
|
25
|
+
for (const [key, value] of Object.entries(template)) {
|
|
26
|
+
let rendered = value;
|
|
27
|
+
for (const [portName, portValue] of Object.entries(ports)) {
|
|
28
|
+
rendered = rendered.replace(
|
|
29
|
+
new RegExp(`\\$\\{${portName}\\}`, "g"),
|
|
30
|
+
String(portValue)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
result[key] = rendered;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
function writeAppEnvFiles(config, sessionDir, sessionId, ports) {
|
|
38
|
+
if (!config.appEnv) return [];
|
|
39
|
+
const writtenFiles = [];
|
|
40
|
+
for (const [appPath, template] of Object.entries(config.appEnv)) {
|
|
41
|
+
const env = renderAppEnv(template, ports);
|
|
42
|
+
const lines = [`# Auto-generated for session ${sessionId}`, `SESSION_ID=${sessionId}`];
|
|
43
|
+
for (const [key, value] of Object.entries(env)) {
|
|
44
|
+
lines.push(`${key}=${value}`);
|
|
45
|
+
}
|
|
46
|
+
const content = lines.join("\n") + "\n";
|
|
47
|
+
const envFilePath = resolve(sessionDir, appPath, ".env.session");
|
|
48
|
+
mkdirSync(dirname(envFilePath), { recursive: true });
|
|
49
|
+
writeFileSync(envFilePath, content, "utf-8");
|
|
50
|
+
writtenFiles.push(envFilePath);
|
|
51
|
+
}
|
|
52
|
+
return writtenFiles;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export {
|
|
56
|
+
generateEnvContent,
|
|
57
|
+
writeEnvFile,
|
|
58
|
+
renderAppEnv,
|
|
59
|
+
writeAppEnvFiles
|
|
60
|
+
};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import {
|
|
2
|
+
writeAppEnvFiles,
|
|
3
|
+
writeEnvFile
|
|
4
|
+
} from "./chunk-J36LRUXM.js";
|
|
5
|
+
import {
|
|
6
|
+
calculatePorts,
|
|
7
|
+
formatPortsTable
|
|
8
|
+
} from "./chunk-3ATDGV4Y.js";
|
|
9
|
+
import {
|
|
10
|
+
createWorktree,
|
|
11
|
+
findNextSessionId,
|
|
12
|
+
generateDefaultBranchName,
|
|
13
|
+
removeWorktree
|
|
14
|
+
} from "./chunk-Y3GR6XK7.js";
|
|
15
|
+
import {
|
|
16
|
+
down,
|
|
17
|
+
logs,
|
|
18
|
+
up
|
|
19
|
+
} from "./chunk-GBN67HYD.js";
|
|
20
|
+
import {
|
|
21
|
+
getSessionDir,
|
|
22
|
+
getSessionsDir,
|
|
23
|
+
loadConfig
|
|
24
|
+
} from "./chunk-25WQHUYW.js";
|
|
25
|
+
import {
|
|
26
|
+
SessionStore
|
|
27
|
+
} from "./chunk-6YMQTISJ.js";
|
|
28
|
+
|
|
29
|
+
// src/commands/create.ts
|
|
30
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync } from "fs";
|
|
31
|
+
import { basename, join } from "path";
|
|
32
|
+
import chalk from "chalk";
|
|
33
|
+
import { execa } from "execa";
|
|
34
|
+
function updateEnvDatabaseUrl(envPath, newDbUrl) {
|
|
35
|
+
if (!existsSync(envPath)) return;
|
|
36
|
+
let content = readFileSync(envPath, "utf-8");
|
|
37
|
+
if (content.includes("DATABASE_URL=")) {
|
|
38
|
+
content = content.replace(/^DATABASE_URL=.*/m, `DATABASE_URL=${newDbUrl}`);
|
|
39
|
+
} else {
|
|
40
|
+
content += `
|
|
41
|
+
DATABASE_URL=${newDbUrl}
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
writeFileSync(envPath, content);
|
|
45
|
+
}
|
|
46
|
+
async function createSession(projectRoot, sessionId, options) {
|
|
47
|
+
const config = await loadConfig(projectRoot);
|
|
48
|
+
const sessionsDir = getSessionsDir(config, projectRoot);
|
|
49
|
+
const store = new SessionStore();
|
|
50
|
+
try {
|
|
51
|
+
if (!sessionId) {
|
|
52
|
+
sessionId = findNextSessionId(store.getUsedSessionIds(projectRoot));
|
|
53
|
+
console.log(chalk.gray(`Auto-assigned session ID: ${sessionId}`));
|
|
54
|
+
}
|
|
55
|
+
if (!/^\d{3}$/.test(sessionId)) {
|
|
56
|
+
console.error(chalk.red("Error: Session ID must be exactly 3 digits (001-999)"));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const inPlace = options.inPlace ?? false;
|
|
60
|
+
const branchName = options.branch || generateDefaultBranchName(sessionId);
|
|
61
|
+
const mode = options.mode || "docker";
|
|
62
|
+
console.log(chalk.blue(`Creating session ${sessionId} (${mode} mode${inPlace ? ", in-place" : ""})...`));
|
|
63
|
+
if (!inPlace) {
|
|
64
|
+
console.log(chalk.gray(`Branch: ${branchName}`));
|
|
65
|
+
}
|
|
66
|
+
const ports = calculatePorts(config, sessionId);
|
|
67
|
+
console.log(chalk.gray("\nPorts:"));
|
|
68
|
+
console.log(chalk.gray(formatPortsTable(ports)));
|
|
69
|
+
let sessionDir;
|
|
70
|
+
if (inPlace) {
|
|
71
|
+
sessionDir = projectRoot;
|
|
72
|
+
console.log(chalk.blue("\nUsing current directory (in-place mode)..."));
|
|
73
|
+
console.log(chalk.green(` Directory: ${sessionDir}`));
|
|
74
|
+
} else {
|
|
75
|
+
if (!existsSync(sessionsDir)) {
|
|
76
|
+
mkdirSync(sessionsDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
sessionDir = getSessionDir(config, projectRoot, sessionId);
|
|
79
|
+
console.log(chalk.blue("\nCreating git worktree..."));
|
|
80
|
+
await createWorktree(projectRoot, sessionDir, branchName);
|
|
81
|
+
console.log(chalk.green(` Created: ${sessionDir}`));
|
|
82
|
+
const sessionDbUrl = `postgresql://postgres:postgres@localhost:${ports.POSTGRES_PORT}/postgres`;
|
|
83
|
+
const envFilesToCopy = config.envFiles ?? [];
|
|
84
|
+
for (const envFile of envFilesToCopy) {
|
|
85
|
+
const srcPath = join(projectRoot, envFile);
|
|
86
|
+
const destPath = join(sessionDir, envFile);
|
|
87
|
+
if (existsSync(srcPath)) {
|
|
88
|
+
copyFileSync(srcPath, destPath);
|
|
89
|
+
updateEnvDatabaseUrl(destPath, sessionDbUrl);
|
|
90
|
+
console.log(chalk.green(` Copied: ${envFile} (updated DATABASE_URL)`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
console.log(chalk.blue("\nGenerating .env.session..."));
|
|
95
|
+
const projectName = config.projectName ?? basename(projectRoot);
|
|
96
|
+
const envPath = writeEnvFile(sessionDir, sessionId, ports, projectName);
|
|
97
|
+
console.log(chalk.green(` Written: ${envPath}`));
|
|
98
|
+
const appEnvFiles = writeAppEnvFiles(config, sessionDir, sessionId, ports);
|
|
99
|
+
for (const file of appEnvFiles) {
|
|
100
|
+
console.log(chalk.green(` Written: ${file}`));
|
|
101
|
+
}
|
|
102
|
+
console.log(chalk.blue("\nStarting Docker services..."));
|
|
103
|
+
let profiles;
|
|
104
|
+
if (mode === "docker") {
|
|
105
|
+
const allApps = config.apps ?? [];
|
|
106
|
+
const excludeApps = options.without ?? [];
|
|
107
|
+
profiles = allApps.filter((app) => !excludeApps.includes(app));
|
|
108
|
+
if (excludeApps.length > 0) {
|
|
109
|
+
console.log(chalk.gray(` Excluding apps: ${excludeApps.join(", ")}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
await up({ cwd: sessionDir, profiles });
|
|
113
|
+
console.log(chalk.blue("Waiting for services to be ready..."));
|
|
114
|
+
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
115
|
+
if (config.setup.length > 0) {
|
|
116
|
+
console.log(chalk.blue("\nRunning setup commands..."));
|
|
117
|
+
const setupEnv = {
|
|
118
|
+
...process.env,
|
|
119
|
+
SESSION_ID: sessionId,
|
|
120
|
+
// Add DATABASE_URL for db commands
|
|
121
|
+
DATABASE_URL: `postgresql://postgres:postgres@localhost:${ports.POSTGRES_PORT}/postgres`
|
|
122
|
+
};
|
|
123
|
+
for (const [name, port] of Object.entries(ports)) {
|
|
124
|
+
setupEnv[name] = String(port);
|
|
125
|
+
}
|
|
126
|
+
for (const cmd of config.setup) {
|
|
127
|
+
console.log(chalk.gray(` Running: ${cmd}`));
|
|
128
|
+
const [command, ...args] = cmd.split(" ");
|
|
129
|
+
try {
|
|
130
|
+
await execa(command, args, {
|
|
131
|
+
cwd: sessionDir,
|
|
132
|
+
stdio: "inherit",
|
|
133
|
+
env: setupEnv
|
|
134
|
+
});
|
|
135
|
+
} catch {
|
|
136
|
+
console.warn(chalk.yellow(` Warning: Command failed: ${cmd}`));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
store.remove(projectRoot, sessionId);
|
|
141
|
+
try {
|
|
142
|
+
store.insert({
|
|
143
|
+
sessionId,
|
|
144
|
+
projectRoot,
|
|
145
|
+
sessionDir,
|
|
146
|
+
branch: inPlace ? "" : branchName,
|
|
147
|
+
mode,
|
|
148
|
+
inPlace
|
|
149
|
+
});
|
|
150
|
+
} catch (dbErr) {
|
|
151
|
+
console.error(chalk.red("Failed to record session in database. Cleaning up..."));
|
|
152
|
+
try {
|
|
153
|
+
await down({ cwd: sessionDir });
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
if (!inPlace) {
|
|
157
|
+
try {
|
|
158
|
+
await removeWorktree(projectRoot, sessionDir, branchName);
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw dbErr;
|
|
163
|
+
}
|
|
164
|
+
console.log(chalk.green(`
|
|
165
|
+
Session ${sessionId} ready!`));
|
|
166
|
+
console.log(chalk.gray(`Directory: ${sessionDir}`));
|
|
167
|
+
if (mode === "docker") {
|
|
168
|
+
console.log(chalk.gray("\nDocker mode - all services in containers."));
|
|
169
|
+
console.log(chalk.gray("View logs: docker compose -f docker-compose.session.yml logs -f"));
|
|
170
|
+
} else {
|
|
171
|
+
console.log(chalk.gray("\nNative mode - run apps with: pnpm dev"));
|
|
172
|
+
}
|
|
173
|
+
console.log(chalk.gray("\nPorts:"));
|
|
174
|
+
for (const [name, port] of Object.entries(ports)) {
|
|
175
|
+
console.log(chalk.cyan(` ${name}: http://localhost:${port}`));
|
|
176
|
+
}
|
|
177
|
+
if (options.detach === false) {
|
|
178
|
+
console.log(chalk.blue("\nStreaming logs (Ctrl+C to stop)..."));
|
|
179
|
+
console.log(chalk.gray("\u2500".repeat(60)));
|
|
180
|
+
try {
|
|
181
|
+
await logs({ cwd: sessionDir, profiles });
|
|
182
|
+
} catch (error) {
|
|
183
|
+
const execaError = error;
|
|
184
|
+
if (execaError.signal === "SIGINT") {
|
|
185
|
+
console.log(chalk.gray("\n\u2500".repeat(60)));
|
|
186
|
+
console.log(chalk.yellow("\nLog streaming stopped. Services are still running."));
|
|
187
|
+
console.log(chalk.gray(`Resume logs: cd ${sessionDir} && docker compose -f docker-compose.session.yml --env-file .env.session logs -f`));
|
|
188
|
+
} else {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} finally {
|
|
194
|
+
store.close();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export {
|
|
199
|
+
createSession
|
|
200
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRunning
|
|
3
|
+
} from "./chunk-GBN67HYD.js";
|
|
4
|
+
|
|
5
|
+
// src/commands/info.ts
|
|
6
|
+
import { existsSync, readFileSync } from "fs";
|
|
7
|
+
import { resolve } from "path";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
async function showInfo(cwd) {
|
|
10
|
+
const envFile = resolve(cwd, ".env.session");
|
|
11
|
+
if (!existsSync(envFile)) {
|
|
12
|
+
console.log(chalk.yellow("No .env.session found in current directory."));
|
|
13
|
+
console.log(chalk.gray("Run `dev-prism create --in-place` to create a session here."));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const envContent = readFileSync(envFile, "utf-8");
|
|
17
|
+
const env = {};
|
|
18
|
+
for (const line of envContent.split("\n")) {
|
|
19
|
+
const match = line.match(/^([^=]+)=(.*)$/);
|
|
20
|
+
if (match) {
|
|
21
|
+
env[match[1]] = match[2];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const sessionId = env.SESSION_ID || "unknown";
|
|
25
|
+
const running = await isRunning({ cwd });
|
|
26
|
+
console.log(chalk.blue(`
|
|
27
|
+
Session ${sessionId}`));
|
|
28
|
+
console.log(chalk.gray(`Directory: ${cwd}`));
|
|
29
|
+
console.log(running ? chalk.green("Status: running") : chalk.yellow("Status: stopped"));
|
|
30
|
+
console.log(chalk.gray("\nPorts:"));
|
|
31
|
+
for (const [key, value] of Object.entries(env)) {
|
|
32
|
+
if (key.includes("PORT")) {
|
|
33
|
+
console.log(chalk.cyan(` ${key}: http://localhost:${value}`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
console.log("");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
showInfo
|
|
41
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isRunning
|
|
3
|
+
} from "./chunk-GBN67HYD.js";
|
|
4
|
+
|
|
5
|
+
// src/commands/info.ts
|
|
6
|
+
import { existsSync, readFileSync } from "fs";
|
|
7
|
+
import { resolve } from "path";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
async function showInfo(cwd) {
|
|
10
|
+
const envFile = resolve(cwd, ".env.session");
|
|
11
|
+
if (!existsSync(envFile)) {
|
|
12
|
+
console.log(chalk.yellow("No .env.session found in current directory."));
|
|
13
|
+
console.log(chalk.gray("Run `dev-prism create --in-place` to create a session here."));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const envContent = readFileSync(envFile, "utf-8");
|
|
17
|
+
const env = {};
|
|
18
|
+
for (const line of envContent.split("\n")) {
|
|
19
|
+
const match = line.match(/^([^=]+)=(.*)$/);
|
|
20
|
+
if (match) {
|
|
21
|
+
env[match[1]] = match[2];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const sessionId = env.SESSION_ID || "unknown";
|
|
25
|
+
const running = await isRunning({ cwd });
|
|
26
|
+
console.log(chalk.blue(`
|
|
27
|
+
Session ${sessionId}`));
|
|
28
|
+
console.log(chalk.gray(`Directory: ${cwd}`));
|
|
29
|
+
console.log(running ? chalk.green("Status: running") : chalk.yellow("Status: stopped"));
|
|
30
|
+
console.log(chalk.gray("\nPorts:"));
|
|
31
|
+
for (const [key, value] of Object.entries(env)) {
|
|
32
|
+
if (key.includes("PORT")) {
|
|
33
|
+
console.log(chalk.gray(` ${key}: ${value}`));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
console.log("");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
showInfo
|
|
41
|
+
};
|