Skip to content

Commit 9daff79

Browse files
修复 make-pdf 的 browse 解析
1 parent e23ff28 commit 9daff79

4 files changed

Lines changed: 70 additions & 12 deletions

File tree

make-pdf/src/browseClient.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
* Binary resolution order (Codex round 2 #4):
1111
* 1. $BROWSE_BIN env override
1212
* 2. sibling dir: dirname(argv[0])/../browse/dist/browse
13-
* 3. ~/.claude/skills/gstack/browse/dist/browse
14-
* 4. PATH lookup: `browse`
15-
* 5. error with setup hint
13+
* 3. ~/.agents/skills/gstack/browse/dist/browse
14+
* 4. ~/.codex/skills/gstack/browse/dist/browse
15+
* 5. ~/.claude/skills/gstack/browse/dist/browse
16+
* 6. PATH lookup: `browse`
17+
* 7. error with setup hint
1618
*/
1719

1820
import { execFileSync } from "node:child_process";
@@ -74,10 +76,17 @@ export function resolveBrowseBin(): string {
7476
if (isExecutable(candidate)) return candidate;
7577
}
7678

77-
// Global install
79+
// Global installs. Prefer the active Codex/agents install before older
80+
// Claude installs that may still exist on disk.
7881
const home = os.homedir();
79-
const globalPath = path.join(home, ".claude/skills/gstack/browse/dist/browse");
80-
if (isExecutable(globalPath)) return globalPath;
82+
const globalCandidates = [
83+
path.join(home, ".agents/skills/gstack/browse/dist/browse"),
84+
path.join(home, ".codex/skills/gstack/browse/dist/browse"),
85+
path.join(home, ".claude/skills/gstack/browse/dist/browse"),
86+
];
87+
for (const candidate of globalCandidates) {
88+
if (isExecutable(candidate)) return candidate;
89+
}
8190

8291
// PATH lookup
8392
try {
@@ -97,7 +106,7 @@ export function resolveBrowseBin(): string {
97106
"Tried:",
98107
` - $BROWSE_BIN (${envOverride || "unset"})`,
99108
` - sibling: ${siblingCandidates.join(", ")}`,
100-
` - global: ${globalPath}`,
109+
` - global: ${globalCandidates.join(", ")}`,
101110
" - PATH: `browse`",
102111
"",
103112
"To fix: run gstack setup from the gstack repo:",
@@ -111,6 +120,8 @@ export function resolveBrowseBin(): string {
111120

112121
function isExecutable(p: string): boolean {
113122
try {
123+
const stat = fs.statSync(p);
124+
if (!stat.isFile()) return false;
114125
fs.accessSync(p, fs.constants.X_OK);
115126
return true;
116127
} catch {

make-pdf/src/orchestrator.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export async function generate(opts: GenerateOptions): Promise<string> {
7272
}
7373

7474
const outputPath = path.resolve(
75-
opts.output ?? path.join(os.tmpdir(), `${deriveSlug(input)}.pdf`),
75+
opts.output ?? path.join(defaultOutputDir(), `${deriveSlug(input)}.pdf`),
7676
);
7777

7878
// Stage 1: read markdown
@@ -216,6 +216,13 @@ function tmpFile(ext: string): string {
216216
return path.join(os.tmpdir(), `make-pdf-${process.pid}-${hash}.${ext}`);
217217
}
218218

219+
function defaultOutputDir(): string {
220+
if (process.platform === "darwin" && fs.existsSync("/private/tmp")) {
221+
return "/private/tmp";
222+
}
223+
return os.tmpdir();
224+
}
225+
219226
function tryOpen(pathOrUrl: string): void {
220227
const platform = process.platform;
221228
const cmd = platform === "darwin" ? "open" :

make-pdf/src/setup.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Flow (per the CEO plan CLI UX spec):
55
* 1. Verify browse binary exists and responds
6-
* 2. Verify Chromium launches via $B goto about:blank
6+
* 2. Verify Chromium launches via a dedicated blank tab
77
* 3. Verify pdftotext is installed (warn, don't fail)
88
* 4. Generate a smoke-test PDF from an inline 2-paragraph fixture
99
* 5. Open it
@@ -32,11 +32,11 @@ export async function runSetup(): Promise<void> {
3232
process.exit(4);
3333
}
3434

35-
// 2. Chromium smoke (navigate a dedicated tab to about:blank)
35+
// 2. Chromium smoke (open a dedicated blank tab)
3636
process.stderr.write(" [2/5] Launching Chromium...");
3737
let chromiumTab: number | null = null;
3838
try {
39-
chromiumTab = browseClient.newtab("about:blank");
39+
chromiumTab = browseClient.newtab();
4040
process.stderr.write(` OK (tab ${chromiumTab})\n`);
4141
} catch (err: any) {
4242
process.stderr.write(" FAIL\n");
@@ -78,7 +78,7 @@ export async function runSetup(): Promise<void> {
7878
"",
7979
].join("\n");
8080
const fixturePath = path.join(os.tmpdir(), `make-pdf-smoke-${process.pid}.md`);
81-
const outPath = path.join(os.tmpdir(), `make-pdf-smoke-${process.pid}.pdf`);
81+
const outPath = path.join(defaultOutputDir(), `make-pdf-smoke-${process.pid}.pdf`);
8282
fs.writeFileSync(fixturePath, fixture, "utf8");
8383

8484
try {
@@ -108,3 +108,10 @@ export async function runSetup(): Promise<void> {
108108
"",
109109
].join("\n"));
110110
}
111+
112+
function defaultOutputDir(): string {
113+
if (process.platform === "darwin" && fs.existsSync("/private/tmp")) {
114+
return "/private/tmp";
115+
}
116+
return os.tmpdir();
117+
}

make-pdf/test/browseClient.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
*/
66

77
import { describe, expect, test } from "bun:test";
8+
import * as fs from "node:fs";
9+
import * as os from "node:os";
10+
import * as path from "node:path";
811

912
import { BrowseClientError } from "../src/types";
1013
import { resolveBrowseBin } from "../src/browseClient";
@@ -57,6 +60,36 @@ describe("resolveBrowseBin", () => {
5760
}
5861
}
5962
});
63+
64+
test("does not treat executable directories as binaries", () => {
65+
const originalEnv = process.env.BROWSE_BIN;
66+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "make-pdf-browse-dir-"));
67+
fs.chmodSync(dir, 0o755);
68+
process.env.BROWSE_BIN = dir;
69+
70+
try {
71+
let resolved: string | null = null;
72+
let thrown: any = null;
73+
try {
74+
resolved = resolveBrowseBin();
75+
} catch (err) {
76+
thrown = err;
77+
}
78+
79+
expect(resolved).not.toBe(dir);
80+
if (thrown) {
81+
expect(thrown).toBeInstanceOf(BrowseClientError);
82+
expect(thrown.message).toContain("browse binary not found");
83+
}
84+
} finally {
85+
if (originalEnv === undefined) {
86+
delete process.env.BROWSE_BIN;
87+
} else {
88+
process.env.BROWSE_BIN = originalEnv;
89+
}
90+
fs.rmSync(dir, { recursive: true, force: true });
91+
}
92+
});
6093
});
6194

6295
describe("BrowseClientError", () => {

0 commit comments

Comments
 (0)