withvibe 0.1.4

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 (62) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +67 -0
  3. package/dist/api.js +29 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/assets/docker-compose.yml +130 -0
  6. package/dist/config.js +48 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/env-command.js +331 -0
  9. package/dist/env-command.js.map +1 -0
  10. package/dist/exec.js +35 -0
  11. package/dist/exec.js.map +1 -0
  12. package/dist/git-auth.js +64 -0
  13. package/dist/git-auth.js.map +1 -0
  14. package/dist/index.js +42 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/install/anthropic-validate.js +73 -0
  17. package/dist/install/anthropic-validate.js.map +1 -0
  18. package/dist/install/build-images.js +99 -0
  19. package/dist/install/build-images.js.map +1 -0
  20. package/dist/install/commands/build-images-cmd.js +68 -0
  21. package/dist/install/commands/build-images-cmd.js.map +1 -0
  22. package/dist/install/commands/configure.js +276 -0
  23. package/dist/install/commands/configure.js.map +1 -0
  24. package/dist/install/commands/lifecycle.js +129 -0
  25. package/dist/install/commands/lifecycle.js.map +1 -0
  26. package/dist/install/commands/uninstall.js +88 -0
  27. package/dist/install/commands/uninstall.js.map +1 -0
  28. package/dist/install/commands/upgrade.js +164 -0
  29. package/dist/install/commands/upgrade.js.map +1 -0
  30. package/dist/install/compose-rewriter.js +298 -0
  31. package/dist/install/compose-rewriter.js.map +1 -0
  32. package/dist/install/compose.js +118 -0
  33. package/dist/install/compose.js.map +1 -0
  34. package/dist/install/doctor.js +176 -0
  35. package/dist/install/doctor.js.map +1 -0
  36. package/dist/install/env-file.js +63 -0
  37. package/dist/install/env-file.js.map +1 -0
  38. package/dist/install/exec.js +32 -0
  39. package/dist/install/exec.js.map +1 -0
  40. package/dist/install/images.js +78 -0
  41. package/dist/install/images.js.map +1 -0
  42. package/dist/install/init.js +584 -0
  43. package/dist/install/init.js.map +1 -0
  44. package/dist/install/log.js +15 -0
  45. package/dist/install/log.js.map +1 -0
  46. package/dist/install/paths.js +26 -0
  47. package/dist/install/paths.js.map +1 -0
  48. package/dist/install/ports.js +23 -0
  49. package/dist/install/ports.js.map +1 -0
  50. package/dist/install/register.js +155 -0
  51. package/dist/install/register.js.map +1 -0
  52. package/dist/install/secrets.js +9 -0
  53. package/dist/install/secrets.js.map +1 -0
  54. package/dist/install/state.js +29 -0
  55. package/dist/install/state.js.map +1 -0
  56. package/dist/login.js +56 -0
  57. package/dist/login.js.map +1 -0
  58. package/dist/ports.js +33 -0
  59. package/dist/ports.js.map +1 -0
  60. package/dist/preflight.js +150 -0
  61. package/dist/preflight.js.map +1 -0
  62. package/package.json +38 -0
