@flowriki/agent 0.1.0-beta.1

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.js ADDED
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AGENT_VERSION,
4
+ connect,
5
+ disconnect,
6
+ doctor,
7
+ resolveCheckouts,
8
+ start,
9
+ status
10
+ } from "./chunk-ATOHKUGQ.js";
11
+
12
+ // src/cli.ts
13
+ import { parseArgs } from "util";
14
+ process.on("warning", (warning) => {
15
+ if (warning.name === "ExperimentalWarning" && /SQLite/i.test(warning.message)) return;
16
+ console.warn(`${warning.name}: ${warning.message}`);
17
+ });
18
+ var paint = (code) => (s) => `\x1B[${code}m${s}\x1B[0m`;
19
+ var bold = paint("1");
20
+ var dim = paint("2");
21
+ var red = paint("31");
22
+ var green = paint("32");
23
+ var yellow = paint("33");
24
+ var HELP = `${bold("Flowriki agent")} ${dim(AGENT_VERSION)}
25
+
26
+ Runs your Flowriki tests on this computer. The browser under test and your
27
+ project files stay here.
28
+
29
+ ${bold("USAGE")}
30
+ flowriki-agent connect connect this computer to your account
31
+ flowriki-agent start run the agent (stays in the foreground)
32
+ flowriki-agent status is it connected, and what is prepared
33
+ flowriki-agent doctor diagnose setup problems
34
+ flowriki-agent disconnect forget this computer's credential
35
+
36
+ ${bold("OPTIONS")}
37
+ --project <dir> a checkout to serve; repeat for several.
38
+ Remembered, so later runs need no arguments.
39
+ --name <label> how this computer appears in your dashboard
40
+ --service <url> advanced: a self-hosted Flowriki. Defaults to the
41
+ hosted service, which is what you want.
42
+ --json machine-readable output for status and doctor
43
+ -h, --help this
44
+ -v, --version version
45
+
46
+ ${bold("NOTE")}
47
+ ${dim("`start` stays in the foreground. Closing the terminal stops the agent;")}
48
+ ${dim("nothing is installed as a background service.")}
49
+ `;
50
+ async function main(argv) {
51
+ let parsed;
52
+ try {
53
+ parsed = parseArgs({
54
+ args: argv,
55
+ allowPositionals: true,
56
+ options: {
57
+ project: { type: "string", multiple: true },
58
+ name: { type: "string" },
59
+ service: { type: "string" },
60
+ json: { type: "boolean" },
61
+ help: { type: "boolean", short: "h" },
62
+ version: { type: "boolean", short: "v" }
63
+ }
64
+ });
65
+ } catch (error) {
66
+ console.error(`${error.message}
67
+
68
+ Run \`flowriki-agent --help\`.`);
69
+ return 2;
70
+ }
71
+ const { values, positionals } = parsed;
72
+ if (values.version) {
73
+ console.log(AGENT_VERSION);
74
+ return 0;
75
+ }
76
+ const command = positionals[0];
77
+ if (values.help || !command) {
78
+ console.log(HELP);
79
+ return values.help || !command ? 0 : 2;
80
+ }
81
+ const service = values.service ? String(values.service) : void 0;
82
+ const projects = (values.project ?? []).map(String);
83
+ switch (command) {
84
+ case "connect": {
85
+ const result = await connect({
86
+ ...service ? { service } : {},
87
+ ...values.name ? { machineName: String(values.name) } : {},
88
+ onProgress: (note) => {
89
+ if (note.kind !== "approve") return;
90
+ const url = String(note.detail?.["url"] ?? "");
91
+ console.log(`
92
+ ${bold("Approve this computer in your browser")}`);
93
+ console.log(` ${url}
94
+ `);
95
+ if (note.detail?.["canOpen"] === false) {
96
+ console.log(dim(" No graphical session here \u2014 open that link on a computer that has one.\n"));
97
+ } else {
98
+ console.log(dim(" Opening it for you. If nothing happens, use the link above.\n"));
99
+ }
100
+ }
101
+ });
102
+ if (!result.ok) {
103
+ console.error(`${red("could not connect")} \u2014 ${result.message}`);
104
+ return 1;
105
+ }
106
+ console.log(
107
+ result.reused ? `${green("already connected")} as ${bold(result.connection.machineName)}` : `${green("connected")} as ${bold(result.connection.machineName)}`
108
+ );
109
+ console.log(dim(" Next: flowriki-agent start"));
110
+ return 0;
111
+ }
112
+ case "start": {
113
+ const started = await start({
114
+ ...service ? { service } : {},
115
+ ...projects.length ? { projects } : {},
116
+ onProgress: (note) => {
117
+ if (note.kind === "skipped") {
118
+ for (const s of note.detail?.["skipped"] ?? []) {
119
+ console.error(yellow(`! skipped ${s.root} \u2014 ${s.why}`));
120
+ }
121
+ }
122
+ if (note.kind === "activity" && note.detail?.["kind"]) {
123
+ console.log(yellow(` > ${note.detail["kind"]} \u2014 ${note.detail["detail"] ?? ""}`));
124
+ }
125
+ },
126
+ onStatus: (state, detail) => console.log(dim(` ${state}${detail ? ` \u2014 ${detail}` : ""}`))
127
+ });
128
+ if (!started.ok) {
129
+ console.error(`${red(started.reason.replace(/-/g, " "))} \u2014 ${started.message}`);
130
+ if (started.reason === "not-connected") console.error(dim(" Run: flowriki-agent connect"));
131
+ return 1;
132
+ }
133
+ console.log(`${bold("Flowriki agent")} ${dim(AGENT_VERSION)}`);
134
+ console.log(dim(` connected as ${started.connection.machineName}`));
135
+ if (started.projects.length === 0) {
136
+ console.log(yellow(" no projects prepared \u2014 this machine is online and idle"));
137
+ console.log(dim(" Add one: flowriki-agent start --project /path/to/a/checkout"));
138
+ }
139
+ for (const project of started.projects) {
140
+ console.log(dim(` project ${project.name} (${project.root})`));
141
+ }
142
+ console.log(dim("\n Running in the foreground. Ctrl-C to stop.\n"));
143
+ let stopping = false;
144
+ const shutdown = async (signal) => {
145
+ if (stopping) return;
146
+ stopping = true;
147
+ console.log(dim(`
148
+ ${signal} \u2014 stopping`));
149
+ await started.stop();
150
+ process.exit(0);
151
+ };
152
+ process.on("SIGINT", () => void shutdown("SIGINT"));
153
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
154
+ return await new Promise(() => {
155
+ });
156
+ }
157
+ case "status": {
158
+ const state = status(service);
159
+ if (values.json) {
160
+ console.log(JSON.stringify(state, null, 2));
161
+ return 0;
162
+ }
163
+ console.log(`${bold("Flowriki agent")} ${dim(AGENT_VERSION)}`);
164
+ console.log(` service ${state.service}`);
165
+ console.log(
166
+ ` account ${state.connected ? green(`connected as ${state.machineName}`) : yellow("not connected")}`
167
+ );
168
+ console.log(
169
+ ` process ${state.running ? green(`running (pid ${state.running.pid})`) : dim("not running here")}`
170
+ );
171
+ if (state.checkouts.length === 0) console.log(dim(" projects none prepared"));
172
+ for (const c of state.checkouts) {
173
+ console.log(
174
+ c.project ? ` project ${c.project} ${dim(c.root)}` : yellow(` project ${c.root} \u2014 ${c.why}`)
175
+ );
176
+ }
177
+ return state.connected ? 0 : 1;
178
+ }
179
+ case "doctor": {
180
+ const found = await doctor(service);
181
+ if (values.json) {
182
+ console.log(JSON.stringify(found, null, 2));
183
+ return found.problems.length ? 1 : 0;
184
+ }
185
+ console.log(`${bold("Flowriki agent")} ${dim(found.agentVersion)}`);
186
+ console.log(` node ${found.node} ${dim(`${found.platform}/${found.arch}`)}`);
187
+ console.log(` protocol ${found.protocolVersion}`);
188
+ console.log(` data ${found.dataDir}`);
189
+ console.log(` service ${found.service} ${found.reachable ? green("reachable") : red("unreachable")}`);
190
+ console.log(` credential ${found.credentialFingerprint ?? dim("none stored")}`);
191
+ console.log(
192
+ ` browsers ${found.playwright.chromiumInstalled ? green("chromium ready") : yellow("chromium not installed")} ${dim(found.playwright.browsersPath ?? "")}`
193
+ );
194
+ for (const problem of found.problems) console.log(red(` ! ${problem}`));
195
+ return found.problems.length ? 1 : 0;
196
+ }
197
+ case "disconnect": {
198
+ const gone = disconnect(service);
199
+ console.log(
200
+ gone.ok ? `${green("disconnected")} from ${gone.service}` : yellow(`nothing stored for ${gone.service}`)
201
+ );
202
+ if (gone.ok) {
203
+ console.log(dim(" The device stays listed in your dashboard until you revoke it there."));
204
+ }
205
+ return 0;
206
+ }
207
+ case "projects": {
208
+ const { projects: found, skipped } = resolveCheckouts(projects.length ? projects : [process.cwd()]);
209
+ for (const p of found) console.log(` ${p.name} ${dim(p.root)}`);
210
+ for (const s of skipped) console.log(yellow(` ${s.root} \u2014 ${s.why}`));
211
+ return found.length ? 0 : 1;
212
+ }
213
+ default:
214
+ console.error(`unknown command: ${command}
215
+
216
+ Run \`flowriki-agent --help\`.`);
217
+ return 2;
218
+ }
219
+ }
220
+ main(process.argv.slice(2)).then((code) => {
221
+ if (code !== 0) process.exitCode = code;
222
+ }).catch((error) => {
223
+ console.error(red(error.message));
224
+ process.exitCode = 1;
225
+ });
package/dist/engine.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AGENT_VERSION,
4
+ connect,
5
+ disconnect,
6
+ doctor,
7
+ resolveCheckouts,
8
+ start,
9
+ status
10
+ } from "./chunk-ATOHKUGQ.js";
11
+ export {
12
+ AGENT_VERSION,
13
+ connect,
14
+ disconnect,
15
+ doctor,
16
+ resolveCheckouts,
17
+ start,
18
+ status
19
+ };
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@flowriki/agent",
3
+ "version": "0.1.0-beta.1",
4
+ "description": "Run Flowriki UI tests on your own computer. Connects this machine to your Flowriki account.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "type": "module",
7
+ "bin": {
8
+ "flowriki-agent": "dist/cli.js"
9
+ },
10
+ "main": "./dist/engine.js",
11
+ "exports": {
12
+ ".": "./dist/engine.js",
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "engines": {
21
+ "node": ">=22.13"
22
+ },
23
+ "os": [
24
+ "darwin",
25
+ "linux",
26
+ "win32"
27
+ ],
28
+ "cpu": [
29
+ "x64",
30
+ "arm64"
31
+ ],
32
+ "dependencies": {
33
+ "playwright": "1.63.0",
34
+ "ws": "^8.21.3",
35
+ "zod": "^3.24.1"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/Muhammad-Safiullah/flowriki.git",
40
+ "directory": "packages/agent-npm"
41
+ },
42
+ "keywords": [
43
+ "flowriki",
44
+ "e2e",
45
+ "testing",
46
+ "playwright",
47
+ "agent"
48
+ ],
49
+ "publishConfig": {
50
+ "access": "public"
51
+ }
52
+ }