browsermation 0.0.89 → 0.0.91
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/bin/cli.js +229 -16
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -18419,8 +18419,8 @@ var require_graceful_fs = __commonJS({
|
|
|
18419
18419
|
fs2.createReadStream = createReadStream;
|
|
18420
18420
|
fs2.createWriteStream = createWriteStream3;
|
|
18421
18421
|
var fs$readFile = fs2.readFile;
|
|
18422
|
-
fs2.readFile =
|
|
18423
|
-
function
|
|
18422
|
+
fs2.readFile = readFile2;
|
|
18423
|
+
function readFile2(path2, options, cb) {
|
|
18424
18424
|
if (typeof options === "function")
|
|
18425
18425
|
cb = options, options = null;
|
|
18426
18426
|
return go$readFile(path2, options, cb);
|
|
@@ -18436,8 +18436,8 @@ var require_graceful_fs = __commonJS({
|
|
|
18436
18436
|
}
|
|
18437
18437
|
}
|
|
18438
18438
|
var fs$writeFile = fs2.writeFile;
|
|
18439
|
-
fs2.writeFile =
|
|
18440
|
-
function
|
|
18439
|
+
fs2.writeFile = writeFile3;
|
|
18440
|
+
function writeFile3(path2, data, options, cb) {
|
|
18441
18441
|
if (typeof options === "function")
|
|
18442
18442
|
cb = options, options = null;
|
|
18443
18443
|
return go$writeFile(path2, data, options, cb);
|
|
@@ -18998,7 +18998,7 @@ var require_BufferList = __commonJS({
|
|
|
18998
18998
|
this.head = this.tail = null;
|
|
18999
18999
|
this.length = 0;
|
|
19000
19000
|
};
|
|
19001
|
-
BufferList.prototype.join = function
|
|
19001
|
+
BufferList.prototype.join = function join4(s) {
|
|
19002
19002
|
if (this.length === 0) return "";
|
|
19003
19003
|
var p = this.head;
|
|
19004
19004
|
var ret = "" + p.data;
|
|
@@ -40603,7 +40603,7 @@ var {
|
|
|
40603
40603
|
// package.json
|
|
40604
40604
|
var package_default = {
|
|
40605
40605
|
name: "browsermation",
|
|
40606
|
-
version: "0.0.
|
|
40606
|
+
version: "0.0.91",
|
|
40607
40607
|
description: "The testing platform for Playwright by Browsermation.",
|
|
40608
40608
|
main: "./dist/index.js",
|
|
40609
40609
|
types: "./dist/index.d.ts",
|
|
@@ -46895,8 +46895,126 @@ async function zipFolderToBuffer(folder, testFiles) {
|
|
|
46895
46895
|
}
|
|
46896
46896
|
|
|
46897
46897
|
// src/core.ts
|
|
46898
|
+
var import_promises2 = require("node:fs/promises");
|
|
46899
|
+
var import_node_fs2 = require("node:fs");
|
|
46900
|
+
|
|
46901
|
+
// src/auth.ts
|
|
46902
|
+
var import_node_os2 = require("node:os");
|
|
46903
|
+
var import_node_path = require("node:path");
|
|
46898
46904
|
var import_promises = require("node:fs/promises");
|
|
46899
46905
|
var import_node_fs = require("node:fs");
|
|
46906
|
+
var API_BASE = "https://browsermation.com";
|
|
46907
|
+
var CREDENTIALS_DIR = (0, import_node_path.join)((0, import_node_os2.homedir)(), ".browsermation");
|
|
46908
|
+
var CREDENTIALS_FILE = (0, import_node_path.join)(CREDENTIALS_DIR, "credentials");
|
|
46909
|
+
async function requestDeviceCode() {
|
|
46910
|
+
const response = await axios_default.post(
|
|
46911
|
+
`${API_BASE}/api/device/code`,
|
|
46912
|
+
{},
|
|
46913
|
+
{
|
|
46914
|
+
headers: {
|
|
46915
|
+
"Content-Type": "application/json",
|
|
46916
|
+
Accept: "application/json"
|
|
46917
|
+
}
|
|
46918
|
+
}
|
|
46919
|
+
);
|
|
46920
|
+
return response.data;
|
|
46921
|
+
}
|
|
46922
|
+
async function pollForToken(deviceCode, interval, expiresIn) {
|
|
46923
|
+
const startTime = Date.now();
|
|
46924
|
+
const expiresAt = startTime + expiresIn * 1e3;
|
|
46925
|
+
while (Date.now() < expiresAt) {
|
|
46926
|
+
await sleep(interval * 1e3);
|
|
46927
|
+
try {
|
|
46928
|
+
const response = await axios_default.post(
|
|
46929
|
+
`${API_BASE}/api/device/token`,
|
|
46930
|
+
{ device_code: deviceCode },
|
|
46931
|
+
{
|
|
46932
|
+
headers: {
|
|
46933
|
+
"Content-Type": "application/json",
|
|
46934
|
+
Accept: "application/json"
|
|
46935
|
+
}
|
|
46936
|
+
}
|
|
46937
|
+
);
|
|
46938
|
+
if (response.data.access_token) {
|
|
46939
|
+
return {
|
|
46940
|
+
token: response.data.access_token,
|
|
46941
|
+
user: response.data.user
|
|
46942
|
+
};
|
|
46943
|
+
}
|
|
46944
|
+
if (response.data.error === "authorization_pending") {
|
|
46945
|
+
continue;
|
|
46946
|
+
}
|
|
46947
|
+
if (response.data.error === "expired_token") {
|
|
46948
|
+
throw new Error("Authentication request expired. Please try again.");
|
|
46949
|
+
}
|
|
46950
|
+
if (response.data.error) {
|
|
46951
|
+
throw new Error(`Authentication failed: ${response.data.error}`);
|
|
46952
|
+
}
|
|
46953
|
+
} catch (error2) {
|
|
46954
|
+
if (axios_default.isAxiosError(error2)) {
|
|
46955
|
+
const data = error2.response?.data;
|
|
46956
|
+
if (data?.error === "authorization_pending") {
|
|
46957
|
+
continue;
|
|
46958
|
+
}
|
|
46959
|
+
if (data?.error === "expired_token") {
|
|
46960
|
+
throw new Error("Authentication request expired. Please try again.");
|
|
46961
|
+
}
|
|
46962
|
+
if (error2.response?.status === 400 && data?.error) {
|
|
46963
|
+
throw new Error(`Authentication failed: ${data.error}`);
|
|
46964
|
+
}
|
|
46965
|
+
}
|
|
46966
|
+
throw error2;
|
|
46967
|
+
}
|
|
46968
|
+
}
|
|
46969
|
+
throw new Error("Authentication timed out. Please try again.");
|
|
46970
|
+
}
|
|
46971
|
+
async function saveCredentials(credentials) {
|
|
46972
|
+
if (!(0, import_node_fs.existsSync)(CREDENTIALS_DIR)) {
|
|
46973
|
+
await (0, import_promises.mkdir)(CREDENTIALS_DIR, { recursive: true, mode: 448 });
|
|
46974
|
+
}
|
|
46975
|
+
await (0, import_promises.writeFile)(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), {
|
|
46976
|
+
mode: 384
|
|
46977
|
+
});
|
|
46978
|
+
}
|
|
46979
|
+
async function loadCredentials() {
|
|
46980
|
+
if (!(0, import_node_fs.existsSync)(CREDENTIALS_FILE)) {
|
|
46981
|
+
return null;
|
|
46982
|
+
}
|
|
46983
|
+
try {
|
|
46984
|
+
const content = await (0, import_promises.readFile)(CREDENTIALS_FILE, "utf-8");
|
|
46985
|
+
return JSON.parse(content);
|
|
46986
|
+
} catch {
|
|
46987
|
+
return null;
|
|
46988
|
+
}
|
|
46989
|
+
}
|
|
46990
|
+
async function clearCredentials() {
|
|
46991
|
+
if ((0, import_node_fs.existsSync)(CREDENTIALS_FILE)) {
|
|
46992
|
+
await (0, import_promises.unlink)(CREDENTIALS_FILE);
|
|
46993
|
+
}
|
|
46994
|
+
}
|
|
46995
|
+
function getToken() {
|
|
46996
|
+
if (process.env.BM_API_TOKEN) {
|
|
46997
|
+
return process.env.BM_API_TOKEN;
|
|
46998
|
+
}
|
|
46999
|
+
if (!(0, import_node_fs.existsSync)(CREDENTIALS_FILE)) {
|
|
47000
|
+
return null;
|
|
47001
|
+
}
|
|
47002
|
+
try {
|
|
47003
|
+
const content = require("fs").readFileSync(CREDENTIALS_FILE, "utf-8");
|
|
47004
|
+
const credentials = JSON.parse(content);
|
|
47005
|
+
return credentials.token || null;
|
|
47006
|
+
} catch {
|
|
47007
|
+
return null;
|
|
47008
|
+
}
|
|
47009
|
+
}
|
|
47010
|
+
function getCredentialsPath() {
|
|
47011
|
+
return CREDENTIALS_FILE;
|
|
47012
|
+
}
|
|
47013
|
+
function sleep(ms) {
|
|
47014
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
47015
|
+
}
|
|
47016
|
+
|
|
47017
|
+
// src/core.ts
|
|
46900
47018
|
async function uploadZip(zipBuffer, shardNumber, totalShards, playwrightConfig, options = {}, eventStream) {
|
|
46901
47019
|
if (!process.env.API_URL) {
|
|
46902
47020
|
console.error(source_default.red("Error: API_URL environment variable is not set."));
|
|
@@ -46912,9 +47030,18 @@ async function uploadZip(zipBuffer, shardNumber, totalShards, playwrightConfig,
|
|
|
46912
47030
|
if (options?.project) {
|
|
46913
47031
|
formData.append("project", options.project);
|
|
46914
47032
|
}
|
|
47033
|
+
const token = getToken();
|
|
47034
|
+
if (!token) {
|
|
47035
|
+
console.error(
|
|
47036
|
+
source_default.red(
|
|
47037
|
+
"Error: Not authenticated. Run 'browsermation login' or set BM_API_TOKEN environment variable."
|
|
47038
|
+
)
|
|
47039
|
+
);
|
|
47040
|
+
process.exit(1);
|
|
47041
|
+
}
|
|
46915
47042
|
const headers = {
|
|
46916
47043
|
"Content-Type": "multipart/form-data",
|
|
46917
|
-
Authorization: `Bearer ${
|
|
47044
|
+
Authorization: `Bearer ${token}`
|
|
46918
47045
|
};
|
|
46919
47046
|
const response = await axios_default.post(process.env.API_URL, formData, {
|
|
46920
47047
|
responseType: "stream",
|
|
@@ -46939,7 +47066,7 @@ async function uploadZip(zipBuffer, shardNumber, totalShards, playwrightConfig,
|
|
|
46939
47066
|
async function zip(folder, testFiles) {
|
|
46940
47067
|
console.log("zipping", folder);
|
|
46941
47068
|
const sourceDir = path.resolve(folder);
|
|
46942
|
-
if (!(0,
|
|
47069
|
+
if (!(0, import_node_fs2.existsSync)(sourceDir)) {
|
|
46943
47070
|
console.error(
|
|
46944
47071
|
source_default.red(`Error: The directory "${sourceDir}" does not exist.`)
|
|
46945
47072
|
);
|
|
@@ -46961,7 +47088,7 @@ async function zip(folder, testFiles) {
|
|
|
46961
47088
|
}
|
|
46962
47089
|
zipSpinner.stop();
|
|
46963
47090
|
const tmpZipPath = path.join("/tmp", `project-${Date.now()}.zip`);
|
|
46964
|
-
await (0,
|
|
47091
|
+
await (0, import_promises2.writeFile)(tmpZipPath, zipBuffer);
|
|
46965
47092
|
console.log(`Zipped project folder to ${tmpZipPath}`);
|
|
46966
47093
|
return zipBuffer;
|
|
46967
47094
|
}
|
|
@@ -47006,10 +47133,11 @@ async function upload(zipBuffer, shardNumber, totalShards, playwrightConfig, opt
|
|
|
47006
47133
|
// src/tunnel.ts
|
|
47007
47134
|
var import_node_child_process = require("node:child_process");
|
|
47008
47135
|
function startTunnel(tunnelProcess, options) {
|
|
47009
|
-
|
|
47136
|
+
const token = getToken();
|
|
47137
|
+
if (!token) {
|
|
47010
47138
|
console.error(
|
|
47011
47139
|
source_default.red.bold(
|
|
47012
|
-
"\u274C Error:
|
|
47140
|
+
"\u274C Error: Not authenticated. Run 'browsermation login' or set BM_API_TOKEN environment variable."
|
|
47013
47141
|
)
|
|
47014
47142
|
);
|
|
47015
47143
|
process.exit(1);
|
|
@@ -47035,7 +47163,7 @@ function startTunnel(tunnelProcess, options) {
|
|
|
47035
47163
|
"2200",
|
|
47036
47164
|
"http",
|
|
47037
47165
|
"--user",
|
|
47038
|
-
|
|
47166
|
+
token
|
|
47039
47167
|
];
|
|
47040
47168
|
if (options.host) {
|
|
47041
47169
|
args.push("--host-header-rewrite", options.host);
|
|
@@ -47084,10 +47212,10 @@ function startTunnel(tunnelProcess, options) {
|
|
|
47084
47212
|
var import_node_stream = __toESM(require("node:stream"));
|
|
47085
47213
|
|
|
47086
47214
|
// src/http.ts
|
|
47087
|
-
var
|
|
47088
|
-
var
|
|
47215
|
+
var import_node_fs3 = require("node:fs");
|
|
47216
|
+
var import_promises3 = require("node:fs/promises");
|
|
47089
47217
|
async function downloadFile(fileUrl, outputLocationPath) {
|
|
47090
|
-
const writer = (0,
|
|
47218
|
+
const writer = (0, import_node_fs3.createWriteStream)(outputLocationPath);
|
|
47091
47219
|
console.log(`Downloading file from ${fileUrl}...`);
|
|
47092
47220
|
try {
|
|
47093
47221
|
const response = await axios_default({
|
|
@@ -47104,7 +47232,7 @@ async function downloadFile(fileUrl, outputLocationPath) {
|
|
|
47104
47232
|
});
|
|
47105
47233
|
writer.on("error", async (err) => {
|
|
47106
47234
|
console.error("Error writing file to disk:", err);
|
|
47107
|
-
await (0,
|
|
47235
|
+
await (0, import_promises3.unlink)(outputLocationPath);
|
|
47108
47236
|
});
|
|
47109
47237
|
});
|
|
47110
47238
|
} catch (err) {
|
|
@@ -47209,6 +47337,91 @@ program2.command("test").option("-t, --tunnel <port>").option("-p, --proxy").opt
|
|
|
47209
47337
|
process.exit(1);
|
|
47210
47338
|
}
|
|
47211
47339
|
});
|
|
47340
|
+
program2.command("login").description("Authenticate with BrowserMation").action(async () => {
|
|
47341
|
+
try {
|
|
47342
|
+
const existingCreds = await loadCredentials();
|
|
47343
|
+
if (existingCreds?.user) {
|
|
47344
|
+
console.log(
|
|
47345
|
+
source_default.yellow(
|
|
47346
|
+
`Already logged in as ${existingCreds.user.email}. Run 'browsermation logout' first to switch accounts.`
|
|
47347
|
+
)
|
|
47348
|
+
);
|
|
47349
|
+
return;
|
|
47350
|
+
}
|
|
47351
|
+
const spinner = ora("Requesting authentication code...").start();
|
|
47352
|
+
const deviceCode = await requestDeviceCode();
|
|
47353
|
+
spinner.stop();
|
|
47354
|
+
console.log("\nTo authenticate, visit:");
|
|
47355
|
+
console.log(source_default.cyan.bold(` ${deviceCode.verification_uri}`));
|
|
47356
|
+
console.log("\nAnd enter code:");
|
|
47357
|
+
console.log(source_default.green.bold(` ${deviceCode.user_code}`));
|
|
47358
|
+
console.log("");
|
|
47359
|
+
const pollSpinner = ora("Waiting for authentication...").start();
|
|
47360
|
+
const credentials = await pollForToken(
|
|
47361
|
+
deviceCode.device_code,
|
|
47362
|
+
deviceCode.interval,
|
|
47363
|
+
deviceCode.expires_in
|
|
47364
|
+
);
|
|
47365
|
+
await saveCredentials(credentials);
|
|
47366
|
+
pollSpinner.succeed(source_default.green("Authentication successful!"));
|
|
47367
|
+
if (credentials.user) {
|
|
47368
|
+
console.log(source_default.green(`Logged in as ${credentials.user.email}`));
|
|
47369
|
+
}
|
|
47370
|
+
console.log(
|
|
47371
|
+
source_default.dim(`Token saved to ${getCredentialsPath()}`)
|
|
47372
|
+
);
|
|
47373
|
+
} catch (error2) {
|
|
47374
|
+
if (error2 instanceof Error) {
|
|
47375
|
+
console.error(source_default.red(`Login failed: ${error2.message}`));
|
|
47376
|
+
} else {
|
|
47377
|
+
console.error(source_default.red("Login failed: An unknown error occurred"));
|
|
47378
|
+
}
|
|
47379
|
+
process.exit(1);
|
|
47380
|
+
}
|
|
47381
|
+
});
|
|
47382
|
+
program2.command("logout").description("Remove stored credentials").action(async () => {
|
|
47383
|
+
try {
|
|
47384
|
+
const creds = await loadCredentials();
|
|
47385
|
+
if (!creds) {
|
|
47386
|
+
console.log(source_default.yellow("Not currently logged in."));
|
|
47387
|
+
return;
|
|
47388
|
+
}
|
|
47389
|
+
await clearCredentials();
|
|
47390
|
+
console.log(source_default.green("Logged out successfully."));
|
|
47391
|
+
} catch (error2) {
|
|
47392
|
+
if (error2 instanceof Error) {
|
|
47393
|
+
console.error(source_default.red(`Logout failed: ${error2.message}`));
|
|
47394
|
+
} else {
|
|
47395
|
+
console.error(source_default.red("Logout failed: An unknown error occurred"));
|
|
47396
|
+
}
|
|
47397
|
+
process.exit(1);
|
|
47398
|
+
}
|
|
47399
|
+
});
|
|
47400
|
+
program2.command("whoami").description("Display current authenticated user").action(async () => {
|
|
47401
|
+
try {
|
|
47402
|
+
const creds = await loadCredentials();
|
|
47403
|
+
if (creds?.user) {
|
|
47404
|
+
console.log(`Logged in as: ${source_default.green(creds.user.email)}`);
|
|
47405
|
+
if (creds.user.name) {
|
|
47406
|
+
console.log(`Name: ${creds.user.name}`);
|
|
47407
|
+
}
|
|
47408
|
+
} else if (creds?.token) {
|
|
47409
|
+
console.log(source_default.yellow("Logged in (user info not available)"));
|
|
47410
|
+
} else if (process.env.BM_API_TOKEN) {
|
|
47411
|
+
console.log(source_default.yellow("Using BM_API_TOKEN environment variable"));
|
|
47412
|
+
} else {
|
|
47413
|
+
console.log(source_default.red("Not logged in."));
|
|
47414
|
+
console.log(
|
|
47415
|
+
source_default.dim("Run 'browsermation login' to authenticate.")
|
|
47416
|
+
);
|
|
47417
|
+
}
|
|
47418
|
+
} catch (error2) {
|
|
47419
|
+
if (error2 instanceof Error) {
|
|
47420
|
+
console.error(source_default.red(`Error: ${error2.message}`));
|
|
47421
|
+
}
|
|
47422
|
+
process.exit(1);
|
|
47423
|
+
}
|
|
47424
|
+
});
|
|
47212
47425
|
program2.parse(process.argv);
|
|
47213
47426
|
/*! Bundled license information:
|
|
47214
47427
|
|