@open-wa/wa-automate 5.4.0 → 5.5.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/dist/cli-runtime-BGz4DzHq.d.cts.map +1 -1
- package/dist/{cli-runtime-BKwXzp9V.cjs → cli-runtime-BRj_LUIT.cjs} +500 -112
- package/dist/cli-runtime-BRj_LUIT.cjs.map +1 -0
- package/dist/cli.cjs +1 -1
- package/dist/index.cjs +27 -211
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/{init-k_0bazOu.cjs → init-CUhC5Wkq.cjs} +4 -4
- package/dist/{init-k_0bazOu.cjs.map → init-CUhC5Wkq.cjs.map} +1 -1
- package/package.json +21 -12
- package/dist/cli-runtime-BKwXzp9V.cjs.map +0 -1
|
@@ -26,11 +26,12 @@ qrcode_terminal = __toESM(qrcode_terminal, 1);
|
|
|
26
26
|
let node_path = require("node:path");
|
|
27
27
|
let _open_wa_core = require("@open-wa/core");
|
|
28
28
|
let _open_wa_client = require("@open-wa/client");
|
|
29
|
-
let _open_wa_driver_lightpanda = require("@open-wa/driver-lightpanda");
|
|
30
|
-
let _open_wa_driver_puppeteer = require("@open-wa/driver-puppeteer");
|
|
31
|
-
let _open_wa_schema = require("@open-wa/schema");
|
|
32
29
|
let _open_wa_config = require("@open-wa/config");
|
|
33
30
|
let node_fs = require("node:fs");
|
|
31
|
+
let node_fs_promises = require("node:fs/promises");
|
|
32
|
+
let node_os = require("node:os");
|
|
33
|
+
let _open_wa_driver_interface = require("@open-wa/driver-interface");
|
|
34
|
+
let _open_wa_schema = require("@open-wa/schema");
|
|
34
35
|
let node_url = require("node:url");
|
|
35
36
|
let _open_wa_runtime_core = require("@open-wa/runtime-core");
|
|
36
37
|
let _open_wa_runtime_node = require("@open-wa/runtime-node");
|
|
@@ -42,6 +43,30 @@ var WAServer = class extends _open_wa_api.ApiServer {
|
|
|
42
43
|
};
|
|
43
44
|
//#endregion
|
|
44
45
|
//#region src/cli/output-sink.ts
|
|
46
|
+
async function promptChoice(question, choices) {
|
|
47
|
+
if (!process.stdin.isTTY || !process.stderr.isTTY) throw new Error("No browser is installed. Choose --use-chrome, --use-chromium or --use-lightpanda to download one without a prompt.");
|
|
48
|
+
const { createInterface } = await import("node:readline/promises");
|
|
49
|
+
const readline = createInterface({
|
|
50
|
+
input: process.stdin,
|
|
51
|
+
output: process.stderr
|
|
52
|
+
});
|
|
53
|
+
const abort = new AbortController();
|
|
54
|
+
readline.on("SIGINT", () => abort.abort());
|
|
55
|
+
readline.on("close", () => abort.abort());
|
|
56
|
+
try {
|
|
57
|
+
process.stderr.write(`\n${question}\n${choices.map((choice, index) => ` ${index + 1}. ${choice}`).join("\n")}\n`);
|
|
58
|
+
while (true) {
|
|
59
|
+
const answer = (await readline.question("Choose a browser [1]: ", { signal: abort.signal })).trim();
|
|
60
|
+
const choice = answer === "" ? 0 : Number(answer) - 1;
|
|
61
|
+
if (Number.isInteger(choice) && choice >= 0 && choice < choices.length) return choice;
|
|
62
|
+
process.stderr.write(`Enter a number from 1 to ${choices.length}.\n`);
|
|
63
|
+
}
|
|
64
|
+
} catch (cause) {
|
|
65
|
+
throw new Error("Browser selection cancelled.", { cause });
|
|
66
|
+
} finally {
|
|
67
|
+
readline.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
45
70
|
function writeToConsole(level, message) {
|
|
46
71
|
if (level === "warn") {
|
|
47
72
|
console.warn(message);
|
|
@@ -55,6 +80,7 @@ function writeToConsole(level, message) {
|
|
|
55
80
|
}
|
|
56
81
|
function createConsoleOutputSink() {
|
|
57
82
|
return {
|
|
83
|
+
promptChoice,
|
|
58
84
|
write(entry) {
|
|
59
85
|
writeToConsole(entry.level, entry.message);
|
|
60
86
|
},
|
|
@@ -77,6 +103,430 @@ function resetCliOutputSink() {
|
|
|
77
103
|
activeSink = createConsoleOutputSink();
|
|
78
104
|
}
|
|
79
105
|
//#endregion
|
|
106
|
+
//#region src/browser-choice.ts
|
|
107
|
+
const choiceFile = (0, node_path.join)((0, node_os.homedir)(), ".cache", "open-wa", "browser-choice.json");
|
|
108
|
+
async function findSystemBrowser(kind) {
|
|
109
|
+
const app = kind === "chrome" ? "Google Chrome" : "Chromium";
|
|
110
|
+
const names = process.platform === "win32" ? ["chrome.exe"] : kind === "chrome" ? [
|
|
111
|
+
"google-chrome-stable",
|
|
112
|
+
"google-chrome",
|
|
113
|
+
"chrome"
|
|
114
|
+
] : ["chromium", "chromium-browser"];
|
|
115
|
+
const candidates = process.platform === "darwin" ? ["/Applications", (0, node_path.join)((0, node_os.homedir)(), "Applications")].map((root) => (0, node_path.join)(root, `${app}.app`, "Contents", "MacOS", app)) : process.platform === "win32" ? [
|
|
116
|
+
process.env.LOCALAPPDATA,
|
|
117
|
+
process.env.PROGRAMFILES,
|
|
118
|
+
process.env["PROGRAMFILES(X86)"]
|
|
119
|
+
].filter((root) => !!root).map((root) => kind === "chrome" ? (0, node_path.join)(root, "Google", "Chrome", "Application", "chrome.exe") : (0, node_path.join)(root, "Chromium", "Application", "chrome.exe")) : kind === "chrome" ? ["/opt/google/chrome/chrome"] : [];
|
|
120
|
+
candidates.push(...(process.env.PATH ?? "").split(node_path.delimiter).filter(Boolean).flatMap((root) => names.map((name) => (0, node_path.join)(root, name))));
|
|
121
|
+
for (const candidate of candidates) try {
|
|
122
|
+
if (!(await (0, node_fs_promises.stat)(candidate)).isFile()) continue;
|
|
123
|
+
await (0, node_fs_promises.access)(candidate, node_fs.constants.X_OK);
|
|
124
|
+
return candidate;
|
|
125
|
+
} catch {}
|
|
126
|
+
}
|
|
127
|
+
async function readBrowserChoice() {
|
|
128
|
+
const { Effect, Schema } = await import("effect");
|
|
129
|
+
const recordSchema = Schema.fromJsonString(Schema.Struct({
|
|
130
|
+
kind: Schema.Literals([
|
|
131
|
+
"chrome",
|
|
132
|
+
"chromium",
|
|
133
|
+
"lightpanda"
|
|
134
|
+
]),
|
|
135
|
+
executablePath: Schema.String
|
|
136
|
+
}));
|
|
137
|
+
const io = (operation) => Effect.tryPromise({
|
|
138
|
+
try: operation,
|
|
139
|
+
catch: _open_wa_driver_interface.toPublicError
|
|
140
|
+
});
|
|
141
|
+
return Effect.runPromise(Effect.gen(function* () {
|
|
142
|
+
const record = yield* Schema.decodeUnknownEffect(recordSchema)(yield* io(() => (0, node_fs_promises.readFile)(choiceFile, "utf8")));
|
|
143
|
+
if (!(yield* io(() => (0, node_fs_promises.stat)(record.executablePath))).isFile()) return void 0;
|
|
144
|
+
yield* io(() => (0, node_fs_promises.access)(record.executablePath, node_fs.constants.X_OK));
|
|
145
|
+
return record;
|
|
146
|
+
}).pipe(Effect.catch(() => Effect.succeed(void 0))));
|
|
147
|
+
}
|
|
148
|
+
async function writeBrowserChoice(kind, executablePath) {
|
|
149
|
+
const { Effect } = await import("effect");
|
|
150
|
+
const io = (operation) => Effect.tryPromise({
|
|
151
|
+
try: operation,
|
|
152
|
+
catch: _open_wa_driver_interface.toPublicError
|
|
153
|
+
});
|
|
154
|
+
const program = Effect.gen(function* () {
|
|
155
|
+
const directory = (0, node_path.dirname)(choiceFile);
|
|
156
|
+
yield* io(() => (0, node_fs_promises.mkdir)(directory, { recursive: true }));
|
|
157
|
+
const staging = yield* Effect.acquireRelease(io(() => (0, node_fs_promises.mkdtemp)((0, node_path.join)(directory, ".browser-choice-"))), (path) => io(() => (0, node_fs_promises.rm)(path, {
|
|
158
|
+
recursive: true,
|
|
159
|
+
force: true
|
|
160
|
+
})).pipe(Effect.orDie));
|
|
161
|
+
const temporary = (0, node_path.join)(staging, "browser-choice.json");
|
|
162
|
+
yield* io(() => (0, node_fs_promises.writeFile)(temporary, JSON.stringify({
|
|
163
|
+
kind,
|
|
164
|
+
executablePath
|
|
165
|
+
}), {
|
|
166
|
+
encoding: "utf8",
|
|
167
|
+
flag: "wx"
|
|
168
|
+
}));
|
|
169
|
+
yield* io(() => (0, node_fs_promises.rename)(temporary, choiceFile));
|
|
170
|
+
});
|
|
171
|
+
await Effect.runPromise(Effect.scoped(Effect.uninterruptible(program))).catch((cause) => {
|
|
172
|
+
throw (0, _open_wa_driver_interface.toPublicError)(cause);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async function chooseBrowser() {
|
|
176
|
+
return [
|
|
177
|
+
"chrome",
|
|
178
|
+
"chromium",
|
|
179
|
+
"lightpanda"
|
|
180
|
+
][await (getCliOutputSink().promptChoice ?? promptChoice)("Google Chrome was not found. Which browser would you like to download?", [
|
|
181
|
+
"Chrome for Testing — recommended; highest compatibility, including video and media.",
|
|
182
|
+
"Chromium — middle ground; proprietary video/audio codecs may be unavailable.",
|
|
183
|
+
"Lightpanda — lightweight basic automation; low compatibility, experimental WhatsApp support, no video or rendering."
|
|
184
|
+
])];
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/runtime-client.ts
|
|
188
|
+
function readChromePathCache(cacheFilePath) {
|
|
189
|
+
try {
|
|
190
|
+
if (!(0, node_fs.existsSync)(cacheFilePath)) return;
|
|
191
|
+
const raw = JSON.parse((0, node_fs.readFileSync)(cacheFilePath, "utf8"));
|
|
192
|
+
if (!raw || typeof raw !== "object") return;
|
|
193
|
+
return { executablePath: typeof raw.executablePath === "string" ? raw.executablePath : void 0 };
|
|
194
|
+
} catch {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function writeChromePathCache(executablePath, cacheFilePath) {
|
|
199
|
+
(0, node_fs.mkdirSync)((0, node_path.dirname)(cacheFilePath), { recursive: true });
|
|
200
|
+
(0, node_fs.writeFileSync)(cacheFilePath, JSON.stringify({
|
|
201
|
+
executablePath,
|
|
202
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
203
|
+
}, null, 2), "utf8");
|
|
204
|
+
}
|
|
205
|
+
function clearChromePathCache(cacheFilePath) {
|
|
206
|
+
if ((0, node_fs.existsSync)(cacheFilePath)) (0, node_fs.rmSync)(cacheFilePath, { force: true });
|
|
207
|
+
}
|
|
208
|
+
function isUsableExecutablePath(executablePath) {
|
|
209
|
+
if (!executablePath) return false;
|
|
210
|
+
try {
|
|
211
|
+
if (!(0, node_fs.statSync)(executablePath).isFile()) return false;
|
|
212
|
+
(0, node_fs.accessSync)(executablePath, node_fs.constants.X_OK);
|
|
213
|
+
return true;
|
|
214
|
+
} catch {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function getExplicitUseChromePreference(rawConfigs) {
|
|
219
|
+
const explicitConfigSources = [
|
|
220
|
+
rawConfigs?.file,
|
|
221
|
+
rawConfigs?.env,
|
|
222
|
+
rawConfigs?.cli,
|
|
223
|
+
rawConfigs?.programmatic
|
|
224
|
+
];
|
|
225
|
+
for (let index = explicitConfigSources.length - 1; index >= 0; index -= 1) {
|
|
226
|
+
const source = explicitConfigSources[index];
|
|
227
|
+
if (source?.useChrome !== void 0) return source.useChrome;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function shouldPreferLocalChrome(config, rawConfigs, defaultPreference = true) {
|
|
231
|
+
if (config.useLightpanda || config.useChromium || config.browser?.kind === "chromium") return false;
|
|
232
|
+
if (config.executablePath || process.env.PUPPETEER_EXECUTABLE_PATH !== void 0) return false;
|
|
233
|
+
const explicitUseChrome = getExplicitUseChromePreference(rawConfigs);
|
|
234
|
+
if (explicitUseChrome !== void 0) return explicitUseChrome;
|
|
235
|
+
return defaultPreference;
|
|
236
|
+
}
|
|
237
|
+
async function resolveExecutablePath(config, options) {
|
|
238
|
+
const configuredPath = config.executablePath ?? process.env.PUPPETEER_EXECUTABLE_PATH;
|
|
239
|
+
if (configuredPath !== void 0) return {
|
|
240
|
+
executablePath: configuredPath,
|
|
241
|
+
source: "config"
|
|
242
|
+
};
|
|
243
|
+
if (!(options.preferLocalChrome ?? config.useChrome)) return { source: "driver_default" };
|
|
244
|
+
const matchesKind = (path) => options.kind === "chromium" ? /chromium/i.test(path) : !/chromium/i.test(path);
|
|
245
|
+
const cachedPath = readChromePathCache(options.cacheFilePath)?.executablePath;
|
|
246
|
+
if (isUsableExecutablePath(cachedPath) && matchesKind(cachedPath)) return {
|
|
247
|
+
executablePath: cachedPath,
|
|
248
|
+
source: "cache"
|
|
249
|
+
};
|
|
250
|
+
if (cachedPath) clearChromePathCache(options.cacheFilePath);
|
|
251
|
+
const detectedPath = await findSystemBrowser(options.kind ?? "chrome");
|
|
252
|
+
if (detectedPath) {
|
|
253
|
+
writeChromePathCache(detectedPath, options.cacheFilePath);
|
|
254
|
+
return {
|
|
255
|
+
executablePath: detectedPath,
|
|
256
|
+
source: "chrome_installation"
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
clearChromePathCache(options.cacheFilePath);
|
|
260
|
+
return { source: "driver_default" };
|
|
261
|
+
}
|
|
262
|
+
function resolveLightpandaExecutablePath(config) {
|
|
263
|
+
const executablePath = config.lightpanda?.executablePath ?? config.executablePath ?? process.env.LIGHTPANDA_EXECUTABLE_PATH;
|
|
264
|
+
if (executablePath) return {
|
|
265
|
+
executablePath,
|
|
266
|
+
source: "lightpanda_config"
|
|
267
|
+
};
|
|
268
|
+
return { source: "lightpanda_managed" };
|
|
269
|
+
}
|
|
270
|
+
async function selectRuntimeDriver$1(config, options) {
|
|
271
|
+
if ([
|
|
272
|
+
config.useChrome,
|
|
273
|
+
config.useChromium,
|
|
274
|
+
config.useLightpanda
|
|
275
|
+
].filter(Boolean).length > 1) throw new Error("Choose only one browser: useChrome, useChromium or useLightpanda.");
|
|
276
|
+
let kind = config.useLightpanda ? "lightpanda" : config.useChromium ? "chromium" : config.useChrome ? "chrome" : config.browser?.kind;
|
|
277
|
+
const explicitChoice = kind !== void 0 || getExplicitUseChromePreference(options.rawConfigs) !== void 0 || config.browser?.download === "auto";
|
|
278
|
+
const preferLocalChrome = shouldPreferLocalChrome(config, options.rawConfigs, options.defaultPreferLocalChrome ?? true);
|
|
279
|
+
let executableResolution = { source: "driver_default" };
|
|
280
|
+
if (kind !== "lightpanda") executableResolution = await resolveExecutablePath(config, {
|
|
281
|
+
preferLocalChrome: kind === "chromium" || preferLocalChrome,
|
|
282
|
+
cacheFilePath: kind === "chromium" ? `${options.cacheFilePath}.chromium` : options.cacheFilePath,
|
|
283
|
+
kind: kind === "chromium" ? "chromium" : "chrome"
|
|
284
|
+
});
|
|
285
|
+
else {
|
|
286
|
+
executableResolution = resolveLightpandaExecutablePath(config);
|
|
287
|
+
executableResolution.executablePath ??= config.executablePath;
|
|
288
|
+
}
|
|
289
|
+
if (!executableResolution.executablePath && (kind !== void 0 || options.promptForMissingBrowser) && !(kind === "lightpanda" && config.lightpanda?.experimentalWhatsApp !== false)) {
|
|
290
|
+
const remembered = await readBrowserChoice();
|
|
291
|
+
if (remembered && (kind === void 0 || remembered.kind === kind)) {
|
|
292
|
+
kind = remembered.kind;
|
|
293
|
+
executableResolution = {
|
|
294
|
+
source: "cache",
|
|
295
|
+
executablePath: remembered.executablePath
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (!kind && executableResolution.executablePath === void 0 && options.promptForMissingBrowser) {
|
|
300
|
+
const { ensureBrowser } = await import("@open-wa/driver-puppeteer");
|
|
301
|
+
try {
|
|
302
|
+
executableResolution = {
|
|
303
|
+
source: "managed_browser",
|
|
304
|
+
executablePath: await ensureBrowser({ browser: {
|
|
305
|
+
...config.browser,
|
|
306
|
+
kind: "chrome",
|
|
307
|
+
download: "never"
|
|
308
|
+
} })
|
|
309
|
+
};
|
|
310
|
+
} catch (error) {
|
|
311
|
+
if (!(error instanceof Error) || !/missing from .* and browser downloads are disabled/.test(error.message)) throw error;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (!kind && !executableResolution.executablePath && !explicitChoice && options.promptForMissingBrowser) {
|
|
315
|
+
if (config.browser?.download === "never") throw new Error("No browser is available and browser downloads are disabled. Configure executablePath or pre-provision a browser cache.");
|
|
316
|
+
if (options.nonInteractive || process.env.CI || process.env.NONINTERACTIVE) throw new Error("No browser is installed. Choose --use-chrome, --use-chromium or --use-lightpanda to download one without a prompt.");
|
|
317
|
+
kind = await chooseBrowser();
|
|
318
|
+
}
|
|
319
|
+
kind ??= "chrome";
|
|
320
|
+
const sink = getCliOutputSink();
|
|
321
|
+
const ctx = { logger: {
|
|
322
|
+
debug: (message) => sink.write({
|
|
323
|
+
level: "debug",
|
|
324
|
+
message
|
|
325
|
+
}),
|
|
326
|
+
info: (message) => sink.write({
|
|
327
|
+
level: "info",
|
|
328
|
+
message
|
|
329
|
+
}),
|
|
330
|
+
warn: (message) => sink.write({
|
|
331
|
+
level: "warn",
|
|
332
|
+
message
|
|
333
|
+
}),
|
|
334
|
+
error: (message) => sink.write({
|
|
335
|
+
level: "error",
|
|
336
|
+
message
|
|
337
|
+
})
|
|
338
|
+
} };
|
|
339
|
+
const browser = {
|
|
340
|
+
...config.browser,
|
|
341
|
+
...explicitChoice || options.promptForMissingBrowser ? { download: config.browser?.download ?? "auto" } : {},
|
|
342
|
+
...kind !== "lightpanda" ? { kind } : {}
|
|
343
|
+
};
|
|
344
|
+
if (kind === "lightpanda") {
|
|
345
|
+
const { LightpandaDriver, ensureLightpanda } = await import("@open-wa/driver-lightpanda");
|
|
346
|
+
config.lightpanda = _open_wa_config.ConfigSchema.shape.lightpanda.parse({
|
|
347
|
+
...config.lightpanda,
|
|
348
|
+
experimentalWhatsApp: config.lightpanda?.experimentalWhatsApp ?? true
|
|
349
|
+
});
|
|
350
|
+
config.browser = browser;
|
|
351
|
+
const executablePath = await ensureLightpanda({
|
|
352
|
+
executablePath: executableResolution.executablePath,
|
|
353
|
+
browser,
|
|
354
|
+
preferPinnedRelease: config.lightpanda?.experimentalWhatsApp ?? true
|
|
355
|
+
}, ctx);
|
|
356
|
+
config.useLightpanda = true;
|
|
357
|
+
config.useChrome = false;
|
|
358
|
+
config.useChromium = false;
|
|
359
|
+
sink.write({
|
|
360
|
+
level: "warn",
|
|
361
|
+
message: "Lightpanda has low compatibility and experimental WhatsApp support; video, screenshots and rendering are unavailable."
|
|
362
|
+
});
|
|
363
|
+
if (options.promptForMissingBrowser) await writeBrowserChoice(kind, executablePath).catch((error) => sink.write({
|
|
364
|
+
level: "warn",
|
|
365
|
+
message: `Could not remember the browser choice: ${error instanceof Error ? error.message : String(error)}`
|
|
366
|
+
}));
|
|
367
|
+
return {
|
|
368
|
+
driver: new LightpandaDriver(),
|
|
369
|
+
engineLabel: "Lightpanda",
|
|
370
|
+
executableResolution: {
|
|
371
|
+
source: "lightpanda_managed",
|
|
372
|
+
executablePath
|
|
373
|
+
},
|
|
374
|
+
preferLocalChrome: false
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
config.browser = browser;
|
|
378
|
+
const { PuppeteerDriver, ensureBrowser } = await import("@open-wa/driver-puppeteer");
|
|
379
|
+
const executablePath = await ensureBrowser({
|
|
380
|
+
executablePath: executableResolution.executablePath,
|
|
381
|
+
browser
|
|
382
|
+
}, ctx);
|
|
383
|
+
if (!executableResolution.executablePath) executableResolution = {
|
|
384
|
+
source: "managed_browser",
|
|
385
|
+
executablePath
|
|
386
|
+
};
|
|
387
|
+
if (options.promptForMissingBrowser) await writeBrowserChoice(kind, executablePath).catch((error) => sink.write({
|
|
388
|
+
level: "warn",
|
|
389
|
+
message: `Could not remember the browser choice: ${error instanceof Error ? error.message : String(error)}`
|
|
390
|
+
}));
|
|
391
|
+
return {
|
|
392
|
+
driver: new PuppeteerDriver(),
|
|
393
|
+
engineLabel: "Puppeteer",
|
|
394
|
+
executableResolution,
|
|
395
|
+
preferLocalChrome
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
/** Load the Node media runtime only when a call needs a source or sink. */
|
|
399
|
+
function makeDefaultCallMediaHost() {
|
|
400
|
+
return { async prepare(...args) {
|
|
401
|
+
const { makeNodeCallMediaHost } = await import("@open-wa/runtime-node");
|
|
402
|
+
return makeNodeCallMediaHost({ log: (message) => getCliOutputSink().write({
|
|
403
|
+
level: "info",
|
|
404
|
+
message
|
|
405
|
+
}) }).prepare(...args);
|
|
406
|
+
} };
|
|
407
|
+
}
|
|
408
|
+
function toCreateClientOptions(config, driverSelection, options = {}) {
|
|
409
|
+
return {
|
|
410
|
+
sessionId: config.sessionId,
|
|
411
|
+
eventMode: config.eventMode,
|
|
412
|
+
driver: driverSelection.driver,
|
|
413
|
+
deleteSessionDataOnLogout: config.deleteSessionDataOnLogout,
|
|
414
|
+
killClientOnLogout: config.killClientOnLogout,
|
|
415
|
+
sessionDataPath: config.sessionDataPath,
|
|
416
|
+
sessionData: config.sessionData,
|
|
417
|
+
skipSessionSave: config.skipSessionSave,
|
|
418
|
+
sessionDataBucketAuth: config.sessionDataBucketAuth,
|
|
419
|
+
sessionDataEncryptionKey: config.sessionDataEncryptionKey,
|
|
420
|
+
sessionDataEncryptionKeyFile: config.sessionDataEncryptionKeyFile,
|
|
421
|
+
s3Sync: config.s3Sync,
|
|
422
|
+
debug: options.debug ?? (config.logLevel === "debug" || config.logConsole),
|
|
423
|
+
headless: config.headless,
|
|
424
|
+
qrTimeoutMs: typeof config.qrTimeout === "number" ? config.qrTimeout * 1e3 : void 0,
|
|
425
|
+
authTimeoutMs: typeof config.authTimeout === "number" ? config.authTimeout * 1e3 : void 0,
|
|
426
|
+
executablePath: driverSelection.executableResolution.executablePath,
|
|
427
|
+
browserArgs: config.chromiumArgs,
|
|
428
|
+
browser: config.browser,
|
|
429
|
+
allowDangerousBrowserArgs: config.allowDangerousBrowserArgs,
|
|
430
|
+
userDataDir: config.userDataDir,
|
|
431
|
+
ephemeral: config.ephemeral,
|
|
432
|
+
linkCode: config.linkCode,
|
|
433
|
+
qrMax: config.qrMax,
|
|
434
|
+
oorTimeoutMs: config.oorTimeout * 1e3,
|
|
435
|
+
logConsole: config.logConsole,
|
|
436
|
+
logConsoleErrors: config.logConsoleErrors,
|
|
437
|
+
blockCrashLogs: config.blockCrashLogs,
|
|
438
|
+
blockAssets: config.blockAssets,
|
|
439
|
+
safeMode: config.safeMode,
|
|
440
|
+
lightpanda: config.useLightpanda ? {
|
|
441
|
+
experimentalWhatsApp: true,
|
|
442
|
+
...config.lightpanda
|
|
443
|
+
} : void 0,
|
|
444
|
+
licenseKey: config.licenseKey,
|
|
445
|
+
calling: config.calling,
|
|
446
|
+
callMediaHost: makeDefaultCallMediaHost(),
|
|
447
|
+
licenseConfig: config.licenseConfig,
|
|
448
|
+
patchConfig: {
|
|
449
|
+
ghPatch: config.ghPatch,
|
|
450
|
+
cachedPatch: config.cachedPatch
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
/** Start WhatsApp, display QR login when needed, and return a ready messaging client. */
|
|
455
|
+
async function create(options = {}) {
|
|
456
|
+
const { createClient: createCoreClient } = await import("@open-wa/core");
|
|
457
|
+
const { Client } = await import("@open-wa/client");
|
|
458
|
+
const { driver, sessionData, sessionDataBucketAuth, s3Sync, calling, sessionDataEncryptionKey, sessionDataEncryptionKeyFile, ...configOverrides } = options;
|
|
459
|
+
const { config, rawConfigs } = await (0, _open_wa_config.resolveConfig)({
|
|
460
|
+
programmaticOverrides: {
|
|
461
|
+
qrTimeout: 0,
|
|
462
|
+
...configOverrides
|
|
463
|
+
},
|
|
464
|
+
skipConfigFile: true,
|
|
465
|
+
skipEnv: true,
|
|
466
|
+
includeRawConfigs: true
|
|
467
|
+
});
|
|
468
|
+
const coreOptions = toCreateClientOptions(config, driver ? {
|
|
469
|
+
driver,
|
|
470
|
+
engineLabel: driver.name === "lightpanda" ? "Lightpanda" : "Puppeteer",
|
|
471
|
+
executableResolution: {
|
|
472
|
+
source: "config",
|
|
473
|
+
executablePath: config.executablePath
|
|
474
|
+
},
|
|
475
|
+
preferLocalChrome: false
|
|
476
|
+
} : await selectRuntimeDriver$1(config, {
|
|
477
|
+
rawConfigs,
|
|
478
|
+
cacheFilePath: (0, node_path.resolve)(process.cwd(), ".open-wa", "chrome-executable-path.json"),
|
|
479
|
+
defaultPreferLocalChrome: false
|
|
480
|
+
}));
|
|
481
|
+
if (calling) coreOptions.calling = calling;
|
|
482
|
+
if (driver) coreOptions.driver = driver;
|
|
483
|
+
coreOptions.sessionData = sessionData;
|
|
484
|
+
coreOptions.sessionDataBucketAuth = sessionDataBucketAuth;
|
|
485
|
+
coreOptions.sessionDataEncryptionKey = sessionDataEncryptionKey;
|
|
486
|
+
coreOptions.sessionDataEncryptionKeyFile = sessionDataEncryptionKeyFile;
|
|
487
|
+
coreOptions.s3Sync = s3Sync;
|
|
488
|
+
const core = await createCoreClient(coreOptions);
|
|
489
|
+
const sink = getCliOutputSink();
|
|
490
|
+
const onQr = (event) => {
|
|
491
|
+
if (config.qrLogSkip || !event.details?.qr) return;
|
|
492
|
+
sink.write({
|
|
493
|
+
level: "info",
|
|
494
|
+
message: "Open WhatsApp on your sending phone: Linked Devices → Link a Device, then scan this QR code."
|
|
495
|
+
});
|
|
496
|
+
sink.qr({
|
|
497
|
+
qr: event.details.qr,
|
|
498
|
+
sessionId: config.sessionId
|
|
499
|
+
});
|
|
500
|
+
};
|
|
501
|
+
const onLinkCode = (event) => {
|
|
502
|
+
if (event.details?.linkCode && !config.qrLogSkip) sink.write({
|
|
503
|
+
level: "info",
|
|
504
|
+
message: `Enter this link code on your sending phone: ${event.details.linkCode}`
|
|
505
|
+
});
|
|
506
|
+
};
|
|
507
|
+
core.events.on("launch.auth.qr.generated", onQr);
|
|
508
|
+
core.events.on("launch.auth.linkCode.generated", onLinkCode);
|
|
509
|
+
let client;
|
|
510
|
+
try {
|
|
511
|
+
client = new Client({
|
|
512
|
+
client: core,
|
|
513
|
+
transport: core.getTransport()
|
|
514
|
+
});
|
|
515
|
+
await client.start();
|
|
516
|
+
if (core.getState() !== "READY" || !core.getReadiness().exposureSafe) throw new Error("WhatsApp startup finished without a messaging-ready session.");
|
|
517
|
+
return client;
|
|
518
|
+
} catch (error) {
|
|
519
|
+
try {
|
|
520
|
+
if (client) await client.stop("startup-failure");
|
|
521
|
+
else await core.stop("startup-failure");
|
|
522
|
+
} catch {}
|
|
523
|
+
throw error;
|
|
524
|
+
} finally {
|
|
525
|
+
core.events.off("launch.auth.qr.generated", onQr);
|
|
526
|
+
core.events.off("launch.auth.linkCode.generated", onLinkCode);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
//#endregion
|
|
80
530
|
//#region src/cli-runtime.ts
|
|
81
531
|
function bridgeRuntimeEvents(openwaClient, publishRuntimeEvent) {
|
|
82
532
|
if (typeof openwaClient.events.onAny === "function" && typeof openwaClient.events.offAny === "function") {
|
|
@@ -84,7 +534,9 @@ function bridgeRuntimeEvents(openwaClient, publishRuntimeEvent) {
|
|
|
84
534
|
return () => openwaClient.events.offAny(publishRuntimeEvent);
|
|
85
535
|
}
|
|
86
536
|
const unsubscribers = [];
|
|
87
|
-
new Set(_open_wa_schema.eventRegistry.getAll().map((def) => def.meta.eventName))
|
|
537
|
+
const bridgedEvents = new Set(_open_wa_schema.eventRegistry.getAll().map((def) => def.meta.eventName));
|
|
538
|
+
bridgedEvents.add("session.refresh.progress");
|
|
539
|
+
bridgedEvents.forEach((eventName) => {
|
|
88
540
|
const handler = (payload) => publishRuntimeEvent(eventName, payload);
|
|
89
541
|
openwaClient.events.on(eventName, handler);
|
|
90
542
|
unsubscribers.push(() => openwaClient.events.off(eventName, handler));
|
|
@@ -271,51 +723,6 @@ const CHROME_CACHE_FILE = (0, node_path.resolve)(process.cwd(), ".open-wa", "chr
|
|
|
271
723
|
function getChromeCacheFilePath() {
|
|
272
724
|
return CHROME_CACHE_FILE;
|
|
273
725
|
}
|
|
274
|
-
function readChromePathCache(cacheFilePath = getChromeCacheFilePath()) {
|
|
275
|
-
if (!(0, node_fs.existsSync)(cacheFilePath)) return;
|
|
276
|
-
try {
|
|
277
|
-
const raw = JSON.parse((0, node_fs.readFileSync)(cacheFilePath, "utf8"));
|
|
278
|
-
if (!raw || typeof raw !== "object") return;
|
|
279
|
-
return {
|
|
280
|
-
executablePath: typeof raw.executablePath === "string" ? raw.executablePath : void 0,
|
|
281
|
-
updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : void 0
|
|
282
|
-
};
|
|
283
|
-
} catch {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
function writeChromePathCache(executablePath, cacheFilePath = getChromeCacheFilePath()) {
|
|
288
|
-
(0, node_fs.mkdirSync)((0, node_path.dirname)(cacheFilePath), { recursive: true });
|
|
289
|
-
(0, node_fs.writeFileSync)(cacheFilePath, JSON.stringify({
|
|
290
|
-
executablePath,
|
|
291
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292
|
-
}, null, 2), "utf8");
|
|
293
|
-
}
|
|
294
|
-
function clearChromePathCache(cacheFilePath = getChromeCacheFilePath()) {
|
|
295
|
-
if ((0, node_fs.existsSync)(cacheFilePath)) (0, node_fs.rmSync)(cacheFilePath, { force: true });
|
|
296
|
-
}
|
|
297
|
-
function isUsableExecutablePath(executablePath) {
|
|
298
|
-
return !!executablePath && (0, node_fs.existsSync)(executablePath);
|
|
299
|
-
}
|
|
300
|
-
function getExplicitUseChromePreference(rawConfigs) {
|
|
301
|
-
const explicitConfigSources = [
|
|
302
|
-
rawConfigs?.file,
|
|
303
|
-
rawConfigs?.env,
|
|
304
|
-
rawConfigs?.cli,
|
|
305
|
-
rawConfigs?.programmatic
|
|
306
|
-
];
|
|
307
|
-
for (let index = explicitConfigSources.length - 1; index >= 0; index -= 1) {
|
|
308
|
-
const source = explicitConfigSources[index];
|
|
309
|
-
if (source?.useChrome !== void 0) return source.useChrome;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
function shouldPreferLocalChrome(config, rawConfigs) {
|
|
313
|
-
if (config.useLightpanda) return false;
|
|
314
|
-
if (config.executablePath) return false;
|
|
315
|
-
const explicitUseChrome = getExplicitUseChromePreference(rawConfigs);
|
|
316
|
-
if (explicitUseChrome !== void 0) return explicitUseChrome;
|
|
317
|
-
return true;
|
|
318
|
-
}
|
|
319
726
|
function getVal(argv, flag) {
|
|
320
727
|
const index = argv.findIndex((arg) => arg === flag);
|
|
321
728
|
return index !== -1 ? argv[index + 1] : void 0;
|
|
@@ -358,8 +765,17 @@ function parseCliArgs(argv = process.argv.slice(2)) {
|
|
|
358
765
|
if (argv.includes("--no-ezqr")) cliOverrides.ezqr = false;
|
|
359
766
|
if (argv.includes("--headful")) cliOverrides.headless = false;
|
|
360
767
|
if (argv.includes("--headless")) cliOverrides.headless = true;
|
|
361
|
-
|
|
362
|
-
|
|
768
|
+
const browserFlags = [
|
|
769
|
+
"--use-chrome",
|
|
770
|
+
"--use-chromium",
|
|
771
|
+
"--use-lightpanda"
|
|
772
|
+
].filter((flag) => argv.includes(flag) || flag === "--use-lightpanda" && argv.includes("--lightpanda"));
|
|
773
|
+
if (browserFlags.length > 1) throw new Error("Choose only one of --use-chrome, --use-chromium or --use-lightpanda.");
|
|
774
|
+
if (browserFlags.length) {
|
|
775
|
+
cliOverrides.useChrome = browserFlags[0] === "--use-chrome";
|
|
776
|
+
cliOverrides.useChromium = browserFlags[0] === "--use-chromium";
|
|
777
|
+
cliOverrides.useLightpanda = browserFlags[0] === "--use-lightpanda";
|
|
778
|
+
}
|
|
363
779
|
if (argv.includes("--log-console")) cliOverrides.logConsole = true;
|
|
364
780
|
if (argv.includes("--aggressive-garbage-collection")) cliOverrides.aggressiveGarbageCollection = true;
|
|
365
781
|
if (argv.includes("--no-dashboard")) cliOverrides.dashboard = false;
|
|
@@ -412,57 +828,13 @@ function parseCliArgs(argv = process.argv.slice(2)) {
|
|
|
412
828
|
unsupportedWarnings
|
|
413
829
|
};
|
|
414
830
|
}
|
|
415
|
-
async function
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const cachedPath = readChromePathCache(cacheFilePath)?.executablePath;
|
|
423
|
-
if (isUsableExecutablePath(cachedPath)) return {
|
|
424
|
-
executablePath: cachedPath,
|
|
425
|
-
source: "cache"
|
|
426
|
-
};
|
|
427
|
-
if (cachedPath) clearChromePathCache(cacheFilePath);
|
|
428
|
-
const { Launcher } = await import("chrome-launcher");
|
|
429
|
-
const detectedPath = Launcher.getInstallations().find((installationPath) => isUsableExecutablePath(installationPath));
|
|
430
|
-
if (detectedPath) {
|
|
431
|
-
writeChromePathCache(detectedPath, cacheFilePath);
|
|
432
|
-
return {
|
|
433
|
-
executablePath: detectedPath,
|
|
434
|
-
source: "chrome_installation"
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
clearChromePathCache(cacheFilePath);
|
|
438
|
-
return {
|
|
439
|
-
source: "driver_default",
|
|
440
|
-
warning: "Chrome resolution warning: no valid local Chrome installation was found. Falling back to Puppeteer/default driver browser resolution."
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
function resolveLightpandaExecutablePath(config) {
|
|
444
|
-
const executablePath = config.lightpanda?.executablePath;
|
|
445
|
-
if (executablePath) return {
|
|
446
|
-
executablePath,
|
|
447
|
-
source: "lightpanda_config"
|
|
448
|
-
};
|
|
449
|
-
return { source: "lightpanda_sdk_default" };
|
|
450
|
-
}
|
|
451
|
-
async function selectRuntimeDriver(config, rawConfigs) {
|
|
452
|
-
if (config.useLightpanda) return {
|
|
453
|
-
driver: new _open_wa_driver_lightpanda.LightpandaDriver(),
|
|
454
|
-
engineLabel: "Lightpanda",
|
|
455
|
-
executableResolution: resolveLightpandaExecutablePath(config),
|
|
456
|
-
preferLocalChrome: false
|
|
457
|
-
};
|
|
458
|
-
const preferLocalChrome = shouldPreferLocalChrome(config, rawConfigs);
|
|
459
|
-
const executableResolution = await resolveExecutablePath(config, { preferLocalChrome });
|
|
460
|
-
return {
|
|
461
|
-
driver: new _open_wa_driver_puppeteer.PuppeteerDriver(),
|
|
462
|
-
engineLabel: "Puppeteer",
|
|
463
|
-
executableResolution,
|
|
464
|
-
preferLocalChrome
|
|
465
|
-
};
|
|
831
|
+
async function selectRuntimeDriver(config, rawConfigs, nonInteractive = false) {
|
|
832
|
+
return selectRuntimeDriver$1(config, {
|
|
833
|
+
rawConfigs,
|
|
834
|
+
cacheFilePath: getChromeCacheFilePath(),
|
|
835
|
+
promptForMissingBrowser: true,
|
|
836
|
+
nonInteractive
|
|
837
|
+
});
|
|
466
838
|
}
|
|
467
839
|
function printStartupSummary(config, resolution, preferLocalChrome, engineLabel) {
|
|
468
840
|
const host = config.host.includes("http") ? config.host : `http://${config.host}`;
|
|
@@ -507,9 +879,9 @@ function printStartupSummary(config, resolution, preferLocalChrome, engineLabel)
|
|
|
507
879
|
level: "info",
|
|
508
880
|
message: `Browser executable: explicit Lightpanda override (${resolution.executablePath})`
|
|
509
881
|
});
|
|
510
|
-
else if (resolution.source === "
|
|
882
|
+
else if (resolution.source === "lightpanda_managed") sink.write({
|
|
511
883
|
level: "info",
|
|
512
|
-
message:
|
|
884
|
+
message: `Browser executable: Lightpanda with experimental WhatsApp setup (${resolution.executablePath})`
|
|
513
885
|
});
|
|
514
886
|
else if (resolution.source === "config" && config.executablePath) sink.write({
|
|
515
887
|
level: "info",
|
|
@@ -517,15 +889,15 @@ function printStartupSummary(config, resolution, preferLocalChrome, engineLabel)
|
|
|
517
889
|
});
|
|
518
890
|
else if (resolution.source === "cache") sink.write({
|
|
519
891
|
level: "info",
|
|
520
|
-
message: `Browser executable:
|
|
892
|
+
message: `Browser executable: cached browser (${resolution.executablePath})`
|
|
521
893
|
});
|
|
522
894
|
else if (resolution.source === "chrome_installation") sink.write({
|
|
523
895
|
level: "info",
|
|
524
|
-
message: `Browser executable:
|
|
896
|
+
message: `Browser executable: installed ${config.browser?.kind === "chromium" ? "Chromium" : "Chrome"} (${resolution.executablePath})`
|
|
525
897
|
});
|
|
526
|
-
else if (
|
|
898
|
+
else if (resolution.source === "managed_browser") sink.write({
|
|
527
899
|
level: "info",
|
|
528
|
-
message:
|
|
900
|
+
message: `Browser executable: managed ${config.browser?.kind === "chromium" ? "Chromium" : "Chrome"} (${resolution.executablePath})`
|
|
529
901
|
});
|
|
530
902
|
if (config.webhook) sink.write({
|
|
531
903
|
level: "warn",
|
|
@@ -564,7 +936,7 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
564
936
|
level: "warn",
|
|
565
937
|
message: `Compatibility warning: ${warning}`
|
|
566
938
|
}));
|
|
567
|
-
const { driver, engineLabel, executableResolution, preferLocalChrome } = await selectRuntimeDriver(config, rawConfigs);
|
|
939
|
+
const { driver, engineLabel, executableResolution, preferLocalChrome } = await selectRuntimeDriver(config, rawConfigs, parsedArgs.forwardedArgs.includes("--non-interactive"));
|
|
568
940
|
if (executableResolution.warning) sink.write({
|
|
569
941
|
level: "warn",
|
|
570
942
|
message: executableResolution.warning
|
|
@@ -592,6 +964,7 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
592
964
|
const executionSandbox = sandboxPolicy ? (0, _open_wa_runtime_node.makeNodeExecutionSandbox)({ workspacePath: process.cwd() }) : void 0;
|
|
593
965
|
const openwaClient = await (0, _open_wa_core.createClient)({
|
|
594
966
|
sessionId: config.sessionId,
|
|
967
|
+
eventMode: config.eventMode,
|
|
595
968
|
driver,
|
|
596
969
|
deleteSessionDataOnLogout: config.deleteSessionDataOnLogout,
|
|
597
970
|
killClientOnLogout: config.killClientOnLogout,
|
|
@@ -618,7 +991,14 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
618
991
|
blockCrashLogs: config.blockCrashLogs,
|
|
619
992
|
blockAssets: config.blockAssets,
|
|
620
993
|
safeMode: config.safeMode,
|
|
994
|
+
lightpanda: config.useLightpanda ? {
|
|
995
|
+
experimentalWhatsApp: true,
|
|
996
|
+
...config.lightpanda
|
|
997
|
+
} : void 0,
|
|
621
998
|
licenseKey: config.licenseKey,
|
|
999
|
+
licenseConfig: config.licenseConfig,
|
|
1000
|
+
calling: config.calling,
|
|
1001
|
+
callMediaHost: makeDefaultCallMediaHost(),
|
|
622
1002
|
sandboxPolicy,
|
|
623
1003
|
executionSandbox,
|
|
624
1004
|
memoryObservation: (observability, getBrowserProcessId) => (0, _open_wa_runtime_node.observeBrowserProcessMemory)(observability, getBrowserProcessId, { attributes: { session: config.sessionId } })
|
|
@@ -628,6 +1008,8 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
628
1008
|
state: openwaClient.getState()
|
|
629
1009
|
}));
|
|
630
1010
|
const detachLaunchNarration = attachLaunchNarration(openwaClient, sink, config.sessionId);
|
|
1011
|
+
openwaClient.events.on("launch.auth.qr.expired", () => server.setQR(null));
|
|
1012
|
+
openwaClient.events.on("launch.auth.qr.scanned", () => server.setQR(null));
|
|
631
1013
|
openwaClient.events.on("launch.auth.qr.generated", (event) => {
|
|
632
1014
|
const qr = event.details?.qr;
|
|
633
1015
|
if (!qr) return;
|
|
@@ -709,7 +1091,7 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
709
1091
|
});
|
|
710
1092
|
sink.write({
|
|
711
1093
|
level: "warn",
|
|
712
|
-
message: "
|
|
1094
|
+
message: "The API server is still running for inspection; the client failed to start."
|
|
713
1095
|
});
|
|
714
1096
|
sink.write({
|
|
715
1097
|
level: "warn",
|
|
@@ -745,7 +1127,7 @@ async function start(parsedArgs = parseCliArgs()) {
|
|
|
745
1127
|
}
|
|
746
1128
|
async function main(argv = process.argv.slice(2)) {
|
|
747
1129
|
if (argv[0] === "init") {
|
|
748
|
-
const { initProject } = await Promise.resolve().then(() => require("./init-
|
|
1130
|
+
const { initProject } = await Promise.resolve().then(() => require("./init-CUhC5Wkq.cjs"));
|
|
749
1131
|
await initProject(argv.slice(1));
|
|
750
1132
|
return;
|
|
751
1133
|
}
|
|
@@ -815,6 +1197,12 @@ Object.defineProperty(exports, "WAServer", {
|
|
|
815
1197
|
return WAServer;
|
|
816
1198
|
}
|
|
817
1199
|
});
|
|
1200
|
+
Object.defineProperty(exports, "create", {
|
|
1201
|
+
enumerable: true,
|
|
1202
|
+
get: function() {
|
|
1203
|
+
return create;
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
818
1206
|
Object.defineProperty(exports, "createConsoleOutputSink", {
|
|
819
1207
|
enumerable: true,
|
|
820
1208
|
get: function() {
|
|
@@ -858,4 +1246,4 @@ Object.defineProperty(exports, "start", {
|
|
|
858
1246
|
}
|
|
859
1247
|
});
|
|
860
1248
|
|
|
861
|
-
//# sourceMappingURL=cli-runtime-
|
|
1249
|
+
//# sourceMappingURL=cli-runtime-BRj_LUIT.cjs.map
|