@@ -0,0 +1,118 @@
1
+ import { promises as fs } from "node:fs";
2
+ import { run } from "../exec.js";
3
+ import { composePath, envPath } from "./paths.js";
4
+ import { log } from "./log.js";
5
+ // Wraps `docker compose -f <installDir>/docker-compose.yml --env-file
6
+ // <installDir>/.env <subcommand>`. Streams to the parent terminal so the
7
+ // operator sees image pulls and container output live.
8
+ export async function compose(installDir, args, opts = {}) {
9
+ const file = composePath(installDir);
10
+ const env = envPath(installDir);
11
+ await assertFile(file, "compose file");
12
+ await assertFile(env, ".env");
13
+ const baseArgs = ["compose", "-f", file, "--env-file", env, ...args];
14
+ return run("docker", baseArgs, {
15
+ streamTo: opts.stream === false ? "pipe" : "inherit",
16
+ cwd: installDir,
17
+ });
18
+ }
19
+ // `docker compose ps --format json` returns one JSON object per line in
20
+ // recent compose versions, an array in older ones. Handle both.
21
+ export async function composePs(installDir) {
22
+ const res = await compose(installDir, ["ps", "--format", "json"], {
23
+ stream: false,
24
+ });
25
+ if (res.code !== 0) {
26
+ return [];
27
+ }
28
+ const trimmed = res.stdout.trim();
29
+ if (!trimmed)
30
+ return [];
31
+ if (trimmed.startsWith("[")) {
32
+ try {
33
+ const arr = JSON.parse(trimmed);
34
+ return arr.map(rowToState);
35
+ }
36
+ catch {
37
+ return [];
38
+ }
39
+ }
40
+ return trimmed
41
+ .split("\n")
42
+ .map((line) => {
43
+ try {
44
+ return rowToState(JSON.parse(line));
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ })
50
+ .filter((x) => x !== null);
51
+ }
52
+ function rowToState(row) {
53
+ return {
54
+ name: String(row.Service ?? row.Name ?? ""),
55
+ state: String(row.State ?? ""),
56
+ health: row.Health ? String(row.Health) : undefined,
57
+ };
58
+ }
59
+ async function assertFile(p, label) {
60
+ try {
61
+ await fs.access(p);
62
+ }
63
+ catch {
64
+ log.fail(`${label} missing at ${p}. Run \`withvibe init\` first.`);
65
+ process.exit(1);
66
+ }
67
+ }
68
+ // Resolve API_PUBLIC_URL / WEB_PUBLIC_URL from the env file so we can poll
69
+ // them from outside the container network.
70
+ export async function readPublicUrls(installDir) {
71
+ const text = await fs.readFile(envPath(installDir), "utf8").catch(() => "");
72
+ const out = {};
73
+ for (const raw of text.split(/\r?\n/)) {
74
+ const line = raw.trim();
75
+ if (!line || line.startsWith("#"))
76
+ continue;
77
+ const eq = line.indexOf("=");
78
+ if (eq === -1)
79
+ continue;
80
+ const key = line.slice(0, eq).trim();
81
+ let value = line.slice(eq + 1).trim();
82
+ if ((value.startsWith('"') && value.endsWith('"')) ||
83
+ (value.startsWith("'") && value.endsWith("'"))) {
84
+ value = value.slice(1, -1);
85
+ }
86
+ if (key === "WEB_PUBLIC_URL")
87
+ out.web = value;
88
+ else if (key === "API_PUBLIC_URL")
89
+ out.api = value;
90
+ }
91
+ return out;
92
+ }
93
+ // Poll a URL until it returns 2xx or 3xx, or timeout.
94
+ export async function waitForUrl(url, timeoutMs = 90_000, intervalMs = 1500) {
95
+ const deadline = Date.now() + timeoutMs;
96
+ let lastStatus;
97
+ let lastError;
98
+ while (Date.now() < deadline) {
99
+ try {
100
+ const res = await fetch(url, {
101
+ method: "GET",
102
+ signal: AbortSignal.timeout(5000),
103
+ });
104
+ lastStatus = res.status;
105
+ if (res.status < 400)
106
+ return { ok: true, lastStatus };
107
+ }
108
+ catch (e) {
109
+ lastError = e.message;
110
+ }
111
+ await sleep(intervalMs);
112
+ }
113
+ return { ok: false, lastStatus, lastError };
114
+ }
115
+ function sleep(ms) {
116
+ return new Promise((r) => setTimeout(r, ms));
117
+ }
118
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/install/compose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,176 @@
1
+ import { promises as fs } from "node:fs";
2
+ import net from "node:net";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { captureVersion, run, which } from "../exec.js";
6
+ import { log } from "./log.js";
7
+ const REQUIRED_PORTS = [3000, 4000];
8
+ const MIN_DISK_FREE_MB = 4 * 1024;
9
+ const MIN_NODE_MAJOR = 20;
10
+ export async function runDoctor(opts = {}) {
11
+ const results = [];
12
+ results.push(await checkNode());
13
+ results.push(await checkDocker());
14
+ results.push(await checkComposePlugin());
15
+ results.push(await checkDockerDaemon());
16
+ const ports = opts.ports ?? REQUIRED_PORTS;
17
+ for (const p of ports) {
18
+ results.push(await checkPort(p));
19
+ }
20
+ results.push(await checkRepoBaseDir(opts.repoBaseDir));
21
+ results.push(await checkDiskSpace(opts.repoBaseDir));
22
+ results.push(await checkOptional("git", ["--version"], "Used for repo cloning inside the api container; nice-to-have on host"));
23
+ results.push(await checkOptional("gh", ["--version"], "GitHub CLI — recommended for repo auth"));
24
+ return {
25
+ ok: results.every((r) => r.ok),
26
+ results,
27
+ };
28
+ }
29
+ export function printReport(report) {
30
+ for (const r of report.results) {
31
+ if (r.ok)
32
+ log.ok(`${r.name}${r.detail ? ` — ${r.detail}` : ""}`);
33
+ else {
34
+ log.fail(`${r.name}${r.detail ? ` — ${r.detail}` : ""}`);
35
+ if (r.fix)
36
+ log.dim(` ${r.fix}`);
37
+ }
38
+ }
39
+ }
40
+ async function checkNode() {
41
+ const v = process.versions.node;
42
+ const major = parseInt(v.split(".")[0], 10);
43
+ if (Number.isNaN(major) || major < MIN_NODE_MAJOR) {
44
+ return {
45
+ name: "Node.js",
46
+ ok: false,
47
+ detail: `${v} (need >= ${MIN_NODE_MAJOR})`,
48
+ fix: "Install Node 20+ from https://nodejs.org",
49
+ };
50
+ }
51
+ return { name: "Node.js", ok: true, detail: v };
52
+ }
53
+ async function checkDocker() {
54
+ if (!(await which("docker"))) {
55
+ return {
56
+ name: "docker CLI",
57
+ ok: false,
58
+ fix: "Install Docker: https://docs.docker.com/get-docker/",
59
+ };
60
+ }
61
+ const v = await captureVersion("docker", ["--version"]);
62
+ return { name: "docker CLI", ok: true, detail: v ?? undefined };
63
+ }
64
+ async function checkComposePlugin() {
65
+ const res = await run("docker", ["compose", "version"]).catch(() => ({ code: 1, stdout: "", stderr: "" }));
66
+ if (res.code !== 0) {
67
+ return {
68
+ name: "docker compose plugin",
69
+ ok: false,
70
+ fix: "Install the compose plugin (Docker Desktop bundles it; on Linux: docker-compose-plugin)",
71
+ };
72
+ }
73
+ return {
74
+ name: "docker compose plugin",
75
+ ok: true,
76
+ detail: res.stdout.trim().split("\n")[0],
77
+ };
78
+ }
79
+ async function checkDockerDaemon() {
80
+ const res = await run("docker", ["info", "--format", "{{.ServerVersion}}"]).catch(() => ({ code: 1, stdout: "", stderr: "" }));
81
+ if (res.code !== 0) {
82
+ return {
83
+ name: "docker daemon",
84
+ ok: false,
85
+ detail: "not reachable",
86
+ fix: process.platform === "darwin"
87
+ ? "Start Docker Desktop"
88
+ : "Start the docker daemon (e.g. `sudo systemctl start docker`) and ensure your user is in the `docker` group",
89
+ };
90
+ }
91
+ return { name: "docker daemon", ok: true, detail: `server ${res.stdout.trim()}` };
92
+ }
93
+ async function checkPort(port) {
94
+ const free = await isPortFree(port);
95
+ if (free)
96
+ return { name: `port ${port}`, ok: true, detail: "free" };
97
+ // Soft warning: `init` auto-bumps to the next free port, so a busy default
98
+ // is informational rather than blocking. We still flag it so the operator
99
+ // knows what changed.
100
+ return {
101
+ name: `port ${port}`,
102
+ ok: true,
103
+ detail: "in use — init will pick an alternate (e.g. " + (port + 1) + ")",
104
+ };
105
+ }
106
+ function isPortFree(port) {
107
+ return new Promise((resolve) => {
108
+ const srv = net.createServer();
109
+ srv.once("error", () => resolve(false));
110
+ srv.once("listening", () => srv.close(() => resolve(true)));
111
+ srv.listen(port, "0.0.0.0");
112
+ });
113
+ }
114
+ async function checkRepoBaseDir(p) {
115
+ const target = expandHome(p ?? path.join(os.homedir(), ".withvibe", "repos"));
116
+ try {
117
+ await fs.mkdir(target, { recursive: true });
118
+ const probe = path.join(target, ".withvibe-write-probe");
119
+ await fs.writeFile(probe, "ok");
120
+ await fs.unlink(probe);
121
+ return { name: "REPO_BASE_DIR writable", ok: true, detail: target };
122
+ }
123
+ catch (e) {
124
+ return {
125
+ name: "REPO_BASE_DIR writable",
126
+ ok: false,
127
+ detail: `${target} (${e.message})`,
128
+ fix: `Create the directory and ensure the user running the installer can write to it.`,
129
+ };
130
+ }
131
+ }
132
+ async function checkDiskSpace(p) {
133
+ const target = expandHome(p ?? os.homedir());
134
+ const res = await run("df", ["-m", target]).catch(() => null);
135
+ if (!res || res.code !== 0) {
136
+ return { name: "disk space", ok: true, detail: "skipped (df unavailable)" };
137
+ }
138
+ const lines = res.stdout.trim().split("\n");
139
+ const last = lines[lines.length - 1];
140
+ if (!last)
141
+ return { name: "disk space", ok: true, detail: "skipped" };
142
+ const cols = last.split(/\s+/);
143
+ // BSD/macOS df: Filesystem 1M-blocks Used Available Capacity Mounted on
144
+ // GNU df: Filesystem 1M-blocks Used Available Use% Mounted on
145
+ const availableMb = parseInt(cols[3] ?? "0", 10);
146
+ if (Number.isNaN(availableMb)) {
147
+ return { name: "disk space", ok: true, detail: "skipped" };
148
+ }
149
+ if (availableMb < MIN_DISK_FREE_MB) {
150
+ return {
151
+ name: "disk space",
152
+ ok: false,
153
+ detail: `${availableMb} MB free at ${target}`,
154
+ fix: `Need at least ${MIN_DISK_FREE_MB} MB. Free up space or pick a different REPO_BASE_DIR.`,
155
+ };
156
+ }
157
+ return { name: "disk space", ok: true, detail: `${availableMb} MB free` };
158
+ }
159
+ async function checkOptional(cmd, versionArgs, why) {
160
+ const ok = await which(cmd);
161
+ if (!ok) {
162
+ return {
163
+ name: `${cmd} (optional)`,
164
+ ok: true,
165
+ detail: `not found — ${why}`,
166
+ };
167
+ }
168
+ const v = await captureVersion(cmd, versionArgs);
169
+ return { name: `${cmd} (optional)`, ok: true, detail: v ?? "found" };
170
+ }
171
+ function expandHome(p) {
172
+ if (p.startsWith("~"))
173
+ return path.join(os.homedir(), p.slice(1));
174
+ return p;
175
+ }
176
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/install/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import { promises as fs } from "node:fs";
2
+ // Minimal dotenv parser: handles KEY=value, # comments, blank lines,
3
+ // and double-quoted values. Anything more exotic isn't needed for our
4
+ // own .env files.
5
+ export function parseEnv(text) {
6
+ const out = {};
7
+ for (const raw of text.split(/\r?\n/)) {
8
+ const line = raw.trim();
9
+ if (!line || line.startsWith("#"))
10
+ continue;
11
+ const eq = line.indexOf("=");
12
+ if (eq === -1)
13
+ continue;
14
+ const key = line.slice(0, eq).trim();
15
+ let value = line.slice(eq + 1).trim();
16
+ if ((value.startsWith('"') && value.endsWith('"')) ||
17
+ (value.startsWith("'") && value.endsWith("'"))) {
18
+ value = value.slice(1, -1);
19
+ }
20
+ out[key] = value;
21
+ }
22
+ return out;
23
+ }
24
+ export function serializeEnv(values, order, comments = {}) {
25
+ const lines = [
26
+ "# Generated by `withvibe`. Edit secrets carefully — rotating",
27
+ "# INTERNAL_JWT_SECRET invalidates every active session.",
28
+ "",
29
+ ];
30
+ const seen = new Set();
31
+ for (const key of order) {
32
+ if (!(key in values))
33
+ continue;
34
+ if (comments[key])
35
+ lines.push(`# ${comments[key]}`);
36
+ lines.push(`${key}=${quoteIfNeeded(values[key])}`);
37
+ lines.push("");
38
+ seen.add(key);
39
+ }
40
+ for (const key of Object.keys(values)) {
41
+ if (seen.has(key))
42
+ continue;
43
+ lines.push(`${key}=${quoteIfNeeded(values[key])}`);
44
+ }
45
+ return lines.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
46
+ }
47
+ function quoteIfNeeded(v) {
48
+ if (v === "")
49
+ return "";
50
+ if (/[\s"'#$`\\]/.test(v)) {
51
+ return `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
52
+ }
53
+ return v;
54
+ }
55
+ export async function writeEnvFile(filePath, text) {
56
+ // Restrictive perms: secrets live here.
57
+ await fs.writeFile(filePath, text, { mode: 0o600 });
58
+ }
59
+ export async function readEnvFile(filePath) {
60
+ const text = await fs.readFile(filePath, "utf8").catch(() => "");
61
+ return parseEnv(text);
62
+ }
63
+ //# sourceMappingURL=env-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-file.js","sourceRoot":"","sources":["../../src/install/env-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { spawn } from "node:child_process";
2
+ export function run(cmd, args, opts = {}) {
3
+ return new Promise((resolve, reject) => {
4
+ const stream = opts.streamTo ?? "pipe";
5
+ const child = spawn(cmd, args, {
6
+ stdio: stream === "inherit" ? "inherit" : ["ignore", "pipe", "pipe"],
7
+ ...opts,
8
+ });
9
+ let stdout = "";
10
+ let stderr = "";
11
+ if (stream === "pipe") {
12
+ child.stdout?.on("data", (d) => (stdout += d.toString()));
13
+ child.stderr?.on("data", (d) => (stderr += d.toString()));
14
+ }
15
+ child.on("error", reject);
16
+ child.on("close", (code) => {
17
+ resolve({ stdout, stderr, code: code ?? -1 });
18
+ });
19
+ });
20
+ }
21
+ export async function which(cmd) {
22
+ const finder = process.platform === "win32" ? "where" : "which";
23
+ const res = await run(finder, [cmd]).catch(() => ({ code: 1 }));
24
+ return res.code === 0;
25
+ }
26
+ export async function captureVersion(cmd, args) {
27
+ const res = await run(cmd, args).catch(() => null);
28
+ if (!res || res.code !== 0)
29
+ return null;
30
+ return (res.stdout || res.stderr).trim().split("\n")[0] ?? null;
31
+ }
32
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/install/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAQ9D,MAAM,UAAU,GAAG,CACjB,GAAW,EACX,IAAc,EACd,OAAyD,EAAE;IAE3D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC;QACvC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;YAC7B,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YACpE,GAAG,IAAI;SACR,CAAC,CAAC;QAEH,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAClE,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAW;IACrC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAChE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAgB,CAAA,CAAC,CAAC;IAC7E,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAW,EACX,IAAc;IAEd,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAClE,CAAC"}
@@ -0,0 +1,78 @@
1
+ // Single source of truth for the image set the api spawns.
2
+ // IMPORTANT: image names + tags must match the defaults baked into the api
3
+ // services. If you rename anything here, also update:
4
+ // - apps/api/src/runner/claude-runner.service.ts (CLAUDE_RUNNER_IMAGE)
5
+ // - apps/api/src/docker/code-server.service.ts (CODE_SERVER_IMAGE)
6
+ // - apps/api/src/docker/browser-sidecar.service.ts (QA_BROWSER_IMAGE)
7
+ //
8
+ // Both compose (api/web) and the api-side sidecar spawners resolve the
9
+ // effective tag from $WITHVIBE_VERSION at runtime; localName here is the
10
+ // :latest fallback used when no version is set (from-source / from-registry).
11
+ // The 5 images that make up the stack:
12
+ // - api, web — the long-running services in docker-compose.yml
13
+ // - claude-runner — sidecar (always built; chat needs it)
14
+ // - code-server — sidecar (feature flag: codeServer)
15
+ // - qa-browser — sidecar (feature flag: qaBrowser)
16
+ export const STACK_IMAGES = [
17
+ {
18
+ localName: "withvibe/api:latest",
19
+ contextDir: ".",
20
+ dockerfile: "apps/api/Dockerfile",
21
+ label: "api",
22
+ },
23
+ {
24
+ localName: "withvibe/web:latest",
25
+ contextDir: ".",
26
+ dockerfile: "apps/web/Dockerfile",
27
+ label: "web",
28
+ },
29
+ {
30
+ localName: "withvibe-claude-runner:latest",
31
+ contextDir: "apps/api/runner",
32
+ label: "claude-runner",
33
+ },
34
+ {
35
+ localName: "withvibe-code-server:latest",
36
+ contextDir: "apps/api/code-server-image",
37
+ feature: "codeServer",
38
+ label: "code-server",
39
+ },
40
+ {
41
+ localName: "withvibe-qa-browser:latest",
42
+ contextDir: "apps/api/qa-browser-image",
43
+ feature: "qaBrowser",
44
+ label: "qa-browser",
45
+ },
46
+ ];
47
+ // External images pulled (not built) — postgres for the data plane,
48
+ // Traefik for the optional reverse-proxy profile.
49
+ export const EXTERNAL_IMAGES = {
50
+ postgres: "postgres:17-alpine",
51
+ traefik: "traefik:v3.1",
52
+ };
53
+ export function imagesForFeatures(features) {
54
+ return STACK_IMAGES.filter((img) => {
55
+ if (img.feature === "qaBrowser")
56
+ return features.qaBrowser;
57
+ if (img.feature === "codeServer")
58
+ return features.codeServer;
59
+ return true;
60
+ });
61
+ }
62
+ // Map a local image name to its registry-qualified counterpart for a given
63
+ // namespace. e.g. ("withvibe/api:latest", "ghcr.io/withvibe", "0.1.0")
64
+ // -> "ghcr.io/withvibe/api:0.1.0"
65
+ // Strips the "withvibe/" or "withvibe-" prefix so the same name works under
66
+ // any namespace.
67
+ export function registryName(local, namespace, tag) {
68
+ const [name] = local.split(":");
69
+ if (!name)
70
+ throw new Error(`Bad image name: ${local}`);
71
+ let stripped = name;
72
+ if (stripped.startsWith("withvibe/"))
73
+ stripped = stripped.slice("withvibe/".length);
74
+ else if (stripped.startsWith("withvibe-"))
75
+ stripped = stripped.slice("withvibe-".length);
76
+ return `${namespace.replace(/\/+$/, "")}/${stripped}:${tag}`;
77
+ }
78
+ //# sourceMappingURL=images.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.js","sourceRoot":"","sources":["../../src/install/images.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,2EAA2E;AAC3E,sDAAsD;AACtD,yEAAyE;AACzE,qEAAqE;AACrE,wEAAwE;AACxE,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,8EAA8E;AAgB9E,uCAAuC;AACvC,sEAAsE;AACtE,4DAA4D;AAC5D,yDAAyD;AACzD,wDAAwD;AACxD,MAAM,CAAC,MAAM,YAAY,GAAgB;IACvC;QACE,SAAS,EAAE,qBAAqB;QAChC,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,qBAAqB;QACjC,KAAK,EAAE,KAAK;KACb;IACD;QACE,SAAS,EAAE,qBAAqB;QAChC,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,qBAAqB;QACjC,KAAK,EAAE,KAAK;KACb;IACD;QACE,SAAS,EAAE,+BAA+B;QAC1C,UAAU,EAAE,iBAAiB;QAC7B,KAAK,EAAE,eAAe;KACvB;IACD;QACE,SAAS,EAAE,6BAA6B;QACxC,UAAU,EAAE,4BAA4B;QACxC,OAAO,EAAE,YAAY;QACrB,KAAK,EAAE,aAAa;KACrB;IACD;QACE,SAAS,EAAE,4BAA4B;QACvC,UAAU,EAAE,2BAA2B;QACvC,OAAO,EAAE,WAAW;QACpB,KAAK,EAAE,YAAY;KACpB;CACF,CAAC;AAEF,oEAAoE;AACpE,kDAAkD;AAClD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,QAAQ,EAAE,oBAAoB;IAC9B,OAAO,EAAE,cAAc;CACf,CAAC;AAEX,MAAM,UAAU,iBAAiB,CAAC,QAGjC;IACC,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QACjC,IAAI,GAAG,CAAC,OAAO,KAAK,WAAW;YAAE,OAAO,QAAQ,CAAC,SAAS,CAAC;QAC3D,IAAI,GAAG,CAAC,OAAO,KAAK,YAAY;YAAE,OAAO,QAAQ,CAAC,UAAU,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2EAA2E;AAC3E,uEAAuE;AACvE,oCAAoC;AACpC,4EAA4E;AAC5E,iBAAiB;AACjB,MAAM,UAAU,YAAY,CAC1B,KAAa,EACb,SAAiB,EACjB,GAAW;IAEX,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC;IACvD,IAAI,QAAQ,GAAG,IAAI,CAAC;IACpB,IAAI,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;SAC/E,IAAI,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACzF,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;AAC/D,CAAC"}