@slowcook-ai/cli 0.16.0-alpha.4 → 0.17.0-alpha.1

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 (91) hide show
  1. package/dist/cli.js +50 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/brew/agent.d.ts +25 -1
  4. package/dist/commands/brew/agent.d.ts.map +1 -1
  5. package/dist/commands/brew/agent.js +123 -20
  6. package/dist/commands/brew/agent.js.map +1 -1
  7. package/dist/commands/brew/halt.d.ts +1 -1
  8. package/dist/commands/brew/halt.d.ts.map +1 -1
  9. package/dist/commands/brew/halt.js +13 -0
  10. package/dist/commands/brew/halt.js.map +1 -1
  11. package/dist/commands/check/index.d.ts +14 -0
  12. package/dist/commands/check/index.d.ts.map +1 -0
  13. package/dist/commands/check/index.js +75 -0
  14. package/dist/commands/check/index.js.map +1 -0
  15. package/dist/commands/check/mock-isolation.d.ts +52 -0
  16. package/dist/commands/check/mock-isolation.d.ts.map +1 -0
  17. package/dist/commands/check/mock-isolation.js +186 -0
  18. package/dist/commands/check/mock-isolation.js.map +1 -0
  19. package/dist/commands/init/from-prod.d.ts +48 -0
  20. package/dist/commands/init/from-prod.d.ts.map +1 -0
  21. package/dist/commands/init/from-prod.js +256 -0
  22. package/dist/commands/init/from-prod.js.map +1 -0
  23. package/dist/commands/init/index.d.ts.map +1 -1
  24. package/dist/commands/init/index.js +9 -0
  25. package/dist/commands/init/index.js.map +1 -1
  26. package/dist/commands/init/mock.d.ts.map +1 -1
  27. package/dist/commands/init/mock.js +47 -13
  28. package/dist/commands/init/mock.js.map +1 -1
  29. package/dist/commands/on-mockup-approved/index.d.ts +25 -0
  30. package/dist/commands/on-mockup-approved/index.d.ts.map +1 -0
  31. package/dist/commands/on-mockup-approved/index.js +359 -0
  32. package/dist/commands/on-mockup-approved/index.js.map +1 -0
  33. package/dist/commands/plate/classify.d.ts +65 -0
  34. package/dist/commands/plate/classify.d.ts.map +1 -0
  35. package/dist/commands/plate/classify.js +194 -0
  36. package/dist/commands/plate/classify.js.map +1 -0
  37. package/dist/commands/plate/index.d.ts.map +1 -1
  38. package/dist/commands/plate/index.js +259 -34
  39. package/dist/commands/plate/index.js.map +1 -1
  40. package/dist/commands/port/index.d.ts +30 -0
  41. package/dist/commands/port/index.d.ts.map +1 -0
  42. package/dist/commands/port/index.js +237 -0
  43. package/dist/commands/port/index.js.map +1 -0
  44. package/dist/commands/port/transform.d.ts +68 -0
  45. package/dist/commands/port/transform.d.ts.map +1 -0
  46. package/dist/commands/port/transform.js +122 -0
  47. package/dist/commands/port/transform.js.map +1 -0
  48. package/dist/commands/preview/config.d.ts +73 -0
  49. package/dist/commands/preview/config.d.ts.map +1 -0
  50. package/dist/commands/preview/config.js +200 -0
  51. package/dist/commands/preview/config.js.map +1 -0
  52. package/dist/commands/preview/deploy.d.ts +35 -0
  53. package/dist/commands/preview/deploy.d.ts.map +1 -0
  54. package/dist/commands/preview/deploy.js +247 -0
  55. package/dist/commands/preview/deploy.js.map +1 -0
  56. package/dist/commands/preview/index.d.ts +9 -0
  57. package/dist/commands/preview/index.d.ts.map +1 -0
  58. package/dist/commands/preview/index.js +67 -0
  59. package/dist/commands/preview/index.js.map +1 -0
  60. package/dist/commands/preview/ssh.d.ts +49 -0
  61. package/dist/commands/preview/ssh.d.ts.map +1 -0
  62. package/dist/commands/preview/ssh.js +99 -0
  63. package/dist/commands/preview/ssh.js.map +1 -0
  64. package/dist/commands/preview/teardown.d.ts +25 -0
  65. package/dist/commands/preview/teardown.d.ts.map +1 -0
  66. package/dist/commands/preview/teardown.js +164 -0
  67. package/dist/commands/preview/teardown.js.map +1 -0
  68. package/dist/commands/recon/index.d.ts +60 -0
  69. package/dist/commands/recon/index.d.ts.map +1 -0
  70. package/dist/commands/recon/index.js +278 -0
  71. package/dist/commands/recon/index.js.map +1 -0
  72. package/dist/commands/refine/context.d.ts +12 -0
  73. package/dist/commands/refine/context.d.ts.map +1 -1
  74. package/dist/commands/refine/context.js +72 -0
  75. package/dist/commands/refine/context.js.map +1 -1
  76. package/dist/commands/refine/history-index.d.ts +84 -0
  77. package/dist/commands/refine/history-index.d.ts.map +1 -0
  78. package/dist/commands/refine/history-index.js +289 -0
  79. package/dist/commands/refine/history-index.js.map +1 -0
  80. package/dist/commands/refine/index.d.ts.map +1 -1
  81. package/dist/commands/refine/index.js +28 -0
  82. package/dist/commands/refine/index.js.map +1 -1
  83. package/dist/commands/run-mock/index.d.ts +34 -0
  84. package/dist/commands/run-mock/index.d.ts.map +1 -0
  85. package/dist/commands/run-mock/index.js +308 -0
  86. package/dist/commands/run-mock/index.js.map +1 -0
  87. package/dist/commands/vibe/index.d.ts.map +1 -1
  88. package/dist/commands/vibe/index.js +38 -4
  89. package/dist/commands/vibe/index.js.map +1 -1
  90. package/package.json +15 -13
  91. package/LICENSE +0 -21
