forked from modelstudioai/openwork
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild-wa-worker.ts
More file actions
131 lines (118 loc) · 4.51 KB
/
Copy pathbuild-wa-worker.ts
File metadata and controls
131 lines (118 loc) · 4.51 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
* WhatsApp worker build script.
*
* Bundles the Baileys-backed WhatsApp subprocess into a single CJS file at
* packages/messaging-whatsapp-worker/dist/worker.cjs.
*
* Baileys is bundled INTO the output (not marked external) so the packaged
* app ships a self-contained worker — users don't have to install anything.
* The dynamic import at runtime still works because esbuild resolves literal
* dynamic-import strings at bundle time.
*
* The worker is spawned as a Node subprocess by the WhatsAppAdapter:
* - Electron: re-enters its embedded Node via ELECTRON_RUN_AS_NODE=1.
* - Headless/Bun server: spawns a system `node` binary (Bun cannot run the
* CJS worker because Baileys' crypto deps depend on Node's runtime).
* That's why we emit CJS + platform=node — it must stay Node-compatible.
*/
import { spawn } from "bun";
import { execSync } from "child_process";
import { existsSync, mkdirSync, statSync } from "fs";
import { join } from "path";
/**
* Resolve a short git SHA for the build, suffixed with `+dirty` when the
* working tree has uncommitted changes. Returns `unknown` outside a git
* checkout.
*/
function resolveGitSha(cwd: string): string {
try {
const sha = execSync("git rev-parse --short HEAD", { cwd }).toString().trim();
let dirty = false;
try {
const status = execSync("git status --porcelain", { cwd }).toString().trim();
dirty = status.length > 0;
} catch {
// ignore — treat as clean
}
return dirty ? `${sha}+dirty` : sha;
} catch {
return "unknown";
}
}
const ROOT_DIR = join(import.meta.dir, "..");
const WORKER_DIR = join(ROOT_DIR, "packages/messaging-whatsapp-worker");
const SOURCE = join(WORKER_DIR, "src/worker.ts");
const DIST_DIR = join(WORKER_DIR, "dist");
const OUTPUT = join(DIST_DIR, "worker.cjs");
async function verifyJsFile(filePath: string): Promise<{ valid: boolean; error?: string }> {
if (!existsSync(filePath)) return { valid: false, error: "File does not exist" };
const stats = statSync(filePath);
if (stats.size === 0) return { valid: false, error: "File is empty" };
const proc = spawn({
cmd: ["node", "--check", filePath],
stdout: "pipe",
stderr: "pipe",
});
const stderr = await new Response(proc.stderr).text();
const exitCode = await proc.exited;
if (exitCode !== 0) return { valid: false, error: stderr || "Syntax error" };
return { valid: true };
}
async function main(): Promise<void> {
if (!existsSync(SOURCE)) {
console.error("❌ WhatsApp worker source not found at", SOURCE);
process.exit(1);
}
if (!existsSync(DIST_DIR)) {
mkdirSync(DIST_DIR, { recursive: true });
}
const buildId = new Date().toISOString();
const gitSha = resolveGitSha(ROOT_DIR);
console.log(`📨 Building WhatsApp worker (bundling Baileys) — build ${buildId} (${gitSha})...`);
const proc = spawn({
cmd: [
"bun", "run", "esbuild",
SOURCE,
"--bundle",
"--platform=node",
"--format=cjs",
"--target=node20",
`--outfile=${OUTPUT}`,
// Inject build provenance. The worker logs these on startup so an
// operator can confirm a rebuild actually propagated to the running
// subprocess after `bun run build:wa-worker`.
`--define:__WA_WORKER_BUILD_ID__=${JSON.stringify(buildId)}`,
`--define:__WA_WORKER_GIT_SHA__=${JSON.stringify(gitSha)}`,
// Mark only Electron + Baileys' runtime-optional peers external.
// Baileys itself and all its required transitive deps get bundled.
//
// The three optional deps below are unused by Qwen Code (no link
// previews, no terminal QR, no inline image transforms). Baileys
// guards them with try/catch so they fail silently at runtime.
"--external:electron",
"--external:link-preview-js",
"--external:qrcode-terminal",
"--external:jimp",
],
cwd: ROOT_DIR,
stdout: "inherit",
stderr: "inherit",
});
const exitCode = await proc.exited;
if (exitCode !== 0) {
console.error("❌ WhatsApp worker build failed with exit code", exitCode);
process.exit(exitCode);
}
console.log("🔍 Verifying worker output...");
const verification = await verifyJsFile(OUTPUT);
if (!verification.valid) {
console.error("❌ Worker build verification failed:", verification.error);
process.exit(1);
}
const { size } = statSync(OUTPUT);
console.log(`✅ WhatsApp worker built (${(size / 1024 / 1024).toFixed(2)} MB) → ${OUTPUT}`);
}
main().catch((err) => {
console.error("❌ Unexpected error:", err);
process.exit(1);
});