-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.ts
More file actions
81 lines (66 loc) · 3.1 KB
/
Copy pathsetup.ts
File metadata and controls
81 lines (66 loc) · 3.1 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
#!/usr/bin/env bun
/**
* setup.ts — One-command install for the sbfbl-router proxy.
*
* Usage:
* bun run ~/Documents/coding_project/sbfbl-router/setup.ts
*
* What it does (idempotent, safe to re-run):
* 1. Detects the SBFBL project directory
* 2. Merges ANTHROPIC_BASE_URL into .claude/settings.local.json (preserves API keys)
* 3. Merges the SessionStart hook into .claude/settings.json (preserves model/permissions)
* 4. Starts the proxy (macOS launchd daemon or background bun process)
* 5. Verifies health — exits 0 on success, non-zero on failure
*
* No sudo, no bashrc edits, no global state changes outside these files.
* The API token stays in .claude/settings.local.json (gitignored).
* The proxy reads config.json (next to proxy.ts) or env vars.
*/
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import { execSync } from "node:child_process";
// ── Configuration ──────────────────────────────────────────────────────────
const PROXY_DIR = import.meta.dirname;
const SBFBL_DIR = "/Users/bubblyducks/SBFBL"; // Update to your SBFBL project dir
const PORT = 3456;
const PLIST_DIR = "/Users/bubblyducks/Library/LaunchAgents";
const PLIST_LABEL = "com.bubblyducks.sbfbl-router";
const BUN_BIN = "/Users/bubblyducks/.bun/bin/bun";
const LOG_DIR = "/Users/bubblyducks/.cache/sbfbl-router";
// ── Helpers ────────────────────────────────────────────────────────────────
function loadJson(path: string): Record<string, unknown> {
if (!existsSync(path)) return {};
return JSON.parse(readFileSync(path, "utf-8"));
}
function saveJson(path: string, data: unknown) {
mkdirSync(new URL(path).pathname.split("/").slice(0, -1).join("/"), {
recursive: true,
});
writeFileSync(path, JSON.stringify(data, null, 2) + "\n");
}
function exec(cmd: string): string {
try {
return execSync(cmd, { stdio: "pipe" }).toString().trim();
} catch {
return "";
}
}
// ── Step 1: Detect SBFBL ───────────────────────────────────────────────────
if (!existsSync(`${SBFBL_DIR}/.claude/`)) {
console.error(
`SBFBL project not found at ${SBFBL_DIR}.`,
);
process.exit(1);
}
console.log(`✓ SBFBL project: ${SBFBL_DIR}`);
// ── Step 2: Merge settings.local.json ──────────────────────────────────────
const localSettingsPath = `${SBFBL_DIR}/.claude/settings.local.json`;
const localCfg = loadJson(localSettingsPath);
const env = (localCfg.env as Record<string, string>) || {};
// Preserve existing ANTHROPIC_AUTH_TOKEN
if (!env.ANTHROPIC_AUTH_TOKEN && process.env.ANTHROPIC_AUTH_TOKEN) {
env.ANTHROPIC_AUTH_TOKEN = process.env.ANTHROPIC_AUTH_TOKEN;
console.log(
`✓ ANTHROPIC_AUTH_TOKEN set in settings.local.json (from env)`,
);
}
env.A