-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Expand file tree
/
Copy pathsetup.ts
More file actions
117 lines (107 loc) · 4 KB
/
setup.ts
File metadata and controls
117 lines (107 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
* `$P setup` — guided smoke test.
*
* Flow (per the CEO plan CLI UX spec):
* 1. Verify browse binary exists and responds
* 2. Verify Chromium launches via a dedicated blank tab
* 3. Verify pdftotext is installed (warn, don't fail)
* 4. Generate a smoke-test PDF from an inline 2-paragraph fixture
* 5. Open it
* 6. Print a 3-command cheatsheet
*/
import * as os from "node:os";
import * as path from "node:path";
import * as fs from "node:fs";
import * as browseClient from "./browseClient";
import { resolvePdftotext, PdftotextUnavailableError } from "./pdftotext";
import { generate } from "./orchestrator";
export async function runSetup(): Promise<void> {
process.stderr.write("make-pdf setup — verifying install\n\n");
// 1. Resolve browse binary
process.stderr.write(" [1/5] Checking browse binary...");
try {
const bin = browseClient.resolveBrowseBin();
process.stderr.write(` OK (${bin})\n`);
} catch (err: any) {
process.stderr.write(" FAIL\n");
process.stderr.write(`\n${err.message}\n`);
process.exit(4);
}
// 2. Chromium smoke (open a dedicated blank tab)
process.stderr.write(" [2/5] Launching Chromium...");
let chromiumTab: number | null = null;
try {
chromiumTab = browseClient.newtab();
process.stderr.write(` OK (tab ${chromiumTab})\n`);
} catch (err: any) {
process.stderr.write(" FAIL\n");
process.stderr.write(`\nChromium failed to launch: ${err.message}\n`);
process.stderr.write("\nTo fix: run gstack setup from the gstack repo:\n");
process.stderr.write(" cd ~/.claude/skills/gstack && ./setup\n");
process.exit(4);
} finally {
if (chromiumTab !== null) {
try { browseClient.closetab(chromiumTab); } catch { /* ignore */ }
}
}
// 3. pdftotext (optional — CI gate only)
process.stderr.write(" [3/5] Checking pdftotext (optional)...");
try {
const info = resolvePdftotext();
process.stderr.write(` OK (${info.flavor}, ${info.version.split(" ").slice(-1)[0] || "version unknown"})\n`);
} catch (err) {
process.stderr.write(" SKIP\n");
if (err instanceof PdftotextUnavailableError) {
process.stderr.write(
" pdftotext not installed. This is optional — only the CI\n" +
" copy-paste gate needs it. To enable:\n" +
" macOS: brew install poppler\n" +
" Ubuntu: sudo apt-get install poppler-utils\n",
);
}
}
// 4. Render smoke-test PDF
process.stderr.write(" [4/5] Generating smoke-test PDF...\n");
const fixture = [
"# Hello from make-pdf",
"",
"This is a two-paragraph smoke test. If you can read this sentence in the PDF that just opened, the pipeline works end-to-end.",
"",
"The second paragraph contains curly quotes (\"hello\"), an em dash -- like this, and an ellipsis... all of which should render correctly.",
"",
].join("\n");
const fixturePath = path.join(os.tmpdir(), `make-pdf-smoke-${process.pid}.md`);
const outPath = path.join(defaultOutputDir(), `make-pdf-smoke-${process.pid}.pdf`);
fs.writeFileSync(fixturePath, fixture, "utf8");
try {
await generate({
input: fixturePath,
output: outPath,
quiet: true,
pageNumbers: true,
});
process.stderr.write(` PASSED. Smoke test saved to ${outPath}\n`);
} catch (err: any) {
process.stderr.write(` FAILED: ${err.message}\n`);
process.exit(2);
} finally {
try { fs.unlinkSync(fixturePath); } catch { /* ignore */ }
}
// 5. Cheatsheet
process.stderr.write(" [5/5] All checks passed.\n\n");
process.stderr.write([
"make-pdf is ready. Try:",
" $P generate letter.md # default memo mode",
" $P generate --cover --toc essay.md # full publication",
" $P generate --watermark DRAFT memo.md # diagonal watermark",
"",
`Smoke-test PDF: ${outPath}`,
"",
].join("\n"));
}
function defaultOutputDir(): string {
if (process.platform === "darwin" && fs.existsSync("/private/tmp")) {
return "/private/tmp";
}
return os.tmpdir();
}