@open-wa/wa-automate 5.0.0-alpha.8 → 5.0.0
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.
- package/LICENSE.md +2 -2
- package/README.md +1 -1
- package/dist/{cli-runtime-CmpYxFCk.d.cts → cli-runtime-BGz4DzHq.d.cts} +3 -2
- package/dist/cli-runtime-BGz4DzHq.d.cts.map +1 -0
- package/dist/{cli-runtime-CEFbPQ-1.cjs → cli-runtime-BeaP2Kjo.cjs} +61 -17
- package/dist/cli-runtime-BeaP2Kjo.cjs.map +1 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +1 -1
- package/dist/index.cjs +70 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -30
- package/dist/index.d.cts.map +1 -1
- package/package.json +21 -18
- package/dist/cli-runtime-CEFbPQ-1.cjs.map +0 -1
- package/dist/cli-runtime-CmpYxFCk.d.cts.map +0 -1
package/LICENSE.md
CHANGED
|
@@ -55,9 +55,9 @@ Licensor hereby grants permission by this license ("License"), free of charge,
|
|
|
55
55
|
4. Compliance with Human Rights Laws and Human Rights Principles:
|
|
56
56
|
|
|
57
57
|
a. **Human Rights Laws**. The Software shall not be used by any person or entity for any systems, activities, or other uses that violate any applicable laws, regulations, or rules that protect human, civil, labor, privacy, political, environmental, security, economic, due process, or similar rights (the "Human Rights Laws"). Where the Human Rights Laws of more than one jurisdiction are applicable to the use of the Software, the Human Rights Laws that are most protective of the individuals or groups harmed shall apply.
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
b. **Human Rights Principles**. Licensee is advised to consult the articles of the United Nations Universal Declaration of Human Rights (https://www.un.org/en/universal-declaration-human-rights/) and the United Nations Global Compact (https://www.unglobalcompact.org/ what-is-gc/mission/principles) that define recognized principles of international human rights (the "Human Rights Principles"). It is Licensor's express intent that all use of the Software be consistent with Human Rights Principles. If Licensor receives notification or otherwise learns of an alleged violation of any Human Rights Principles relating to Licensee's use of the Software, Licensor may in its discretion and without obligation (i) (a) notify Licensee of such allegation and (b) allow Licensee 90 days from notification under (i)(a) to investigate and respond to Licensor regarding the allegation and (ii) (a) after the earlier of 90 days from notification under (i)(a), or Licensee's response under (i)(b), notify Licensee of License termination and (b) allow Licensee an additional 90 days from notification under (ii)(a) to cease use of the Software.
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
c. **Indemnity**. Licensee shall hold harmless and indemnify Licensor against all losses, damages, liabilities, deficiencies, claims, actions, judgments, settlements, interest, awards, penalties, fines, costs, or expenses of whatever kind, including Licensor's reasonable attorneys' fees, arising out of or relating to Licensee's non-compliance with this License or use of the Software in violation of Human Rights Laws or Human Rights Principles.
|
|
62
62
|
|
|
63
63
|
5. Enforceability: If any portion or provision of this License is determined to be invalid, illegal, or unenforceable by a court of competent jurisdiction, then such invalidity, illegality, or unenforceability shall not affect any other term or provision of this License or invalidate or render unenforceable such term or provision in any other jurisdiction. Upon a determination that any term or provision is invalid, illegal, or unenforceable, to the extent permitted by applicable law, the court may modify this License to affect the original intent of the parties as closely as possible. The section headings are for convenience only and are not intended to affect the construction or interpretation of this License. Any rule of construction to the effect that ambiguities are to be resolved against the drafting party shall not apply in interpreting this License. The language in this License shall be interpreted as to its fair meaning and not strictly for or against any party.
|
package/README.md
CHANGED
|
@@ -29,4 +29,4 @@ For full guides and API reference, visit the [docs site](https://openwa.dev).
|
|
|
29
29
|
|
|
30
30
|
## License
|
|
31
31
|
|
|
32
|
-
[H-DNH
|
|
32
|
+
[H-DNH 1.1](https://github.com/open-wa/wa-automate-nodejs/blob/master/LICENSE.md) — Hippocratic + Do Not Harm
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Client } from "@open-wa/client";
|
|
2
2
|
import { ApiServer } from "@open-wa/api";
|
|
3
3
|
import { Config, PartialConfig } from "@open-wa/config";
|
|
4
|
-
|
|
4
|
+
import "@open-wa/runtime-core";
|
|
5
5
|
//#region src/server/hono-server.d.ts
|
|
6
6
|
declare class WAServer extends ApiServer {
|
|
7
7
|
constructor(config: Config);
|
|
@@ -13,6 +13,7 @@ interface CliRuntimeResult {
|
|
|
13
13
|
client: Client;
|
|
14
14
|
config: Config;
|
|
15
15
|
events: Client['events'];
|
|
16
|
+
dispose(): void;
|
|
16
17
|
}
|
|
17
18
|
interface ParsedCliArgs {
|
|
18
19
|
procName: string;
|
|
@@ -28,4 +29,4 @@ declare function start(parsedArgs?: ParsedCliArgs): Promise<CliRuntimeResult>;
|
|
|
28
29
|
declare function main(argv?: string[]): Promise<CliRuntimeResult | void>;
|
|
29
30
|
//#endregion
|
|
30
31
|
export { WAServer as a, start as i, main as n, parseCliArgs as r, CliRuntimeResult as t };
|
|
31
|
-
//# sourceMappingURL=cli-runtime-
|
|
32
|
+
//# sourceMappingURL=cli-runtime-BGz4DzHq.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-runtime-BGz4DzHq.d.cts","names":["ClientFacade"],"sources":["../src/server/hono-server.ts","../src/cli-runtime.ts"],"mappings":";;;;;cAGa,iBAAiB;EAC5B,YAAY,QAAQ;;;;UCcL;EACb,QAAQ;EACR,QAAQA;EACR,QAAQ;EACR,QAAQA;EACR;;UA8La;EACb;EACA;EACA;EACA;EACA,cAAc;EACd;EACA;;iBA+GY,aAAa,kBAAyC;iBA+OhD,MAAM,aAAY,gBAAiC,QAAQ;iBAuL3D,KAAK,kBAAyC,QAAQ"}
|
|
@@ -15,16 +15,17 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
15
|
}
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
19
19
|
value: mod,
|
|
20
20
|
enumerable: true
|
|
21
21
|
}) : target, mod));
|
|
22
22
|
//#endregion
|
|
23
23
|
let _open_wa_api = require("@open-wa/api");
|
|
24
|
+
let node_path = require("node:path");
|
|
24
25
|
let boxen = require("boxen");
|
|
25
|
-
boxen = __toESM(boxen);
|
|
26
|
+
boxen = __toESM(boxen, 1);
|
|
26
27
|
let qrcode_terminal = require("qrcode-terminal");
|
|
27
|
-
qrcode_terminal = __toESM(qrcode_terminal);
|
|
28
|
+
qrcode_terminal = __toESM(qrcode_terminal, 1);
|
|
28
29
|
let _open_wa_core = require("@open-wa/core");
|
|
29
30
|
let _open_wa_client = require("@open-wa/client");
|
|
30
31
|
let _open_wa_driver_lightpanda = require("@open-wa/driver-lightpanda");
|
|
@@ -32,8 +33,9 @@ let _open_wa_driver_puppeteer = require("@open-wa/driver-puppeteer");
|
|
|
32
33
|
let _open_wa_schema = require("@open-wa/schema");
|
|
33
34
|
let _open_wa_config = require("@open-wa/config");
|
|
34
35
|
let node_fs = require("node:fs");
|
|
35
|
-
let node_path = require("node:path");
|
|
36
36
|
let node_url = require("node:url");
|
|
37
|
+
let _open_wa_runtime_core = require("@open-wa/runtime-core");
|
|
38
|
+
let _open_wa_runtime_node = require("@open-wa/runtime-node");
|
|
37
39
|
//#region src/server/hono-server.ts
|
|
38
40
|
var WAServer = class extends _open_wa_api.ApiServer {
|
|
39
41
|
constructor(config) {
|
|
@@ -324,6 +326,26 @@ function getVal(argv, flag) {
|
|
|
324
326
|
const index = argv.findIndex((arg) => arg === flag);
|
|
325
327
|
return index !== -1 ? argv[index + 1] : void 0;
|
|
326
328
|
}
|
|
329
|
+
function parseSandboxIsolation(value) {
|
|
330
|
+
switch (value) {
|
|
331
|
+
case "worker":
|
|
332
|
+
case "process":
|
|
333
|
+
case "container": return value;
|
|
334
|
+
default: throw new Error(`Invalid --sandbox-isolation value: ${value}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function resolveChatSandboxPolicy(config) {
|
|
338
|
+
if (!config) return void 0;
|
|
339
|
+
const overrides = typeof config === "object" ? config : {};
|
|
340
|
+
return {
|
|
341
|
+
..._open_wa_runtime_core.defaultChatSandboxPolicy,
|
|
342
|
+
...overrides,
|
|
343
|
+
chats: true,
|
|
344
|
+
networkAllowlist: [...overrides.networkAllowlist ?? _open_wa_runtime_core.defaultChatSandboxPolicy.networkAllowlist],
|
|
345
|
+
env: overrides.env === "none" || overrides.env === void 0 ? overrides.env ?? _open_wa_runtime_core.defaultChatSandboxPolicy.env : [...overrides.env],
|
|
346
|
+
capabilities: [...overrides.capabilities ?? _open_wa_runtime_core.defaultChatSandboxPolicy.capabilities]
|
|
347
|
+
};
|
|
348
|
+
}
|
|
327
349
|
function parseCliArgs(argv = process.argv.slice(2)) {
|
|
328
350
|
const cliOverrides = {};
|
|
329
351
|
const unsupportedWarnings = [];
|
|
@@ -348,6 +370,18 @@ function parseCliArgs(argv = process.argv.slice(2)) {
|
|
|
348
370
|
if (argv.includes("--aggressive-garbage-collection")) cliOverrides.aggressiveGarbageCollection = true;
|
|
349
371
|
if (argv.includes("--no-dashboard")) cliOverrides.dashboard = false;
|
|
350
372
|
if (argv.includes("--ephemeral")) cliOverrides.ephemeral = true;
|
|
373
|
+
if (argv.includes("--sandbox-chats")) cliOverrides.sandboxChats = true;
|
|
374
|
+
const sandboxIsolation = getVal(argv, "--sandbox-isolation");
|
|
375
|
+
if (sandboxIsolation) {
|
|
376
|
+
const { chats: _chats, ...sandboxDefaults } = _open_wa_runtime_core.defaultChatSandboxPolicy;
|
|
377
|
+
cliOverrides.sandboxChats = {
|
|
378
|
+
...sandboxDefaults,
|
|
379
|
+
isolation: parseSandboxIsolation(sandboxIsolation),
|
|
380
|
+
networkAllowlist: [..._open_wa_runtime_core.defaultChatSandboxPolicy.networkAllowlist],
|
|
381
|
+
env: _open_wa_runtime_core.defaultChatSandboxPolicy.env === "none" ? "none" : [..._open_wa_runtime_core.defaultChatSandboxPolicy.env],
|
|
382
|
+
capabilities: [..._open_wa_runtime_core.defaultChatSandboxPolicy.capabilities]
|
|
383
|
+
};
|
|
384
|
+
}
|
|
351
385
|
const qrTimeout = getVal(argv, "--qr-timeout");
|
|
352
386
|
if (qrTimeout) cliOverrides.qrTimeout = parseInt(qrTimeout, 10);
|
|
353
387
|
const dashboardPort = getVal(argv, "--dashboard-port");
|
|
@@ -562,6 +596,8 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
562
596
|
level: "info",
|
|
563
597
|
message: "Starting WhatsApp Client..."
|
|
564
598
|
});
|
|
599
|
+
const sandboxPolicy = resolveChatSandboxPolicy(config.sandboxChats);
|
|
600
|
+
const executionSandbox = sandboxPolicy ? (0, _open_wa_runtime_node.makeNodeExecutionSandbox)({ workspacePath: process.cwd() }) : void 0;
|
|
565
601
|
const openwaClient = await (0, _open_wa_core.createClient)({
|
|
566
602
|
sessionId: config.sessionId,
|
|
567
603
|
driver,
|
|
@@ -582,7 +618,10 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
582
618
|
blockCrashLogs: config.blockCrashLogs,
|
|
583
619
|
blockAssets: config.blockAssets,
|
|
584
620
|
safeMode: config.safeMode,
|
|
585
|
-
licenseKey: config.licenseKey
|
|
621
|
+
licenseKey: config.licenseKey,
|
|
622
|
+
sandboxPolicy,
|
|
623
|
+
executionSandbox,
|
|
624
|
+
memoryObservation: (observability, getBrowserProcessId) => (0, _open_wa_runtime_node.observeBrowserProcessMemory)(observability, getBrowserProcessId, { attributes: { session: config.sessionId } })
|
|
586
625
|
});
|
|
587
626
|
server.setReadinessProvider(() => ({
|
|
588
627
|
...openwaClient.getReadiness(),
|
|
@@ -625,7 +664,7 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
625
664
|
publisher.publish(eventName, payload);
|
|
626
665
|
});
|
|
627
666
|
};
|
|
628
|
-
bridgeRuntimeEvents(openwaClient, publishRuntimeEvent);
|
|
667
|
+
const detachRuntimeBridge = bridgeRuntimeEvents(openwaClient, publishRuntimeEvent);
|
|
629
668
|
if (typeof server.setEventBridge === "function") server.setEventBridge({
|
|
630
669
|
onAny: (listener) => {
|
|
631
670
|
runtimeBridgeListeners.add(listener);
|
|
@@ -634,10 +673,19 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
634
673
|
runtimeBridgeListeners.delete(listener);
|
|
635
674
|
}
|
|
636
675
|
});
|
|
637
|
-
|
|
676
|
+
const handleBrowserInit = async () => {
|
|
638
677
|
const page = openwaClient.getTransport().getPage();
|
|
639
678
|
if (page) await server.setPage(page).catch(() => {});
|
|
640
|
-
}
|
|
679
|
+
};
|
|
680
|
+
openwaClient.events.on("launch.browser.init.after", handleBrowserInit);
|
|
681
|
+
let disposed = false;
|
|
682
|
+
const dispose = () => {
|
|
683
|
+
if (disposed) return;
|
|
684
|
+
disposed = true;
|
|
685
|
+
detachRuntimeBridge();
|
|
686
|
+
openwaClient.events.off("launch.browser.init.after", handleBrowserInit);
|
|
687
|
+
runtimeBridgeListeners.clear();
|
|
688
|
+
};
|
|
641
689
|
try {
|
|
642
690
|
await client.start();
|
|
643
691
|
} catch (startError) {
|
|
@@ -659,7 +707,8 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
659
707
|
server,
|
|
660
708
|
client,
|
|
661
709
|
config,
|
|
662
|
-
events: openwaClient.events
|
|
710
|
+
events: openwaClient.events,
|
|
711
|
+
dispose
|
|
663
712
|
};
|
|
664
713
|
}
|
|
665
714
|
const readiness = openwaClient.getReadiness();
|
|
@@ -677,7 +726,8 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
677
726
|
server,
|
|
678
727
|
client,
|
|
679
728
|
config,
|
|
680
|
-
events: openwaClient.events
|
|
729
|
+
events: openwaClient.events,
|
|
730
|
+
dispose
|
|
681
731
|
};
|
|
682
732
|
}
|
|
683
733
|
async function main(argv = process.argv.slice(2)) {
|
|
@@ -747,12 +797,6 @@ Object.defineProperty(exports, "WAServer", {
|
|
|
747
797
|
return WAServer;
|
|
748
798
|
}
|
|
749
799
|
});
|
|
750
|
-
Object.defineProperty(exports, "__toESM", {
|
|
751
|
-
enumerable: true,
|
|
752
|
-
get: function() {
|
|
753
|
-
return __toESM;
|
|
754
|
-
}
|
|
755
|
-
});
|
|
756
800
|
Object.defineProperty(exports, "createConsoleOutputSink", {
|
|
757
801
|
enumerable: true,
|
|
758
802
|
get: function() {
|
|
@@ -796,4 +840,4 @@ Object.defineProperty(exports, "start", {
|
|
|
796
840
|
}
|
|
797
841
|
});
|
|
798
842
|
|
|
799
|
-
//# sourceMappingURL=cli-runtime-
|
|
843
|
+
//# sourceMappingURL=cli-runtime-BeaP2Kjo.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-runtime-BeaP2Kjo.cjs","names":["ApiServer","eventRegistry","resolve","existsSync","readFileSync","dirname","defaultChatSandboxPolicy","LightpandaDriver","PuppeteerDriver","resolveConfig","makeNodeExecutionSandbox","createClient","observeBrowserProcessMemory","ClientFacade","fileURLToPath"],"sources":["../src/server/hono-server.ts","../src/cli/output-sink.ts","../src/cli-runtime.ts"],"sourcesContent":["import { ApiServer, createApiServer as createSharedApiServer } from '@open-wa/api';\nimport type { Config } from '@open-wa/config';\n\nexport class WAServer extends ApiServer {\n constructor(config: Config) {\n super({ config });\n }\n}\n\nexport function createApiServer(config: Config) {\n return createSharedApiServer(config);\n}\n","import boxen from 'boxen';\nimport qrcode from 'qrcode-terminal';\n\nexport type CliOutputLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface CliOutputEntry {\n level: CliOutputLevel;\n message: string;\n meta?: Record<string, unknown>;\n}\n\nexport interface CliStatusUpdate {\n phase:\n | 'boot'\n | 'config.resolved'\n | 'server.starting'\n | 'server.started'\n | 'client.starting'\n | 'launch.auth'\n | 'launch.helpers'\n | 'launch.patch'\n | 'launch.license'\n | 'launch.finalize'\n | 'auth.qr'\n | 'client.ready'\n | 'shutdown.starting'\n | 'shutdown.complete'\n | 'error';\n detail?: string;\n sessionId?: string;\n}\n\nexport interface CliQrPayload {\n qr: string;\n sessionId: string;\n}\n\nexport interface CliOutputSink {\n write(entry: CliOutputEntry): void;\n status(update: CliStatusUpdate): void;\n qr(payload: CliQrPayload): void;\n}\n\nfunction writeToConsole(level: CliOutputLevel, message: string): void {\n if (level === 'warn') {\n console.warn(message);\n return;\n }\n\n if (level === 'error') {\n console.error(message);\n return;\n }\n\n console.log(message);\n}\n\nexport function createConsoleOutputSink(): CliOutputSink {\n return {\n write(entry) {\n writeToConsole(entry.level, entry.message);\n },\n status() {\n // Status updates are semantic signals for richer presenters.\n },\n qr(payload) {\n qrcode.generate(payload.qr, { small: true }, (terminalQrCode) => {\n console.log(\n boxen(terminalQrCode, {\n title: payload.sessionId,\n padding: 1,\n titleAlignment: 'center',\n })\n );\n });\n },\n };\n}\n\nlet activeSink: CliOutputSink = createConsoleOutputSink();\n\nexport function getCliOutputSink(): CliOutputSink {\n return activeSink;\n}\n\nexport function setCliOutputSink(sink: CliOutputSink): void {\n activeSink = sink;\n}\n\nexport function resetCliOutputSink(): void {\n activeSink = createConsoleOutputSink();\n}\n","import { createClient } from '@open-wa/core';\nimport { Client as ClientFacade } from '@open-wa/client';\nimport { LightpandaDriver } from '@open-wa/driver-lightpanda';\nimport { PuppeteerDriver } from '@open-wa/driver-puppeteer';\nimport { eventRegistry } from '@open-wa/schema';\nimport { WAServer } from './server/hono-server';\nimport { resolveConfig, type PartialConfig, type Config, type TrackedConfig } from '@open-wa/config';\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { getCliOutputSink, type CliOutputSink } from './cli/output-sink';\nimport {\n defaultChatSandboxPolicy,\n type ChatSandboxPolicy,\n type SandboxIsolation,\n} from '@open-wa/runtime-core';\nimport { makeNodeExecutionSandbox, observeBrowserProcessMemory } from '@open-wa/runtime-node';\n\nexport interface CliRuntimeResult {\n server: WAServer;\n client: ClientFacade;\n config: Config;\n events: ClientFacade['events'];\n dispose(): void;\n}\n\ninterface RuntimeEventPublisher {\n publish(eventName: string, payload: any): void;\n}\n\nfunction bridgeRuntimeEvents(\n openwaClient: Awaited<ReturnType<typeof createClient>>,\n publishRuntimeEvent: (eventName: string, payload: any) => void,\n): () => void {\n if (typeof openwaClient.events.onAny === 'function' && typeof openwaClient.events.offAny === 'function') {\n openwaClient.events.onAny(publishRuntimeEvent);\n return () => openwaClient.events.offAny(publishRuntimeEvent);\n }\n\n const unsubscribers: Array<() => void> = [];\n const bridgedEvents = new Set(eventRegistry.getAll().map((def) => def.meta.eventName));\n\n bridgedEvents.forEach((eventName) => {\n const handler = (payload: any) => publishRuntimeEvent(eventName, payload);\n openwaClient.events.on(eventName as never, handler);\n unsubscribers.push(() => openwaClient.events.off(eventName as never, handler));\n });\n\n return () => {\n unsubscribers.forEach((unsubscribe) => unsubscribe());\n };\n}\n\nfunction attachLaunchNarration(\n openwaClient: Awaited<ReturnType<typeof createClient>>,\n sink: CliOutputSink,\n sessionId: string,\n): () => void {\n const unsubscribers: Array<() => void> = [];\n const on = (event: string, handler: (...args: any[]) => void) => {\n openwaClient.events.on(event as never, handler);\n unsubscribers.push(() => openwaClient.events.off(event as never, handler));\n };\n\n on('launch.auth.check.before', (event: any) => {\n sink.status({ phase: 'launch.auth', sessionId, detail: `Authenticating (timeout=${event?.details?.timeoutMs ?? '∞'}ms)` });\n });\n\n on('launch.auth.check.after', (event: any) => {\n sink.write({ level: 'info', message: `Auth detected via ${event?.details?.method ?? 'unknown'} (${event?.details?.isAuthenticated ? 'authenticated' : 'not-authenticated'})` });\n });\n\n on('launch.helper.pre_api.before', (event: any) => {\n sink.status({ phase: 'launch.helpers', sessionId, detail: event?.details?.mode === 'scripts' ? 'Injecting pre-API helper assets...' : 'Pre-API helpers already present' });\n });\n\n on('launch.helper.pre_api.after', (event: any) => {\n sink.write({ level: 'info', message: `Pre-API helper phase complete (${event?.details?.mode ?? 'unknown'})` });\n });\n\n on('internal_launch_progress', (event: any) => {\n const prefix = (event?.value || event?.value === 0) ? `${event.value}% ` : '';\n if (event?.text) sink.write({ level: 'info', message: `${prefix}${event.text}`.trim() });\n });\n\n on('critical_internal_message', (event: any) => {\n sink.write({ level: 'warn', message: `Internal launch message: ${event?.text ?? event?.value ?? 'unknown'}` });\n });\n\n on('launch.patch.init.before', (event: any) => {\n if (event?.details?.phase === 'preload') {\n sink.status({ phase: 'launch.patch', sessionId, detail: 'Downloading patches from CDN...' });\n }\n });\n\n on('launch.patch.init.after', (event: any) => {\n if (event?.details?.phase === 'preload') {\n const source = event?.details?.source ?? 'unknown';\n const tag = event?.details?.tag ? ` tag=${event.details.tag}` : '';\n const available = Array.isArray(event?.details?.available) ? event.details.available.length : 0;\n const durationMs = event?.durationMs;\n const durationStr = durationMs ? ` in ${(durationMs / 1000).toFixed(2)}s` : '';\n sink.write({ level: 'info', message: `Downloaded patches${durationStr} (source=${source}, available=${available}${tag})` });\n }\n });\n\n on('patch.init.before', (event: any) => {\n if (event?.details?.phase === 'apply') {\n const count = Array.isArray(event?.details?.patchIds) ? event.details.patchIds.length : 0;\n sink.status({ phase: 'launch.patch', sessionId, detail: `Installing patches (${count} queued)` });\n }\n });\n\n on('patch.apply.before', (event: any) => {\n sink.status({ phase: 'launch.patch', sessionId, detail: `Applying patch: ${event?.details?.patchId ?? 'unknown'}` });\n });\n\n on('patch.apply.after', (event: any) => {\n sink.write({\n level: event?.details?.outcome === 'failed' ? 'warn' : 'info',\n message: `Patch ${event?.details?.patchId ?? 'unknown'}: ${event?.details?.outcome ?? (event?.details?.applied ? 'applied' : 'unknown')}${event?.details?.detail ? ` — ${event.details.detail}` : ''}`,\n });\n });\n\n on('patch.init.after', (event: any) => {\n if (event?.details?.phase === 'apply') {\n const applied = Array.isArray(event?.details?.applied) ? event.details.applied : [];\n const outcome = event?.details?.outcome ?? 'unknown';\n const tag = applied.length > 0 ? applied.join(', ') : outcome;\n const durationMs = event?.durationMs;\n const durationStr = durationMs ? ` in ${(durationMs / 1000).toFixed(2)}s` : '';\n sink.write({\n level: event?.details?.blockingFailure ? 'warn' : 'info',\n message: `Patches Installed${durationStr}: ${tag}`,\n });\n }\n });\n\n on('launch.patch.integrity.before', () => {\n sink.status({ phase: 'launch.patch', sessionId, detail: 'Validating patched runtime...' });\n });\n\n on('launch.patch.integrity.after', (event: any) => {\n sink.write({ level: event?.details?.usable ? 'info' : 'warn', message: `Patched runtime validation: ${event?.details?.usable ? 'usable' : 'failed'}${event?.details?.failureReason ? ` (${event.details.failureReason})` : ''}` });\n });\n\n on('launch.license.check.before', (event: any) => {\n sink.status({ phase: 'launch.license', sessionId, detail: `Checking license (${event?.details?.source ?? 'local'})` });\n });\n\n on('launch.license.check.after', (event: any) => {\n sink.write({ level: event?.details?.blockingFailure ? 'warn' : 'info', message: `License check: ${event?.details?.status ?? 'unknown'}${event?.details?.detail ? ` — ${event.details.detail}` : ''}` });\n });\n\n on('license.inject.before', () => {\n sink.status({ phase: 'launch.license', sessionId, detail: 'Injecting license payload...' });\n });\n\n on('license.inject.after', (event: any) => {\n sink.write({ level: event?.details?.success ? 'info' : 'warn', message: `License inject: ${event?.details?.success ? 'success' : 'failed'}${event?.details?.status ? ` (${event.details.status})` : ''}${event?.details?.detail ? ` — ${event.details.detail}` : ''}` });\n });\n\n on('launch.auth.qr.generated', (event: any) => {\n const qrIndex = event?.details?.qrIndex ?? '?';\n sink.status({ phase: 'launch.auth', sessionId, detail: `QR code generated (#${qrIndex})` });\n });\n\n on('launch.client.finalize.before', (event: any) => {\n sink.status({ phase: 'launch.finalize', sessionId, detail: `Finalizing session (${event?.details?.validationStage ?? 'unknown'})` });\n });\n\n on('launch.client.finalize.after', (event: any) => {\n sink.write({ level: event?.details?.success ? 'info' : 'warn', message: `Finalize: ${event?.details?.outcome ?? 'unknown'}${event?.details?.detail ? ` — ${event.details.detail}` : ''}` });\n });\n\n on('error', (event: any) => {\n const scope = event?.scope ?? 'unknown';\n const msg = event?.error?.message ?? String(event?.error ?? 'unknown error');\n const fatal = event?.fatal ? ' [FATAL]' : '';\n sink.write({ level: 'error', message: `${fatal} ${scope}: ${msg}`.trim() });\n });\n\n return () => {\n unsubscribers.forEach((unsubscribe) => unsubscribe());\n };\n}\n\nconst CHROME_CACHE_FILE = resolve(process.cwd(), '.open-wa', 'chrome-executable-path.json');\n\ninterface ChromePathCacheRecord {\n executablePath?: string;\n updatedAt?: string;\n}\n\ninterface ExecutablePathResolution {\n executablePath?: string;\n source:\n | 'config'\n | 'cache'\n | 'chrome_installation'\n | 'driver_default'\n | 'lightpanda_config'\n | 'lightpanda_sdk_default';\n warning?: string;\n}\n\ninterface DriverSelection {\n driver: PuppeteerDriver | LightpandaDriver;\n engineLabel: 'Puppeteer' | 'Lightpanda';\n executableResolution: ExecutablePathResolution;\n preferLocalChrome: boolean;\n}\n\nexport interface ParsedCliArgs {\n procName: string;\n pm2: boolean;\n forwardedArgs: string[];\n configPath?: string;\n cliOverrides: PartialConfig;\n verbose: boolean;\n unsupportedWarnings: string[];\n}\n\nexport function getChromeCacheFilePath(): string {\n return CHROME_CACHE_FILE;\n}\n\nexport function readChromePathCache(cacheFilePath: string = getChromeCacheFilePath()): ChromePathCacheRecord | undefined {\n if (!existsSync(cacheFilePath)) {\n return undefined;\n }\n\n try {\n const raw = JSON.parse(readFileSync(cacheFilePath, 'utf8')) as ChromePathCacheRecord;\n if (!raw || typeof raw !== 'object') {\n return undefined;\n }\n\n return {\n executablePath: typeof raw.executablePath === 'string' ? raw.executablePath : undefined,\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : undefined,\n };\n } catch {\n return undefined;\n }\n}\n\nexport function writeChromePathCache(executablePath: string, cacheFilePath: string = getChromeCacheFilePath()): void {\n mkdirSync(dirname(cacheFilePath), { recursive: true });\n writeFileSync(cacheFilePath, JSON.stringify({ executablePath, updatedAt: new Date().toISOString() }, null, 2), 'utf8');\n}\n\nexport function clearChromePathCache(cacheFilePath: string = getChromeCacheFilePath()): void {\n if (existsSync(cacheFilePath)) {\n rmSync(cacheFilePath, { force: true });\n }\n}\n\nexport function isUsableExecutablePath(executablePath?: string): executablePath is string {\n return !!executablePath && existsSync(executablePath);\n}\n\nfunction getExplicitUseChromePreference(rawConfigs?: TrackedConfig['rawConfigs']): boolean | undefined {\n const explicitConfigSources = [\n rawConfigs?.file,\n rawConfigs?.env,\n rawConfigs?.cli,\n rawConfigs?.programmatic,\n ];\n\n for (let index = explicitConfigSources.length - 1; index >= 0; index -= 1) {\n const source = explicitConfigSources[index];\n if (source?.useChrome !== undefined) {\n return source.useChrome;\n }\n }\n\n return undefined;\n}\n\nexport function shouldPreferLocalChrome(config: Config, rawConfigs?: TrackedConfig['rawConfigs']): boolean {\n if (config.useLightpanda) {\n return false;\n }\n\n if (config.executablePath) {\n return false;\n }\n\n const explicitUseChrome = getExplicitUseChromePreference(rawConfigs);\n if (explicitUseChrome !== undefined) {\n return explicitUseChrome;\n }\n\n return true;\n}\n\nfunction getVal(argv: string[], flag: string): string | undefined {\n const index = argv.findIndex(arg => arg === flag);\n return index !== -1 ? argv[index + 1] : undefined;\n}\n\nfunction parseSandboxIsolation(value: string): SandboxIsolation {\n switch (value) {\n case 'worker':\n case 'process':\n case 'container':\n return value;\n default:\n throw new Error(`Invalid --sandbox-isolation value: ${value}`);\n }\n}\n\nexport function resolveChatSandboxPolicy(config: Config['sandboxChats']): ChatSandboxPolicy | undefined {\n if (!config) return undefined;\n\n const overrides = typeof config === 'object'\n ? config as Partial<Omit<ChatSandboxPolicy, 'chats'>>\n : {};\n return {\n ...defaultChatSandboxPolicy,\n ...overrides,\n chats: true,\n networkAllowlist: [...(overrides.networkAllowlist ?? defaultChatSandboxPolicy.networkAllowlist)],\n env: overrides.env === 'none' || overrides.env === undefined\n ? overrides.env ?? defaultChatSandboxPolicy.env\n : [...overrides.env],\n capabilities: [...(overrides.capabilities ?? defaultChatSandboxPolicy.capabilities)],\n };\n}\n\nexport function parseCliArgs(argv: string[] = process.argv.slice(2)): ParsedCliArgs {\n const cliOverrides: PartialConfig = {};\n const unsupportedWarnings: string[] = [];\n let verbose = false;\n let configPath: string | undefined;\n\n const sessionId = getVal(argv, '--session-id') || 'session';\n cliOverrides.sessionId = sessionId;\n\n const portValue = getVal(argv, '--port') || getVal(argv, '-p');\n if (portValue) cliOverrides.port = parseInt(portValue, 10);\n\n const host = getVal(argv, '--host') || getVal(argv, '-h');\n if (host) cliOverrides.host = host;\n\n const apiKey = getVal(argv, '--api-key') || getVal(argv, '--key') || getVal(argv, '-k');\n if (apiKey) cliOverrides.apiKey = apiKey;\n\n const logLevel = getVal(argv, '--log-level');\n if (logLevel) cliOverrides.logLevel = logLevel;\n\n if (argv.includes('--no-ezqr')) cliOverrides.ezqr = false;\n if (argv.includes('--headful')) cliOverrides.headless = false;\n if (argv.includes('--headless')) cliOverrides.headless = true;\n if (argv.includes('--use-chrome')) cliOverrides.useChrome = true;\n if (argv.includes('--use-lightpanda')) cliOverrides.useLightpanda = true;\n if (argv.includes('--log-console')) cliOverrides.logConsole = true;\n if (argv.includes('--aggressive-garbage-collection')) cliOverrides.aggressiveGarbageCollection = true;\n if (argv.includes('--no-dashboard')) cliOverrides.dashboard = false;\n if (argv.includes('--ephemeral')) cliOverrides.ephemeral = true;\n if (argv.includes('--sandbox-chats')) cliOverrides.sandboxChats = true;\n\n const sandboxIsolation = getVal(argv, '--sandbox-isolation');\n if (sandboxIsolation) {\n const { chats: _chats, ...sandboxDefaults } = defaultChatSandboxPolicy;\n cliOverrides.sandboxChats = {\n ...sandboxDefaults,\n isolation: parseSandboxIsolation(sandboxIsolation),\n networkAllowlist: [...defaultChatSandboxPolicy.networkAllowlist],\n env: defaultChatSandboxPolicy.env === 'none'\n ? 'none'\n : [...defaultChatSandboxPolicy.env],\n capabilities: [...defaultChatSandboxPolicy.capabilities],\n };\n }\n\n const qrTimeout = getVal(argv, '--qr-timeout');\n if (qrTimeout) cliOverrides.qrTimeout = parseInt(qrTimeout, 10);\n\n const dashboardPort = getVal(argv, '--dashboard-port');\n if (dashboardPort) cliOverrides.dashboardPort = parseInt(dashboardPort, 10);\n\n const licenseKey = getVal(argv, '--license-key') || getVal(argv, '-l');\n if (licenseKey) cliOverrides.licenseKey = licenseKey;\n\n const webhook = getVal(argv, '--webhook') || getVal(argv, '-w');\n if (webhook) cliOverrides.webhook = webhook;\n\n const proxyHost = getVal(argv, '--proxy-host');\n if (proxyHost) cliOverrides.proxyHost = proxyHost;\n\n const proxyToken = getVal(argv, '--proxy-token');\n if (proxyToken) cliOverrides.proxyToken = proxyToken;\n\n const readyWebhook = getVal(argv, '--ready-webhook');\n if (readyWebhook) unsupportedWarnings.push(`--ready-webhook was provided (${readyWebhook}) but ready-webhook delivery is not yet wired in v5 CLI.`);\n\n if (argv.includes('-v') || argv.includes('--verbose')) {\n verbose = true;\n cliOverrides.disableSpins = true;\n cliOverrides.logConsole = true;\n cliOverrides.logLevel = cliOverrides.logLevel || 'debug';\n }\n\n if (argv.includes('--stats')) {\n unsupportedWarnings.push('--stats was provided but swagger-stats parity is not restored; v5 only exposes the compatibility redirect/deprecation route.');\n }\n\n if (argv.includes('--generate-api-docs')) {\n unsupportedWarnings.push('--generate-api-docs was provided; runtime docs are always served from the shared API layer and no separate collection generation step currently runs at CLI boot.');\n }\n\n if (argv.includes('--tunnel')) {\n unsupportedWarnings.push('--tunnel was provided but tunnel setup parity is not yet restored in the v5 CLI.');\n }\n\n if (argv.includes('--chatwoot-url') || argv.includes('--twilio-webhook') || argv.includes('--bot-press-url')) {\n unsupportedWarnings.push('Legacy integration flags (Chatwoot/Twilio/BotPress) were provided but are not yet wired into the v5 CLI boot path.');\n }\n\n if (argv.includes('--use-session-id-in-path')) {\n cliOverrides.sessionId = sessionId;\n }\n\n configPath = getVal(argv, '--config') || getVal(argv, '-c');\n\n const procName = getVal(argv, '--name') || sessionId || '@OPEN-WA EASY API';\n const pm2 = argv.includes('--pm2');\n\n return {\n procName,\n pm2,\n forwardedArgs: argv,\n configPath,\n cliOverrides,\n verbose,\n unsupportedWarnings,\n };\n}\n\nexport async function resolveExecutablePath(\n config: Config,\n options: {\n preferLocalChrome?: boolean;\n cacheFilePath?: string;\n } = {}\n): Promise<ExecutablePathResolution> {\n if (config.executablePath) {\n return {\n executablePath: config.executablePath,\n source: 'config',\n };\n }\n\n const preferLocalChrome = options.preferLocalChrome ?? config.useChrome;\n if (!preferLocalChrome) {\n return {\n source: 'driver_default',\n };\n }\n\n const cacheFilePath = options.cacheFilePath ?? getChromeCacheFilePath();\n const cachedPath = readChromePathCache(cacheFilePath)?.executablePath;\n if (isUsableExecutablePath(cachedPath)) {\n return {\n executablePath: cachedPath,\n source: 'cache',\n };\n }\n\n if (cachedPath) {\n clearChromePathCache(cacheFilePath);\n }\n\n const { Launcher } = await import('chrome-launcher');\n const detectedPath = Launcher.getInstallations().find((installationPath) => isUsableExecutablePath(installationPath));\n\n if (detectedPath) {\n writeChromePathCache(detectedPath, cacheFilePath);\n return {\n executablePath: detectedPath,\n source: 'chrome_installation',\n };\n }\n\n clearChromePathCache(cacheFilePath);\n\n return {\n source: 'driver_default',\n warning: 'Chrome resolution warning: no valid local Chrome installation was found. Falling back to Puppeteer/default driver browser resolution.',\n };\n}\n\nfunction resolveLightpandaExecutablePath(config: Config): ExecutablePathResolution {\n const executablePath = config.lightpanda?.executablePath;\n if (executablePath) {\n return {\n executablePath,\n source: 'lightpanda_config',\n };\n }\n\n return {\n source: 'lightpanda_sdk_default',\n };\n}\n\nasync function selectRuntimeDriver(config: Config, rawConfigs?: TrackedConfig['rawConfigs']): Promise<DriverSelection> {\n if (config.useLightpanda) {\n return {\n driver: new LightpandaDriver(),\n engineLabel: 'Lightpanda',\n executableResolution: resolveLightpandaExecutablePath(config),\n preferLocalChrome: false,\n };\n }\n\n const preferLocalChrome = shouldPreferLocalChrome(config, rawConfigs);\n const executableResolution = await resolveExecutablePath(config, { preferLocalChrome });\n\n return {\n driver: new PuppeteerDriver(),\n engineLabel: 'Puppeteer',\n executableResolution,\n preferLocalChrome,\n };\n}\n\nfunction printStartupSummary(\n config: Config,\n resolution: ExecutablePathResolution,\n preferLocalChrome: boolean,\n engineLabel: DriverSelection['engineLabel'],\n) {\n const host = config.host.includes('http') ? config.host : `http://${config.host}`;\n const sink = getCliOutputSink();\n sink.write({ level: 'info', message: `Easy API session: ${config.sessionId}` });\n sink.write({ level: 'info', message: `Health: ${host}:${config.port}/health` });\n sink.write({ level: 'info', message: `API Explorer: ${host}:${config.port}/api-docs/` });\n sink.write({ level: 'info', message: `Swagger JSON: ${host}:${config.port}/meta/swagger.json` });\n sink.write({ level: 'info', message: `Postman JSON: ${host}:${config.port}/meta/postman.json` });\n sink.write({ level: 'info', message: `Browser mode: ${config.headless ? 'headless' : 'headful'}` });\n sink.write({ level: 'info', message: `Browser engine: ${engineLabel}` });\n if (config.dashboard) {\n sink.write({ level: 'info', message: `Dashboard: ${host}:${config.port}/dashboard/` });\n } else {\n sink.write({ level: 'info', message: 'Dashboard: disabled (--no-dashboard)' });\n }\n if (resolution.source === 'lightpanda_config' && resolution.executablePath) {\n sink.write({ level: 'info', message: `Browser executable: explicit Lightpanda override (${resolution.executablePath})` });\n } else if (resolution.source === 'lightpanda_sdk_default') {\n sink.write({ level: 'info', message: 'Browser executable: Lightpanda SDK managed executable (shared cache/default resolution)' });\n } else if (resolution.source === 'config' && config.executablePath) {\n sink.write({ level: 'info', message: `Browser executable: explicit override (${config.executablePath})` });\n } else if (resolution.source === 'cache') {\n sink.write({ level: 'info', message: `Browser executable: local Chrome from cache (${resolution.executablePath})` });\n } else if (resolution.source === 'chrome_installation') {\n sink.write({ level: 'info', message: `Browser executable: local Chrome detected (${resolution.executablePath})` });\n } else if (preferLocalChrome) {\n sink.write({ level: 'info', message: 'Browser executable: driver default fallback (local Chrome unavailable)' });\n }\n if (config.webhook) {\n sink.write({\n level: 'warn',\n message: `Webhook configured (${config.webhook}) but v5 CLI webhook registration parity is not yet restored.`,\n });\n }\n}\n\nexport async function start(parsedArgs: ParsedCliArgs = parseCliArgs()): Promise<CliRuntimeResult> {\n const { configPath, cliOverrides, verbose, unsupportedWarnings } = parsedArgs;\n const sink = getCliOutputSink();\n\n sink.status({ phase: 'boot' });\n\n const { config, sources, configFilePath, rawConfigs } = await resolveConfig({\n configPath,\n cliOverrides: {\n disableSpins: true,\n apiLifecycle: 'hybrid',\n host: '0.0.0.0',\n port: 8002,\n ...cliOverrides,\n },\n includeRawConfigs: true,\n verbose,\n });\n\n if (verbose) {\n sink.write({ level: 'info', message: `Config sources: ${sources.join(', ')}` });\n if (configFilePath) {\n sink.write({ level: 'info', message: `Config file: ${configFilePath}` });\n }\n }\n\n sink.status({ phase: 'config.resolved', sessionId: config.sessionId });\n\n unsupportedWarnings.forEach((warning) => sink.write({ level: 'warn', message: `Compatibility warning: ${warning}` }));\n\n const driverSelection = await selectRuntimeDriver(config, rawConfigs);\n const { driver, engineLabel, executableResolution, preferLocalChrome } = driverSelection;\n if (executableResolution.warning) {\n sink.write({ level: 'warn', message: executableResolution.warning });\n }\n\n const server = new WAServer(config);\n sink.status({ phase: 'server.starting', sessionId: config.sessionId });\n\n await server.start();\n sink.status({ phase: 'server.started', sessionId: config.sessionId });\n\n printStartupSummary(config, executableResolution, preferLocalChrome, engineLabel);\n\n sink.status({ phase: 'client.starting', sessionId: config.sessionId });\n sink.write({ level: 'info', message: 'Starting WhatsApp Client...' });\n\n const sandboxPolicy = resolveChatSandboxPolicy(config.sandboxChats);\n const executionSandbox = sandboxPolicy\n ? makeNodeExecutionSandbox({ workspacePath: process.cwd() })\n : undefined;\n\n const openwaClient = await createClient({\n sessionId: config.sessionId,\n driver,\n deleteSessionDataOnLogout: config.deleteSessionDataOnLogout,\n killClientOnLogout: config.killClientOnLogout,\n sessionDataPath: config.sessionDataPath,\n debug: config.logLevel === 'debug' || verbose || config.logConsole,\n headless: config.headless,\n qrTimeoutMs: typeof config.qrTimeout === 'number' ? config.qrTimeout * 1000 : undefined,\n authTimeoutMs: typeof config.authTimeout === 'number' ? config.authTimeout * 1000 : undefined,\n executablePath: executableResolution.executablePath,\n browserArgs: config.chromiumArgs,\n allowDangerousBrowserArgs: config.allowDangerousBrowserArgs,\n userDataDir: config.userDataDir,\n ephemeral: config.ephemeral,\n logConsole: config.logConsole,\n logConsoleErrors: config.logConsoleErrors,\n blockCrashLogs: config.blockCrashLogs,\n blockAssets: config.blockAssets,\n safeMode: config.safeMode,\n licenseKey: config.licenseKey as any,\n sandboxPolicy,\n executionSandbox,\n memoryObservation: (observability, getBrowserProcessId) => observeBrowserProcessMemory(\n observability,\n getBrowserProcessId,\n { attributes: { session: config.sessionId } },\n ),\n });\n\n server.setReadinessProvider(() => ({ ...openwaClient.getReadiness(), state: openwaClient.getState() }));\n const detachLaunchNarration = attachLaunchNarration(openwaClient, sink, config.sessionId);\n\n openwaClient.events.on('launch.auth.qr.generated', (event) => {\n const qr = event.details?.qr;\n if (!qr) {\n return;\n }\n\n if (config.qrLogSkip) {\n sink.write({ level: 'info', message: 'New QR Code generated. Not printing in console because qrLogSkip is set to true' });\n } else {\n sink.status({ phase: 'auth.qr', sessionId: config.sessionId, detail: 'QR code generated' });\n sink.qr({ qr, sessionId: config.sessionId });\n }\n\n server.setQR(qr);\n });\n\n const client = new ClientFacade({\n client: openwaClient,\n transport: openwaClient.getTransport(),\n });\n\n server.setClient(client as any);\n\n const runtimeBridgeListeners = new Set<(event: string, value: any) => void>();\n const runtimeEventPublishers: RuntimeEventPublisher[] = [\n {\n publish: (eventName, payload) => {\n runtimeBridgeListeners.forEach((listener) => {\n listener(eventName, payload);\n });\n },\n },\n ];\n\n const publishRuntimeEvent = (eventName: string, payload: any) => {\n runtimeEventPublishers.forEach((publisher) => {\n publisher.publish(eventName, payload);\n });\n };\n\n const detachRuntimeBridge = bridgeRuntimeEvents(openwaClient, publishRuntimeEvent);\n\n // Bridge core events to the socket manager so dashboard clients\n // receive live events when they send register_ev.\n if (typeof server.setEventBridge === 'function') {\n server.setEventBridge({\n onAny: (listener: (event: string, value: any) => void) => {\n runtimeBridgeListeners.add(listener);\n },\n offAny: (listener: (event: string, value: any) => void) => {\n runtimeBridgeListeners.delete(listener);\n },\n });\n }\n\n const handleBrowserInit = async () => {\n const page = openwaClient.getTransport().getPage();\n if (page) {\n await server.setPage(page as any).catch(() => {});\n }\n };\n openwaClient.events.on('launch.browser.init.after', handleBrowserInit);\n\n let disposed = false;\n const dispose = () => {\n if (disposed) return;\n disposed = true;\n detachRuntimeBridge();\n openwaClient.events.off('launch.browser.init.after', handleBrowserInit);\n runtimeBridgeListeners.clear();\n };\n\n try {\n await client.start();\n } catch (startError) {\n const msg = startError instanceof Error ? startError.message : String(startError);\n sink.write({ level: 'error', message: `Bootstrap failed: ${msg}` });\n sink.write({ level: 'warn', message: 'Session kept alive for debugging. Browser page is still open.' });\n sink.write({ level: 'warn', message: 'The server is running — use /health and /api-docs to inspect state.' });\n detachLaunchNarration();\n return { server, client, config, events: openwaClient.events, dispose };\n }\n\n const readiness = openwaClient.getReadiness();\n sink.status({\n phase: 'client.ready',\n sessionId: config.sessionId,\n detail: `status=${readiness.status}, exposureSafe=${readiness.exposureSafe}`,\n });\n sink.write({\n level: 'info',\n message: `WhatsApp Client ready with state: ${client.getState()} (status=${readiness.status}, exposureSafe=${readiness.exposureSafe})`,\n });\n\n detachLaunchNarration();\n return { server, client, config, events: openwaClient.events, dispose };\n}\n\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<CliRuntimeResult | void> {\n const parsedArgs = parseCliArgs(argv);\n\n if (parsedArgs.pm2) {\n const { spawn } = require('child_process');\n const sink = getCliOutputSink();\n const pm2Command = getPm2Command();\n const pm2SpawnOptions = getPm2SpawnOptions();\n try {\n const pm2 = spawn(pm2Command, ['--version'], pm2SpawnOptions);\n\n await new Promise<void>((resolve, reject) => {\n pm2.on('error', reject);\n pm2.on('exit', (code: number | null) => {\n if (code === 0) {\n resolve();\n return;\n }\n\n reject(new Error(`pm2 probe exited with code ${code ?? 'unknown'}`));\n });\n pm2.stdout.on('data', () => resolve());\n });\n\n const pm2Flags = parsedArgs.forwardedArgs.filter((flag: string) => flag !== '--pm2');\n const cliPath = fileURLToPath(new URL('../dist/cli.cjs', import.meta.url));\n\n spawn(pm2Command, [\n 'start',\n cliPath,\n '--name', parsedArgs.procName,\n '--stop-exit-codes', '88',\n '--',\n ...pm2Flags,\n ], {\n ...pm2SpawnOptions,\n stdio: 'inherit',\n detached: true\n });\n return;\n } catch (error: unknown) {\n if (isCommandNotFoundError(error)) {\n sink.write({ level: 'error', message: 'pm2 not found. Please install with: npm install -g pm2' });\n return;\n }\n throw error;\n }\n }\n\n return await start(parsedArgs);\n}\n\nfunction getPm2Command(): string {\n return process.platform === 'win32' ? 'pm2.cmd' : 'pm2';\n}\n\nfunction getPm2SpawnOptions(): { shell: boolean } {\n return { shell: process.platform === 'win32' };\n}\n\nfunction isCommandNotFoundError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) {\n return false;\n }\n\n const systemError = error as { code?: unknown; errno?: unknown };\n return systemError.code === 'ENOENT' || systemError.errno === -2;\n}\n"],"mappings":"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"}
|
package/dist/cli.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_cli_runtime = require("./cli-runtime-
|
|
3
|
+
const require_cli_runtime = require("./cli-runtime-BeaP2Kjo.cjs");
|
|
4
4
|
//#region src/cli.ts
|
|
5
5
|
if (require.main === module) require_cli_runtime.main().catch((err) => {
|
|
6
6
|
require_cli_runtime.getCliOutputSink().write({
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { main, parseCliArgs, start } from './cli-runtime';\nimport { getCliOutputSink } from './cli/output-sink';\n\nexport { main, parseCliArgs, start };\n\nif (require.main === module) {\n main().catch((err: any) => {\n getCliOutputSink().write({ level: 'error', message: `Failed to start: ${err instanceof Error ? err.message : String(err)}` });\n process.exit(1);\n });\n}\n"],"mappings":";;;;AAMA,IAAI,QAAQ,SAAS,
|
|
1
|
+
{"version":3,"file":"cli.cjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { main, parseCliArgs, start } from './cli-runtime';\nimport { getCliOutputSink } from './cli/output-sink';\n\nexport { main, parseCliArgs, start };\n\nif (require.main === module) {\n main().catch((err: any) => {\n getCliOutputSink().write({ level: 'error', message: `Failed to start: ${err instanceof Error ? err.message : String(err)}` });\n process.exit(1);\n });\n}\n"],"mappings":";;;;AAMA,IAAI,QAAQ,SAAS,QACjB,oBAAA,KAAK,CAAC,CAAC,OAAO,QAAa;CACvB,oBAAA,iBAAiB,CAAC,CAAC,MAAM;EAAE,OAAO;EAAS,SAAS,oBAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;CAAI,CAAC;CAC5H,QAAQ,KAAK,CAAC;AAClB,CAAC"}
|
package/dist/cli.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as start, n as main, r as parseCliArgs } from "./cli-runtime-
|
|
1
|
+
import { i as start, n as main, r as parseCliArgs } from "./cli-runtime-BGz4DzHq.cjs";
|
|
2
2
|
export { main, parseCliArgs, start };
|
package/dist/index.cjs
CHANGED
|
@@ -1,110 +1,96 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_cli_runtime = require("./cli-runtime-
|
|
2
|
+
const require_cli_runtime = require("./cli-runtime-BeaP2Kjo.cjs");
|
|
3
3
|
let _open_wa_api = require("@open-wa/api");
|
|
4
|
-
let
|
|
4
|
+
let node_path = require("node:path");
|
|
5
5
|
let _open_wa_logger = require("@open-wa/logger");
|
|
6
|
+
let _open_wa_session_sync = require("@open-wa/session-sync");
|
|
6
7
|
let _open_wa_core = require("@open-wa/core");
|
|
7
8
|
let _open_wa_config = require("@open-wa/config");
|
|
8
|
-
//#region src/session/
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
//#region src/session/SessionArchiveManager.ts
|
|
10
|
+
var SessionArchiveManager = class SessionArchiveManager {
|
|
11
|
+
config;
|
|
12
|
+
logger = (0, _open_wa_logger.createLogger)({ component: "SessionArchiveManager" });
|
|
13
|
+
localCompression;
|
|
14
|
+
s3Sync;
|
|
15
|
+
syncTimer;
|
|
16
|
+
started = false;
|
|
13
17
|
constructor(config) {
|
|
14
18
|
this.config = config;
|
|
15
|
-
this.logger.info("
|
|
19
|
+
this.logger.info("session_archive_manager_initialized", { sessionId: config.sessionId });
|
|
16
20
|
}
|
|
17
21
|
async start() {
|
|
18
|
-
if (this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.
|
|
29
|
-
this.startPeriodicSync();
|
|
30
|
-
this.logger.info("S3 session sync started");
|
|
22
|
+
if (this.started) return;
|
|
23
|
+
this.started = true;
|
|
24
|
+
this.localCompression = new _open_wa_session_sync.LocalSessionCompression({
|
|
25
|
+
sessionPath: this.config.dataDir,
|
|
26
|
+
sessionId: this.config.sessionId,
|
|
27
|
+
outputPath: this.archivePath,
|
|
28
|
+
intervalMs: this.config.syncInterval
|
|
29
|
+
});
|
|
30
|
+
if (this.config.watchLocalChanges !== false) {
|
|
31
|
+
this.localCompression.start();
|
|
32
|
+
this.logger.info("local_session_compression_started");
|
|
31
33
|
}
|
|
34
|
+
this.s3Sync = new _open_wa_session_sync.S3SyncManager(this.config.s3Config);
|
|
35
|
+
this.startPeriodicSync();
|
|
36
|
+
this.logger.info("s3_session_sync_started");
|
|
32
37
|
}
|
|
33
38
|
async stop() {
|
|
34
|
-
if (this.
|
|
35
|
-
|
|
36
|
-
this.
|
|
39
|
+
if (this.syncTimer) {
|
|
40
|
+
clearInterval(this.syncTimer);
|
|
41
|
+
this.syncTimer = void 0;
|
|
37
42
|
}
|
|
38
|
-
|
|
39
|
-
this.
|
|
43
|
+
this.localCompression?.stop();
|
|
44
|
+
this.localCompression = void 0;
|
|
45
|
+
this.s3Sync = void 0;
|
|
46
|
+
this.started = false;
|
|
47
|
+
this.logger.info("session_archive_manager_stopped");
|
|
40
48
|
}
|
|
41
49
|
async backupSession() {
|
|
42
|
-
if (!this.s3Sync) {
|
|
43
|
-
this.logger.warn("
|
|
50
|
+
if (!this.s3Sync || !this.localCompression) {
|
|
51
|
+
this.logger.warn("session_archive_manager_not_started");
|
|
44
52
|
return null;
|
|
45
53
|
}
|
|
46
54
|
try {
|
|
47
|
-
|
|
48
|
-
const filename = await this.s3Sync.backupSession(
|
|
49
|
-
this.logger.info("
|
|
55
|
+
await this.localCompression.compress();
|
|
56
|
+
const filename = await this.s3Sync.backupSession(this.archivePath);
|
|
57
|
+
this.logger.info("session_backed_up", { filename });
|
|
50
58
|
return filename;
|
|
51
59
|
} catch (error) {
|
|
52
|
-
this.logger.error("
|
|
60
|
+
this.logger.error("session_backup_failed", { error });
|
|
53
61
|
throw error;
|
|
54
62
|
}
|
|
55
63
|
}
|
|
56
64
|
async restoreSession(filename) {
|
|
57
|
-
if (!this.s3Sync)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
await this.s3Sync.restoreSession(filename, this.config.dataDir);
|
|
63
|
-
this.logger.info("Session restored from S3", { filename });
|
|
64
|
-
} catch (error) {
|
|
65
|
-
this.logger.error("Failed to restore session from S3", { error: error.message });
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
65
|
+
if (!this.s3Sync) throw new Error("Session archive manager is not started");
|
|
66
|
+
if (!await this.s3Sync.restoreSession(filename, this.config.dataDir)) throw new Error(`Failed to restore session backup: ${filename}`);
|
|
67
|
+
this.logger.info("session_restored", { filename });
|
|
68
68
|
}
|
|
69
69
|
async getSessionBackupUrl(filename, expiresIn = 3600) {
|
|
70
|
-
if (!this.s3Sync)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
70
|
+
if (!this.s3Sync) return null;
|
|
71
|
+
return this.s3Sync.getDownloadUrl(filename, expiresIn);
|
|
72
|
+
}
|
|
73
|
+
static createFromConfig(config) {
|
|
74
|
+
if (!config.s3Sync) throw new Error("Session archive management requires s3Sync configuration");
|
|
75
|
+
return new SessionArchiveManager({
|
|
76
|
+
sessionId: config.sessionId,
|
|
77
|
+
dataDir: config.userDataDir || config.sessionDataPath || "./.wwebjs",
|
|
78
|
+
s3Config: config.s3Sync,
|
|
79
|
+
syncInterval: config.s3Sync.syncInterval,
|
|
80
|
+
watchLocalChanges: config.s3Sync.enableLocalCompression !== false
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
get archivePath() {
|
|
84
|
+
return (0, node_path.join)((0, node_path.dirname)(this.config.dataDir), `${this.config.sessionId}.data.zst`);
|
|
85
85
|
}
|
|
86
86
|
startPeriodicSync() {
|
|
87
|
-
if (!this.s3Sync) return;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
}, syncInterval);
|
|
96
|
-
}
|
|
97
|
-
static createFromConfig(clientConfig) {
|
|
98
|
-
const s3Config = clientConfig.s3Sync;
|
|
99
|
-
return new SessionManager({
|
|
100
|
-
sessionId: clientConfig.sessionId || "session",
|
|
101
|
-
dataDir: clientConfig.sessionDataPath || "./.wwebjs",
|
|
102
|
-
s3Config,
|
|
103
|
-
syncInterval: clientConfig.s3Sync?.syncInterval,
|
|
104
|
-
compressionOptions: "-1 -T0",
|
|
105
|
-
enableLocalCompression: clientConfig.s3Sync?.enableLocalCompression !== false,
|
|
106
|
-
enableS3Backup: !!s3Config
|
|
107
|
-
});
|
|
87
|
+
if (!this.s3Sync || this.syncTimer) return;
|
|
88
|
+
this.syncTimer = setInterval(() => {
|
|
89
|
+
this.backupSession().catch((error) => {
|
|
90
|
+
this.logger.error("periodic_session_sync_failed", { error });
|
|
91
|
+
});
|
|
92
|
+
}, this.config.syncInterval ?? 6e5);
|
|
93
|
+
this.syncTimer.unref();
|
|
108
94
|
}
|
|
109
95
|
};
|
|
110
96
|
//#endregion
|
|
@@ -112,11 +98,11 @@ var SessionManager = class SessionManager {
|
|
|
112
98
|
var APILifecycleManager = class {
|
|
113
99
|
config;
|
|
114
100
|
server;
|
|
115
|
-
|
|
101
|
+
sessionArchiveManager;
|
|
116
102
|
_sessionConnected = false;
|
|
117
103
|
constructor(config) {
|
|
118
104
|
this.config = config;
|
|
119
|
-
if (config.s3Sync) this.
|
|
105
|
+
if (config.s3Sync) this.sessionArchiveManager = SessionArchiveManager.createFromConfig(config);
|
|
120
106
|
}
|
|
121
107
|
async initialize() {
|
|
122
108
|
const sink = require_cli_runtime.getCliOutputSink();
|
|
@@ -139,9 +125,8 @@ var APILifecycleManager = class {
|
|
|
139
125
|
detail: "Starting minimal API (QR only)"
|
|
140
126
|
});
|
|
141
127
|
await this.startMinimalAPI();
|
|
142
|
-
break;
|
|
143
128
|
}
|
|
144
|
-
if (this.
|
|
129
|
+
if (this.sessionArchiveManager) await this.sessionArchiveManager.start();
|
|
145
130
|
}
|
|
146
131
|
async waitForConnection() {
|
|
147
132
|
require_cli_runtime.getCliOutputSink().write({
|
|
@@ -166,7 +151,7 @@ var APILifecycleManager = class {
|
|
|
166
151
|
await this.server.start();
|
|
167
152
|
}
|
|
168
153
|
async stop() {
|
|
169
|
-
if (this.
|
|
154
|
+
if (this.sessionArchiveManager) await this.sessionArchiveManager.stop();
|
|
170
155
|
if (this.server) await this.server.stop();
|
|
171
156
|
}
|
|
172
157
|
};
|
|
@@ -184,7 +169,7 @@ Object.defineProperty(exports, "ConfigSchema", {
|
|
|
184
169
|
return _open_wa_config.ConfigSchema;
|
|
185
170
|
}
|
|
186
171
|
});
|
|
187
|
-
exports.
|
|
172
|
+
exports.SessionArchiveManager = SessionArchiveManager;
|
|
188
173
|
exports.WAServer = require_cli_runtime.WAServer;
|
|
189
174
|
Object.defineProperty(exports, "createApiMiddleware", {
|
|
190
175
|
enumerable: true,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["LocalSessionCompression","S3SyncManager","getCliOutputSink","WAServer"],"sources":["../src/session/SessionManager.ts","../src/server/lifecycle-manager.ts"],"sourcesContent":["import { LocalSessionCompression, S3SyncManager } from '@open-wa/session-sync';\nimport type { ClientConfig } from '@open-wa/schema';\nimport { createLogger } from '@open-wa/logger';\n\nexport interface SessionManagerConfig {\n sessionId: string;\n dataDir: string;\n s3Config?: {\n bucket: string;\n region: string;\n accessKeyId: string;\n secretAccessKey: string;\n endpoint?: string;\n host?: string;\n };\n syncInterval?: number;\n compressionOptions?: string;\n enableLocalCompression?: boolean;\n enableS3Backup?: boolean;\n}\n\nexport class SessionManager {\n private logger = createLogger({ component: 'SessionManager' });\n private localCompression: LocalSessionCompression | null = null;\n private s3Sync: S3SyncManager | null = null;\n\n constructor(private config: SessionManagerConfig) {\n this.logger.info('SessionManager initialized', { sessionId: config.sessionId });\n }\n\n async start(): Promise<void> {\n // Start local compression if enabled\n if (this.config.enableLocalCompression !== false) {\n this.localCompression = new LocalSessionCompression({\n sessionPath: this.config.dataDir,\n sessionId: this.config.sessionId,\n intervalMs: this.config.syncInterval || 600000\n });\n \n await this.localCompression.start();\n this.logger.info('Local session compression started');\n }\n\n // Start S3 sync if configured\n if (this.config.enableS3Backup && this.config.s3Config) {\n this.s3Sync = new S3SyncManager(this.config.s3Config);\n \n // Start periodic S3 sync\n this.startPeriodicSync();\n this.logger.info('S3 session sync started');\n }\n }\n\n async stop(): Promise<void> {\n // Stop local compression\n if (this.localCompression) {\n await this.localCompression.stop();\n this.localCompression = null;\n }\n\n // Stop S3 sync\n if (this.s3Sync) {\n // Note: S3SyncManager doesn't have stop method in current implementation\n // This would need to be added to session-sync package\n this.s3Sync = null;\n }\n\n this.logger.info('SessionManager stopped');\n }\n\n async backupSession(): Promise<string | null> {\n if (!this.s3Sync) {\n this.logger.warn('S3 sync not configured');\n return null;\n }\n\n try {\n // Find the latest compressed session file\n const sessionZstPath = `${this.config.dataDir}/${this.config.sessionId}.data.zst`;\n \n const filename = await this.s3Sync.backupSession(sessionZstPath);\n this.logger.info('Session backed up to S3', { filename });\n return filename;\n } catch (error) {\n this.logger.error('Failed to backup session to S3', { error: error.message });\n throw error;\n }\n }\n\n async restoreSession(filename: string): Promise<void> {\n if (!this.s3Sync) {\n this.logger.warn('S3 sync not configured');\n throw new Error('S3 sync not configured for session restore');\n }\n\n try {\n await this.s3Sync.restoreSession(filename, this.config.dataDir);\n this.logger.info('Session restored from S3', { filename });\n } catch (error) {\n this.logger.error('Failed to restore session from S3', { error: error.message });\n throw error;\n }\n }\n\n async getSessionBackupUrl(filename: string, expiresIn = 3600): Promise<string | null> {\n if (!this.s3Sync) {\n this.logger.warn('S3 sync not configured');\n return null;\n }\n\n try {\n const url = await this.s3Sync.getDownloadUrl(filename, expiresIn);\n this.logger.info('Generated session backup URL', { filename, expiresIn });\n return url;\n } catch (error) {\n this.logger.error('Failed to generate backup URL', { error: error.message });\n throw error;\n }\n }\n\n private startPeriodicSync(): void {\n if (!this.s3Sync) return;\n\n const syncInterval = this.config.syncInterval || 600000; // Default 10 minutes\n\n setInterval(async () => {\n try {\n await this.backupSession();\n } catch (error) {\n this.logger.error('Periodic sync failed', { error: error.message });\n }\n }, syncInterval);\n }\n\n static createFromConfig(clientConfig: ClientConfig): SessionManager {\n const s3Config = clientConfig.s3Sync;\n \n return new SessionManager({\n sessionId: clientConfig.sessionId || 'session',\n dataDir: clientConfig.sessionDataPath || './.wwebjs',\n s3Config: s3Config,\n syncInterval: clientConfig.s3Sync?.syncInterval,\n compressionOptions: '-1 -T0', // Default compression options\n enableLocalCompression: clientConfig.s3Sync?.enableLocalCompression !== false,\n enableS3Backup: !!s3Config\n });\n }\n}","import { WAServer } from './hono-server';\nimport type { Config } from '@open-wa/config';\nimport { SessionManager } from '../session/SessionManager';\nimport { getCliOutputSink } from '../cli/output-sink';\n\nexport class APILifecycleManager {\n private config: Config;\n private server?: WAServer;\n private sessionManager?: SessionManager;\n // @ts-ignore\n private _sessionConnected: boolean = false;\n\n constructor(config: Config) {\n this.config = config;\n \n // Initialize session manager if S3 sync is configured\n if (config.s3Sync) {\n this.sessionManager = SessionManager.createFromConfig(config);\n }\n }\n\n public async initialize(): Promise<void> {\n const sink = getCliOutputSink();\n switch (this.config.apiLifecycle) {\n case 'immediate':\n sink.status({ phase: 'server.starting', sessionId: this.config.sessionId, detail: 'Starting full API immediately' });\n await this.startFullAPI();\n break;\n case 'post-connection':\n this.waitForConnection();\n break;\n case 'hybrid':\n sink.status({ phase: 'server.starting', sessionId: this.config.sessionId, detail: 'Starting minimal API (QR only)' });\n await this.startMinimalAPI();\n break;\n }\n \n // Start session manager after all other initialization\n if (this.sessionManager) {\n await this.sessionManager.start();\n }\n }\n\n private async waitForConnection(): Promise<void> {\n getCliOutputSink().write({ level: 'info', message: 'Waiting for session connection before starting API...' });\n // This will be called by the orchestrator or main entry point when session connects\n }\n\n private async startFullAPI(): Promise<void> {\n getCliOutputSink().write({ level: 'info', message: 'Starting full API immediately...' });\n this.server = new WAServer(this.config);\n await this.server.start();\n }\n\n private async startMinimalAPI(): Promise<void> {\n getCliOutputSink().write({ level: 'info', message: 'Starting minimal API (QR only)...' });\n \n // Create server with full config but we might restrict routes in the future\n // For now, hybrid mode just starts the server, but logic inside routes (like sendText)\n // should check if session is connected before execution.\n // In a more advanced implementation, we would only register public routes here.\n \n this.server = new WAServer(this.config);\n await this.server.start();\n }\n \n public async stop(): Promise<void> {\n // Stop session manager first\n if (this.sessionManager) {\n await this.sessionManager.stop();\n }\n \n // Then stop server\n if (this.server) {\n await this.server.stop();\n }\n }\n}\n"],"mappings":";;;;;;;;AAqBA,IAAa,iBAAb,MAAa,eAAe;CAC1B,UAAQ,GAAA,gBAAA,cAAsB,EAAE,WAAW,kBAAkB,CAAC;CAC9D,mBAA2D;CAC3D,SAAuC;CAEvC,YAAY,QAAsC;AAA9B,OAAA,SAAA;AAClB,OAAK,OAAO,KAAK,8BAA8B,EAAE,WAAW,OAAO,WAAW,CAAC;;CAGjF,MAAM,QAAuB;AAE3B,MAAI,KAAK,OAAO,2BAA2B,OAAO;AAChD,QAAK,mBAAmB,IAAIA,sBAAAA,wBAAwB;IAClD,aAAa,KAAK,OAAO;IACzB,WAAW,KAAK,OAAO;IACvB,YAAY,KAAK,OAAO,gBAAgB;IACzC,CAAC;AAEF,SAAM,KAAK,iBAAiB,OAAO;AACnC,QAAK,OAAO,KAAK,oCAAoC;;AAIvD,MAAI,KAAK,OAAO,kBAAkB,KAAK,OAAO,UAAU;AACtD,QAAK,SAAS,IAAIC,sBAAAA,cAAc,KAAK,OAAO,SAAS;AAGrD,QAAK,mBAAmB;AACxB,QAAK,OAAO,KAAK,0BAA0B;;;CAI/C,MAAM,OAAsB;AAE1B,MAAI,KAAK,kBAAkB;AACzB,SAAM,KAAK,iBAAiB,MAAM;AAClC,QAAK,mBAAmB;;AAI1B,MAAI,KAAK,OAGP,MAAK,SAAS;AAGhB,OAAK,OAAO,KAAK,yBAAyB;;CAG5C,MAAM,gBAAwC;AAC5C,MAAI,CAAC,KAAK,QAAQ;AAChB,QAAK,OAAO,KAAK,yBAAyB;AAC1C,UAAO;;AAGT,MAAI;GAEF,MAAM,iBAAiB,GAAG,KAAK,OAAO,QAAQ,GAAG,KAAK,OAAO,UAAU;GAEvE,MAAM,WAAW,MAAM,KAAK,OAAO,cAAc,eAAe;AAChE,QAAK,OAAO,KAAK,2BAA2B,EAAE,UAAU,CAAC;AACzD,UAAO;WACA,OAAO;AACd,QAAK,OAAO,MAAM,kCAAkC,EAAE,OAAO,MAAM,SAAS,CAAC;AAC7E,SAAM;;;CAIV,MAAM,eAAe,UAAiC;AACpD,MAAI,CAAC,KAAK,QAAQ;AAChB,QAAK,OAAO,KAAK,yBAAyB;AAC1C,SAAM,IAAI,MAAM,6CAA6C;;AAG/D,MAAI;AACF,SAAM,KAAK,OAAO,eAAe,UAAU,KAAK,OAAO,QAAQ;AAC/D,QAAK,OAAO,KAAK,4BAA4B,EAAE,UAAU,CAAC;WACnD,OAAO;AACd,QAAK,OAAO,MAAM,qCAAqC,EAAE,OAAO,MAAM,SAAS,CAAC;AAChF,SAAM;;;CAIV,MAAM,oBAAoB,UAAkB,YAAY,MAA8B;AACpF,MAAI,CAAC,KAAK,QAAQ;AAChB,QAAK,OAAO,KAAK,yBAAyB;AAC1C,UAAO;;AAGT,MAAI;GACF,MAAM,MAAM,MAAM,KAAK,OAAO,eAAe,UAAU,UAAU;AACjE,QAAK,OAAO,KAAK,gCAAgC;IAAE;IAAU;IAAW,CAAC;AACzE,UAAO;WACA,OAAO;AACd,QAAK,OAAO,MAAM,iCAAiC,EAAE,OAAO,MAAM,SAAS,CAAC;AAC5E,SAAM;;;CAIV,oBAAkC;AAChC,MAAI,CAAC,KAAK,OAAQ;EAElB,MAAM,eAAe,KAAK,OAAO,gBAAgB;AAEjD,cAAY,YAAY;AACtB,OAAI;AACF,UAAM,KAAK,eAAe;YACnB,OAAO;AACd,SAAK,OAAO,MAAM,wBAAwB,EAAE,OAAO,MAAM,SAAS,CAAC;;KAEpE,aAAa;;CAGlB,OAAO,iBAAiB,cAA4C;EAClE,MAAM,WAAW,aAAa;AAE9B,SAAO,IAAI,eAAe;GACxB,WAAW,aAAa,aAAa;GACrC,SAAS,aAAa,mBAAmB;GAC/B;GACV,cAAc,aAAa,QAAQ;GACnC,oBAAoB;GACpB,wBAAwB,aAAa,QAAQ,2BAA2B;GACxE,gBAAgB,CAAC,CAAC;GACnB,CAAC;;;;;AC5IN,IAAa,sBAAb,MAAiC;CAC7B;CACA;CACA;CAEA,oBAAqC;CAErC,YAAY,QAAgB;AACxB,OAAK,SAAS;AAGd,MAAI,OAAO,OACP,MAAK,iBAAiB,eAAe,iBAAiB,OAAO;;CAIrE,MAAa,aAA4B;EACrC,MAAM,OAAOC,oBAAAA,kBAAkB;AAC/B,UAAQ,KAAK,OAAO,cAApB;GACI,KAAK;AACD,SAAK,OAAO;KAAE,OAAO;KAAmB,WAAW,KAAK,OAAO;KAAW,QAAQ;KAAiC,CAAC;AACpH,UAAM,KAAK,cAAc;AACzB;GACJ,KAAK;AACD,SAAK,mBAAmB;AACxB;GACJ,KAAK;AACD,SAAK,OAAO;KAAE,OAAO;KAAmB,WAAW,KAAK,OAAO;KAAW,QAAQ;KAAkC,CAAC;AACrH,UAAM,KAAK,iBAAiB;AAC5B;;AAIR,MAAI,KAAK,eACL,OAAM,KAAK,eAAe,OAAO;;CAIzC,MAAc,oBAAmC;AAC7C,sBAAA,kBAAkB,CAAC,MAAM;GAAE,OAAO;GAAQ,SAAS;GAAyD,CAAC;;CAIjH,MAAc,eAA8B;AACxC,sBAAA,kBAAkB,CAAC,MAAM;GAAE,OAAO;GAAQ,SAAS;GAAoC,CAAC;AACxF,OAAK,SAAS,IAAIC,oBAAAA,SAAS,KAAK,OAAO;AACvC,QAAM,KAAK,OAAO,OAAO;;CAG7B,MAAc,kBAAiC;AAC3C,sBAAA,kBAAkB,CAAC,MAAM;GAAE,OAAO;GAAQ,SAAS;GAAqC,CAAC;AAOzF,OAAK,SAAS,IAAIA,oBAAAA,SAAS,KAAK,OAAO;AACvC,QAAM,KAAK,OAAO,OAAO;;CAG7B,MAAa,OAAsB;AAE/B,MAAI,KAAK,eACL,OAAM,KAAK,eAAe,MAAM;AAIpC,MAAI,KAAK,OACL,OAAM,KAAK,OAAO,MAAM"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createLogger","LocalSessionCompression","S3SyncManager","join","dirname","getCliOutputSink","WAServer"],"sources":["../src/session/SessionArchiveManager.ts","../src/server/lifecycle-manager.ts"],"sourcesContent":["import { dirname, join } from 'node:path';\nimport type { Config } from '@open-wa/config';\nimport { createLogger } from '@open-wa/logger';\nimport { LocalSessionCompression, S3SyncManager, type S3Config } from '@open-wa/session-sync';\n\nexport interface SessionArchiveManagerConfig {\n sessionId: string;\n dataDir: string;\n s3Config: S3Config;\n syncInterval?: number;\n watchLocalChanges?: boolean;\n}\n\nexport class SessionArchiveManager {\n private readonly logger = createLogger({ component: 'SessionArchiveManager' });\n private localCompression?: LocalSessionCompression;\n private s3Sync?: S3SyncManager;\n private syncTimer?: NodeJS.Timeout;\n private started = false;\n\n constructor(private readonly config: SessionArchiveManagerConfig) {\n this.logger.info('session_archive_manager_initialized', { sessionId: config.sessionId });\n }\n\n async start(): Promise<void> {\n if (this.started) return;\n this.started = true;\n\n this.localCompression = new LocalSessionCompression({\n sessionPath: this.config.dataDir,\n sessionId: this.config.sessionId,\n outputPath: this.archivePath,\n intervalMs: this.config.syncInterval,\n });\n if (this.config.watchLocalChanges !== false) {\n this.localCompression.start();\n this.logger.info('local_session_compression_started');\n }\n\n this.s3Sync = new S3SyncManager(this.config.s3Config);\n this.startPeriodicSync();\n this.logger.info('s3_session_sync_started');\n }\n\n async stop(): Promise<void> {\n if (this.syncTimer) {\n clearInterval(this.syncTimer);\n this.syncTimer = undefined;\n }\n\n this.localCompression?.stop();\n this.localCompression = undefined;\n this.s3Sync = undefined;\n this.started = false;\n this.logger.info('session_archive_manager_stopped');\n }\n\n async backupSession(): Promise<string | null> {\n if (!this.s3Sync || !this.localCompression) {\n this.logger.warn('session_archive_manager_not_started');\n return null;\n }\n\n try {\n await this.localCompression.compress();\n const filename = await this.s3Sync.backupSession(this.archivePath);\n this.logger.info('session_backed_up', { filename });\n return filename;\n } catch (error) {\n this.logger.error('session_backup_failed', { error });\n throw error;\n }\n }\n\n async restoreSession(filename: string): Promise<void> {\n if (!this.s3Sync) {\n throw new Error('Session archive manager is not started');\n }\n\n const restored = await this.s3Sync.restoreSession(filename, this.config.dataDir);\n if (!restored) {\n throw new Error(`Failed to restore session backup: ${filename}`);\n }\n this.logger.info('session_restored', { filename });\n }\n\n async getSessionBackupUrl(filename: string, expiresIn = 3600): Promise<string | null> {\n if (!this.s3Sync) {\n return null;\n }\n\n return this.s3Sync.getDownloadUrl(filename, expiresIn);\n }\n\n static createFromConfig(config: Config): SessionArchiveManager {\n if (!config.s3Sync) {\n throw new Error('Session archive management requires s3Sync configuration');\n }\n\n return new SessionArchiveManager({\n sessionId: config.sessionId,\n dataDir: config.userDataDir || config.sessionDataPath || './.wwebjs',\n s3Config: config.s3Sync,\n syncInterval: config.s3Sync.syncInterval,\n watchLocalChanges: config.s3Sync.enableLocalCompression !== false,\n });\n }\n\n private get archivePath(): string {\n return join(dirname(this.config.dataDir), `${this.config.sessionId}.data.zst`);\n }\n\n private startPeriodicSync(): void {\n if (!this.s3Sync || this.syncTimer) return;\n\n this.syncTimer = setInterval(() => {\n void this.backupSession().catch((error) => {\n this.logger.error('periodic_session_sync_failed', { error });\n });\n }, this.config.syncInterval ?? 600_000);\n this.syncTimer.unref();\n }\n}\n","import { WAServer } from './hono-server';\nimport type { Config } from '@open-wa/config';\nimport { SessionArchiveManager } from '../session/SessionArchiveManager';\nimport { getCliOutputSink } from '../cli/output-sink';\n\nexport class APILifecycleManager {\n private config: Config;\n private server?: WAServer;\n private sessionArchiveManager?: SessionArchiveManager;\n // @ts-ignore\n private _sessionConnected: boolean = false;\n\n constructor(config: Config) {\n this.config = config;\n \n // Initialize session manager if S3 sync is configured\n if (config.s3Sync) {\n this.sessionArchiveManager = SessionArchiveManager.createFromConfig(config);\n }\n }\n\n public async initialize(): Promise<void> {\n const sink = getCliOutputSink();\n switch (this.config.apiLifecycle) {\n case 'immediate':\n sink.status({ phase: 'server.starting', sessionId: this.config.sessionId, detail: 'Starting full API immediately' });\n await this.startFullAPI();\n break;\n case 'post-connection':\n this.waitForConnection();\n break;\n case 'hybrid':\n sink.status({ phase: 'server.starting', sessionId: this.config.sessionId, detail: 'Starting minimal API (QR only)' });\n await this.startMinimalAPI();\n break;\n }\n \n // Start session manager after all other initialization\n if (this.sessionArchiveManager) {\n await this.sessionArchiveManager.start();\n }\n }\n\n private async waitForConnection(): Promise<void> {\n getCliOutputSink().write({ level: 'info', message: 'Waiting for session connection before starting API...' });\n // This will be called by the orchestrator or main entry point when session connects\n }\n\n private async startFullAPI(): Promise<void> {\n getCliOutputSink().write({ level: 'info', message: 'Starting full API immediately...' });\n this.server = new WAServer(this.config);\n await this.server.start();\n }\n\n private async startMinimalAPI(): Promise<void> {\n getCliOutputSink().write({ level: 'info', message: 'Starting minimal API (QR only)...' });\n \n // Create server with full config but we might restrict routes in the future\n // For now, hybrid mode just starts the server, but logic inside routes (like sendText)\n // should check if session is connected before execution.\n // In a more advanced implementation, we would only register public routes here.\n \n this.server = new WAServer(this.config);\n await this.server.start();\n }\n \n public async stop(): Promise<void> {\n // Stop session manager first\n if (this.sessionArchiveManager) {\n await this.sessionArchiveManager.stop();\n }\n \n // Then stop server\n if (this.server) {\n await this.server.stop();\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { a as WAServer, i as start, n as main, r as parseCliArgs, t as CliRuntimeResult } from "./cli-runtime-
|
|
1
|
+
import { a as WAServer, i as start, n as main, r as parseCliArgs, t as CliRuntimeResult } from "./cli-runtime-BGz4DzHq.cjs";
|
|
2
2
|
import { createApiMiddleware, createApiServer } from "@open-wa/api";
|
|
3
3
|
import { Config, Config as Config$1, ConfigSchema } from "@open-wa/config";
|
|
4
|
-
import {
|
|
4
|
+
import { S3Config } from "@open-wa/session-sync";
|
|
5
5
|
import { CreateClientOptions, OpenWAClient, createClient } from "@open-wa/core";
|
|
6
6
|
export * from "@open-wa/client";
|
|
7
|
-
|
|
8
7
|
//#region src/server/lifecycle-manager.d.ts
|
|
9
|
-
declare class APILifecycleManager {
|
|
8
|
+
export declare class APILifecycleManager {
|
|
10
9
|
private config;
|
|
11
10
|
private server?;
|
|
12
|
-
private
|
|
11
|
+
private sessionArchiveManager?;
|
|
13
12
|
private _sessionConnected;
|
|
14
13
|
constructor(config: Config$1);
|
|
15
14
|
initialize(): Promise<void>;
|
|
@@ -19,36 +18,30 @@ declare class APILifecycleManager {
|
|
|
19
18
|
stop(): Promise<void>;
|
|
20
19
|
}
|
|
21
20
|
//#endregion
|
|
22
|
-
//#region src/session/
|
|
23
|
-
interface
|
|
21
|
+
//#region src/session/SessionArchiveManager.d.ts
|
|
22
|
+
interface SessionArchiveManagerConfig {
|
|
24
23
|
sessionId: string;
|
|
25
24
|
dataDir: string;
|
|
26
|
-
s3Config
|
|
27
|
-
bucket: string;
|
|
28
|
-
region: string;
|
|
29
|
-
accessKeyId: string;
|
|
30
|
-
secretAccessKey: string;
|
|
31
|
-
endpoint?: string;
|
|
32
|
-
host?: string;
|
|
33
|
-
};
|
|
25
|
+
s3Config: S3Config;
|
|
34
26
|
syncInterval?: number;
|
|
35
|
-
|
|
36
|
-
enableLocalCompression?: boolean;
|
|
37
|
-
enableS3Backup?: boolean;
|
|
27
|
+
watchLocalChanges?: boolean;
|
|
38
28
|
}
|
|
39
|
-
declare class
|
|
40
|
-
private config;
|
|
41
|
-
private logger;
|
|
42
|
-
private localCompression
|
|
43
|
-
private s3Sync
|
|
44
|
-
|
|
29
|
+
export declare class SessionArchiveManager {
|
|
30
|
+
private readonly config;
|
|
31
|
+
private readonly logger;
|
|
32
|
+
private localCompression?;
|
|
33
|
+
private s3Sync?;
|
|
34
|
+
private syncTimer?;
|
|
35
|
+
private started;
|
|
36
|
+
constructor(config: SessionArchiveManagerConfig);
|
|
45
37
|
start(): Promise<void>;
|
|
46
38
|
stop(): Promise<void>;
|
|
47
39
|
backupSession(): Promise<string | null>;
|
|
48
40
|
restoreSession(filename: string): Promise<void>;
|
|
49
41
|
getSessionBackupUrl(filename: string, expiresIn?: number): Promise<string | null>;
|
|
42
|
+
static createFromConfig(config: Config$1): SessionArchiveManager;
|
|
43
|
+
private get archivePath();
|
|
50
44
|
private startPeriodicSync;
|
|
51
|
-
static createFromConfig(clientConfig: ClientConfig): SessionManager;
|
|
52
45
|
}
|
|
53
46
|
//#endregion
|
|
54
47
|
//#region src/cli/output-sink.d.ts
|
|
@@ -72,10 +65,10 @@ interface CliOutputSink {
|
|
|
72
65
|
status(update: CliStatusUpdate): void;
|
|
73
66
|
qr(payload: CliQrPayload): void;
|
|
74
67
|
}
|
|
75
|
-
declare function createConsoleOutputSink(): CliOutputSink;
|
|
76
|
-
declare function getCliOutputSink(): CliOutputSink;
|
|
77
|
-
declare function setCliOutputSink(sink: CliOutputSink): void;
|
|
78
|
-
declare function resetCliOutputSink(): void;
|
|
68
|
+
export declare function createConsoleOutputSink(): CliOutputSink;
|
|
69
|
+
export declare function getCliOutputSink(): CliOutputSink;
|
|
70
|
+
export declare function setCliOutputSink(sink: CliOutputSink): void;
|
|
71
|
+
export declare function resetCliOutputSink(): void;
|
|
79
72
|
//#endregion
|
|
80
|
-
export {
|
|
73
|
+
export { type CliOutputEntry, type CliOutputLevel, type CliOutputSink, type CliQrPayload, type CliRuntimeResult, type CliStatusUpdate, Config, ConfigSchema, type CreateClientOptions, type OpenWAClient, WAServer, createApiMiddleware, createApiServer, createClient, parseCliArgs, main as runCli, start as startCli };
|
|
81
74
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/server/lifecycle-manager.ts","../src/session/
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":["Config","Config"],"sources":["../src/server/lifecycle-manager.ts","../src/session/SessionArchiveManager.ts","../src/cli/output-sink.ts"],"mappings":";;;;;;;qBAKa;UACD;UACA;UACA;UAEA;EAER,YAAY,QAAQA;EASP,cAAc;UAsBb;UAKA;UAMA;EAYD,QAAQ;;;;UC7DR;EACb;EACA;EACA,UAAU;EACV;EACA;;qBAGS;mBAOoB;mBANZ;UACT;UACA;UACA;UACA;EAER,YAA6B,QAAQ;EAI/B,SAAS;EAoBT,QAAQ;EAaR,iBAAiB;EAiBjB,eAAe,mBAAmB;EAYlC,oBAAoB,kBAAkB,qBAAmB;SAQxD,iBAAiB,QAAQC,WAAS;cAc7B;UAIJ;;;;KC7GA;UAEK;EACf,OAAO;EACP;EACA,OAAO;;UAGQ;EACf;EAgBA;EACA;;UAGe;EACf;EACA;;UAGe;EACf,MAAM,OAAO;EACb,OAAO,QAAQ;EACf,GAAG,SAAS;;wBAiBE,2BAA2B;wBAwB3B,oBAAoB;wBAIpB,iBAAiB,MAAM;wBAIvB"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-wa/wa-automate",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.0.0
|
|
4
|
+
"version": "5.0.0",
|
|
5
5
|
"description": "WhatsApp automation CLI and API server",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"types": "./dist/index.d.cts",
|
|
@@ -13,30 +13,33 @@
|
|
|
13
13
|
"skills"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@elastic/elasticsearch": "^9.
|
|
17
|
-
"@hono/node-server": "^1.
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"@open-wa/
|
|
23
|
-
"@open-wa/
|
|
24
|
-
"@open-wa/
|
|
25
|
-
"@open-wa/core": "5.0.0
|
|
26
|
-
"@open-wa/
|
|
27
|
-
"@open-wa/
|
|
28
|
-
"@open-wa/
|
|
29
|
-
"
|
|
30
|
-
"
|
|
16
|
+
"@elastic/elasticsearch": "^9.5.1",
|
|
17
|
+
"@hono/node-server": "^2.1.1",
|
|
18
|
+
"@open-wa/api": "5.0.0",
|
|
19
|
+
"@open-wa/client": "5.0.0",
|
|
20
|
+
"@open-wa/config": "5.0.0",
|
|
21
|
+
"@open-wa/core": "5.0.0",
|
|
22
|
+
"@open-wa/driver-lightpanda": "5.0.0",
|
|
23
|
+
"@open-wa/driver-puppeteer": "5.0.0",
|
|
24
|
+
"@open-wa/logger": "5.0.0",
|
|
25
|
+
"@open-wa/runtime-core": "5.0.0",
|
|
26
|
+
"@open-wa/runtime-node": "5.0.0",
|
|
27
|
+
"@open-wa/schema": "5.0.0",
|
|
28
|
+
"@open-wa/session-sync": "5.0.0",
|
|
29
|
+
"boxen": "^9.0.0",
|
|
30
|
+
"chrome-launcher": "^1.2.1",
|
|
31
|
+
"hono": "^4.13.9",
|
|
32
|
+
"qrcode-terminal": "^0.12.0"
|
|
31
33
|
},
|
|
32
34
|
"devDependencies": {
|
|
33
|
-
"typescript": "^
|
|
34
|
-
"vitest": "
|
|
35
|
+
"typescript": "^7.0.2",
|
|
36
|
+
"vitest": "5.0.2"
|
|
35
37
|
},
|
|
36
38
|
"repository": {
|
|
37
39
|
"type": "git",
|
|
38
40
|
"url": "https://github.com/open-wa/wa-automate-nodejs.git"
|
|
39
41
|
},
|
|
42
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
40
43
|
"scripts": {
|
|
41
44
|
"build": "tsdown",
|
|
42
45
|
"dev": "tsdown --watch",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-runtime-CEFbPQ-1.cjs","names":["ApiServer","eventRegistry","LightpandaDriver","PuppeteerDriver","ClientFacade"],"sources":["../src/server/hono-server.ts","../src/cli/output-sink.ts","../src/cli-runtime.ts"],"sourcesContent":["import { ApiServer, createApiServer as createSharedApiServer } from '@open-wa/api';\nimport type { Config } from '@open-wa/config';\n\nexport class WAServer extends ApiServer {\n constructor(config: Config) {\n super({ config });\n }\n}\n\nexport function createApiServer(config: Config) {\n return createSharedApiServer(config);\n}\n","import boxen from 'boxen';\nimport qrcode from 'qrcode-terminal';\n\nexport type CliOutputLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface CliOutputEntry {\n level: CliOutputLevel;\n message: string;\n meta?: Record<string, unknown>;\n}\n\nexport interface CliStatusUpdate {\n phase:\n | 'boot'\n | 'config.resolved'\n | 'server.starting'\n | 'server.started'\n | 'client.starting'\n | 'launch.auth'\n | 'launch.helpers'\n | 'launch.patch'\n | 'launch.license'\n | 'launch.finalize'\n | 'auth.qr'\n | 'client.ready'\n | 'shutdown.starting'\n | 'shutdown.complete'\n | 'error';\n detail?: string;\n sessionId?: string;\n}\n\nexport interface CliQrPayload {\n qr: string;\n sessionId: string;\n}\n\nexport interface CliOutputSink {\n write(entry: CliOutputEntry): void;\n status(update: CliStatusUpdate): void;\n qr(payload: CliQrPayload): void;\n}\n\nfunction writeToConsole(level: CliOutputLevel, message: string): void {\n if (level === 'warn') {\n console.warn(message);\n return;\n }\n\n if (level === 'error') {\n console.error(message);\n return;\n }\n\n console.log(message);\n}\n\nexport function createConsoleOutputSink(): CliOutputSink {\n return {\n write(entry) {\n writeToConsole(entry.level, entry.message);\n },\n status() {\n // Status updates are semantic signals for richer presenters.\n },\n qr(payload) {\n qrcode.generate(payload.qr, { small: true }, (terminalQrCode) => {\n console.log(\n boxen(terminalQrCode, {\n title: payload.sessionId,\n padding: 1,\n titleAlignment: 'center',\n })\n );\n });\n },\n };\n}\n\nlet activeSink: CliOutputSink = createConsoleOutputSink();\n\nexport function getCliOutputSink(): CliOutputSink {\n return activeSink;\n}\n\nexport function setCliOutputSink(sink: CliOutputSink): void {\n activeSink = sink;\n}\n\nexport function resetCliOutputSink(): void {\n activeSink = createConsoleOutputSink();\n}\n","import { createClient } from '@open-wa/core';\nimport { Client as ClientFacade } from '@open-wa/client';\nimport { LightpandaDriver } from '@open-wa/driver-lightpanda';\nimport { PuppeteerDriver } from '@open-wa/driver-puppeteer';\nimport { eventRegistry } from '@open-wa/schema';\nimport { WAServer } from './server/hono-server';\nimport { resolveConfig, type PartialConfig, type Config, type TrackedConfig } from '@open-wa/config';\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { getCliOutputSink, type CliOutputSink } from './cli/output-sink';\n\nexport interface CliRuntimeResult {\n server: WAServer;\n client: ClientFacade;\n config: Config;\n events: ClientFacade['events'];\n}\n\ninterface RuntimeEventPublisher {\n publish(eventName: string, payload: any): void;\n}\n\nfunction bridgeRuntimeEvents(\n openwaClient: Awaited<ReturnType<typeof createClient>>,\n publishRuntimeEvent: (eventName: string, payload: any) => void,\n): () => void {\n if (typeof openwaClient.events.onAny === 'function' && typeof openwaClient.events.offAny === 'function') {\n openwaClient.events.onAny(publishRuntimeEvent);\n return () => openwaClient.events.offAny(publishRuntimeEvent);\n }\n\n const unsubscribers: Array<() => void> = [];\n const bridgedEvents = new Set(eventRegistry.getAll().map((def) => def.meta.eventName));\n\n bridgedEvents.forEach((eventName) => {\n const handler = (payload: any) => publishRuntimeEvent(eventName, payload);\n openwaClient.events.on(eventName as never, handler);\n unsubscribers.push(() => openwaClient.events.off(eventName as never, handler));\n });\n\n return () => {\n unsubscribers.forEach((unsubscribe) => unsubscribe());\n };\n}\n\nfunction attachLaunchNarration(\n openwaClient: Awaited<ReturnType<typeof createClient>>,\n sink: CliOutputSink,\n sessionId: string,\n): () => void {\n const unsubscribers: Array<() => void> = [];\n const on = (event: string, handler: (...args: any[]) => void) => {\n openwaClient.events.on(event as never, handler);\n unsubscribers.push(() => openwaClient.events.off(event as never, handler));\n };\n\n on('launch.auth.check.before', (event: any) => {\n sink.status({ phase: 'launch.auth', sessionId, detail: `Authenticating (timeout=${event?.details?.timeoutMs ?? '∞'}ms)` });\n });\n\n on('launch.auth.check.after', (event: any) => {\n sink.write({ level: 'info', message: `Auth detected via ${event?.details?.method ?? 'unknown'} (${event?.details?.isAuthenticated ? 'authenticated' : 'not-authenticated'})` });\n });\n\n on('launch.helper.pre_api.before', (event: any) => {\n sink.status({ phase: 'launch.helpers', sessionId, detail: event?.details?.mode === 'scripts' ? 'Injecting pre-API helper assets...' : 'Pre-API helpers already present' });\n });\n\n on('launch.helper.pre_api.after', (event: any) => {\n sink.write({ level: 'info', message: `Pre-API helper phase complete (${event?.details?.mode ?? 'unknown'})` });\n });\n\n on('internal_launch_progress', (event: any) => {\n const prefix = (event?.value || event?.value === 0) ? `${event.value}% ` : '';\n if (event?.text) sink.write({ level: 'info', message: `${prefix}${event.text}`.trim() });\n });\n\n on('critical_internal_message', (event: any) => {\n sink.write({ level: 'warn', message: `Internal launch message: ${event?.text ?? event?.value ?? 'unknown'}` });\n });\n\n on('launch.patch.init.before', (event: any) => {\n if (event?.details?.phase === 'preload') {\n sink.status({ phase: 'launch.patch', sessionId, detail: 'Downloading patches from CDN...' });\n }\n });\n\n on('launch.patch.init.after', (event: any) => {\n if (event?.details?.phase === 'preload') {\n const source = event?.details?.source ?? 'unknown';\n const tag = event?.details?.tag ? ` tag=${event.details.tag}` : '';\n const available = Array.isArray(event?.details?.available) ? event.details.available.length : 0;\n const durationMs = event?.durationMs;\n const durationStr = durationMs ? ` in ${(durationMs / 1000).toFixed(2)}s` : '';\n sink.write({ level: 'info', message: `Downloaded patches${durationStr} (source=${source}, available=${available}${tag})` });\n }\n });\n\n on('patch.init.before', (event: any) => {\n if (event?.details?.phase === 'apply') {\n const count = Array.isArray(event?.details?.patchIds) ? event.details.patchIds.length : 0;\n sink.status({ phase: 'launch.patch', sessionId, detail: `Installing patches (${count} queued)` });\n }\n });\n\n on('patch.apply.before', (event: any) => {\n sink.status({ phase: 'launch.patch', sessionId, detail: `Applying patch: ${event?.details?.patchId ?? 'unknown'}` });\n });\n\n on('patch.apply.after', (event: any) => {\n sink.write({\n level: event?.details?.outcome === 'failed' ? 'warn' : 'info',\n message: `Patch ${event?.details?.patchId ?? 'unknown'}: ${event?.details?.outcome ?? (event?.details?.applied ? 'applied' : 'unknown')}${event?.details?.detail ? ` — ${event.details.detail}` : ''}`,\n });\n });\n\n on('patch.init.after', (event: any) => {\n if (event?.details?.phase === 'apply') {\n const applied = Array.isArray(event?.details?.applied) ? event.details.applied : [];\n const outcome = event?.details?.outcome ?? 'unknown';\n const tag = applied.length > 0 ? applied.join(', ') : outcome;\n const durationMs = event?.durationMs;\n const durationStr = durationMs ? ` in ${(durationMs / 1000).toFixed(2)}s` : '';\n sink.write({\n level: event?.details?.blockingFailure ? 'warn' : 'info',\n message: `Patches Installed${durationStr}: ${tag}`,\n });\n }\n });\n\n on('launch.patch.integrity.before', () => {\n sink.status({ phase: 'launch.patch', sessionId, detail: 'Validating patched runtime...' });\n });\n\n on('launch.patch.integrity.after', (event: any) => {\n sink.write({ level: event?.details?.usable ? 'info' : 'warn', message: `Patched runtime validation: ${event?.details?.usable ? 'usable' : 'failed'}${event?.details?.failureReason ? ` (${event.details.failureReason})` : ''}` });\n });\n\n on('launch.license.check.before', (event: any) => {\n sink.status({ phase: 'launch.license', sessionId, detail: `Checking license (${event?.details?.source ?? 'local'})` });\n });\n\n on('launch.license.check.after', (event: any) => {\n sink.write({ level: event?.details?.blockingFailure ? 'warn' : 'info', message: `License check: ${event?.details?.status ?? 'unknown'}${event?.details?.detail ? ` — ${event.details.detail}` : ''}` });\n });\n\n on('license.inject.before', () => {\n sink.status({ phase: 'launch.license', sessionId, detail: 'Injecting license payload...' });\n });\n\n on('license.inject.after', (event: any) => {\n sink.write({ level: event?.details?.success ? 'info' : 'warn', message: `License inject: ${event?.details?.success ? 'success' : 'failed'}${event?.details?.status ? ` (${event.details.status})` : ''}${event?.details?.detail ? ` — ${event.details.detail}` : ''}` });\n });\n\n on('launch.auth.qr.generated', (event: any) => {\n const qrIndex = event?.details?.qrIndex ?? '?';\n sink.status({ phase: 'launch.auth', sessionId, detail: `QR code generated (#${qrIndex})` });\n });\n\n on('launch.client.finalize.before', (event: any) => {\n sink.status({ phase: 'launch.finalize', sessionId, detail: `Finalizing session (${event?.details?.validationStage ?? 'unknown'})` });\n });\n\n on('launch.client.finalize.after', (event: any) => {\n sink.write({ level: event?.details?.success ? 'info' : 'warn', message: `Finalize: ${event?.details?.outcome ?? 'unknown'}${event?.details?.detail ? ` — ${event.details.detail}` : ''}` });\n });\n\n on('error', (event: any) => {\n const scope = event?.scope ?? 'unknown';\n const msg = event?.error?.message ?? String(event?.error ?? 'unknown error');\n const fatal = event?.fatal ? ' [FATAL]' : '';\n sink.write({ level: 'error', message: `${fatal} ${scope}: ${msg}`.trim() });\n });\n\n return () => {\n unsubscribers.forEach((unsubscribe) => unsubscribe());\n };\n}\n\nconst CHROME_CACHE_FILE = resolve(process.cwd(), '.open-wa', 'chrome-executable-path.json');\n\ninterface ChromePathCacheRecord {\n executablePath?: string;\n updatedAt?: string;\n}\n\ninterface ExecutablePathResolution {\n executablePath?: string;\n source:\n | 'config'\n | 'cache'\n | 'chrome_installation'\n | 'driver_default'\n | 'lightpanda_config'\n | 'lightpanda_sdk_default';\n warning?: string;\n}\n\ninterface DriverSelection {\n driver: PuppeteerDriver | LightpandaDriver;\n engineLabel: 'Puppeteer' | 'Lightpanda';\n executableResolution: ExecutablePathResolution;\n preferLocalChrome: boolean;\n}\n\nexport interface ParsedCliArgs {\n procName: string;\n pm2: boolean;\n forwardedArgs: string[];\n configPath?: string;\n cliOverrides: PartialConfig;\n verbose: boolean;\n unsupportedWarnings: string[];\n}\n\nexport function getChromeCacheFilePath(): string {\n return CHROME_CACHE_FILE;\n}\n\nexport function readChromePathCache(cacheFilePath: string = getChromeCacheFilePath()): ChromePathCacheRecord | undefined {\n if (!existsSync(cacheFilePath)) {\n return undefined;\n }\n\n try {\n const raw = JSON.parse(readFileSync(cacheFilePath, 'utf8')) as ChromePathCacheRecord;\n if (!raw || typeof raw !== 'object') {\n return undefined;\n }\n\n return {\n executablePath: typeof raw.executablePath === 'string' ? raw.executablePath : undefined,\n updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : undefined,\n };\n } catch {\n return undefined;\n }\n}\n\nexport function writeChromePathCache(executablePath: string, cacheFilePath: string = getChromeCacheFilePath()): void {\n mkdirSync(dirname(cacheFilePath), { recursive: true });\n writeFileSync(cacheFilePath, JSON.stringify({ executablePath, updatedAt: new Date().toISOString() }, null, 2), 'utf8');\n}\n\nexport function clearChromePathCache(cacheFilePath: string = getChromeCacheFilePath()): void {\n if (existsSync(cacheFilePath)) {\n rmSync(cacheFilePath, { force: true });\n }\n}\n\nexport function isUsableExecutablePath(executablePath?: string): executablePath is string {\n return !!executablePath && existsSync(executablePath);\n}\n\nfunction getExplicitUseChromePreference(rawConfigs?: TrackedConfig['rawConfigs']): boolean | undefined {\n const explicitConfigSources = [\n rawConfigs?.file,\n rawConfigs?.env,\n rawConfigs?.cli,\n rawConfigs?.programmatic,\n ];\n\n for (let index = explicitConfigSources.length - 1; index >= 0; index -= 1) {\n const source = explicitConfigSources[index];\n if (source?.useChrome !== undefined) {\n return source.useChrome;\n }\n }\n\n return undefined;\n}\n\nexport function shouldPreferLocalChrome(config: Config, rawConfigs?: TrackedConfig['rawConfigs']): boolean {\n if (config.useLightpanda) {\n return false;\n }\n\n if (config.executablePath) {\n return false;\n }\n\n const explicitUseChrome = getExplicitUseChromePreference(rawConfigs);\n if (explicitUseChrome !== undefined) {\n return explicitUseChrome;\n }\n\n return true;\n}\n\nfunction getVal(argv: string[], flag: string): string | undefined {\n const index = argv.findIndex(arg => arg === flag);\n return index !== -1 ? argv[index + 1] : undefined;\n}\n\nexport function parseCliArgs(argv: string[] = process.argv.slice(2)): ParsedCliArgs {\n const cliOverrides: PartialConfig = {};\n const unsupportedWarnings: string[] = [];\n let verbose = false;\n let configPath: string | undefined;\n\n const sessionId = getVal(argv, '--session-id') || 'session';\n cliOverrides.sessionId = sessionId;\n\n const portValue = getVal(argv, '--port') || getVal(argv, '-p');\n if (portValue) cliOverrides.port = parseInt(portValue, 10);\n\n const host = getVal(argv, '--host') || getVal(argv, '-h');\n if (host) cliOverrides.host = host;\n\n const apiKey = getVal(argv, '--api-key') || getVal(argv, '--key') || getVal(argv, '-k');\n if (apiKey) cliOverrides.apiKey = apiKey;\n\n const logLevel = getVal(argv, '--log-level');\n if (logLevel) cliOverrides.logLevel = logLevel;\n\n if (argv.includes('--no-ezqr')) cliOverrides.ezqr = false;\n if (argv.includes('--headful')) cliOverrides.headless = false;\n if (argv.includes('--headless')) cliOverrides.headless = true;\n if (argv.includes('--use-chrome')) cliOverrides.useChrome = true;\n if (argv.includes('--use-lightpanda')) cliOverrides.useLightpanda = true;\n if (argv.includes('--log-console')) cliOverrides.logConsole = true;\n if (argv.includes('--aggressive-garbage-collection')) cliOverrides.aggressiveGarbageCollection = true;\n if (argv.includes('--no-dashboard')) cliOverrides.dashboard = false;\n if (argv.includes('--ephemeral')) cliOverrides.ephemeral = true;\n\n const qrTimeout = getVal(argv, '--qr-timeout');\n if (qrTimeout) cliOverrides.qrTimeout = parseInt(qrTimeout, 10);\n\n const dashboardPort = getVal(argv, '--dashboard-port');\n if (dashboardPort) cliOverrides.dashboardPort = parseInt(dashboardPort, 10);\n\n const licenseKey = getVal(argv, '--license-key') || getVal(argv, '-l');\n if (licenseKey) cliOverrides.licenseKey = licenseKey;\n\n const webhook = getVal(argv, '--webhook') || getVal(argv, '-w');\n if (webhook) cliOverrides.webhook = webhook;\n\n const proxyHost = getVal(argv, '--proxy-host');\n if (proxyHost) cliOverrides.proxyHost = proxyHost;\n\n const proxyToken = getVal(argv, '--proxy-token');\n if (proxyToken) cliOverrides.proxyToken = proxyToken;\n\n const readyWebhook = getVal(argv, '--ready-webhook');\n if (readyWebhook) unsupportedWarnings.push(`--ready-webhook was provided (${readyWebhook}) but ready-webhook delivery is not yet wired in v5 CLI.`);\n\n if (argv.includes('-v') || argv.includes('--verbose')) {\n verbose = true;\n cliOverrides.disableSpins = true;\n cliOverrides.logConsole = true;\n cliOverrides.logLevel = cliOverrides.logLevel || 'debug';\n }\n\n if (argv.includes('--stats')) {\n unsupportedWarnings.push('--stats was provided but swagger-stats parity is not restored; v5 only exposes the compatibility redirect/deprecation route.');\n }\n\n if (argv.includes('--generate-api-docs')) {\n unsupportedWarnings.push('--generate-api-docs was provided; runtime docs are always served from the shared API layer and no separate collection generation step currently runs at CLI boot.');\n }\n\n if (argv.includes('--tunnel')) {\n unsupportedWarnings.push('--tunnel was provided but tunnel setup parity is not yet restored in the v5 CLI.');\n }\n\n if (argv.includes('--chatwoot-url') || argv.includes('--twilio-webhook') || argv.includes('--bot-press-url')) {\n unsupportedWarnings.push('Legacy integration flags (Chatwoot/Twilio/BotPress) were provided but are not yet wired into the v5 CLI boot path.');\n }\n\n if (argv.includes('--use-session-id-in-path')) {\n cliOverrides.sessionId = sessionId;\n }\n\n configPath = getVal(argv, '--config') || getVal(argv, '-c');\n\n const procName = getVal(argv, '--name') || sessionId || '@OPEN-WA EASY API';\n const pm2 = argv.includes('--pm2');\n\n return {\n procName,\n pm2,\n forwardedArgs: argv,\n configPath,\n cliOverrides,\n verbose,\n unsupportedWarnings,\n };\n}\n\nexport async function resolveExecutablePath(\n config: Config,\n options: {\n preferLocalChrome?: boolean;\n cacheFilePath?: string;\n } = {}\n): Promise<ExecutablePathResolution> {\n if (config.executablePath) {\n return {\n executablePath: config.executablePath,\n source: 'config',\n };\n }\n\n const preferLocalChrome = options.preferLocalChrome ?? config.useChrome;\n if (!preferLocalChrome) {\n return {\n source: 'driver_default',\n };\n }\n\n const cacheFilePath = options.cacheFilePath ?? getChromeCacheFilePath();\n const cachedPath = readChromePathCache(cacheFilePath)?.executablePath;\n if (isUsableExecutablePath(cachedPath)) {\n return {\n executablePath: cachedPath,\n source: 'cache',\n };\n }\n\n if (cachedPath) {\n clearChromePathCache(cacheFilePath);\n }\n\n const { Launcher } = await import('chrome-launcher');\n const detectedPath = Launcher.getInstallations().find((installationPath) => isUsableExecutablePath(installationPath));\n\n if (detectedPath) {\n writeChromePathCache(detectedPath, cacheFilePath);\n return {\n executablePath: detectedPath,\n source: 'chrome_installation',\n };\n }\n\n clearChromePathCache(cacheFilePath);\n\n return {\n source: 'driver_default',\n warning: 'Chrome resolution warning: no valid local Chrome installation was found. Falling back to Puppeteer/default driver browser resolution.',\n };\n}\n\nfunction resolveLightpandaExecutablePath(config: Config): ExecutablePathResolution {\n const executablePath = config.lightpanda?.executablePath;\n if (executablePath) {\n return {\n executablePath,\n source: 'lightpanda_config',\n };\n }\n\n return {\n source: 'lightpanda_sdk_default',\n };\n}\n\nasync function selectRuntimeDriver(config: Config, rawConfigs?: TrackedConfig['rawConfigs']): Promise<DriverSelection> {\n if (config.useLightpanda) {\n return {\n driver: new LightpandaDriver(),\n engineLabel: 'Lightpanda',\n executableResolution: resolveLightpandaExecutablePath(config),\n preferLocalChrome: false,\n };\n }\n\n const preferLocalChrome = shouldPreferLocalChrome(config, rawConfigs);\n const executableResolution = await resolveExecutablePath(config, { preferLocalChrome });\n\n return {\n driver: new PuppeteerDriver(),\n engineLabel: 'Puppeteer',\n executableResolution,\n preferLocalChrome,\n };\n}\n\nfunction printStartupSummary(\n config: Config,\n resolution: ExecutablePathResolution,\n preferLocalChrome: boolean,\n engineLabel: DriverSelection['engineLabel'],\n) {\n const host = config.host.includes('http') ? config.host : `http://${config.host}`;\n const sink = getCliOutputSink();\n sink.write({ level: 'info', message: `Easy API session: ${config.sessionId}` });\n sink.write({ level: 'info', message: `Health: ${host}:${config.port}/health` });\n sink.write({ level: 'info', message: `API Explorer: ${host}:${config.port}/api-docs/` });\n sink.write({ level: 'info', message: `Swagger JSON: ${host}:${config.port}/meta/swagger.json` });\n sink.write({ level: 'info', message: `Postman JSON: ${host}:${config.port}/meta/postman.json` });\n sink.write({ level: 'info', message: `Browser mode: ${config.headless ? 'headless' : 'headful'}` });\n sink.write({ level: 'info', message: `Browser engine: ${engineLabel}` });\n if (config.dashboard) {\n sink.write({ level: 'info', message: `Dashboard: ${host}:${config.port}/dashboard/` });\n } else {\n sink.write({ level: 'info', message: 'Dashboard: disabled (--no-dashboard)' });\n }\n if (resolution.source === 'lightpanda_config' && resolution.executablePath) {\n sink.write({ level: 'info', message: `Browser executable: explicit Lightpanda override (${resolution.executablePath})` });\n } else if (resolution.source === 'lightpanda_sdk_default') {\n sink.write({ level: 'info', message: 'Browser executable: Lightpanda SDK managed executable (shared cache/default resolution)' });\n } else if (resolution.source === 'config' && config.executablePath) {\n sink.write({ level: 'info', message: `Browser executable: explicit override (${config.executablePath})` });\n } else if (resolution.source === 'cache') {\n sink.write({ level: 'info', message: `Browser executable: local Chrome from cache (${resolution.executablePath})` });\n } else if (resolution.source === 'chrome_installation') {\n sink.write({ level: 'info', message: `Browser executable: local Chrome detected (${resolution.executablePath})` });\n } else if (preferLocalChrome) {\n sink.write({ level: 'info', message: 'Browser executable: driver default fallback (local Chrome unavailable)' });\n }\n if (config.webhook) {\n sink.write({\n level: 'warn',\n message: `Webhook configured (${config.webhook}) but v5 CLI webhook registration parity is not yet restored.`,\n });\n }\n}\n\nexport async function start(parsedArgs: ParsedCliArgs = parseCliArgs()): Promise<CliRuntimeResult> {\n const { configPath, cliOverrides, verbose, unsupportedWarnings } = parsedArgs;\n const sink = getCliOutputSink();\n\n sink.status({ phase: 'boot' });\n\n const { config, sources, configFilePath, rawConfigs } = await resolveConfig({\n configPath,\n cliOverrides: {\n disableSpins: true,\n apiLifecycle: 'hybrid',\n host: '0.0.0.0',\n port: 8002,\n ...cliOverrides,\n },\n includeRawConfigs: true,\n verbose,\n });\n\n if (verbose) {\n sink.write({ level: 'info', message: `Config sources: ${sources.join(', ')}` });\n if (configFilePath) {\n sink.write({ level: 'info', message: `Config file: ${configFilePath}` });\n }\n }\n\n sink.status({ phase: 'config.resolved', sessionId: config.sessionId });\n\n unsupportedWarnings.forEach((warning) => sink.write({ level: 'warn', message: `Compatibility warning: ${warning}` }));\n\n const driverSelection = await selectRuntimeDriver(config, rawConfigs);\n const { driver, engineLabel, executableResolution, preferLocalChrome } = driverSelection;\n if (executableResolution.warning) {\n sink.write({ level: 'warn', message: executableResolution.warning });\n }\n\n const server = new WAServer(config);\n sink.status({ phase: 'server.starting', sessionId: config.sessionId });\n\n await server.start();\n sink.status({ phase: 'server.started', sessionId: config.sessionId });\n\n printStartupSummary(config, executableResolution, preferLocalChrome, engineLabel);\n\n sink.status({ phase: 'client.starting', sessionId: config.sessionId });\n sink.write({ level: 'info', message: 'Starting WhatsApp Client...' });\n\n const openwaClient = await createClient({\n sessionId: config.sessionId,\n driver,\n deleteSessionDataOnLogout: config.deleteSessionDataOnLogout,\n killClientOnLogout: config.killClientOnLogout,\n sessionDataPath: config.sessionDataPath,\n debug: config.logLevel === 'debug' || verbose || config.logConsole,\n headless: config.headless,\n qrTimeoutMs: typeof config.qrTimeout === 'number' ? config.qrTimeout * 1000 : undefined,\n authTimeoutMs: typeof config.authTimeout === 'number' ? config.authTimeout * 1000 : undefined,\n executablePath: executableResolution.executablePath,\n browserArgs: config.chromiumArgs,\n allowDangerousBrowserArgs: config.allowDangerousBrowserArgs,\n userDataDir: config.userDataDir,\n ephemeral: config.ephemeral,\n logConsole: config.logConsole,\n logConsoleErrors: config.logConsoleErrors,\n blockCrashLogs: config.blockCrashLogs,\n blockAssets: config.blockAssets,\n safeMode: config.safeMode,\n licenseKey: config.licenseKey as any,\n });\n\n server.setReadinessProvider(() => ({ ...openwaClient.getReadiness(), state: openwaClient.getState() }));\n const detachLaunchNarration = attachLaunchNarration(openwaClient, sink, config.sessionId);\n\n openwaClient.events.on('launch.auth.qr.generated', (event) => {\n const qr = event.details?.qr;\n if (!qr) {\n return;\n }\n\n if (config.qrLogSkip) {\n sink.write({ level: 'info', message: 'New QR Code generated. Not printing in console because qrLogSkip is set to true' });\n } else {\n sink.status({ phase: 'auth.qr', sessionId: config.sessionId, detail: 'QR code generated' });\n sink.qr({ qr, sessionId: config.sessionId });\n }\n\n server.setQR(qr);\n });\n\n const client = new ClientFacade({\n client: openwaClient,\n transport: openwaClient.getTransport(),\n });\n\n server.setClient(client as any);\n\n const runtimeBridgeListeners = new Set<(event: string, value: any) => void>();\n const runtimeEventPublishers: RuntimeEventPublisher[] = [\n {\n publish: (eventName, payload) => {\n runtimeBridgeListeners.forEach((listener) => {\n listener(eventName, payload);\n });\n },\n },\n ];\n\n const publishRuntimeEvent = (eventName: string, payload: any) => {\n runtimeEventPublishers.forEach((publisher) => {\n publisher.publish(eventName, payload);\n });\n };\n\n const detachRuntimeBridge = bridgeRuntimeEvents(openwaClient, publishRuntimeEvent);\n\n // Bridge core events to the socket manager so dashboard clients\n // receive live events when they send register_ev.\n if (typeof server.setEventBridge === 'function') {\n server.setEventBridge({\n onAny: (listener: (event: string, value: any) => void) => {\n runtimeBridgeListeners.add(listener);\n },\n offAny: (listener: (event: string, value: any) => void) => {\n runtimeBridgeListeners.delete(listener);\n },\n });\n }\n\n openwaClient.events.on('launch.browser.init.after', async () => {\n const page = openwaClient.getTransport().getPage();\n if (page) {\n await server.setPage(page as any).catch(() => {});\n }\n });\n\n try {\n await client.start();\n } catch (startError) {\n const msg = startError instanceof Error ? startError.message : String(startError);\n sink.write({ level: 'error', message: `Bootstrap failed: ${msg}` });\n sink.write({ level: 'warn', message: 'Session kept alive for debugging. Browser page is still open.' });\n sink.write({ level: 'warn', message: 'The server is running — use /health and /api-docs to inspect state.' });\n detachLaunchNarration();\n return { server, client, config, events: openwaClient.events };\n }\n\n const readiness = openwaClient.getReadiness();\n sink.status({\n phase: 'client.ready',\n sessionId: config.sessionId,\n detail: `status=${readiness.status}, exposureSafe=${readiness.exposureSafe}`,\n });\n sink.write({\n level: 'info',\n message: `WhatsApp Client ready with state: ${client.getState()} (status=${readiness.status}, exposureSafe=${readiness.exposureSafe})`,\n });\n\n detachLaunchNarration();\n return { server, client, config, events: openwaClient.events };\n}\n\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<CliRuntimeResult | void> {\n const parsedArgs = parseCliArgs(argv);\n\n if (parsedArgs.pm2) {\n const { spawn } = require('child_process');\n const sink = getCliOutputSink();\n const pm2Command = getPm2Command();\n const pm2SpawnOptions = getPm2SpawnOptions();\n try {\n const pm2 = spawn(pm2Command, ['--version'], pm2SpawnOptions);\n\n await new Promise<void>((resolve, reject) => {\n pm2.on('error', reject);\n pm2.on('exit', (code: number | null) => {\n if (code === 0) {\n resolve();\n return;\n }\n\n reject(new Error(`pm2 probe exited with code ${code ?? 'unknown'}`));\n });\n pm2.stdout.on('data', () => resolve());\n });\n\n const pm2Flags = parsedArgs.forwardedArgs.filter((flag: string) => flag !== '--pm2');\n const cliPath = fileURLToPath(new URL('../dist/cli.cjs', import.meta.url));\n\n spawn(pm2Command, [\n 'start',\n cliPath,\n '--name', parsedArgs.procName,\n '--stop-exit-codes', '88',\n '--',\n ...pm2Flags,\n ], {\n ...pm2SpawnOptions,\n stdio: 'inherit',\n detached: true\n });\n return;\n } catch (error: unknown) {\n if (isCommandNotFoundError(error)) {\n sink.write({ level: 'error', message: 'pm2 not found. Please install with: npm install -g pm2' });\n return;\n }\n throw error;\n }\n }\n\n return await start(parsedArgs);\n}\n\nfunction getPm2Command(): string {\n return process.platform === 'win32' ? 'pm2.cmd' : 'pm2';\n}\n\nfunction getPm2SpawnOptions(): { shell: boolean } {\n return { shell: process.platform === 'win32' };\n}\n\nfunction isCommandNotFoundError(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) {\n return false;\n }\n\n const systemError = error as { code?: unknown; errno?: unknown };\n return systemError.code === 'ENOENT' || systemError.errno === -2;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-runtime-CmpYxFCk.d.cts","names":[],"sources":["../src/server/hono-server.ts","../src/cli-runtime.ts"],"mappings":";;;;;cAGa,QAAA,SAAiB,SAAA;cAChB,MAAA,EAAQ,MAAA;AAAA;;;UCQL,gBAAA;EACb,MAAA,EAAQ,QAAA;EACR,MAAA,EAAQ,MAAA;EACR,MAAA,EAAQ,MAAA;EACR,MAAA,EAAQ,MAAA;AAAA;AAAA,UA8LK,aAAA;EACb,QAAA;EACA,GAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA,EAAc,aAAA;EACd,OAAA;EACA,mBAAA;AAAA;AAAA,iBAkFY,YAAA,CAAa,IAAA,cAAyC,aAAA;AAAA,iBAgOhD,KAAA,CAAM,UAAA,GAAY,aAAA,GAAiC,OAAA,CAAQ,gBAAA;AAAA,iBAiK3D,IAAA,CAAK,IAAA,cAAyC,OAAA,CAAQ,gBAAA"}
|