flowstride 1.0.1 → 1.0.3
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/README.md +38 -0
- package/dist/cli/commands/init.js +17 -0
- package/dist/cli/commands/run.d.ts +1 -1
- package/dist/cli/commands/run.js +31 -7
- package/dist/cli/index.js +170 -5
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -49,6 +49,44 @@ _(This command executes your test while instantly booting up the local Flowstrid
|
|
|
49
49
|
|
|
50
50
|
---
|
|
51
51
|
|
|
52
|
+
## Privacy & Data Security
|
|
53
|
+
|
|
54
|
+
Flowstride is designed with a **zero-trust, local-first architecture**. Whether you are testing internal microservices, staging environments, or production APIs, your sensitive data, PHI, and credentials remain strictly on your infrastructure.
|
|
55
|
+
|
|
56
|
+
### Open Source Core: 100% Local Isolation
|
|
57
|
+
|
|
58
|
+
The Open Source version of Flowstride operates entirely as an ephemeral, local execution engine.
|
|
59
|
+
|
|
60
|
+
- **Air-Gapped Execution:** Flowstride does not "phone home." The OS runner executes strictly on your local machine or within your private CI/CD pipeline. There is no external Flowstride database storing your test payloads or network logs.
|
|
61
|
+
|
|
62
|
+
- **Ephemeral Memory:** Flowstride is inherently stateless. API request headers, response bodies, and DOM snapshots exist only in Node.js RAM during execution. The millisecond the execution process terminates, all memory is wiped.
|
|
63
|
+
|
|
64
|
+
- **The DevTools Analogy:** Our network interception works natively within Playwright's browser context routing. It is fundamentally identical to opening the Chrome DevTools "Network" tab locally. We do not act as an external Man-In-The-Middle (MITM) proxy.
|
|
65
|
+
|
|
66
|
+
- **Opt-In Local State:** Artifacts saved to disk—such as execution videos (`.flowstride/mock-cloud/`) or explicitly persisted browser states (`.flowstride/sessions/`)—are written strictly to your local file system to mirror the functionality of tools like Postman. _(Note: We strongly recommend adding `.flowstride/` to your `.gitignore` to prevent accidental credential commits)._
|
|
67
|
+
|
|
68
|
+
### Flowstride Enterprise: Secure Cloud Telemetry
|
|
69
|
+
|
|
70
|
+
For teams using Flowstride Enterprise to aggregate multi-flow metrics, we maintain a strict metadata-only boundary.
|
|
71
|
+
|
|
72
|
+
- **Metadata, Not Payloads:** When cloud sync is active, Flowstride routes only execution metadata to your team's dashboard (e.g., Workspace ID, execution duration, total steps, and pass/fail tallies). **Zero bytes** of request headers, bearer tokens, or JSON bodies are ever transmitted to Flowstride Cloud.
|
|
73
|
+
|
|
74
|
+
- **Deep Debugging Stays Local:** The rich, interactive visual dashboard, including the JSON explorer and API Replay tools, runs on a dynamic, locally bound port on your machine. You get the UI of a modern cloud app with the security of a local binary.
|
|
75
|
+
|
|
76
|
+
### CI/CD Leak Protection & Terminal Hygiene
|
|
77
|
+
|
|
78
|
+
Standard testing frameworks do dump failing API requests, and response bodies directly into the terminal so developers can debug them. In a CI/CD environment, this is a massive security liability, as those secrets are permanently burned into plaintext Jenkins, GitHub Actions, or GitLab CI logs.
|
|
79
|
+
|
|
80
|
+
Flowstride completely neutralizes this threat by architecturally separating execution output from diagnostic telemetry:
|
|
81
|
+
|
|
82
|
+
1. **Clean Standard Output (`stdout`):** Flowstride's terminal output only displays high-level, human-readable BDD execution steps (e.g., `✔ Given "User lands on home page"` or `✖ Action Failed`). No JSON request bodies, `cURL` strings, or bearer tokens are ever printed to the terminal console.
|
|
83
|
+
|
|
84
|
+
2. **WebSocket Isolation:** All sensitive raw network data (payloads, headers, and responses) is emitted _exclusively_ over a secure, ephemeral WebSocket connection directly to the developer's local UI dashboard.
|
|
85
|
+
|
|
86
|
+
3. **Total Evaporation:** Because CI/CD runners only capture `stdout` text and do not connect to local WebSockets, your highly sensitive diagnostic data simply evaporates the moment the pipeline execution ends. Your pipeline logs remain 100% free of plaintext secrets.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
52
90
|
## The `.flow` Syntax
|
|
53
91
|
|
|
54
92
|
Flowstride utilizes a custom `.flow` file extension. It seamlessly pairs descriptive BDD steps (Given, When, And, Then) with direct, executable `flow.` commands.
|
|
@@ -49,7 +49,9 @@ async function initAction() {
|
|
|
49
49
|
const folders = ["flows", "reports", "plugins", "fixtures"];
|
|
50
50
|
const sampleFlowPath = path.join(root, "flows", "example.flow");
|
|
51
51
|
const fixturesReadmePath = path.join(root, "fixtures", "README.md");
|
|
52
|
+
const gitignorePath = path.join(root, ".gitignore");
|
|
52
53
|
console.log("\n[Info] Initializing new workspace...");
|
|
54
|
+
// 1. Create Directories
|
|
53
55
|
folders.forEach((folder) => {
|
|
54
56
|
const dirPath = path.join(root, folder);
|
|
55
57
|
if (!fs.existsSync(dirPath)) {
|
|
@@ -60,6 +62,7 @@ async function initAction() {
|
|
|
60
62
|
console.log(`[Info] Directory already exists: ${folder}/`);
|
|
61
63
|
}
|
|
62
64
|
});
|
|
65
|
+
// 2. Create Sample Flow
|
|
63
66
|
const exampleContent = `
|
|
64
67
|
Feature: User login flow
|
|
65
68
|
|
|
@@ -91,6 +94,7 @@ Then "Logout of the Admin dashboard"
|
|
|
91
94
|
fs.writeFileSync(sampleFlowPath, exampleContent);
|
|
92
95
|
console.log("[OK] Created sample flow: flows/example.flow");
|
|
93
96
|
}
|
|
97
|
+
// 3. Create Fixtures Readme
|
|
94
98
|
const fixturesReadmeContent = `# Fixtures
|
|
95
99
|
|
|
96
100
|
Store your audio, image, and data assets in this directory.
|
|
@@ -102,6 +106,19 @@ Store your audio, image, and data assets in this directory.
|
|
|
102
106
|
fs.writeFileSync(fixturesReadmePath, fixturesReadmeContent);
|
|
103
107
|
console.log("[OK] Created fixtures/README.md");
|
|
104
108
|
}
|
|
109
|
+
// 4. Secure the Workspace (.gitignore)
|
|
110
|
+
const ignorePayload = `\n# Flowstride Execution Data\n.flowstride\nflowstride-artifacts\n`;
|
|
111
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
112
|
+
fs.writeFileSync(gitignorePath, `node_modules\n${ignorePayload}`);
|
|
113
|
+
console.log("[OK] Created .gitignore to secure local session data");
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const existingIgnore = fs.readFileSync(gitignorePath, "utf8");
|
|
117
|
+
if (!existingIgnore.includes(".flowstride")) {
|
|
118
|
+
fs.appendFileSync(gitignorePath, ignorePayload);
|
|
119
|
+
console.log("[OK] Updated .gitignore to secure local session data");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
105
122
|
console.log();
|
|
106
123
|
const browserSpinner = (0, ora_1.default)("Downloading Chromium execution engine...").start();
|
|
107
124
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function runAction(targetPath: string
|
|
1
|
+
export declare function runAction(targetPath: string, options: Record<string, any>): Promise<void>;
|
package/dist/cli/commands/run.js
CHANGED
|
@@ -38,6 +38,7 @@ const fs = __importStar(require("fs"));
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const dotenv = __importStar(require("dotenv"));
|
|
40
40
|
const os = __importStar(require("os"));
|
|
41
|
+
const url_1 = require("url");
|
|
41
42
|
const flow_worker_1 = require("../../runner/flow_worker");
|
|
42
43
|
const flow_orchestrator_1 = require("../../runner/flow_orchestrator");
|
|
43
44
|
const playwright_1 = require("../../adapters/web/playwright");
|
|
@@ -76,12 +77,10 @@ async function runAction(targetPath, options) {
|
|
|
76
77
|
if (isCI) {
|
|
77
78
|
console.log("\n[CI/CD]: Environment detected. Engaging Smart Automation Behaviors...");
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
: path.resolve(root, "flows");
|
|
80
|
+
// The CLI (index.ts) now strictly guarantees targetPath is the absolute path to ./flows
|
|
81
|
+
const resolvedPath = targetPath;
|
|
82
82
|
if (!fs.existsSync(resolvedPath)) {
|
|
83
|
-
console.error(`\n[Error]: Cannot find target path "${resolvedPath}".`);
|
|
84
|
-
console.log(`[Info]: Try running 'flowstride init' to create a workspace first.\n`);
|
|
83
|
+
console.error(`\n[Error]: Cannot find strict target path "${resolvedPath}".`);
|
|
85
84
|
process.exit(1);
|
|
86
85
|
}
|
|
87
86
|
console.log(`\n[Flowstride]: Starting execution suite at "${resolvedPath}"...`);
|
|
@@ -130,8 +129,33 @@ async function runAction(targetPath, options) {
|
|
|
130
129
|
const pluginManager = new plugin_manager_1.PluginManager(config, web, store, apiProxy);
|
|
131
130
|
const vault = new persistent_vault_1.PersistentVault(root);
|
|
132
131
|
const sessions = new manager_1.SessionManager(web, api, vault);
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
// ==========================================
|
|
133
|
+
// DYNAMIC ENGINE ROUTER (The Plugin Handoff)
|
|
134
|
+
// ==========================================
|
|
135
|
+
let ActiveFlowWorker = flow_worker_1.FlowWorker;
|
|
136
|
+
let ActiveFlowOrchestrator = flow_orchestrator_1.FlowOrchestrator;
|
|
137
|
+
let engineMode = "Open Source";
|
|
138
|
+
// Check the local node_modules for the Pro engine
|
|
139
|
+
const proWorkerPath = path.join(process.cwd(), "node_modules", "@flowstride", "pro", "dist", "runner", "flow_worker.js");
|
|
140
|
+
const proOrchPath = path.join(process.cwd(), "node_modules", "@flowstride", "pro", "dist", "runner", "flow_orchestrator.js");
|
|
141
|
+
if (fs.existsSync(proWorkerPath) && fs.existsSync(proOrchPath)) {
|
|
142
|
+
try {
|
|
143
|
+
// Dynamically load the Pro classes using native absolute URL resolution
|
|
144
|
+
const proWorkerModule = await import((0, url_1.pathToFileURL)(proWorkerPath).href);
|
|
145
|
+
const proOrchModule = await import((0, url_1.pathToFileURL)(proOrchPath).href);
|
|
146
|
+
ActiveFlowWorker = proWorkerModule.FlowWorker;
|
|
147
|
+
ActiveFlowOrchestrator = proOrchModule.FlowOrchestrator;
|
|
148
|
+
engineMode = "Enterprise (Pro)";
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.warn(`\n[Warning]: Found @flowstride/pro but failed to boot it. Falling back to Open Source engine. (${err.message})`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
console.log(`\n[Engine]: Booting Flowstride in ${engineMode} mode...`);
|
|
155
|
+
// ==========================================
|
|
156
|
+
// Instantiate the chosen engine (OS or Pro)
|
|
157
|
+
const worker = new ActiveFlowWorker(config, web, api, store, sessions, pluginManager);
|
|
158
|
+
const orchestrator = new ActiveFlowOrchestrator(worker, workerCount, config);
|
|
135
159
|
try {
|
|
136
160
|
await orchestrator.start(resolvedPath);
|
|
137
161
|
console.log("\n[Flowstride]: Execution suite completed successfully.");
|
package/dist/cli/index.js
CHANGED
|
@@ -1,34 +1,199 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
3
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
40
|
const commander_1 = require("commander");
|
|
5
41
|
const run_1 = require("./commands/run");
|
|
6
42
|
const report_1 = require("./commands/report");
|
|
7
43
|
const init_1 = require("./commands/init");
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
47
|
+
const child_process_1 = require("child_process");
|
|
48
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
49
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
50
|
+
const ora_1 = __importDefault(require("ora"));
|
|
8
51
|
const program = new commander_1.Command();
|
|
9
52
|
program
|
|
10
53
|
.name("flowstride")
|
|
11
54
|
.description("Flowstride: The Enterprise Flow-First Automation CLI")
|
|
12
|
-
.version("1.0.
|
|
55
|
+
.version("1.0.3");
|
|
13
56
|
program
|
|
14
57
|
.command("init")
|
|
15
58
|
.description("Initialise a new Flowstride workspace in the current directory")
|
|
16
59
|
.action(async () => {
|
|
17
60
|
await (0, init_1.initAction)();
|
|
18
61
|
});
|
|
62
|
+
program
|
|
63
|
+
.command("login")
|
|
64
|
+
.description("Authenticate this machine with Flowstride Enterprise Cloud")
|
|
65
|
+
.action(async () => {
|
|
66
|
+
console.log(chalk_1.default.bold.blue("\n🌐 Flowstride Enterprise Cloud Sync\n"));
|
|
67
|
+
const userEmail = await (0, prompts_1.input)({ message: "Enter your Flowstride email:" });
|
|
68
|
+
const userPass = await (0, prompts_1.password)({ message: "Enter your password:" });
|
|
69
|
+
// Silently grab the computer's hostname without bothering the user
|
|
70
|
+
const deviceName = os.hostname();
|
|
71
|
+
const spinner = (0, ora_1.default)("Authenticating with Cloud...").start();
|
|
72
|
+
try {
|
|
73
|
+
// 1. Authenticate with Next.js Server
|
|
74
|
+
const authString = Buffer.from(`${userEmail}:${userPass}`).toString("base64");
|
|
75
|
+
const response = await fetch("https://cloud.flowstride.io/api/auth/cli-sync", {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: {
|
|
78
|
+
"Content-Type": "application/json",
|
|
79
|
+
Authorization: `Basic ${authString}`,
|
|
80
|
+
},
|
|
81
|
+
body: JSON.stringify({ deviceName }),
|
|
82
|
+
});
|
|
83
|
+
const data = await response.json();
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
spinner.fail(chalk_1.default.red(`Authentication Failed: ${data.error || "Unknown error"}`));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
spinner.succeed(chalk_1.default.green("Authentication Successful!"));
|
|
89
|
+
// 2. Save Machine Token Locally
|
|
90
|
+
const configDir = path.join(os.homedir(), ".flowstride");
|
|
91
|
+
if (!fs.existsSync(configDir)) {
|
|
92
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
const credentialsPath = path.join(configDir, "credentials.json");
|
|
95
|
+
fs.writeFileSync(credentialsPath, JSON.stringify({
|
|
96
|
+
machineToken: data.machineToken,
|
|
97
|
+
workspaceId: data.workspaceId,
|
|
98
|
+
deviceName: deviceName,
|
|
99
|
+
syncedAt: new Date().toISOString(),
|
|
100
|
+
}, null, 2));
|
|
101
|
+
console.log(chalk_1.default.gray(`\nCredentials saved to ${credentialsPath}`));
|
|
102
|
+
console.log(chalk_1.default.bold.green(`\n✅ Machine synchronized with Flowstride Enterprise.`));
|
|
103
|
+
// 3. The Auto-Installer Logic
|
|
104
|
+
if (data.githubToken) {
|
|
105
|
+
console.log(chalk_1.default.yellow(`\n⚙️ System Check: You have access to Enterprise features (AI Healing, Parallel Workers).`));
|
|
106
|
+
const localPackageJsonPath = path.join(process.cwd(), "package.json");
|
|
107
|
+
const nodeModulesPath = path.join(process.cwd(), "node_modules", "@heroamogs", "pro");
|
|
108
|
+
if (fs.existsSync(localPackageJsonPath) &&
|
|
109
|
+
!fs.existsSync(nodeModulesPath)) {
|
|
110
|
+
const installPro = await (0, prompts_1.confirm)({
|
|
111
|
+
message: "The Pro Engine is not installed in this project. Would you like Flowstride to automatically install it now?",
|
|
112
|
+
default: true,
|
|
113
|
+
});
|
|
114
|
+
if (installPro) {
|
|
115
|
+
const installSpinner = (0, ora_1.default)("Configuring secure registry and installing @heroamogs/pro...").start();
|
|
116
|
+
const npmrcPath = path.join(process.cwd(), ".npmrc");
|
|
117
|
+
let originalNpmrc = "";
|
|
118
|
+
try {
|
|
119
|
+
// A. Backup existing .npmrc if it exists
|
|
120
|
+
if (fs.existsSync(npmrcPath)) {
|
|
121
|
+
originalNpmrc = fs.readFileSync(npmrcPath, "utf8");
|
|
122
|
+
}
|
|
123
|
+
// B. Write the temporary GitHub configuration
|
|
124
|
+
const tempNpmrcContent = `
|
|
125
|
+
@heroamogs:registry=https://npm.pkg.github.com
|
|
126
|
+
//npm.pkg.github.com/:_authToken=${data.githubToken}
|
|
127
|
+
`;
|
|
128
|
+
fs.writeFileSync(npmrcPath, (originalNpmrc ? originalNpmrc + "\n" : "") + tempNpmrcContent);
|
|
129
|
+
installSpinner.stop(); // Stop spinner so we can see the exact npm logs
|
|
130
|
+
console.log(chalk_1.default.cyan("Starting npm install..."));
|
|
131
|
+
// C. Run the install WITH INHERIT to expose the exact error
|
|
132
|
+
(0, child_process_1.execSync)("npm install @heroamogs/pro --save-dev", {
|
|
133
|
+
stdio: "inherit",
|
|
134
|
+
cwd: process.cwd(),
|
|
135
|
+
});
|
|
136
|
+
console.log(chalk_1.default.green("\n✅ Enterprise Engine installed successfully!"));
|
|
137
|
+
}
|
|
138
|
+
catch (installError) {
|
|
139
|
+
console.log(chalk_1.default.red("\n✖ Failed to install the Enterprise Engine automatically."));
|
|
140
|
+
console.error(chalk_1.default.gray("You may need to install it manually."));
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
// D. Secure Cleanup: Remove the token from .npmrc
|
|
144
|
+
if (originalNpmrc) {
|
|
145
|
+
fs.writeFileSync(npmrcPath, originalNpmrc); // Restore original
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
if (fs.existsSync(npmrcPath))
|
|
149
|
+
fs.unlinkSync(npmrcPath); // Delete if we created it
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
console.log(chalk_1.default.gray(`\nSkipped installation. You can run 'npm install @heroamogs/pro --save-dev' manually later.`));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
spinner.fail(chalk_1.default.red(`Network Error: Could not connect to cloud.flowstride.io`));
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
19
164
|
program
|
|
20
165
|
.command("run")
|
|
21
|
-
.description("Execute .flow automation journeys")
|
|
22
|
-
.argument("[path]", "Path to .flow files or directory (defaults to ./flows)")
|
|
166
|
+
.description("Execute all .flow automation journeys in the ./flows directory")
|
|
23
167
|
.option("-b, --bail", "Stop execution on first failure", false)
|
|
24
168
|
.option("--headed", "Run browser in headed mode", false)
|
|
25
169
|
.option("--headless <boolean>", "Run browser in headless mode", (val) => val === "true", true)
|
|
26
170
|
.option("--workers <number>", "Number of parallel workers to spawn (Pro feature)")
|
|
27
|
-
.action(async (
|
|
171
|
+
.action(async (options) => {
|
|
172
|
+
const credentialsPath = path.join(os.homedir(), ".flowstride", "credentials.json");
|
|
173
|
+
let isPro = false;
|
|
174
|
+
if (fs.existsSync(credentialsPath)) {
|
|
175
|
+
try {
|
|
176
|
+
const credentials = JSON.parse(fs.readFileSync(credentialsPath, "utf8"));
|
|
177
|
+
isPro = true;
|
|
178
|
+
console.log(`[PRO]: Machine Synced to Enterprise Cloud`);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
console.log(`[AUTH ERROR]: Invalid credentials file. Run 'flowstride login' to reconnect.`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
console.log(`[OS]: Local Mode (Run 'flowstride login' to sync to Enterprise Cloud)`);
|
|
186
|
+
}
|
|
28
187
|
if (options.headed) {
|
|
29
188
|
options.headless = false;
|
|
30
189
|
}
|
|
31
|
-
|
|
190
|
+
const strictUiFlowsDir = path.resolve(process.cwd(), "./flows");
|
|
191
|
+
if (!fs.existsSync(strictUiFlowsDir)) {
|
|
192
|
+
console.error(`\n[ERROR]: Could not find ${strictUiFlowsDir}.`);
|
|
193
|
+
console.error(`Flowstride requires all UI tests to be inside a './flows' directory.`);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
await (0, run_1.runAction)(strictUiFlowsDir, options);
|
|
32
197
|
});
|
|
33
198
|
program
|
|
34
199
|
.command("report")
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flowstride",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "An opensource flowfirst test automation framework",
|
|
5
5
|
"workspaces": [
|
|
6
|
-
"packages
|
|
6
|
+
"packages/flowstride_report"
|
|
7
7
|
],
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"node": ">=18.0.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
+
"@inquirer/prompts": "^7.10.1",
|
|
35
36
|
"chalk": "^4.1.2",
|
|
36
37
|
"commander": "^11.0.0",
|
|
37
38
|
"dotenv": "^17.4.2",
|