ghostlight 0.4.0
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 +37 -0
- package/bin/ghostlight.js +143 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# ghostlight (npm launcher)
|
|
2
|
+
|
|
3
|
+
Governed browser automation over your own authenticated Chromium session, for AI coding
|
|
4
|
+
agents: any MCP client, your real logged-in browser, with capability grants, sacred domains,
|
|
5
|
+
and a structured audit trail. All-open by default; governance when you want it.
|
|
6
|
+
|
|
7
|
+
This npm package is a thin launcher: on first run it downloads the version-matched Ghostlight
|
|
8
|
+
role executables from the GitHub release and caches them under `~/.ghostlight/bin/` (zero runtime
|
|
9
|
+
dependencies). Since ADR-0046 there are three: `ghostlight` (the CLI + the persistent service) plus
|
|
10
|
+
the two thin pass-throughs `ghostlight-adapter-agent` and `ghostlight-adapter-browser`. A bare
|
|
11
|
+
`npx ghostlight` runs the MCP agent adapter (what your client relays through); `npx ghostlight
|
|
12
|
+
install` runs the CLI installer. Everything real lives in the binaries.
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
Add to any MCP client as a stdio server:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{ "command": "npx", "args": ["-y", "ghostlight"] }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then connect the browser side (once, idempotent):
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
npx ghostlight install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
and add the "Ghostlight in Browser" extension from the Chrome Web Store. Full walkthrough,
|
|
29
|
+
one-click client buttons, and the manual paths:
|
|
30
|
+
https://sylin-org.github.io/ghostlight/install.html
|
|
31
|
+
|
|
32
|
+
## Links
|
|
33
|
+
|
|
34
|
+
- Project: https://github.com/sylin-org/ghostlight
|
|
35
|
+
- What it is and why: https://sylin-org.github.io/ghostlight/
|
|
36
|
+
- License: engine Apache-2.0 OR MIT; the governance module's source is readable under the
|
|
37
|
+
Ghostlight Commercial License (see the repository's LICENSE for the split).
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
3
|
+
// Thin launcher: fetch the version-matched ghostlight role executables from the GitHub release on
|
|
4
|
+
// first run, cache them under ~/.ghostlight/bin/<version>/, then exec the right one for the caller.
|
|
5
|
+
// ADR-0046: a bare `npx ghostlight` (what an MCP client launches) execs ghostlight-adapter-agent
|
|
6
|
+
// (the MCP pass-through); a CLI subcommand (install/doctor/...) execs ghostlight. Zero dependencies.
|
|
7
|
+
// IMPORTANT: stdout belongs to the MCP stdio protocol when a client spawns this; every message this
|
|
8
|
+
// launcher prints goes to stderr.
|
|
9
|
+
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
const fs = require("fs");
|
|
13
|
+
const os = require("os");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const https = require("https");
|
|
16
|
+
const { spawnSync } = require("child_process");
|
|
17
|
+
|
|
18
|
+
const VERSION = require("../package.json").version;
|
|
19
|
+
const REPO = "sylin-org/ghostlight";
|
|
20
|
+
|
|
21
|
+
// The three role executables (ADR-0046): ghostlight = CLI + service; the two adapters are thin
|
|
22
|
+
// pass-throughs. All three are cached in ONE dir, so `ghostlight install` resolves the adapters as
|
|
23
|
+
// siblings.
|
|
24
|
+
const BINS = ["ghostlight", "ghostlight-adapter-agent", "ghostlight-adapter-browser"];
|
|
25
|
+
|
|
26
|
+
// When the caller names one of these `ghostlight` CLI subcommands, exec `ghostlight`; otherwise
|
|
27
|
+
// this is an MCP launch (bare, or with only flags like --instance), so exec the agent adapter.
|
|
28
|
+
const CLI_SUBCOMMANDS = new Set([
|
|
29
|
+
"install",
|
|
30
|
+
"uninstall",
|
|
31
|
+
"doctor",
|
|
32
|
+
"status",
|
|
33
|
+
"config",
|
|
34
|
+
"policy",
|
|
35
|
+
"service",
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
function targetTriple() {
|
|
39
|
+
const { platform, arch } = process;
|
|
40
|
+
if (platform === "win32" && arch === "x64") return "x86_64-pc-windows-msvc";
|
|
41
|
+
if (platform === "darwin" && arch === "arm64") return "aarch64-apple-darwin";
|
|
42
|
+
if (platform === "darwin" && arch === "x64") return "x86_64-apple-darwin";
|
|
43
|
+
if (platform === "linux" && arch === "x64") return "x86_64-unknown-linux-gnu";
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function download(url, dest, redirectsLeft) {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
if (redirectsLeft <= 0) return reject(new Error("too many redirects"));
|
|
50
|
+
https
|
|
51
|
+
.get(url, { headers: { "User-Agent": `ghostlight-npm/${VERSION}` } }, (res) => {
|
|
52
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
53
|
+
res.resume();
|
|
54
|
+
return resolve(download(res.headers.location, dest, redirectsLeft - 1));
|
|
55
|
+
}
|
|
56
|
+
if (res.statusCode !== 200) {
|
|
57
|
+
res.resume();
|
|
58
|
+
return reject(new Error(`download failed: HTTP ${res.statusCode} for ${url}`));
|
|
59
|
+
}
|
|
60
|
+
const tmp = `${dest}.download-${process.pid}`;
|
|
61
|
+
const out = fs.createWriteStream(tmp, { mode: 0o755 });
|
|
62
|
+
res.pipe(out);
|
|
63
|
+
out.on("finish", () => {
|
|
64
|
+
out.close(() => {
|
|
65
|
+
try {
|
|
66
|
+
fs.renameSync(tmp, dest);
|
|
67
|
+
resolve();
|
|
68
|
+
} catch (e) {
|
|
69
|
+
// A concurrent launcher won the rename race; theirs is identical.
|
|
70
|
+
if (fs.existsSync(dest)) {
|
|
71
|
+
fs.rmSync(tmp, { force: true });
|
|
72
|
+
resolve();
|
|
73
|
+
} else {
|
|
74
|
+
reject(e);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
out.on("error", (e) => {
|
|
80
|
+
fs.rmSync(tmp, { force: true });
|
|
81
|
+
reject(e);
|
|
82
|
+
});
|
|
83
|
+
})
|
|
84
|
+
.on("error", reject);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function ensureBinaries() {
|
|
89
|
+
const triple = targetTriple();
|
|
90
|
+
if (!triple) {
|
|
91
|
+
process.stderr.write(
|
|
92
|
+
`ghostlight: no prebuilt binary for ${process.platform}/${process.arch}.\n` +
|
|
93
|
+
`Build from source (cargo install --git https://github.com/${REPO}) or see\n` +
|
|
94
|
+
`https://sylin-org.github.io/ghostlight/install.html for options.\n`
|
|
95
|
+
);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const exe = process.platform === "win32" ? ".exe" : "";
|
|
99
|
+
const dir = path.join(os.homedir(), ".ghostlight", "bin", `v${VERSION}`);
|
|
100
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
101
|
+
|
|
102
|
+
let announced = false;
|
|
103
|
+
for (const b of BINS) {
|
|
104
|
+
const bin = path.join(dir, `${b}${exe}`);
|
|
105
|
+
if (fs.existsSync(bin)) continue;
|
|
106
|
+
if (!announced) {
|
|
107
|
+
process.stderr.write(`ghostlight: first run, fetching v${VERSION} for ${triple}...\n`);
|
|
108
|
+
announced = true;
|
|
109
|
+
}
|
|
110
|
+
const asset = `${b}-${triple}${exe}`;
|
|
111
|
+
const url = `https://github.com/${REPO}/releases/download/v${VERSION}/${asset}`;
|
|
112
|
+
await download(url, bin, 5);
|
|
113
|
+
if (process.platform !== "win32") fs.chmodSync(bin, 0o755);
|
|
114
|
+
}
|
|
115
|
+
if (announced) {
|
|
116
|
+
process.stderr.write(
|
|
117
|
+
`ghostlight: ready. Tip: run "npx ghostlight install" once to connect the browser\n` +
|
|
118
|
+
`extension and register your MCP clients (idempotent; --dry-run to preview).\n`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return { dir, exe };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
ensureBinaries()
|
|
125
|
+
.then(({ dir, exe }) => {
|
|
126
|
+
const args = process.argv.slice(2);
|
|
127
|
+
// ADR-0046: a CLI subcommand runs the `ghostlight` binary; a bare/flags-only invocation is an
|
|
128
|
+
// MCP launch and runs the agent adapter (the pass-through your client relays through).
|
|
129
|
+
const binName = args.some((a) => CLI_SUBCOMMANDS.has(a))
|
|
130
|
+
? "ghostlight"
|
|
131
|
+
: "ghostlight-adapter-agent";
|
|
132
|
+
const bin = path.join(dir, `${binName}${exe}`);
|
|
133
|
+
const result = spawnSync(bin, args, { stdio: "inherit" });
|
|
134
|
+
if (result.error) {
|
|
135
|
+
process.stderr.write(`ghostlight: failed to launch binary: ${result.error.message}\n`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
139
|
+
})
|
|
140
|
+
.catch((e) => {
|
|
141
|
+
process.stderr.write(`ghostlight: ${e.message}\n`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ghostlight",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Governed browser automation over your own authenticated Chromium session, for AI coding agents. Thin npm launcher for the ghostlight binary.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"ghostlight": "bin/ghostlight.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/ghostlight.js",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"mcp-server",
|
|
18
|
+
"browser",
|
|
19
|
+
"automation",
|
|
20
|
+
"chrome",
|
|
21
|
+
"governance",
|
|
22
|
+
"audit",
|
|
23
|
+
"ai-agent"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://sylin-org.github.io/ghostlight/",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/sylin-org/ghostlight.git",
|
|
29
|
+
"directory": "packaging/npm"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/sylin-org/ghostlight/issues"
|
|
33
|
+
},
|
|
34
|
+
"license": "(Apache-2.0 OR MIT)"
|
|
35
|
+
}
|