dev-prism 0.3.0 → 0.6.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.
Files changed (100) hide show
  1. package/README.md +262 -70
  2. package/bin/dev-prism.js +45 -42
  3. package/dist/chunk-24OM3LGM.js +35 -0
  4. package/dist/chunk-3FYEGH2G.js +217 -0
  5. package/dist/chunk-3NW2OWIU.js +78 -0
  6. package/dist/chunk-3Q454U3I.js +71 -0
  7. package/dist/chunk-3TRRZEFR.js +38 -0
  8. package/dist/chunk-4UNCSJRM.js +70 -0
  9. package/dist/chunk-63II3EL4.js +98 -0
  10. package/dist/chunk-76LSQIZI.js +31 -0
  11. package/dist/chunk-7OSTLJLO.js +219 -0
  12. package/dist/chunk-7YEZWM6Y.js +97 -0
  13. package/dist/chunk-AEVARZQ4.js +203 -0
  14. package/dist/chunk-AIVPJ467.js +70 -0
  15. package/dist/chunk-AOM6BONB.js +98 -0
  16. package/dist/chunk-BXYXWNGH.js +30 -0
  17. package/dist/chunk-CUQVGZBX.js +44 -0
  18. package/dist/chunk-DQER5GNG.js +72 -0
  19. package/dist/chunk-DTE5YQMI.js +41 -0
  20. package/dist/chunk-EXRHG5KQ.js +60 -0
  21. package/dist/chunk-FKTFCSU7.js +78 -0
  22. package/dist/chunk-GKXXK2ZH.js +203 -0
  23. package/dist/chunk-H3L73URT.js +65 -0
  24. package/dist/chunk-H4HPDIY3.js +95 -0
  25. package/dist/chunk-HZBJF67X.js +60 -0
  26. package/dist/chunk-I3U6JK77.js +66 -0
  27. package/dist/chunk-JMKE3ZKI.js +61 -0
  28. package/dist/chunk-JZ2VPQXP.js +132 -0
  29. package/dist/chunk-KDYKLH6P.js +40 -0
  30. package/dist/chunk-KZJE62TK.js +203 -0
  31. package/dist/chunk-LAOWFCQL.js +21 -0
  32. package/dist/chunk-LNIOSGC4.js +78 -0
  33. package/dist/chunk-MRBTH5PL.js +66 -0
  34. package/dist/chunk-NNVP5F6I.js +77 -0
  35. package/dist/chunk-OL25TBYX.js +67 -0
  36. package/dist/chunk-OOJ6YOGS.js +53 -0
  37. package/dist/chunk-OPEZFBBI.js +219 -0
  38. package/dist/chunk-Q5DPX4WL.js +219 -0
  39. package/dist/chunk-RC2AUYZ7.js +49 -0
  40. package/dist/chunk-RQ245R7T.js +67 -0
  41. package/dist/chunk-SD3TON6N.js +32 -0
  42. package/dist/chunk-SEKH4ZV6.js +60 -0
  43. package/dist/chunk-SUMJLXT7.js +30 -0
  44. package/dist/chunk-UKYQN4A3.js +38 -0
  45. package/dist/chunk-URGGS3XM.js +95 -0
  46. package/dist/chunk-VUNPVDSO.js +74 -0
  47. package/dist/chunk-VXP2SPRI.js +51 -0
  48. package/dist/chunk-W54CPPSK.js +217 -0
  49. package/dist/chunk-X2PXZRYU.js +41 -0
  50. package/dist/chunk-X6FHBEAS.js +200 -0
  51. package/dist/chunk-YSO3IDZZ.js +40 -0
  52. package/dist/chunk-YY5DA35Z.js +40 -0
  53. package/dist/chunk-Z2ISJMLW.js +92 -0
  54. package/dist/chunk-ZKHNUDSL.js +119 -0
  55. package/dist/commands/create.d.ts +2 -1
  56. package/dist/commands/create.js +6 -5
  57. package/dist/commands/destroy.d.ts +1 -1
  58. package/dist/commands/destroy.js +4 -3
  59. package/dist/commands/info.d.ts +3 -0
  60. package/dist/commands/info.js +3 -2
  61. package/dist/commands/list.d.ts +1 -1
  62. package/dist/commands/list.js +3 -5
  63. package/dist/commands/logs.d.ts +8 -0
  64. package/dist/commands/logs.js +3 -2
  65. package/dist/commands/prune.d.ts +6 -0
  66. package/dist/commands/prune.js +4 -3
  67. package/dist/commands/start.d.ts +7 -0
  68. package/dist/commands/start.js +3 -2
  69. package/dist/commands/stop-all.d.ts +3 -0
  70. package/dist/commands/stop-all.js +3 -4
  71. package/dist/commands/stop.d.ts +3 -0
  72. package/dist/commands/stop.js +3 -2
  73. package/dist/index.d.ts +14 -2
  74. package/dist/index.js +36 -18
  75. package/dist/lib/compose.d.ts +12 -0
  76. package/dist/lib/compose.js +12 -0
  77. package/dist/lib/config.d.ts +4 -0
  78. package/dist/lib/config.test.d.ts +1 -0
  79. package/dist/lib/config.test.js +32 -0
  80. package/dist/lib/docker-inspect.d.ts +24 -0
  81. package/dist/lib/docker-inspect.js +16 -0
  82. package/dist/lib/env.d.ts +5 -4
  83. package/dist/lib/env.js +1 -1
  84. package/dist/lib/env.test.d.ts +1 -0
  85. package/dist/lib/env.test.js +68 -0
  86. package/dist/lib/ports.d.ts +3 -3
  87. package/dist/lib/ports.js +3 -3
  88. package/dist/lib/ports.test.d.ts +1 -0
  89. package/dist/lib/ports.test.js +61 -0
  90. package/dist/lib/session.d.ts +16 -0
  91. package/dist/lib/session.js +13 -0
  92. package/dist/lib/store.d.ts +46 -0
  93. package/dist/lib/store.js +1 -1
  94. package/dist/lib/store.test.d.ts +1 -0
  95. package/dist/lib/store.test.js +205 -0
  96. package/dist/lib/worktree.d.ts +3 -13
  97. package/dist/lib/worktree.js +1 -1
  98. package/dist/lib/worktree.test.d.ts +1 -0
  99. package/dist/lib/worktree.test.js +41 -0
  100. package/package.json +2 -5
