pw-automation-framework 2.0.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/README.md +93 -0
- package/bin/lexxit-automation-framework.js +427 -0
- package/dist/app.d.ts +2 -0
- package/dist/app.js +26 -0
- package/dist/app.js.map +1 -0
- package/dist/controllers/controller.d.ts +57 -0
- package/dist/controllers/controller.js +263 -0
- package/dist/controllers/controller.js.map +1 -0
- package/dist/core/BrowserManager.d.ts +46 -0
- package/dist/core/BrowserManager.js +377 -0
- package/dist/core/BrowserManager.js.map +1 -0
- package/dist/core/PlaywrightEngine.d.ts +16 -0
- package/dist/core/PlaywrightEngine.js +246 -0
- package/dist/core/PlaywrightEngine.js.map +1 -0
- package/dist/core/ScreenshotManager.d.ts +10 -0
- package/dist/core/ScreenshotManager.js +28 -0
- package/dist/core/ScreenshotManager.js.map +1 -0
- package/dist/core/TestData.d.ts +12 -0
- package/dist/core/TestData.js +29 -0
- package/dist/core/TestData.js.map +1 -0
- package/dist/core/TestExecutor.d.ts +16 -0
- package/dist/core/TestExecutor.js +355 -0
- package/dist/core/TestExecutor.js.map +1 -0
- package/dist/core/handlers/AllHandlers.d.ts +116 -0
- package/dist/core/handlers/AllHandlers.js +648 -0
- package/dist/core/handlers/AllHandlers.js.map +1 -0
- package/dist/core/handlers/BaseHandler.d.ts +16 -0
- package/dist/core/handlers/BaseHandler.js +27 -0
- package/dist/core/handlers/BaseHandler.js.map +1 -0
- package/dist/core/handlers/ClickHandler.d.ts +34 -0
- package/dist/core/handlers/ClickHandler.js +359 -0
- package/dist/core/handlers/ClickHandler.js.map +1 -0
- package/dist/core/handlers/CustomCodeHandler.d.ts +35 -0
- package/dist/core/handlers/CustomCodeHandler.js +102 -0
- package/dist/core/handlers/CustomCodeHandler.js.map +1 -0
- package/dist/core/handlers/DropdownHandler.d.ts +43 -0
- package/dist/core/handlers/DropdownHandler.js +304 -0
- package/dist/core/handlers/DropdownHandler.js.map +1 -0
- package/dist/core/handlers/InputHandler.d.ts +24 -0
- package/dist/core/handlers/InputHandler.js +197 -0
- package/dist/core/handlers/InputHandler.js.map +1 -0
- package/dist/core/registry/ActionRegistry.d.ts +8 -0
- package/dist/core/registry/ActionRegistry.js +35 -0
- package/dist/core/registry/ActionRegistry.js.map +1 -0
- package/dist/installer/frameworkLauncher.d.ts +31 -0
- package/dist/installer/frameworkLauncher.js +198 -0
- package/dist/installer/frameworkLauncher.js.map +1 -0
- package/dist/queue/ExecutionQueue.d.ts +52 -0
- package/dist/queue/ExecutionQueue.js +175 -0
- package/dist/queue/ExecutionQueue.js.map +1 -0
- package/dist/routes/api.routes.d.ts +2 -0
- package/dist/routes/api.routes.js +16 -0
- package/dist/routes/api.routes.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +30 -0
- package/dist/server.js.map +1 -0
- package/dist/types/types.d.ts +135 -0
- package/dist/types/types.js +4 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/elementHighlight.d.ts +35 -0
- package/dist/utils/elementHighlight.js +136 -0
- package/dist/utils/elementHighlight.js.map +1 -0
- package/dist/utils/locatorHelper.d.ts +7 -0
- package/dist/utils/locatorHelper.js +53 -0
- package/dist/utils/locatorHelper.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.js +35 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/response.d.ts +4 -0
- package/dist/utils/response.js +25 -0
- package/dist/utils/response.js.map +1 -0
- package/dist/utils/responseFormatter.d.ts +78 -0
- package/dist/utils/responseFormatter.js +123 -0
- package/dist/utils/responseFormatter.js.map +1 -0
- package/dist/utils/sseManager.d.ts +32 -0
- package/dist/utils/sseManager.js +122 -0
- package/dist/utils/sseManager.js.map +1 -0
- package/lexxit-automation-framework-2.0.0.tgz +0 -0
- package/npmignore +5 -0
- package/package.json +36 -0
- package/scripts/postinstall.js +52 -0
- package/src/app.ts +27 -0
- package/src/controllers/controller.ts +282 -0
- package/src/core/BrowserManager.ts +398 -0
- package/src/core/PlaywrightEngine.ts +371 -0
- package/src/core/ScreenshotManager.ts +25 -0
- package/src/core/TestData.ts +25 -0
- package/src/core/TestExecutor.ts +436 -0
- package/src/core/handlers/AllHandlers.ts +626 -0
- package/src/core/handlers/BaseHandler.ts +41 -0
- package/src/core/handlers/ClickHandler.ts +482 -0
- package/src/core/handlers/CustomCodeHandler.ts +123 -0
- package/src/core/handlers/DropdownHandler.ts +438 -0
- package/src/core/handlers/InputHandler.ts +192 -0
- package/src/core/registry/ActionRegistry.ts +31 -0
- package/src/installer/frameworkLauncher.ts +242 -0
- package/src/installer/install.sh +107 -0
- package/src/public/dashboard.html +540 -0
- package/src/public/queue-monitor.html +190 -0
- package/src/queue/ExecutionQueue.ts +200 -0
- package/src/routes/api.routes.ts +16 -0
- package/src/server.ts +29 -0
- package/src/types/types.ts +169 -0
- package/src/utils/elementHighlight.ts +174 -0
- package/src/utils/locatorHelper.ts +49 -0
- package/src/utils/logger.ts +40 -0
- package/src/utils/response.ts +27 -0
- package/src/utils/responseFormatter.ts +167 -0
- package/src/utils/sseManager.ts +127 -0
- package/tsconfig.json +18 -0
- package/videos/fb1b94b6-6639-4c9a-82bb-63572606f403/page@5bd5c6c8b62baa700e9810cdd64f5c49.webm +0 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frameworkLauncher.ts
|
|
3
|
+
* ====================
|
|
4
|
+
* Called by your MAIN APP's backend to ensure the framework is running.
|
|
5
|
+
* Handles: health check → install if missing → start → return URL.
|
|
6
|
+
*
|
|
7
|
+
* Usage in your main app's API route:
|
|
8
|
+
* import { ensureFramework, submitExecution } from './frameworkLauncher';
|
|
9
|
+
* const url = await ensureFramework();
|
|
10
|
+
* const run = await submitExecution(payload);
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { execFile, exec } from "child_process";
|
|
14
|
+
import { promisify } from "util";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import http from "http";
|
|
17
|
+
import fs from "fs";
|
|
18
|
+
|
|
19
|
+
const execFileAsync = promisify(execFile);
|
|
20
|
+
const execAsync = promisify(exec);
|
|
21
|
+
|
|
22
|
+
const FRAMEWORK_PORT = parseInt(process.env.FRAMEWORK_PORT || "3000", 10);
|
|
23
|
+
const FRAMEWORK_BASE = `http://localhost:${FRAMEWORK_PORT}`;
|
|
24
|
+
const INSTALL_DIR = path.join(
|
|
25
|
+
process.env.HOME || process.env.USERPROFILE || "/tmp",
|
|
26
|
+
".pw-automation-framework"
|
|
27
|
+
);
|
|
28
|
+
const PID_FILE = path.join(INSTALL_DIR, ".pid");
|
|
29
|
+
const LOG_FILE = path.join(INSTALL_DIR, "server.log");
|
|
30
|
+
const VERSION_FILE = path.join(INSTALL_DIR, "VERSION");
|
|
31
|
+
const CURRENT_VERSION = "2.0.0";
|
|
32
|
+
// ⬇️ Update this to your actual hosted bundle URL
|
|
33
|
+
const BUNDLE_BASE_URL = process.env.FRAMEWORK_BUNDLE_URL || "https://your-registry.example.com/releases";
|
|
34
|
+
|
|
35
|
+
// ─── Health check ─────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
export async function isFrameworkRunning(): Promise<boolean> {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
const req = http.get(
|
|
40
|
+
`${FRAMEWORK_BASE}/api/health`,
|
|
41
|
+
{ timeout: 800 },
|
|
42
|
+
(res) => resolve(res.statusCode === 200)
|
|
43
|
+
);
|
|
44
|
+
req.on("error", () => resolve(false));
|
|
45
|
+
req.on("timeout", () => { req.destroy(); resolve(false); });
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function getFrameworkVersion(): Promise<string | null> {
|
|
50
|
+
try {
|
|
51
|
+
if (fs.existsSync(VERSION_FILE)) {
|
|
52
|
+
return fs.readFileSync(VERSION_FILE, "utf-8").trim();
|
|
53
|
+
}
|
|
54
|
+
} catch { /* ignore */ }
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── Download & install ───────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
export async function installFramework(
|
|
61
|
+
onLog?: (msg: string) => void
|
|
62
|
+
): Promise<void> {
|
|
63
|
+
const log = (msg: string) => { console.log(`[Installer] ${msg}`); onLog?.(msg); };
|
|
64
|
+
|
|
65
|
+
fs.mkdirSync(INSTALL_DIR, { recursive: true });
|
|
66
|
+
|
|
67
|
+
log(`Installing framework v${CURRENT_VERSION} to ${INSTALL_DIR}…`);
|
|
68
|
+
|
|
69
|
+
const bundleURL = `${BUNDLE_BASE_URL}/pw-framework-v${CURRENT_VERSION}.tar.gz`;
|
|
70
|
+
const bundleTmp = path.join(INSTALL_DIR, "bundle.tar.gz");
|
|
71
|
+
|
|
72
|
+
// Download bundle
|
|
73
|
+
log("Downloading framework bundle…");
|
|
74
|
+
await new Promise<void>((resolve, reject) => {
|
|
75
|
+
const file = fs.createWriteStream(bundleTmp);
|
|
76
|
+
http.get(bundleURL.replace("https://", "http://"), (res) => {
|
|
77
|
+
res.pipe(file);
|
|
78
|
+
file.on("finish", () => { file.close(); resolve(); });
|
|
79
|
+
}).on("error", (err) => {
|
|
80
|
+
fs.unlink(bundleTmp, () => {});
|
|
81
|
+
reject(new Error(`Download failed: ${err.message}. \nManually download from: ${bundleURL}`));
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
log("Extracting…");
|
|
86
|
+
await execAsync(`tar -xzf "${bundleTmp}" -C "${INSTALL_DIR}" --strip-components=1`);
|
|
87
|
+
fs.unlinkSync(bundleTmp);
|
|
88
|
+
|
|
89
|
+
log("Installing dependencies (this may take a minute)…");
|
|
90
|
+
await execAsync(`npm install --omit=dev --silent`, { cwd: INSTALL_DIR });
|
|
91
|
+
|
|
92
|
+
log("Installing Playwright browsers…");
|
|
93
|
+
try {
|
|
94
|
+
await execAsync(`npx playwright install chromium --with-deps`, { cwd: INSTALL_DIR });
|
|
95
|
+
} catch {
|
|
96
|
+
await execAsync(`npx playwright install chromium`, { cwd: INSTALL_DIR });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Write version marker
|
|
100
|
+
fs.writeFileSync(VERSION_FILE, CURRENT_VERSION);
|
|
101
|
+
log(`Installation complete ✓`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ─── Start server ─────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
export async function startFramework(onLog?: (msg: string) => void): Promise<void> {
|
|
107
|
+
const log = (msg: string) => { console.log(`[Launcher] ${msg}`); onLog?.(msg); };
|
|
108
|
+
|
|
109
|
+
log(`Starting framework on port ${FRAMEWORK_PORT}…`);
|
|
110
|
+
|
|
111
|
+
const serverFile = path.join(INSTALL_DIR, "dist", "server.js");
|
|
112
|
+
if (!fs.existsSync(serverFile)) {
|
|
113
|
+
throw new Error(`server.js not found at ${serverFile}. Run installFramework() first.`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// const child = execFile("node", [serverFile], {
|
|
117
|
+
// env: { ...process.env, PORT: String(FRAMEWORK_PORT) },
|
|
118
|
+
// cwd: INSTALL_DIR,
|
|
119
|
+
// detached: true,
|
|
120
|
+
// stdio: ["ignore", fs.openSync(LOG_FILE, "a"), fs.openSync(LOG_FILE, "a")],
|
|
121
|
+
// });
|
|
122
|
+
const child = execFile(
|
|
123
|
+
"node",
|
|
124
|
+
[serverFile],
|
|
125
|
+
{
|
|
126
|
+
env: { ...process.env, PORT: String(FRAMEWORK_PORT) },
|
|
127
|
+
cwd: INSTALL_DIR,
|
|
128
|
+
detached: true,
|
|
129
|
+
stdio: ["ignore", fs.openSync(LOG_FILE, "a"), fs.openSync(LOG_FILE, "a")]
|
|
130
|
+
} as any // 👈 THIS LINE FIXES YOUR ERROR
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
child.unref();
|
|
134
|
+
if (child.pid) fs.writeFileSync(PID_FILE, String(child.pid));
|
|
135
|
+
|
|
136
|
+
// Wait for health (up to 15s)
|
|
137
|
+
for (let i = 0; i < 30; i++) {
|
|
138
|
+
await new Promise(r => setTimeout(r, 500));
|
|
139
|
+
if (await isFrameworkRunning()) {
|
|
140
|
+
log(`Framework ready at ${FRAMEWORK_BASE} ✓`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
throw new Error(`Framework did not start within 15s. Check ${LOG_FILE}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ─── Main entry — ensures framework is running ────────────────────────────────
|
|
149
|
+
|
|
150
|
+
export interface EnsureResult {
|
|
151
|
+
baseUrl: string;
|
|
152
|
+
installed: boolean;
|
|
153
|
+
started: boolean;
|
|
154
|
+
version: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function ensureFramework(
|
|
158
|
+
onLog?: (msg: string) => void
|
|
159
|
+
): Promise<EnsureResult> {
|
|
160
|
+
const log = (msg: string) => { console.log(`[ensureFramework] ${msg}`); onLog?.(msg); };
|
|
161
|
+
|
|
162
|
+
// 1. Already running?
|
|
163
|
+
if (await isFrameworkRunning()) {
|
|
164
|
+
log("Framework already running ✓");
|
|
165
|
+
return { baseUrl: FRAMEWORK_BASE, installed: false, started: false, version: CURRENT_VERSION };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 2. Installed but not running?
|
|
169
|
+
const installedVersion = await getFrameworkVersion();
|
|
170
|
+
let needsInstall = installedVersion !== CURRENT_VERSION;
|
|
171
|
+
|
|
172
|
+
if (needsInstall) {
|
|
173
|
+
log(installedVersion
|
|
174
|
+
? `Updating from v${installedVersion} → v${CURRENT_VERSION}…`
|
|
175
|
+
: "First install…");
|
|
176
|
+
await installFramework(onLog);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 3. Start
|
|
180
|
+
await startFramework(onLog);
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
baseUrl: FRAMEWORK_BASE,
|
|
184
|
+
installed: needsInstall,
|
|
185
|
+
started: true,
|
|
186
|
+
version: CURRENT_VERSION,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ─── Submit execution ─────────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
export async function submitExecution(
|
|
193
|
+
payload: any,
|
|
194
|
+
onLog?: (msg: string) => void
|
|
195
|
+
): Promise<{ executionId: string; dashboardURL: string; status: string }> {
|
|
196
|
+
|
|
197
|
+
await ensureFramework(onLog);
|
|
198
|
+
|
|
199
|
+
const body = JSON.stringify(payload);
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
const req = http.request(
|
|
202
|
+
{
|
|
203
|
+
hostname: "localhost",
|
|
204
|
+
port: FRAMEWORK_PORT,
|
|
205
|
+
path: "/api/run-test",
|
|
206
|
+
method: "POST",
|
|
207
|
+
headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) },
|
|
208
|
+
},
|
|
209
|
+
(res) => {
|
|
210
|
+
let data = "";
|
|
211
|
+
res.on("data", c => data += c);
|
|
212
|
+
res.on("end", () => {
|
|
213
|
+
try { resolve(JSON.parse(data)); }
|
|
214
|
+
catch { reject(new Error(`Invalid response: ${data}`)); }
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
req.on("error", reject);
|
|
219
|
+
req.write(body);
|
|
220
|
+
req.end();
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ─── Cancel execution ─────────────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
export async function cancelExecution(executionId: string): Promise<{ success: boolean; message: string }> {
|
|
227
|
+
return new Promise((resolve, reject) => {
|
|
228
|
+
const req = http.request(
|
|
229
|
+
{ hostname: "localhost", port: FRAMEWORK_PORT, path: `/api/cancel/${executionId}`, method: "DELETE" },
|
|
230
|
+
(res) => {
|
|
231
|
+
let data = "";
|
|
232
|
+
res.on("data", c => data += c);
|
|
233
|
+
res.on("end", () => {
|
|
234
|
+
try { resolve(JSON.parse(data)); }
|
|
235
|
+
catch { reject(new Error(`Invalid response: ${data}`)); }
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
req.on("error", reject);
|
|
240
|
+
req.end();
|
|
241
|
+
});
|
|
242
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================
|
|
3
|
+
# PW Automation Framework — Auto Installer
|
|
4
|
+
# Triggered from the web UI on first Execute click.
|
|
5
|
+
# Runs silently, installs framework locally, starts server.
|
|
6
|
+
# ============================================================
|
|
7
|
+
|
|
8
|
+
set -e
|
|
9
|
+
|
|
10
|
+
FRAMEWORK_VERSION="2.0.0"
|
|
11
|
+
INSTALL_DIR="$HOME/.pw-automation-framework"
|
|
12
|
+
PORT="${PW_PORT:-3000}"
|
|
13
|
+
PID_FILE="$INSTALL_DIR/.pid"
|
|
14
|
+
LOG_FILE="$INSTALL_DIR/server.log"
|
|
15
|
+
REGISTRY_URL="${PW_REGISTRY_URL:-https://your-registry.example.com/releases}"
|
|
16
|
+
|
|
17
|
+
log() { echo "[$(date '+%H:%M:%S')] $*"; }
|
|
18
|
+
|
|
19
|
+
# ── 1. Check if already running ──────────────────────────────────────────────
|
|
20
|
+
if [ -f "$PID_FILE" ]; then
|
|
21
|
+
PID=$(cat "$PID_FILE")
|
|
22
|
+
if kill -0 "$PID" 2>/dev/null; then
|
|
23
|
+
# Check if it's the right version
|
|
24
|
+
CURRENT_VER=$(curl -s "http://localhost:$PORT/api/health" 2>/dev/null | grep -o '"version":"[^"]*"' | grep -o '[0-9.]*' || echo "")
|
|
25
|
+
if [ "$CURRENT_VER" = "$FRAMEWORK_VERSION" ]; then
|
|
26
|
+
log "Framework v$FRAMEWORK_VERSION already running (PID $PID)"
|
|
27
|
+
echo "RUNNING:$PORT"
|
|
28
|
+
exit 0
|
|
29
|
+
else
|
|
30
|
+
log "Outdated version ($CURRENT_VER) running — upgrading to $FRAMEWORK_VERSION"
|
|
31
|
+
kill "$PID" 2>/dev/null || true
|
|
32
|
+
sleep 1
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# ── 2. Check Node.js ─────────────────────────────────────────────────────────
|
|
38
|
+
if ! command -v node &>/dev/null; then
|
|
39
|
+
log "ERROR: Node.js not found. Please install Node.js 18+ from https://nodejs.org"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
NODE_MAJOR=$(node -e "process.stdout.write(process.version.slice(1).split('.')[0])")
|
|
44
|
+
if [ "$NODE_MAJOR" -lt 18 ]; then
|
|
45
|
+
log "ERROR: Node.js 18+ required. Current: $(node -v)"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
log "Node.js $(node -v) ✓"
|
|
50
|
+
|
|
51
|
+
# ── 3. Install / update framework ────────────────────────────────────────────
|
|
52
|
+
mkdir -p "$INSTALL_DIR"
|
|
53
|
+
|
|
54
|
+
INSTALLED_VER=""
|
|
55
|
+
[ -f "$INSTALL_DIR/package.json" ] && \
|
|
56
|
+
INSTALLED_VER=$(node -e "try{process.stdout.write(require('$INSTALL_DIR/package.json').version||'')}catch{}" 2>/dev/null || echo "")
|
|
57
|
+
|
|
58
|
+
if [ "$INSTALLED_VER" != "$FRAMEWORK_VERSION" ]; then
|
|
59
|
+
log "Installing framework v$FRAMEWORK_VERSION → $INSTALL_DIR"
|
|
60
|
+
|
|
61
|
+
# Download release bundle (update URL to your actual hosting)
|
|
62
|
+
BUNDLE_URL="$REGISTRY_URL/pw-framework-v$FRAMEWORK_VERSION.tar.gz"
|
|
63
|
+
BUNDLE_TMP="/tmp/pw-framework-$FRAMEWORK_VERSION.tar.gz"
|
|
64
|
+
|
|
65
|
+
if command -v curl &>/dev/null; then
|
|
66
|
+
curl -fsSL "$BUNDLE_URL" -o "$BUNDLE_TMP"
|
|
67
|
+
elif command -v wget &>/dev/null; then
|
|
68
|
+
wget -q "$BUNDLE_URL" -O "$BUNDLE_TMP"
|
|
69
|
+
else
|
|
70
|
+
log "ERROR: Neither curl nor wget found"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
tar -xzf "$BUNDLE_TMP" -C "$INSTALL_DIR" --strip-components=1
|
|
75
|
+
rm -f "$BUNDLE_TMP"
|
|
76
|
+
|
|
77
|
+
cd "$INSTALL_DIR"
|
|
78
|
+
log "Installing npm dependencies…"
|
|
79
|
+
npm install --omit=dev --silent
|
|
80
|
+
|
|
81
|
+
log "Installing Playwright browsers…"
|
|
82
|
+
npx playwright install chromium firefox webkit --with-deps 2>/dev/null || \
|
|
83
|
+
npx playwright install chromium --with-deps 2>/dev/null || true
|
|
84
|
+
|
|
85
|
+
log "Build complete ✓"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# ── 4. Start the server ───────────────────────────────────────────────────────
|
|
89
|
+
cd "$INSTALL_DIR"
|
|
90
|
+
log "Starting framework server on port $PORT…"
|
|
91
|
+
|
|
92
|
+
PORT=$PORT nohup node dist/server.js >> "$LOG_FILE" 2>&1 &
|
|
93
|
+
echo $! > "$PID_FILE"
|
|
94
|
+
log "Server started (PID $(cat $PID_FILE))"
|
|
95
|
+
|
|
96
|
+
# ── 5. Wait for health check ──────────────────────────────────────────────────
|
|
97
|
+
for i in $(seq 1 20); do
|
|
98
|
+
sleep 0.5
|
|
99
|
+
if curl -sf "http://localhost:$PORT/api/health" >/dev/null 2>&1; then
|
|
100
|
+
log "Framework ready at http://localhost:$PORT ✓"
|
|
101
|
+
echo "RUNNING:$PORT"
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
done
|
|
105
|
+
|
|
106
|
+
log "ERROR: Framework did not start within 10 seconds. Check $LOG_FILE"
|
|
107
|
+
exit 1
|