gologin-api-wrapper 0.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 ADDED
@@ -0,0 +1,63 @@
1
+ # gologin-api-wrapper (local GoLogin API wrapper)
2
+
3
+ This is a small local companion you run on **your own machine** to launch GoLogin profiles locally.
4
+
5
+ The hosted web app (e.g. Vercel) cannot launch local browsers, so it talks to this runner over `http://127.0.0.1`.
6
+
7
+ ## Prerequisites
8
+
9
+ - Node.js **20.x** installed
10
+ - A GoLogin API token
11
+
12
+ ## Setup
13
+
14
+ ## Install (recommended)
15
+
16
+ ```bash
17
+ npm i -g gologin-api-wrapper
18
+ ```
19
+
20
+ 1) Create a `.env` file (or export env vars in your shell):
21
+
22
+ ```bash
23
+ export GOLOGIN_TOKEN="..."
24
+ ```
25
+
26
+ Optional (recommended): restrict which web origins can call the localhost API:
27
+
28
+ ```bash
29
+ export ALLOWED_ORIGINS="https://app.flygen.in,http://localhost:3001"
30
+ ```
31
+
32
+ 2) Install deps and build:
33
+
34
+ ```bash
35
+ npm install
36
+ npm run build
37
+ ```
38
+
39
+ ## Run
40
+
41
+ ```bash
42
+ gologin-api-wrapper start --port 17327
43
+ ```
44
+
45
+ Health check:
46
+
47
+ ```bash
48
+ curl http://127.0.0.1:17327/health
49
+ ```
50
+
51
+ Open a GoLogin profile (launches a local headful Orbita):
52
+
53
+ ```bash
54
+ curl -X POST http://127.0.0.1:17327/gologin/open \
55
+ -H 'Content-Type: application/json' \
56
+ -d '{"profileId":"YOUR_PROFILE_ID"}'
57
+ ```
58
+
59
+ ## API
60
+
61
+ - `GET /health`
62
+ - `POST /gologin/open` `{ profileId }` -> `{ profileId, wsEndpoint }`
63
+
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/index.js"
3
+
package/dist/index.js ADDED
@@ -0,0 +1,140 @@
1
+ import http from "node:http";
2
+ import { URL } from "node:url";
3
+ function parseArgs(argv) {
4
+ const cmd = argv[2] ?? "start";
5
+ let port = 17327;
6
+ let host = "127.0.0.1";
7
+ for (let i = 3; i < argv.length; i += 1) {
8
+ const a = argv[i];
9
+ if (a === "--port") {
10
+ const v = argv[i + 1];
11
+ if (v)
12
+ port = Number(v);
13
+ i += 1;
14
+ continue;
15
+ }
16
+ if (a === "--host") {
17
+ const v = argv[i + 1];
18
+ if (v)
19
+ host = v;
20
+ i += 1;
21
+ continue;
22
+ }
23
+ }
24
+ return { cmd, port, host };
25
+ }
26
+ function sendJson(res, status, body) {
27
+ const buf = Buffer.from(JSON.stringify(body));
28
+ res.statusCode = status;
29
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
30
+ res.setHeader("Content-Length", String(buf.length));
31
+ res.end(buf);
32
+ }
33
+ function applyCors(req, res) {
34
+ // With no-auth localhost API, we should at least avoid blindly enabling CORS in production.
35
+ // Configure a strict allowlist via ALLOWED_ORIGINS="https://app.example.com,http://localhost:3001"
36
+ const origin = req.headers.origin;
37
+ const allow = (process.env.ALLOWED_ORIGINS || "")
38
+ .split(",")
39
+ .map(s => s.trim())
40
+ .filter(Boolean);
41
+ if (!origin)
42
+ return true;
43
+ if (allow.length === 0) {
44
+ // Dev-friendly default: allow all origins if allowlist isn't set.
45
+ // Users can lock it down with ALLOWED_ORIGINS.
46
+ res.setHeader("Access-Control-Allow-Origin", origin);
47
+ res.setHeader("Vary", "Origin");
48
+ }
49
+ else if (allow.includes(origin)) {
50
+ res.setHeader("Access-Control-Allow-Origin", origin);
51
+ res.setHeader("Vary", "Origin");
52
+ }
53
+ else {
54
+ sendJson(res, 403, { message: "Origin not allowed" });
55
+ return false;
56
+ }
57
+ res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
58
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
59
+ res.setHeader("Access-Control-Max-Age", "600");
60
+ return true;
61
+ }
62
+ async function readJsonBody(req) {
63
+ const chunks = [];
64
+ for await (const chunk of req) {
65
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
66
+ }
67
+ const raw = Buffer.concat(chunks).toString("utf8");
68
+ if (!raw)
69
+ return {};
70
+ return JSON.parse(raw);
71
+ }
72
+ async function main() {
73
+ // gologin@2.1.29 currently uses JSON import assertions syntax that breaks on Node 23+.
74
+ // In practice, this runner is expected to run on Node 20.x.
75
+ const major = Number(process.versions.node.split(".")[0] || "0");
76
+ if (major !== 20) {
77
+ console.error(`Unsupported Node.js version: ${process.versions.node}. ` +
78
+ "Please use Node 20.x to run gologin-api-wrapper.");
79
+ process.exit(1);
80
+ }
81
+ const { cmd, port, host } = parseArgs(process.argv);
82
+ if (cmd !== "start") {
83
+ console.error(`Unknown command: ${cmd}. Supported: start`);
84
+ process.exit(1);
85
+ }
86
+ const gologinToken = process.env.GOLOGIN_TOKEN;
87
+ if (!gologinToken) {
88
+ console.error("GOLOGIN_TOKEN is not set");
89
+ process.exit(1);
90
+ }
91
+ // Lazy-load gologin to ensure the Node version guard runs before the module is evaluated.
92
+ const { GologinApi: GL } = (await import("gologin"));
93
+ const server = http.createServer(async (req, res) => {
94
+ try {
95
+ if (!applyCors(req, res))
96
+ return;
97
+ if (req.method === "OPTIONS") {
98
+ res.statusCode = 204;
99
+ res.end();
100
+ return;
101
+ }
102
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
103
+ if (req.method === "GET" && url.pathname === "/health") {
104
+ sendJson(res, 200, { ok: true, name: "gologin-api-wrapper" });
105
+ return;
106
+ }
107
+ if (req.method === "POST" && url.pathname === "/gologin/open") {
108
+ const body = await readJsonBody(req).catch(() => ({}));
109
+ const profileId = (body.profileId || "").trim();
110
+ if (!profileId) {
111
+ sendJson(res, 400, { message: "profileId is required" });
112
+ return;
113
+ }
114
+ const gologin = GL({ token: gologinToken });
115
+ // gologin types model launch params as a union; for local launch we combine fields.
116
+ const { browser } = await gologin.launch({
117
+ cloud: false,
118
+ headless: false,
119
+ profileId,
120
+ });
121
+ const wsEndpoint = browser.wsEndpoint();
122
+ const out = { profileId, wsEndpoint };
123
+ sendJson(res, 200, out);
124
+ return;
125
+ }
126
+ sendJson(res, 404, { message: "Not found" });
127
+ }
128
+ catch (error) {
129
+ const message = error instanceof Error ? error.message : "Unknown error";
130
+ sendJson(res, 500, { message });
131
+ }
132
+ });
133
+ server.listen(port, host, () => {
134
+ console.log(`gologin-api-wrapper listening on http://${host}:${port}`);
135
+ });
136
+ }
137
+ main().catch(err => {
138
+ console.error(err);
139
+ process.exit(1);
140
+ });
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "gologin-api-wrapper",
3
+ "version": "0.0.1",
4
+ "description": "Local GoLogin API wrapper (launches GoLogin locally via a localhost API)",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "bin": {
9
+ "gologin-api-wrapper": "bin/be-linkedbot-runner.js"
10
+ },
11
+ "files": [
12
+ "bin",
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": "20.x"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "scripts": {
23
+ "dev": "tsx src/index.ts start",
24
+ "build": "tsc",
25
+ "start": "node dist/index.js start",
26
+ "lint": "eslint src/**/*.ts",
27
+ "type-check": "tsc --noEmit",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "dependencies": {
31
+ "gologin": "^2.1.29"
32
+ },
33
+ "devDependencies": {
34
+ "@eslint/js": "^9.27.0",
35
+ "@types/node": "^22.13.10",
36
+ "@typescript-eslint/eslint-plugin": "^8.33.0",
37
+ "@typescript-eslint/parser": "^8.33.0",
38
+ "eslint": "^9.27.0",
39
+ "globals": "^15.15.0",
40
+ "tsx": "^4.20.5",
41
+ "typescript": "^5.8.3"
42
+ }
43
+ }
44
+