@itznexus/itznexus-cli 1.141.0 → 1.143.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.
@@ -0,0 +1,228 @@
1
+ import path from "node:path";
2
+ import { setTimeout as sleep } from "node:timers/promises";
3
+ import { readStackManifest, } from "./manifest.js";
4
+ import { alive, composeProjects, gitBranch, hashFile, killGroup, logTail, probeOnce, runStep, spawnDetached, waitReady, } from "./processes.js";
5
+ import { listRecords, logPath, readRecord, removeRecord, writeRecord, } from "./registry.js";
6
+ const now = () => new Date().toISOString();
7
+ // Branch of the first service folder that is a git clone; every clone of a
8
+ // project sits on the same branch during a batch, so one is enough.
9
+ function stackBranch(manifest) {
10
+ for (const s of manifest.services) {
11
+ const branch = gitBranch(s.cwd);
12
+ if (branch)
13
+ return branch;
14
+ }
15
+ return null;
16
+ }
17
+ // Installs run first, once per folder and lockfile, only when the lockfile
18
+ // moved since the last install; a folder installed this way gets its start
19
+ // services restarted below, since the running process holds the old modules.
20
+ function runInstalls(manifest, rec) {
21
+ const refreshed = new Set();
22
+ const seen = new Set();
23
+ for (const s of manifest.services) {
24
+ if (!s.install || !s.lockfile)
25
+ continue;
26
+ const key = `${s.cwd}::${s.lockfile}`;
27
+ if (seen.has(key))
28
+ continue;
29
+ seen.add(key);
30
+ const hash = hashFile(s.lockfile);
31
+ if (rec.installs[key] === hash)
32
+ continue;
33
+ console.log(`[stack] ${s.name}: lockfile moved, installing in ${path.basename(s.cwd)}`);
34
+ runStep(s.install, s.cwd, manifest.env);
35
+ rec.installs[key] = hash;
36
+ refreshed.add(s.cwd);
37
+ writeRecord(rec);
38
+ }
39
+ return refreshed;
40
+ }
41
+ function tryReadManifest(root) {
42
+ try {
43
+ return readStackManifest(root);
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ }
49
+ // A live process that misses one probe is not dead (a dev server recompiling,
50
+ // a listener mid-restart): three misses in a row are.
51
+ async function stillAnswers(probe) {
52
+ for (let i = 0; i < 3; i++) {
53
+ if (await probeOnce(probe))
54
+ return true;
55
+ await sleep(500);
56
+ }
57
+ return false;
58
+ }
59
+ async function startService(s, rec, env, restart) {
60
+ if (!s.ready)
61
+ throw new Error(`${s.name}: a start service needs a ready probe`);
62
+ const cur = rec.services[s.name];
63
+ const running = cur?.pid !== null && cur?.pid !== undefined && alive(cur.pid);
64
+ if (running && !restart && (await stillAnswers(s.ready)))
65
+ return;
66
+ if (running && cur.pid !== null)
67
+ await killGroup(cur.pid);
68
+ // A probe that already answers with no live pid of ours is a copy running
69
+ // outside the driver (an operator's terminal): never start a second one.
70
+ if (await probeOnce(s.ready)) {
71
+ throw new Error(`${s.name}: already answering on its port from a process the driver did not start; use it or stop it`);
72
+ }
73
+ const log = logPath(rec.slug, s.name);
74
+ const pid = spawnDetached(s.command, s.cwd, log, env);
75
+ rec.services[s.name] = { pid, startedAt: now() };
76
+ // Recorded before the wait, so a crash of the driver mid-up still leaves
77
+ // the pid known to a later down.
78
+ writeRecord(rec);
79
+ try {
80
+ await waitReady(s.ready, s.readyTimeoutMs, s.name, log, pid);
81
+ }
82
+ catch (err) {
83
+ await killGroup(pid);
84
+ rec.services[s.name] = { pid: null, startedAt: now() };
85
+ writeRecord(rec);
86
+ throw err;
87
+ }
88
+ console.log(`[stack] ${s.name}: up (pid ${pid})`);
89
+ }
90
+ export async function upStack(root) {
91
+ const manifest = readStackManifest(root);
92
+ // One stack per machine: another project's stack goes down first.
93
+ for (const other of listRecords()) {
94
+ if (other.slug !== manifest.slug)
95
+ await downRecord(other, true);
96
+ }
97
+ const branch = stackBranch(manifest);
98
+ let rec = readRecord(manifest.slug);
99
+ if (rec && rec.branch !== branch) {
100
+ console.log(`[stack] branch moved from ${rec.branch} to ${branch}: wiping the data`);
101
+ await downRecord(rec, true);
102
+ rec = null;
103
+ }
104
+ rec ??= {
105
+ slug: manifest.slug,
106
+ root,
107
+ branch,
108
+ startedAt: now(),
109
+ lastUsedAt: now(),
110
+ services: {},
111
+ ups: {},
112
+ installs: {},
113
+ composeProjects: [],
114
+ };
115
+ writeRecord(rec);
116
+ const refreshed = runInstalls(manifest, rec);
117
+ for (const s of manifest.services) {
118
+ if (s.kind === "up") {
119
+ const before = composeProjects();
120
+ runStep(s.command, s.cwd, manifest.env);
121
+ const after = composeProjects();
122
+ const added = before && after ? [...after].filter((p) => !before.has(p)) : [];
123
+ for (const p of added) {
124
+ if (!rec.composeProjects.includes(p))
125
+ rec.composeProjects.push(p);
126
+ }
127
+ // Owned when this up made resources appear, or when Docker could not
128
+ // say; a resource that was already there belongs to whoever started it
129
+ // and is left as found on down.
130
+ if (!(s.name in rec.ups)) {
131
+ rec.ups[s.name] = { owned: added.length > 0 || before === null };
132
+ }
133
+ writeRecord(rec);
134
+ if (s.ready)
135
+ await waitReady(s.ready, s.readyTimeoutMs, s.name);
136
+ }
137
+ else if (s.kind === "run") {
138
+ runStep(s.command, s.cwd, manifest.env);
139
+ }
140
+ else {
141
+ await startService(s, rec, manifest.env, refreshed.has(s.cwd));
142
+ }
143
+ }
144
+ rec.lastUsedAt = now();
145
+ writeRecord(rec);
146
+ return stackStatus(root);
147
+ }
148
+ // Takes a recorded stack down from its own manifest when it still reads;
149
+ // without one (folder gone, block removed) only the recorded pids are ended.
150
+ async function downRecord(rec, wipe) {
151
+ const manifest = tryReadManifest(rec.root);
152
+ const ordered = manifest ? [...manifest.services].reverse() : [];
153
+ const env = manifest?.env ?? {};
154
+ for (const s of ordered) {
155
+ if (s.kind === "start") {
156
+ const pid = rec.services[s.name]?.pid;
157
+ if (pid && alive(pid))
158
+ await killGroup(pid);
159
+ rec.services[s.name] = { pid: null, startedAt: now() };
160
+ }
161
+ else if (s.kind === "up") {
162
+ const command = wipe ? (s.wipe ?? s.down) : s.down;
163
+ if (!command || !rec.ups[s.name]?.owned)
164
+ continue;
165
+ try {
166
+ runStep(command, s.cwd, env);
167
+ }
168
+ catch (err) {
169
+ console.error(`[stack] ${s.name}: ${command} failed:`, err);
170
+ }
171
+ }
172
+ }
173
+ // Services the manifest no longer names still hold a pid here.
174
+ for (const r of Object.values(rec.services)) {
175
+ if (r.pid && alive(r.pid))
176
+ await killGroup(r.pid);
177
+ }
178
+ removeRecord(rec.slug);
179
+ console.log(`[stack] ${rec.slug}: down${wipe ? " (data wiped)" : ""}`);
180
+ }
181
+ export async function downStack(root, wipe) {
182
+ const rec = readRecord(path.basename(root));
183
+ if (!rec)
184
+ return false;
185
+ await downRecord(rec, wipe);
186
+ return true;
187
+ }
188
+ export async function stackStatus(root) {
189
+ const manifest = readStackManifest(root);
190
+ const rec = readRecord(manifest.slug);
191
+ const rows = [];
192
+ for (const s of manifest.services) {
193
+ const pid = rec?.services[s.name]?.pid ?? null;
194
+ rows.push({
195
+ name: s.name,
196
+ kind: s.kind,
197
+ pid,
198
+ alive: pid !== null && alive(pid),
199
+ ready: s.ready ? await probeOnce(s.ready) : null,
200
+ log: s.kind === "start" ? logPath(manifest.slug, s.name) : null,
201
+ });
202
+ }
203
+ return rows;
204
+ }
205
+ export async function restartService(root, name) {
206
+ const manifest = readStackManifest(root);
207
+ const rec = readRecord(manifest.slug);
208
+ if (!rec)
209
+ throw new Error(`${manifest.slug}: the stack is not up`);
210
+ const s = manifest.services.find((x) => x.name === name);
211
+ if (!s)
212
+ throw new Error(`unknown service "${name}"`);
213
+ if (s.kind !== "start")
214
+ throw new Error(`${name} is a ${s.kind} step, not a service to restart`);
215
+ await startService(s, rec, manifest.env, true);
216
+ rec.lastUsedAt = now();
217
+ writeRecord(rec);
218
+ }
219
+ export function serviceLog(root, name, lines) {
220
+ const manifest = readStackManifest(root);
221
+ const s = manifest.services.find((x) => x.name === name);
222
+ if (!s)
223
+ throw new Error(`unknown service "${name}"`);
224
+ if (s.kind !== "start")
225
+ throw new Error(`${name} is a ${s.kind} step and keeps no log`);
226
+ return logTail(logPath(manifest.slug, name), lines);
227
+ }
228
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../../src/lib/stack/driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export declare const PROCESS_FILE = "DEVELOPMENT.md";
2
+ export type ReadyProbe = {
3
+ kind: "tcp";
4
+ port: number;
5
+ } | {
6
+ kind: "http";
7
+ url: string;
8
+ };
9
+ export type StackService = {
10
+ name: string;
11
+ cwd: string;
12
+ after: string[];
13
+ kind: "up" | "run" | "start";
14
+ command: string;
15
+ down?: string;
16
+ wipe?: string;
17
+ install?: string;
18
+ lockfile?: string;
19
+ ready?: ReadyProbe;
20
+ readyTimeoutMs: number;
21
+ };
22
+ export type StackManifest = {
23
+ root: string;
24
+ slug: string;
25
+ env: Record<string, string>;
26
+ services: StackService[];
27
+ };
28
+ export declare function readStackManifest(root: string): StackManifest;
@@ -0,0 +1,182 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parse } from "yaml";
4
+ // The stack manifest is the fenced ```yaml stack block of the project's
5
+ // DEVELOPMENT.md: the dev process already lives there, so no second file kind
6
+ // reaches the project folder. A project without the block has no managed
7
+ // stack, and sessions keep booting and tearing down per run.
8
+ export const PROCESS_FILE = "DEVELOPMENT.md";
9
+ const BLOCK = /```(?:yaml|yml)[ \t]+stack[ \t]*\r?\n([\s\S]*?)```/;
10
+ const SERVICE_KEYS = new Set([
11
+ "name",
12
+ "cwd",
13
+ "after",
14
+ "up",
15
+ "down",
16
+ "wipe",
17
+ "run",
18
+ "start",
19
+ "install",
20
+ "lockfile",
21
+ "ready",
22
+ "readyTimeoutMs",
23
+ ]);
24
+ const NAME = /^[a-z0-9][a-z0-9-]*$/;
25
+ // A cold Vite start after an install can sit silent for a couple of minutes
26
+ // before its banner; three minutes covers it without hiding a real hang.
27
+ const DEFAULT_READY_TIMEOUT_MS = 180_000;
28
+ export function readStackManifest(root) {
29
+ const file = path.join(root, PROCESS_FILE);
30
+ if (!fs.existsSync(file)) {
31
+ throw new Error(`${PROCESS_FILE} not found in ${root}: run inside a project folder`);
32
+ }
33
+ const match = BLOCK.exec(fs.readFileSync(file, "utf8"));
34
+ if (!match) {
35
+ throw new Error(`${PROCESS_FILE} carries no \`\`\`yaml stack block: this project has no managed stack`);
36
+ }
37
+ const doc = parse(match[1]);
38
+ return {
39
+ root,
40
+ slug: path.basename(root),
41
+ env: parseEnv(doc),
42
+ services: order(parseServices(doc, root)),
43
+ };
44
+ }
45
+ function isRecord(v) {
46
+ return typeof v === "object" && v !== null && !Array.isArray(v);
47
+ }
48
+ function str(obj, key, where) {
49
+ const v = obj[key];
50
+ if (v === undefined)
51
+ return undefined;
52
+ if (typeof v !== "string" || v.trim() === "") {
53
+ throw new Error(`${where}: ${key} must be a non-empty string`);
54
+ }
55
+ return v.trim();
56
+ }
57
+ function parseReady(raw, where) {
58
+ const tcp = /^tcp:(\d{1,5})$/.exec(raw);
59
+ if (tcp)
60
+ return { kind: "tcp", port: Number(tcp[1]) };
61
+ if (/^https?:\/\//.test(raw))
62
+ return { kind: "http", url: raw };
63
+ throw new Error(`${where}: ready must be tcp:<port> or an http(s) url`);
64
+ }
65
+ function parseService(raw, root, index) {
66
+ const where = `services[${index}]`;
67
+ if (!isRecord(raw))
68
+ throw new Error(`${where}: must be a mapping`);
69
+ for (const key of Object.keys(raw)) {
70
+ if (!SERVICE_KEYS.has(key))
71
+ throw new Error(`${where}: unknown key "${key}"`);
72
+ }
73
+ const name = str(raw, "name", where);
74
+ if (!name || !NAME.test(name))
75
+ throw new Error(`${where}: name must be kebab-case`);
76
+ const cwd = str(raw, "cwd", `${name}`);
77
+ if (!cwd)
78
+ throw new Error(`${name}: cwd is required`);
79
+ const commands = ["up", "run", "start"].filter((k) => raw[k] !== undefined);
80
+ if (commands.length !== 1) {
81
+ throw new Error(`${name}: exactly one of up, run, start is required`);
82
+ }
83
+ const kind = commands[0];
84
+ const command = str(raw, kind, name);
85
+ const down = str(raw, "down", name);
86
+ const wipe = str(raw, "wipe", name);
87
+ if ((down || wipe) && kind !== "up")
88
+ throw new Error(`${name}: down and wipe only go with up`);
89
+ const install = str(raw, "install", name);
90
+ const lockfile = str(raw, "lockfile", name);
91
+ if ((install === undefined) !== (lockfile === undefined)) {
92
+ throw new Error(`${name}: install and lockfile go together`);
93
+ }
94
+ if (install && kind === "up")
95
+ throw new Error(`${name}: install does not go with up`);
96
+ const readyRaw = str(raw, "ready", name);
97
+ if (kind === "start" && !readyRaw)
98
+ throw new Error(`${name}: a start service needs a ready probe`);
99
+ if (kind === "run" && readyRaw)
100
+ throw new Error(`${name}: a run step takes no ready probe`);
101
+ const after = raw.after === undefined ? [] : raw.after;
102
+ if (!Array.isArray(after) || !after.every((a) => typeof a === "string")) {
103
+ throw new Error(`${name}: after must be a list of service names`);
104
+ }
105
+ const timeout = raw.readyTimeoutMs;
106
+ if (timeout !== undefined && (typeof timeout !== "number" || timeout <= 0)) {
107
+ throw new Error(`${name}: readyTimeoutMs must be a positive number`);
108
+ }
109
+ const absCwd = path.resolve(root, cwd);
110
+ return {
111
+ name,
112
+ cwd: absCwd,
113
+ after: after,
114
+ kind,
115
+ command,
116
+ ...(down ? { down } : {}),
117
+ ...(wipe ? { wipe } : {}),
118
+ ...(install ? { install } : {}),
119
+ ...(lockfile ? { lockfile: path.resolve(absCwd, lockfile) } : {}),
120
+ ...(readyRaw ? { ready: parseReady(readyRaw, name) } : {}),
121
+ readyTimeoutMs: typeof timeout === "number" ? timeout : DEFAULT_READY_TIMEOUT_MS,
122
+ };
123
+ }
124
+ function parseEnv(doc) {
125
+ if (!isRecord(doc) || doc.env === undefined)
126
+ return {};
127
+ if (!isRecord(doc.env))
128
+ throw new Error("stack manifest: env must be a mapping");
129
+ const out = {};
130
+ for (const [k, v] of Object.entries(doc.env)) {
131
+ if (!/^[A-Z][A-Z0-9_]*$/.test(k) || typeof v !== "string") {
132
+ throw new Error(`stack manifest: env.${k} must be an UPPER_SNAKE key with a string value`);
133
+ }
134
+ out[k] = v;
135
+ }
136
+ return out;
137
+ }
138
+ function parseServices(doc, root) {
139
+ if (!isRecord(doc) ||
140
+ !Array.isArray(doc.services) ||
141
+ doc.services.length === 0) {
142
+ throw new Error("stack manifest must be a mapping with a non-empty services list");
143
+ }
144
+ for (const key of Object.keys(doc)) {
145
+ if (key !== "services" && key !== "env") {
146
+ throw new Error(`stack manifest: unknown key "${key}"`);
147
+ }
148
+ }
149
+ const services = doc.services.map((s, i) => parseService(s, root, i));
150
+ const names = new Set();
151
+ for (const s of services) {
152
+ if (names.has(s.name))
153
+ throw new Error(`duplicate service name "${s.name}"`);
154
+ names.add(s.name);
155
+ }
156
+ for (const s of services) {
157
+ for (const dep of s.after) {
158
+ if (!names.has(dep))
159
+ throw new Error(`${s.name}: after names unknown service "${dep}"`);
160
+ }
161
+ }
162
+ return services;
163
+ }
164
+ // Stable topological order: the first service in list order whose `after`
165
+ // are all placed goes next, one at a time, so the list order is kept wherever
166
+ // the dependencies allow it.
167
+ function order(services) {
168
+ const out = [];
169
+ const placed = new Set();
170
+ let remaining = services;
171
+ while (remaining.length > 0) {
172
+ const next = remaining.find((s) => s.after.every((a) => placed.has(a)));
173
+ if (!next) {
174
+ throw new Error(`cycle in after: ${remaining.map((s) => s.name).join(", ")}`);
175
+ }
176
+ out.push(next);
177
+ placed.add(next.name);
178
+ remaining = remaining.filter((s) => s.name !== next.name);
179
+ }
180
+ return out;
181
+ }
182
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/lib/stack/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ReadyProbe } from "./manifest.js";
2
+ export declare function runStep(command: string, cwd: string, env: Record<string, string>): void;
3
+ export declare function spawnDetached(command: string, cwd: string, logFile: string, env: Record<string, string>): number;
4
+ export declare function alive(pid: number): boolean;
5
+ export declare function killGroup(pid: number): Promise<void>;
6
+ export declare function probeOnce(probe: ReadyProbe): Promise<boolean>;
7
+ export declare function waitReady(probe: ReadyProbe, timeoutMs: number, label: string, logFile?: string, pid?: number): Promise<void>;
8
+ export declare function hashFile(file: string): string;
9
+ export declare function logTail(file: string, lines?: number): string;
10
+ export declare function composeProjects(): Set<string> | null;
11
+ export declare function gitBranch(cwd: string): string | null;
@@ -0,0 +1,139 @@
1
+ import { execFileSync, execSync, spawn } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs";
4
+ import net from "node:net";
5
+ import { setTimeout as sleep } from "node:timers/promises";
6
+ const POLL_MS = 500;
7
+ const PROBE_TIMEOUT_MS = 1500;
8
+ const KILL_GRACE_MS = 10_000;
9
+ // One-shot step (compose up, a migration, an install): the command's output
10
+ // streams to the caller's terminal, and a non-zero exit stops the driver.
11
+ export function runStep(command, cwd, env) {
12
+ execSync(command, { cwd, stdio: "inherit", env: { ...process.env, ...env } });
13
+ }
14
+ // Long-lived service, detached in its own process group so the driver can
15
+ // end the whole tree later (npm and bun fork the real server), with both
16
+ // streams appended to the service's log file.
17
+ export function spawnDetached(command, cwd, logFile, env) {
18
+ const fd = fs.openSync(logFile, "a");
19
+ const child = spawn(command, {
20
+ cwd,
21
+ shell: true,
22
+ detached: true,
23
+ stdio: ["ignore", fd, fd],
24
+ env: { ...process.env, ...env },
25
+ });
26
+ fs.closeSync(fd);
27
+ child.unref();
28
+ if (child.pid === undefined)
29
+ throw new Error(`could not start: ${command}`);
30
+ return child.pid;
31
+ }
32
+ export function alive(pid) {
33
+ try {
34
+ process.kill(pid, 0);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ function signalGroup(pid, signal) {
42
+ try {
43
+ process.kill(-pid, signal);
44
+ }
45
+ catch (err) {
46
+ // ESRCH: the group is already gone, which is the outcome wanted.
47
+ if (err.code !== "ESRCH")
48
+ throw err;
49
+ }
50
+ }
51
+ export async function killGroup(pid) {
52
+ signalGroup(pid, "SIGTERM");
53
+ const deadline = Date.now() + KILL_GRACE_MS;
54
+ while (alive(pid) && Date.now() < deadline)
55
+ await sleep(POLL_MS);
56
+ if (alive(pid))
57
+ signalGroup(pid, "SIGKILL");
58
+ }
59
+ function probeTcp(port) {
60
+ return new Promise((resolve) => {
61
+ const socket = net.createConnection({
62
+ port,
63
+ host: "localhost",
64
+ autoSelectFamily: true,
65
+ });
66
+ const done = (ok) => {
67
+ socket.destroy();
68
+ resolve(ok);
69
+ };
70
+ socket.setTimeout(PROBE_TIMEOUT_MS, () => done(false));
71
+ socket.once("connect", () => done(true));
72
+ socket.once("error", () => done(false));
73
+ });
74
+ }
75
+ async function probeHttp(url) {
76
+ try {
77
+ const res = await fetch(url, {
78
+ signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
79
+ });
80
+ return res.status < 500;
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ export function probeOnce(probe) {
87
+ return probe.kind === "tcp" ? probeTcp(probe.port) : probeHttp(probe.url);
88
+ }
89
+ export async function waitReady(probe, timeoutMs, label, logFile, pid) {
90
+ const deadline = Date.now() + timeoutMs;
91
+ const tail = () => logFile ? `\n--- ${logFile} ---\n${logTail(logFile)}` : "";
92
+ while (Date.now() < deadline) {
93
+ if (await probeOnce(probe))
94
+ return;
95
+ if (pid !== undefined && !alive(pid)) {
96
+ throw new Error(`${label}: exited before answering its ready probe${tail()}`);
97
+ }
98
+ await sleep(POLL_MS);
99
+ }
100
+ throw new Error(`${label}: not ready after ${Math.round(timeoutMs / 1000)}s${tail()}`);
101
+ }
102
+ export function hashFile(file) {
103
+ return crypto
104
+ .createHash("sha256")
105
+ .update(fs.readFileSync(file))
106
+ .digest("hex");
107
+ }
108
+ export function logTail(file, lines = 40) {
109
+ if (!fs.existsSync(file))
110
+ return "";
111
+ return fs.readFileSync(file, "utf8").split("\n").slice(-lines).join("\n");
112
+ }
113
+ // Compose project labels of the running containers; null when Docker is
114
+ // absent or down (then nothing is recorded, and nothing is exempted).
115
+ export function composeProjects() {
116
+ try {
117
+ const out = execFileSync("docker", ["ps", "--format", '{{.Label "com.docker.compose.project"}}'], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
118
+ return new Set(out
119
+ .split("\n")
120
+ .map((l) => l.trim())
121
+ .filter((l) => l !== ""));
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ export function gitBranch(cwd) {
128
+ try {
129
+ return execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
130
+ cwd,
131
+ encoding: "utf8",
132
+ stdio: ["ignore", "pipe", "ignore"],
133
+ }).trim();
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ }
139
+ //# sourceMappingURL=processes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processes.js","sourceRoot":"","sources":["../../../src/lib/stack/processes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export declare const STACKS_DIR: string;
2
+ export type ServiceRecord = {
3
+ pid: number | null;
4
+ startedAt: string;
5
+ };
6
+ export type StackRecord = {
7
+ slug: string;
8
+ root: string;
9
+ branch: string | null;
10
+ startedAt: string;
11
+ lastUsedAt: string;
12
+ services: Record<string, ServiceRecord>;
13
+ ups: Record<string, {
14
+ owned: boolean;
15
+ }>;
16
+ installs: Record<string, string>;
17
+ composeProjects: string[];
18
+ };
19
+ export declare function logDir(slug: string): string;
20
+ export declare function logPath(slug: string, service: string): string;
21
+ export declare function readRecord(slug: string): StackRecord | null;
22
+ export declare function writeRecord(rec: StackRecord): void;
23
+ export declare function removeRecord(slug: string): void;
24
+ export declare function listRecords(): StackRecord[];