@@ -0,0 +1,217 @@
1
+ import {
2
+ extractPorts,
3
+ formatPortsTable
4
+ } from "./chunk-LAOWFCQL.js";
5
+ import {
6
+ generateEnvStub,
7
+ writeComposeFile
8
+ } from "./chunk-JMKE3ZKI.js";
9
+ import {
10
+ writeAppEnvFiles,
11
+ writeEnvFile
12
+ } from "./chunk-SEKH4ZV6.js";
13
+ import {
14
+ createWorktree,
15
+ generateDefaultBranchName,
16
+ removeWorktree
17
+ } from "./chunk-3Q454U3I.js";
18
+ import {
19
+ down,
20
+ logs,
21
+ up
22
+ } from "./chunk-GBN67HYD.js";
23
+ import {
24
+ getSessionsDir,
25
+ loadConfig
26
+ } from "./chunk-25WQHUYW.js";
27
+ import {
28
+ getPortMappings,
29
+ sessionExists
30
+ } from "./chunk-JZ2VPQXP.js";
31
+
32
+ // src/commands/create.ts
33
+ import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync } from "fs";
34
+ import { basename, join, resolve } from "path";
35
+ import chalk from "chalk";
36
+ import { execa } from "execa";
37
+ function updateEnvDatabaseUrl(envPath, newDbUrl) {
38
+ if (!existsSync(envPath)) return;
39
+ let content = readFileSync(envPath, "utf-8");
40
+ if (content.includes("DATABASE_URL=")) {
41
+ content = content.replace(/^DATABASE_URL=.*/m, `DATABASE_URL=${newDbUrl}`);
42
+ } else {
43
+ content += `
44
+ DATABASE_URL=${newDbUrl}
45
+ `;
46
+ }
47
+ writeFileSync(envPath, content);
48
+ }
49
+ async function createSession(projectRoot, _sessionId, options) {
50
+ const config = await loadConfig(projectRoot);
51
+ const sessionsDir = getSessionsDir(config, projectRoot);
52
+ const inPlace = options.inPlace ?? false;
53
+ const mode = options.mode || "docker";
54
+ let workingDir;
55
+ let branchName = "";
56
+ if (inPlace) {
57
+ workingDir = projectRoot;
58
+ console.log(chalk.blue(`Creating session in current directory (${mode} mode)...`));
59
+ } else {
60
+ branchName = options.branch || generateDefaultBranchName();
61
+ workingDir = resolve(sessionsDir, branchName);
62
+ console.log(chalk.blue(`Creating session (${mode} mode)...`));
63
+ console.log(chalk.gray(`Branch: ${branchName}`));
64
+ console.log(chalk.gray(`Directory: ${workingDir}`));
65
+ }
66
+ if (await sessionExists(workingDir)) {
67
+ console.error(chalk.red(`
68
+ Error: Session already running in this directory.`));
69
+ console.error(chalk.gray(`Stop it first with: dev-prism stop`));
70
+ process.exit(1);
71
+ }
72
+ let profiles;
73
+ try {
74
+ if (!inPlace) {
75
+ if (!existsSync(sessionsDir)) {
76
+ mkdirSync(sessionsDir, { recursive: true });
77
+ }
78
+ console.log(chalk.blue("\nCreating git worktree..."));
79
+ await createWorktree(projectRoot, workingDir, branchName);
80
+ console.log(chalk.green(` Created: ${workingDir}`));
81
+ }
82
+ const projectName = config.projectName ?? basename(projectRoot);
83
+ const services = [
84
+ { name: "postgres", internalPort: 5432 },
85
+ { name: "app", internalPort: 3e3 }
86
+ ];
87
+ console.log(chalk.blue("\nGenerating docker-compose.session.yml..."));
88
+ const composePath = writeComposeFile(workingDir, projectName, services);
89
+ console.log(chalk.green(` Written: ${composePath}`));
90
+ console.log(chalk.blue("\nGenerating .env.session stub..."));
91
+ const envStub = generateEnvStub(workingDir, projectName);
92
+ const envPath = resolve(workingDir, ".env.session");
93
+ writeFileSync(envPath, envStub, "utf-8");
94
+ console.log(chalk.green(` Written: ${envPath}`));
95
+ if (!inPlace) {
96
+ const envFilesToCopy = config.envFiles ?? [];
97
+ for (const envFile of envFilesToCopy) {
98
+ const srcPath = join(projectRoot, envFile);
99
+ const destPath = join(workingDir, envFile);
100
+ if (existsSync(srcPath)) {
101
+ copyFileSync(srcPath, destPath);
102
+ console.log(chalk.green(` Copied: ${envFile}`));
103
+ }
104
+ }
105
+ }
106
+ console.log(chalk.blue("\nStarting Docker services..."));
107
+ if (mode === "docker") {
108
+ const allApps = config.apps ?? [];
109
+ const excludeApps = options.without ?? [];
110
+ profiles = allApps.filter((app) => !excludeApps.includes(app));
111
+ if (excludeApps.length > 0) {
112
+ console.log(chalk.gray(` Excluding apps: ${excludeApps.join(", ")}`));
113
+ }
114
+ }
115
+ await up({ cwd: workingDir, profiles, detach: true });
116
+ console.log(chalk.blue("Waiting for services to be ready..."));
117
+ await new Promise((resolve2) => setTimeout(resolve2, 3e3));
118
+ console.log(chalk.blue("\nDiscovering ports from containers..."));
119
+ const portMappings = await getPortMappings(workingDir);
120
+ const ports = extractPorts(portMappings);
121
+ console.log(chalk.gray("Discovered ports:"));
122
+ console.log(chalk.gray(formatPortsTable(ports)));
123
+ const finalEnvContent = writeEnvFile(workingDir, ports, projectName);
124
+ console.log(chalk.green(` Updated: ${finalEnvContent}`));
125
+ const appEnvFiles = writeAppEnvFiles(config, workingDir, ports);
126
+ for (const file of appEnvFiles) {
127
+ console.log(chalk.green(` Written: ${file}`));
128
+ }
129
+ if (!inPlace && ports.POSTGRES_PORT) {
130
+ const sessionDbUrl = `postgresql://postgres:postgres@localhost:${ports.POSTGRES_PORT}/postgres`;
131
+ const envFilesToCopy = config.envFiles ?? [];
132
+ for (const envFile of envFilesToCopy) {
133
+ const destPath = join(workingDir, envFile);
134
+ if (existsSync(destPath)) {
135
+ updateEnvDatabaseUrl(destPath, sessionDbUrl);
136
+ console.log(chalk.green(` Updated DATABASE_URL in: ${envFile}`));
137
+ }
138
+ }
139
+ }
140
+ if (config.setup.length > 0) {
141
+ console.log(chalk.blue("\nRunning setup commands..."));
142
+ const setupEnv = {
143
+ ...process.env,
144
+ SESSION_DIR: workingDir
145
+ };
146
+ for (const [name, port] of Object.entries(ports)) {
147
+ setupEnv[name] = String(port);
148
+ }
149
+ if (ports.POSTGRES_PORT) {
150
+ setupEnv.DATABASE_URL = `postgresql://postgres:postgres@localhost:${ports.POSTGRES_PORT}/postgres`;
151
+ }
152
+ for (const cmd of config.setup) {
153
+ console.log(chalk.gray(` Running: ${cmd}`));
154
+ const [command, ...args] = cmd.split(" ");
155
+ try {
156
+ await execa(command, args, {
157
+ cwd: workingDir,
158
+ stdio: "inherit",
159
+ env: setupEnv
160
+ });
161
+ } catch {
162
+ console.warn(chalk.yellow(` Warning: Command failed: ${cmd}`));
163
+ }
164
+ }
165
+ }
166
+ console.log(chalk.green(`
167
+ Session ready!`));
168
+ console.log(chalk.gray(`Directory: ${workingDir}`));
169
+ if (mode === "docker") {
170
+ console.log(chalk.gray("\nDocker mode - all services in containers."));
171
+ console.log(chalk.gray("View logs: docker compose -f docker-compose.session.yml logs -f"));
172
+ } else {
173
+ console.log(chalk.gray("\nNative mode - run apps with: pnpm dev"));
174
+ }
175
+ console.log(chalk.gray("\nPorts:"));
176
+ for (const [name, port] of Object.entries(ports)) {
177
+ console.log(chalk.cyan(` ${name}: http://localhost:${port}`));
178
+ }
179
+ if (options.detach === false) {
180
+ console.log(chalk.blue("\nStreaming logs (Ctrl+C to stop)..."));
181
+ console.log(chalk.gray("\u2500".repeat(60)));
182
+ try {
183
+ await logs({ cwd: workingDir, profiles });
184
+ } catch (error) {
185
+ const execaError = error;
186
+ if (execaError.signal === "SIGINT") {
187
+ console.log(chalk.gray("\n\u2500".repeat(60)));
188
+ console.log(chalk.yellow("\nLog streaming stopped. Services are still running."));
189
+ console.log(
190
+ chalk.gray(
191
+ `Resume logs: cd ${workingDir} && docker compose -f docker-compose.session.yml --env-file .env.session logs -f`
192
+ )
193
+ );
194
+ } else {
195
+ throw error;
196
+ }
197
+ }
198
+ }
199
+ } catch (error) {
200
+ console.error(chalk.red("Session creation failed. Cleaning up..."));
201
+ try {
202
+ await down({ cwd: workingDir });
203
+ } catch {
204
+ }
205
+ if (!inPlace && branchName) {
206
+ try {
207
+ await removeWorktree(projectRoot, workingDir, branchName);
208
+ } catch {
209
+ }
210
+ }
211
+ throw error;
212
+ }
213
+ }
214
+
215
+ export {
216
+ createSession
217
+ };
@@ -0,0 +1,41 @@
1
+ import {
2
+ loadConfig
3
+ } from "./chunk-25WQHUYW.js";
4
+ import {
5
+ getSession
6
+ } from "./chunk-DQER5GNG.js";
7
+
8
+ // src/commands/stop.ts
9
+ import { existsSync, unlinkSync } from "fs";
10
+ import { resolve } from "path";
11
+ import chalk from "chalk";
12
+ import { execa } from "execa";
13
+ async function stopSession(workingDir) {
14
+ const session = await getSession(workingDir);
15
+ if (!session) {
16
+ console.error(chalk.red(`Error: No session found in directory: ${workingDir}`));
17
+ process.exit(1);
18
+ }
19
+ console.log(chalk.blue(`Stopping session in ${workingDir}...`));
20
+ const config = await loadConfig(workingDir);
21
+ const allApps = config.apps ?? [];
22
+ const composeArgs = ["compose", "-f", "docker-compose.session.yml"];
23
+ const envPath = resolve(workingDir, ".env.session");
24
+ if (existsSync(envPath)) {
25
+ composeArgs.push("--env-file", ".env.session");
26
+ }
27
+ for (const profile of allApps) {
28
+ composeArgs.push("--profile", profile);
29
+ }
30
+ composeArgs.push("stop");
31
+ await execa("docker", composeArgs, { cwd: workingDir, stdio: "inherit" });
32
+ if (existsSync(envPath)) {
33
+ unlinkSync(envPath);
34
+ console.log(chalk.gray(`Deleted: ${envPath}`));
35
+ }
36
+ console.log(chalk.green("Session stopped."));
37
+ }
38
+
39
+ export {
40
+ stopSession
41
+ };
@@ -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-FKTFCSU7.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,40 @@
1
+ import {
2
+ getSession
3
+ } from "./chunk-DQER5GNG.js";
4
+
5
+ // src/commands/info.ts
6
+ import chalk from "chalk";
7
+ async function showInfo(cwd) {
8
+ const session = await getSession(cwd);
9
+ if (!session) {
10
+ console.log(chalk.yellow("No session running in this directory."));
11
+ console.log(chalk.gray("Run `dev-prism create --in-place` to create a session here."));
12
+ process.exit(1);
13
+ }
14
+ console.log(chalk.blue(`
15
+ Session`));
16
+ console.log(chalk.gray(`Directory: ${session.workingDir}`));
17
+ console.log(
18
+ session.running ? chalk.green("Status: running") : chalk.yellow("Status: stopped")
19
+ );
20
+ if (session.containers.length > 0) {
21
+ console.log(chalk.gray(`
22
+ Containers (${session.containers.length}):`));
23
+ for (const container of session.containers) {
24
+ const serviceName = container.labels["dev-prism.service"] || container.name;
25
+ const state = container.state === "running" ? chalk.green("\u25CF") : chalk.gray("\u25CB");
26
+ console.log(` ${state} ${serviceName}`);
27
+ }
28
+ }
29
+ if (session.ports.length > 0) {
30
+ console.log(chalk.gray("\nPorts:"));
31
+ for (const port of session.ports) {
32
+ console.log(chalk.cyan(` ${port.service}: http://localhost:${port.externalPort}`));
33
+ }
34
+ }
35
+ console.log("");
36
+ }
37
+
38
+ export {
39
+ showInfo
40
+ };
@@ -0,0 +1,40 @@
1
+ import {
2
+ loadConfig
3
+ } from "./chunk-25WQHUYW.js";
4
+ import {
5
+ up
6
+ } from "./chunk-GBN67HYD.js";
7
+ import {
8
+ SessionStore
9
+ } from "./chunk-ZKHNUDSL.js";
10
+
11
+ // src/commands/start.ts
12
+ import chalk from "chalk";
13
+ async function startSession(sessionId, options) {
14
+ const store = new SessionStore();
15
+ let sessionDir;
16
+ let projectRoot;
17
+ try {
18
+ const session = store.findSession(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
+ projectRoot = session.project_root;
25
+ } finally {
26
+ store.close();
27
+ }
28
+ const config = await loadConfig(projectRoot);
29
+ let profiles;
30
+ if (options.mode === "docker") {
31
+ const allApps = config.apps ?? [];
32
+ const excludeApps = options.without ?? [];
33
+ profiles = allApps.filter((app) => !excludeApps.includes(app));
34
+ }
35
+ await up({ cwd: sessionDir, profiles });
36
+ }
37
+
38
+ export {
39
+ startSession
40
+ };
@@ -0,0 +1,92 @@
1
+ import {
2
+ removeWorktree
3
+ } from "./chunk-3Q454U3I.js";
4
+ import {
5
+ down
6
+ } from "./chunk-GBN67HYD.js";
7
+ import {
8
+ getSessionsDir,
9
+ loadConfig
10
+ } from "./chunk-25WQHUYW.js";
11
+ import {
12
+ listActiveSessions
13
+ } from "./chunk-DQER5GNG.js";
14
+
15
+ // src/commands/prune.ts
16
+ import { existsSync, readdirSync, statSync } from "fs";
17
+ import { resolve, basename, join } from "path";
18
+ import { createInterface } from "readline";
19
+ import chalk from "chalk";
20
+ async function pruneSessions(options) {
21
+ const runningSessions = await listActiveSessions();
22
+ const runningDirs = new Set(runningSessions.map((s) => s.workingDir));
23
+ const projectRoot = process.cwd();
24
+ const config = await loadConfig(projectRoot);
25
+ const sessionsDir = getSessionsDir(config, projectRoot);
26
+ if (!existsSync(sessionsDir)) {
27
+ console.log(chalk.gray("No sessions directory found."));
28
+ return;
29
+ }
30
+ const sessionDirs = readdirSync(sessionsDir).map((name) => join(sessionsDir, name)).filter((path) => {
31
+ try {
32
+ return statSync(path).isDirectory();
33
+ } catch {
34
+ return false;
35
+ }
36
+ });
37
+ const stoppedSessions = sessionDirs.filter((dir) => !runningDirs.has(dir));
38
+ if (stoppedSessions.length === 0) {
39
+ console.log(chalk.gray("No stopped sessions to prune."));
40
+ return;
41
+ }
42
+ console.log(chalk.yellow(`
43
+ Found ${stoppedSessions.length} stopped session(s) to prune:`));
44
+ for (const sessionDir of stoppedSessions) {
45
+ const dirName = basename(sessionDir);
46
+ console.log(chalk.gray(` - ${dirName}`));
47
+ }
48
+ console.log("");
49
+ if (!options.yes) {
50
+ const rl = createInterface({
51
+ input: process.stdin,
52
+ output: process.stdout
53
+ });
54
+ const answer = await new Promise((resolve2) => {
55
+ rl.question(
56
+ chalk.red(
57
+ "Are you sure you want to delete these sessions? This cannot be undone. [y/N] "
58
+ ),
59
+ resolve2
60
+ );
61
+ });
62
+ rl.close();
63
+ if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
64
+ console.log(chalk.gray("Cancelled."));
65
+ return;
66
+ }
67
+ }
68
+ console.log(chalk.blue("\nPruning stopped sessions...\n"));
69
+ for (const sessionDir of stoppedSessions) {
70
+ const dirName = basename(sessionDir);
71
+ console.log(chalk.gray(` Removing ${dirName}...`));
72
+ try {
73
+ const envFile = resolve(sessionDir, ".env.session");
74
+ if (existsSync(envFile)) {
75
+ try {
76
+ await down({ cwd: sessionDir });
77
+ } catch {
78
+ }
79
+ }
80
+ await removeWorktree(projectRoot, sessionDir, dirName);
81
+ console.log(chalk.green(` Removed ${dirName}`));
82
+ } catch {
83
+ console.log(chalk.yellow(` Warning: Could not fully remove ${dirName}`));
84
+ }
85
+ }
86
+ console.log(chalk.green(`
87
+ Pruned ${stoppedSessions.length} session(s).`));
88
+ }
89
+
90
+ export {
91
+ pruneSessions
92
+ };