@@ -0,0 +1,200 @@
1
+ /**
2
+ * `.brewing/preview.yaml` schema + parser — 0.16.0-α.5.
3
+ *
4
+ * Lightweight config that tells `slowcook preview deploy/teardown` how
5
+ * to ssh into the consumer's box, where to put files, what port range
6
+ * to allocate from, and what URL pattern the box's reverse proxy serves.
7
+ *
8
+ * Slowcook is stateless re: hosting. Each consumer provides their own
9
+ * SSH-reachable box (Docker engine + reverse proxy with wildcard cert);
10
+ * this config tells slowcook how to reach it.
11
+ *
12
+ * Hand-parsed (no yaml dep) because the schema is small + flat. If we
13
+ * ever need anchors / multi-doc / etc, switch to the workspace's `yaml`
14
+ * package — it's already a transitive dep via cli/dependencies.
15
+ */
16
+ import { existsSync, readFileSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ export class PreviewConfigError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = "PreviewConfigError";
22
+ }
23
+ }
24
+ export const PREVIEW_CONFIG_PATH = ".brewing/preview.yaml";
25
+ /**
26
+ * Parse `.brewing/preview.yaml`. Returns the typed config or throws
27
+ * `PreviewConfigError` with a precise message identifying the missing
28
+ * or malformed field.
29
+ *
30
+ * Accepts either:
31
+ *
32
+ * preview:
33
+ * type: ssh
34
+ * host: ...
35
+ * ...
36
+ *
37
+ * or top-level keys (the `preview:` wrapper is optional but
38
+ * recommended; consumers will likely add a `box:` or other top-level
39
+ * sections later).
40
+ */
41
+ export function parsePreviewConfig(yamlText) {
42
+ const lines = yamlText.split(/\r?\n/);
43
+ // Track inside-`preview:` block by indentation; allow flat top-level too.
44
+ const flat = {};
45
+ let inPreview = false;
46
+ let baseIndent = -1;
47
+ for (const raw of lines) {
48
+ const line = raw.replace(/#.*$/, ""); // strip comments
49
+ if (line.trim() === "")
50
+ continue;
51
+ const indentMatch = line.match(/^(\s*)/);
52
+ const indent = indentMatch ? indentMatch[1].length : 0;
53
+ const content = line.slice(indent);
54
+ if (indent === 0 && content.startsWith("preview:")) {
55
+ inPreview = true;
56
+ baseIndent = -1;
57
+ continue;
58
+ }
59
+ if (indent === 0 && content.includes(":") && !content.startsWith("preview:")) {
60
+ // top-level key outside preview — could be a sibling block we don't parse
61
+ inPreview = false;
62
+ const m = content.match(/^([a-zA-Z_][\w-]*)\s*:\s*(.*)$/);
63
+ if (m && m[2]?.trim()) {
64
+ flat[m[1]] = stripQuotes(m[2].trim());
65
+ }
66
+ continue;
67
+ }
68
+ if (inPreview) {
69
+ if (baseIndent === -1)
70
+ baseIndent = indent;
71
+ if (indent < baseIndent) {
72
+ inPreview = false;
73
+ continue;
74
+ }
75
+ const m = content.match(/^([a-zA-Z_][\w-]*)\s*:\s*(.*)$/);
76
+ if (m) {
77
+ flat[m[1]] = stripQuotes(m[2].trim());
78
+ }
79
+ }
80
+ }
81
+ const required = [
82
+ "type",
83
+ "host",
84
+ "user",
85
+ "keySecret",
86
+ "urlTemplate",
87
+ "remoteRoot",
88
+ ];
89
+ // Map snake_case YAML to camelCase keys.
90
+ const aliasMap = {
91
+ type: "type",
92
+ host: "host",
93
+ user: "user",
94
+ key_secret: "keySecret",
95
+ keysecret: "keySecret",
96
+ url_template: "urlTemplate",
97
+ urltemplate: "urlTemplate",
98
+ remote_root: "remoteRoot",
99
+ remoteroot: "remoteRoot",
100
+ port: "port",
101
+ port_range: "portRange",
102
+ portrange: "portRange",
103
+ mock_dir: "mockDir",
104
+ mockdir: "mockDir",
105
+ };
106
+ const cfg = {};
107
+ for (const [k, v] of Object.entries(flat)) {
108
+ const camel = aliasMap[k.toLowerCase()];
109
+ if (!camel)
110
+ continue;
111
+ if (camel === "port") {
112
+ cfg.port = parseInt(v, 10);
113
+ }
114
+ else if (camel === "portRange") {
115
+ const m = v.match(/^\[?\s*(\d+)\s*,\s*(\d+)\s*\]?$/);
116
+ if (!m) {
117
+ throw new PreviewConfigError(`preview.port_range: expected "[lo, hi]"; got ${JSON.stringify(v)}`);
118
+ }
119
+ const lo = parseInt(m[1], 10);
120
+ const hi = parseInt(m[2], 10);
121
+ if (lo > hi || lo <= 0 || hi > 65535) {
122
+ throw new PreviewConfigError(`preview.port_range: ${lo}..${hi} is not a valid port range`);
123
+ }
124
+ cfg.portRange = [lo, hi];
125
+ }
126
+ else {
127
+ // string fields
128
+ cfg[camel] = v;
129
+ }
130
+ }
131
+ for (const k of required) {
132
+ if (cfg[k] === undefined || cfg[k] === "") {
133
+ throw new PreviewConfigError(`preview.${snakeOf(k)} is required in ${PREVIEW_CONFIG_PATH}.`);
134
+ }
135
+ }
136
+ if (cfg.type !== "ssh") {
137
+ throw new PreviewConfigError(`preview.type must be "ssh" (got ${JSON.stringify(cfg.type)}). Other deploy types may ship later.`);
138
+ }
139
+ if (!cfg.urlTemplate.includes("{port}")) {
140
+ throw new PreviewConfigError(`preview.url_template must contain the literal "{port}" placeholder; got ${JSON.stringify(cfg.urlTemplate)}.`);
141
+ }
142
+ return {
143
+ type: "ssh",
144
+ host: cfg.host,
145
+ user: cfg.user,
146
+ keySecret: cfg.keySecret,
147
+ port: cfg.port ?? 22,
148
+ portRange: cfg.portRange ?? [4000, 4099],
149
+ urlTemplate: cfg.urlTemplate,
150
+ remoteRoot: cfg.remoteRoot,
151
+ mockDir: cfg.mockDir ?? "mock",
152
+ };
153
+ }
154
+ export function readPreviewConfig(repoRoot) {
155
+ const p = join(repoRoot, PREVIEW_CONFIG_PATH);
156
+ if (!existsSync(p)) {
157
+ throw new PreviewConfigError(`${PREVIEW_CONFIG_PATH} not found at ${p}. ` +
158
+ `See docs/operating-guide.md for the schema + box setup steps.`);
159
+ }
160
+ const text = readFileSync(p, "utf8");
161
+ return parsePreviewConfig(text);
162
+ }
163
+ function stripQuotes(s) {
164
+ if ((s.startsWith('"') && s.endsWith('"')) ||
165
+ (s.startsWith("'") && s.endsWith("'"))) {
166
+ return s.slice(1, -1);
167
+ }
168
+ return s;
169
+ }
170
+ function snakeOf(camel) {
171
+ return camel.replace(/([A-Z])/g, "_$1").toLowerCase();
172
+ }
173
+ /**
174
+ * Build the preview URL for a given allocated port.
175
+ */
176
+ export function urlForPort(cfg, port) {
177
+ return cfg.urlTemplate.replace("{port}", String(port));
178
+ }
179
+ /**
180
+ * Container name for a given PR. Single source of truth so deploy +
181
+ * teardown agree on the name.
182
+ */
183
+ export function containerNameForPr(pr) {
184
+ return `slowcook-mock-pr-${pr}`;
185
+ }
186
+ /**
187
+ * Image tag for a given PR. We rebuild per-PR (each PR has its own
188
+ * scenario set), so tags don't collide.
189
+ */
190
+ export function imageTagForPr(pr) {
191
+ return `slowcook-mock-pr-${pr}:latest`;
192
+ }
193
+ /**
194
+ * Remote staging directory for a given PR. Build artifacts + the tar
195
+ * extract live here.
196
+ */
197
+ export function remoteDirForPr(cfg, pr) {
198
+ return `${cfg.remoteRoot.replace(/\/+$/, "")}/pr-${pr}`;
199
+ }
200
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/commands/preview/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `slowcook preview deploy --pr <n>` — 0.16.0-α.5.
3
+ *
4
+ * Builds the consumer's mock app remotely on their SSH-reachable box,
5
+ * runs it as a Docker container, posts the preview URL to the PR.
6
+ *
7
+ * Flow:
8
+ * 1. Read `.brewing/preview.yaml` (host, user, key secret, port range,
9
+ * URL template, remote root)
10
+ * 2. tar the local mock/ directory (excluding node_modules + .next)
11
+ * 3. scp tarball to `${remote_root}/pr-N/`
12
+ * 4. ssh: extract, `docker build`, allocate port, `docker rm -f` old
13
+ * container if any, `docker run -d`
14
+ * 5. Compose URL via `url_template`; upsert PR comment
15
+ *
16
+ * The container always exposes 3100 internally (matches the mock's
17
+ * Dockerfile from `slowcook init mock`); the host port is allocated
18
+ * from `port_range` and substituted into `url_template`.
19
+ *
20
+ * Error semantics: any sub-step failure throws; the caller prints +
21
+ * exits non-zero. The workflow surfaces the failure as a check on the
22
+ * PR; consumers can re-run via workflow_dispatch.
23
+ */
24
+ interface ParsedArgs {
25
+ pr: number | undefined;
26
+ repoRoot: string;
27
+ keyPath: string | undefined;
28
+ owner: string | undefined;
29
+ repo: string | undefined;
30
+ dryRun: boolean;
31
+ }
32
+ export declare function parseDeployArgs(argv: string[]): ParsedArgs;
33
+ export declare function deploy(argv: string[], cliVersion: string): Promise<void>;
34
+ export {};
35
+ //# sourceMappingURL=deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/preview/deploy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAsBH,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAoB1D;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0I9E"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * `slowcook preview deploy --pr <n>` — 0.16.0-α.5.
3
+ *
4
+ * Builds the consumer's mock app remotely on their SSH-reachable box,
5
+ * runs it as a Docker container, posts the preview URL to the PR.
6
+ *
7
+ * Flow:
8
+ * 1. Read `.brewing/preview.yaml` (host, user, key secret, port range,
9
+ * URL template, remote root)
10
+ * 2. tar the local mock/ directory (excluding node_modules + .next)
11
+ * 3. scp tarball to `${remote_root}/pr-N/`
12
+ * 4. ssh: extract, `docker build`, allocate port, `docker rm -f` old
13
+ * container if any, `docker run -d`
14
+ * 5. Compose URL via `url_template`; upsert PR comment
15
+ *
16
+ * The container always exposes 3100 internally (matches the mock's
17
+ * Dockerfile from `slowcook init mock`); the host port is allocated
18
+ * from `port_range` and substituted into `url_template`.
19
+ *
20
+ * Error semantics: any sub-step failure throws; the caller prints +
21
+ * exits non-zero. The workflow surfaces the failure as a check on the
22
+ * PR; consumers can re-run via workflow_dispatch.
23
+ */
24
+ import { execSync, spawnSync } from "node:child_process";
25
+ import { existsSync, statSync } from "node:fs";
26
+ import { join } from "node:path";
27
+ import { tmpdir } from "node:os";
28
+ import { readPreviewConfig, containerNameForPr, imageTagForPr, remoteDirForPr, urlForPort, } from "./config.js";
29
+ import { sshExec, scpUpload, pickRemotePort, } from "./ssh.js";
30
+ export function parseDeployArgs(argv) {
31
+ const args = {
32
+ pr: undefined,
33
+ repoRoot: process.cwd(),
34
+ keyPath: undefined,
35
+ owner: undefined,
36
+ repo: undefined,
37
+ dryRun: false,
38
+ };
39
+ for (let i = 0; i < argv.length; i++) {
40
+ const a = argv[i];
41
+ const next = argv[i + 1];
42
+ if (a === "--pr" && next) {
43
+ args.pr = parseInt(next, 10);
44
+ i++;
45
+ }
46
+ else if (a === "--cwd" && next) {
47
+ args.repoRoot = next;
48
+ i++;
49
+ }
50
+ else if (a === "--ssh-key" && next) {
51
+ args.keyPath = next;
52
+ i++;
53
+ }
54
+ else if (a === "--owner" && next) {
55
+ args.owner = next;
56
+ i++;
57
+ }
58
+ else if (a === "--repo" && next) {
59
+ args.repo = next;
60
+ i++;
61
+ }
62
+ else if (a === "--dry-run") {
63
+ args.dryRun = true;
64
+ }
65
+ }
66
+ return args;
67
+ }
68
+ export async function deploy(argv, cliVersion) {
69
+ const parsed = parseDeployArgs(argv);
70
+ if (!parsed.pr || Number.isNaN(parsed.pr)) {
71
+ console.error("--pr <number> is required.");
72
+ process.exit(64);
73
+ }
74
+ const cfg = readPreviewConfig(parsed.repoRoot);
75
+ const mockPath = join(parsed.repoRoot, cfg.mockDir);
76
+ if (!existsSync(mockPath) || !statSync(mockPath).isDirectory()) {
77
+ console.error(`Mock directory ${cfg.mockDir} not found at ${mockPath}. Run \`slowcook init mock\` first.`);
78
+ process.exit(2);
79
+ }
80
+ if (parsed.dryRun) {
81
+ console.log(`slowcook preview deploy · pr ${parsed.pr} (dry-run)`);
82
+ console.log(` config: host=${cfg.host} user=${cfg.user} port=${cfg.port}`);
83
+ console.log(` remote: ${remoteDirForPr(cfg, parsed.pr)}`);
84
+ console.log(` container: ${containerNameForPr(parsed.pr)}`);
85
+ console.log(` image: ${imageTagForPr(parsed.pr)}`);
86
+ console.log(` url_template: ${cfg.urlTemplate}`);
87
+ return;
88
+ }
89
+ const keyPath = parsed.keyPath ?? process.env["SLOWCOOK_PREVIEW_SSH_KEY_PATH"];
90
+ if (!keyPath) {
91
+ console.error("SSH key path not provided. Pass --ssh-key <path> or set SLOWCOOK_PREVIEW_SSH_KEY_PATH.");
92
+ process.exit(2);
93
+ }
94
+ const target = {
95
+ host: cfg.host,
96
+ user: cfg.user,
97
+ port: cfg.port,
98
+ keyPath,
99
+ };
100
+ console.log(`slowcook preview deploy · pr ${parsed.pr} → ${cfg.user}@${cfg.host}:${cfg.port}`);
101
+ // Step 1: tar the mock directory locally. Exclude node_modules, .next,
102
+ // .turbo (we'll rebuild on the box). Keeps the tarball small + the
103
+ // remote build deterministic.
104
+ const tarball = join(tmpdir(), `slowcook-mock-pr-${parsed.pr}.tgz`);
105
+ console.log(` tar → ${tarball}`);
106
+ const tarResult = spawnSync("tar", [
107
+ "czf", tarball,
108
+ "-C", parsed.repoRoot,
109
+ "--exclude=node_modules",
110
+ "--exclude=.next",
111
+ "--exclude=.turbo",
112
+ "--exclude=out",
113
+ cfg.mockDir,
114
+ ], { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" });
115
+ if (tarResult.status !== 0) {
116
+ throw new Error(`tar failed (status ${tarResult.status}): ${tarResult.stderr}`);
117
+ }
118
+ const remoteDir = remoteDirForPr(cfg, parsed.pr);
119
+ const remoteTarball = `${remoteDir}/mock-src.tgz`;
120
+ const containerName = containerNameForPr(parsed.pr);
121
+ const imageTag = imageTagForPr(parsed.pr);
122
+ // Step 2: ensure remote dir exists, scp tarball.
123
+ console.log(` ssh mkdir -p ${remoteDir}`);
124
+ sshExec(target, `mkdir -p ${shellQuote(remoteDir)}`);
125
+ console.log(` scp → ${cfg.host}:${remoteTarball}`);
126
+ scpUpload(target, tarball, remoteTarball);
127
+ // Step 3: extract + build remotely.
128
+ // 0.16.0-α.6 — pass the review-overlay env vars as build args so the
129
+ // mock's compile-time `process.env.NEXT_PUBLIC_SLOWCOOK_*` references
130
+ // resolve to the right values (Next inlines NEXT_PUBLIC_* at build time).
131
+ const owner = parsed.owner ?? detectOwner(parsed.repoRoot) ?? "";
132
+ const repo = parsed.repo ?? detectRepo(parsed.repoRoot) ?? "";
133
+ console.log(` ssh docker build ${imageTag}`);
134
+ const buildEnv = [
135
+ `NEXT_PUBLIC_SLOWCOOK_REVIEW=1`,
136
+ `NEXT_PUBLIC_SLOWCOOK_OWNER=${shellQuote(owner)}`,
137
+ `NEXT_PUBLIC_SLOWCOOK_REPO=${shellQuote(repo)}`,
138
+ `NEXT_PUBLIC_SLOWCOOK_PR_NUMBER=${parsed.pr}`,
139
+ ].join(" ");
140
+ const buildScript = [
141
+ `cd ${shellQuote(remoteDir)}`,
142
+ `rm -rf ${shellQuote(cfg.mockDir)}`,
143
+ `tar xzf mock-src.tgz`,
144
+ `cd ${shellQuote(cfg.mockDir)}`,
145
+ // Write the env vars to .env.production so `next build` picks them up.
146
+ // (Build args via --build-arg would also work but require Dockerfile
147
+ // edits in the consumer's repo; .env.production keeps the contract
148
+ // backward-compatible with the init-mock template.)
149
+ `printf '%s\\n' ${shellQuote(buildEnv.split(" ").join("\\n"))} > .env.production`,
150
+ `docker build -t ${shellQuote(imageTag)} -f Dockerfile ..`,
151
+ ].join(" && ");
152
+ sshExec(target, buildScript);
153
+ // Step 4: stop existing container (idempotent), allocate port, run.
154
+ console.log(` ssh docker rm -f ${containerName} (if present)`);
155
+ sshExec(target, `docker rm -f ${shellQuote(containerName)} 2>/dev/null || true`);
156
+ console.log(` ssh pick free port in ${cfg.portRange[0]}..${cfg.portRange[1]}`);
157
+ const hostPort = pickRemotePort(target, cfg.portRange[0], cfg.portRange[1]);
158
+ console.log(` → ${hostPort}`);
159
+ console.log(` ssh docker run -d --name ${containerName} -p ${hostPort}:3100`);
160
+ sshExec(target, `docker run -d --name ${shellQuote(containerName)} ` +
161
+ `--restart unless-stopped ` +
162
+ `--label slowcook.pr=${parsed.pr} ` +
163
+ `-p ${hostPort}:3100 ${shellQuote(imageTag)}`);
164
+ // Step 5: compose URL + upsert PR comment.
165
+ const url = urlForPort(cfg, hostPort);
166
+ console.log(` url ${url}`);
167
+ const githubToken = process.env["GITHUB_TOKEN"];
168
+ if (githubToken && owner && repo) {
169
+ await upsertPreviewComment({
170
+ owner, repo, pr: parsed.pr, url, hostPort, cliVersion, githubToken,
171
+ });
172
+ }
173
+ else {
174
+ console.log(` (skipped PR comment: no GITHUB_TOKEN or unknown owner/repo. URL: ${url})`);
175
+ }
176
+ console.log(`Done. Preview ready at ${url}`);
177
+ }
178
+ const PREVIEW_COMMENT_MARKER = "<!-- slowcook-preview-deploy -->";
179
+ async function upsertPreviewComment(args) {
180
+ // Use Octokit transitively via dynamic import to avoid pulling it
181
+ // for the dry-run path.
182
+ const { Octokit } = await import("@octokit/rest");
183
+ const octokit = new Octokit({ auth: args.githubToken });
184
+ const body = [
185
+ PREVIEW_COMMENT_MARKER,
186
+ `## 🍳 Mockup preview ready`,
187
+ ``,
188
+ `**Live URL:** ${args.url}`,
189
+ ``,
190
+ `Open the scenario picker; click any \`?scenario=story-N\` link to deep-link into a specific story's mock.`,
191
+ ``,
192
+ `_Deployed by \`slowcook preview deploy@${args.cliVersion}\` on host port ${args.hostPort}. Updated each time the mockup branch changes._`,
193
+ ].join("\n");
194
+ const list = await octokit.rest.issues.listComments({
195
+ owner: args.owner,
196
+ repo: args.repo,
197
+ issue_number: args.pr,
198
+ per_page: 100,
199
+ });
200
+ const existing = list.data.find((c) => c.body?.includes(PREVIEW_COMMENT_MARKER));
201
+ if (existing) {
202
+ await octokit.rest.issues.updateComment({
203
+ owner: args.owner,
204
+ repo: args.repo,
205
+ comment_id: existing.id,
206
+ body,
207
+ });
208
+ console.log(` pr comment ${existing.id} updated`);
209
+ }
210
+ else {
211
+ const created = await octokit.rest.issues.createComment({
212
+ owner: args.owner,
213
+ repo: args.repo,
214
+ issue_number: args.pr,
215
+ body,
216
+ });
217
+ console.log(` pr comment ${created.data.id} created`);
218
+ }
219
+ }
220
+ function detectOwner(repoRoot) {
221
+ return detectOwnerRepo(repoRoot)?.owner;
222
+ }
223
+ function detectRepo(repoRoot) {
224
+ return detectOwnerRepo(repoRoot)?.repo;
225
+ }
226
+ function detectOwnerRepo(repoRoot) {
227
+ try {
228
+ const url = execSync("git remote get-url origin", {
229
+ cwd: repoRoot,
230
+ encoding: "utf8",
231
+ stdio: ["ignore", "pipe", "ignore"],
232
+ }).trim();
233
+ const m = url.match(/github\.com[:/]([^/]+)\/([^/.]+)(?:\.git)?$/);
234
+ if (m && m[1] && m[2])
235
+ return { owner: m[1], repo: m[2] };
236
+ }
237
+ catch {
238
+ // not a git repo
239
+ }
240
+ return undefined;
241
+ }
242
+ function shellQuote(s) {
243
+ // Minimal quoting for ssh-side bash. Single-quote everything; escape
244
+ // existing single-quotes via the close-quote-then-escape pattern.
245
+ return `'${s.replace(/'/g, "'\\''")}'`;
246
+ }
247
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../../src/commands/preview/deploy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `slowcook preview <subcommand>` dispatch — 0.16.0-α.5.
3
+ *
4
+ * Subcommands:
5
+ * - `slowcook preview deploy --pr <n>` — build + run + post URL
6
+ * - `slowcook preview teardown --pr <n>` — stop + remove + mark
7
+ */
8
+ export declare function preview(argv: string[], cliVersion: string): Promise<void>;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/preview/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB/E"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * `slowcook preview <subcommand>` dispatch — 0.16.0-α.5.
3
+ *
4
+ * Subcommands:
5
+ * - `slowcook preview deploy --pr <n>` — build + run + post URL
6
+ * - `slowcook preview teardown --pr <n>` — stop + remove + mark
7
+ */
8
+ import { deploy } from "./deploy.js";
9
+ import { teardown } from "./teardown.js";
10
+ export async function preview(argv, cliVersion) {
11
+ const sub = argv[0];
12
+ switch (sub) {
13
+ case "deploy":
14
+ await deploy(argv.slice(1), cliVersion);
15
+ return;
16
+ case "teardown":
17
+ await teardown(argv.slice(1), cliVersion);
18
+ return;
19
+ case undefined:
20
+ case "help":
21
+ case "--help":
22
+ case "-h":
23
+ printHelp();
24
+ return;
25
+ default:
26
+ console.error(`Unknown preview subcommand: ${sub}`);
27
+ printHelp();
28
+ process.exit(64);
29
+ }
30
+ }
31
+ function printHelp() {
32
+ console.log(`
33
+ slowcook preview — SSH preview deploy for the consumer's mock app (0.16-α.5)
34
+
35
+ Usage:
36
+ slowcook preview deploy --pr <n> [--ssh-key <path>] [--cwd <path>] [--owner <login>] [--repo <name>] [--dry-run]
37
+ slowcook preview teardown --pr <n> [--ssh-key <path>] [--cwd <path>] [--owner <login>] [--repo <name>] [--prune-image] [--dry-run]
38
+
39
+ Reads .brewing/preview.yaml for SSH host / user / port range / URL
40
+ template / remote root. See docs/operating-guide.md for the schema +
41
+ box setup steps.
42
+
43
+ Common options:
44
+ --pr <n> PR number to deploy/teardown for. REQUIRED.
45
+ --ssh-key <path> Path to the SSH private key file. Defaults to
46
+ SLOWCOOK_PREVIEW_SSH_KEY_PATH.
47
+ --cwd <path> Repo root (default: cwd).
48
+ --owner <login> GitHub owner (default: detect from git remote).
49
+ --repo <name> GitHub repo (default: detect from git remote).
50
+ --dry-run Print planned actions, don't ssh/scp/docker.
51
+
52
+ deploy-only:
53
+ (none)
54
+
55
+ teardown-only:
56
+ --prune-image Also \`docker rmi\` the per-PR image (frees disk).
57
+
58
+ Environment:
59
+ GITHUB_TOKEN For PR-comment upsert (otherwise skipped).
60
+ SLOWCOOK_PREVIEW_SSH_KEY_PATH Default for --ssh-key.
61
+
62
+ Workflow templates installed by \`slowcook init\`:
63
+ .github/workflows/slowcook-preview-deploy.yml fires on PR opened/synchronized with the slowcook-mockup label
64
+ .github/workflows/slowcook-preview-teardown.yml fires on PR closed
65
+ `);
66
+ }
67
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/preview/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAc,EAAE,UAAkB;IAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,QAAQ;YACX,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YACxC,OAAO;QACT,KAAK,UAAU;YACb,MAAM,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YAC1C,OAAO;QACT,KAAK,SAAS,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI;YACP,SAAS,EAAE,CAAC;YACZ,OAAO;QACT;YACE,OAAO,CAAC,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;YACpD,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCb,CAAC,CAAC;AACH,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Thin shell wrappers around `ssh` + `scp` so deploy/teardown can talk
3
+ * to the consumer's box without pulling in a JS ssh library.
4
+ *
5
+ * Why shell out: ops people debug `ssh` + `scp` invocations daily; if a
6
+ * deploy fails, the failing command is something they can copy + paste.
7
+ * Pulling in a JS library for marginal API ergonomics adds a maintenance
8
+ * surface for a feature that's already box-specific.
9
+ *
10
+ * All wrappers use BatchMode=yes (no interactive password prompts) and
11
+ * StrictHostKeyChecking=accept-new (first connect adds the key, future
12
+ * connects verify against it). Failures throw with stderr captured so
13
+ * the workflow logs surface the underlying error.
14
+ */
15
+ export interface SshTarget {
16
+ host: string;
17
+ user: string;
18
+ port: number;
19
+ /** Absolute path to the private key file on the runner's filesystem. */
20
+ keyPath: string;
21
+ }
22
+ export interface SshResult {
23
+ stdout: string;
24
+ stderr: string;
25
+ }
26
+ /**
27
+ * Run a remote command via ssh. Throws on non-zero exit with the
28
+ * captured stderr inlined into the error message.
29
+ */
30
+ export declare function sshExec(target: SshTarget, command: string): SshResult;
31
+ /**
32
+ * scp a local file to a remote path. Throws on non-zero exit.
33
+ */
34
+ export declare function scpUpload(target: SshTarget, localPath: string, remotePath: string): void;
35
+ /**
36
+ * Find the first free TCP port in [lo, hi] on the remote box. Uses
37
+ * `ss -ltn` (universally available on modern Linux) to enumerate
38
+ * already-bound ports.
39
+ *
40
+ * Returns the chosen port. Throws if every port in the range is in use.
41
+ */
42
+ export declare function pickRemotePort(target: SshTarget, lo: number, hi: number): number;
43
+ /**
44
+ * Get the host port a running container is publishing to (the value
45
+ * after `0.0.0.0:` in `docker port` output). Returns null if the
46
+ * container isn't running or doesn't publish 3100.
47
+ */
48
+ export declare function getContainerPort(target: SshTarget, containerName: string): number | null;
49
+ //# sourceMappingURL=ssh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../../../src/commands/preview/ssh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAkBD;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAcrE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAsBxF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAehF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKxF"}