cwbridge 1.7.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 +30 -0
- package/bin/cwbridge.exe +0 -0
- package/bin/cwbridge.js +158 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# CWBridge
|
|
2
|
+
|
|
3
|
+
CWBridge is a Windows command-line bridge for locally configured Roblox
|
|
4
|
+
services. This npm package contains the compiled Windows executable and a
|
|
5
|
+
minimal launcher; it does not include the Python source code.
|
|
6
|
+
|
|
7
|
+
## Run
|
|
8
|
+
|
|
9
|
+
```powershell
|
|
10
|
+
npx --yes cwbridge
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Pass normal CWBridge arguments after the command, for example:
|
|
14
|
+
|
|
15
|
+
```powershell
|
|
16
|
+
npx --yes cwbridge --version
|
|
17
|
+
npx --yes cwbridge start --only weather
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The launcher checks npm at most once every 24 hours and reports when a newer
|
|
21
|
+
package is available. It does not update the executable automatically. Fetch
|
|
22
|
+
the newest launcher with:
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
npx --yes cwbridge@latest
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Set `CWBRIDGE_NO_UPDATE_CHECK=1` to disable the launcher update check.
|
|
29
|
+
|
|
30
|
+
This package is currently Windows x64 only.
|
package/bin/cwbridge.exe
ADDED
|
Binary file
|
package/bin/cwbridge.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const https = require("node:https");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const { spawnSync } = require("node:child_process");
|
|
8
|
+
|
|
9
|
+
const packageInfo = require("../package.json");
|
|
10
|
+
const UPDATE_CHECK_MS = 24 * 60 * 60 * 1000;
|
|
11
|
+
const UPDATE_TIMEOUT_MS = 900;
|
|
12
|
+
|
|
13
|
+
function parseVersion(value) {
|
|
14
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(String(value));
|
|
15
|
+
return match ? match.slice(1, 4).map(Number) : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isNewerVersion(latest, current) {
|
|
19
|
+
const latestParts = parseVersion(latest);
|
|
20
|
+
const currentParts = parseVersion(current);
|
|
21
|
+
if (!latestParts || !currentParts) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
for (let index = 0; index < latestParts.length; index += 1) {
|
|
25
|
+
if (latestParts[index] !== currentParts[index]) {
|
|
26
|
+
return latestParts[index] > currentParts[index];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function cachePath() {
|
|
33
|
+
const base = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
34
|
+
return base ? path.join(base, "CWBridge", "npm-update-check.json") : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readCachedLatest() {
|
|
38
|
+
const filename = cachePath();
|
|
39
|
+
if (!filename) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const cached = JSON.parse(fs.readFileSync(filename, "utf8"));
|
|
44
|
+
if (
|
|
45
|
+
cached.package === packageInfo.name &&
|
|
46
|
+
typeof cached.checkedAt === "number" &&
|
|
47
|
+
Date.now() - cached.checkedAt < UPDATE_CHECK_MS &&
|
|
48
|
+
typeof cached.latest === "string"
|
|
49
|
+
) {
|
|
50
|
+
return cached.latest;
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
// A missing or malformed cache must never prevent CWBridge from starting.
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function writeCachedLatest(latest) {
|
|
59
|
+
const filename = cachePath();
|
|
60
|
+
if (!filename) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
fs.mkdirSync(path.dirname(filename), { recursive: true });
|
|
65
|
+
fs.writeFileSync(
|
|
66
|
+
filename,
|
|
67
|
+
JSON.stringify({ package: packageInfo.name, latest, checkedAt: Date.now() }),
|
|
68
|
+
"utf8",
|
|
69
|
+
);
|
|
70
|
+
} catch {
|
|
71
|
+
// The update cache is optional and must not affect command execution.
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function fetchLatestVersion() {
|
|
76
|
+
return new Promise((resolve) => {
|
|
77
|
+
const request = https.get(
|
|
78
|
+
`https://registry.npmjs.org/${encodeURIComponent(packageInfo.name)}/latest`,
|
|
79
|
+
{ headers: { Accept: "application/json", "User-Agent": "cwbridge-npx-launcher" } },
|
|
80
|
+
(response) => {
|
|
81
|
+
let body = "";
|
|
82
|
+
response.setEncoding("utf8");
|
|
83
|
+
response.on("data", (chunk) => {
|
|
84
|
+
body += chunk;
|
|
85
|
+
});
|
|
86
|
+
response.on("end", () => {
|
|
87
|
+
if (response.statusCode !== 200) {
|
|
88
|
+
resolve(null);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
resolve(JSON.parse(body).version || null);
|
|
93
|
+
} catch {
|
|
94
|
+
resolve(null);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
request.setTimeout(UPDATE_TIMEOUT_MS, () => request.destroy());
|
|
100
|
+
request.on("error", () => resolve(null));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function reportAvailableUpdate() {
|
|
105
|
+
if (process.env.CWBRIDGE_NO_UPDATE_CHECK === "1") {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const cachedLatest = readCachedLatest();
|
|
109
|
+
const latest = cachedLatest || (await fetchLatestVersion());
|
|
110
|
+
if (!latest) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (!cachedLatest) {
|
|
114
|
+
writeCachedLatest(latest);
|
|
115
|
+
}
|
|
116
|
+
if (isNewerVersion(latest, packageInfo.version)) {
|
|
117
|
+
console.error(
|
|
118
|
+
`A newer CWBridge version is available: v${packageInfo.version} -> v${latest}\n` +
|
|
119
|
+
"Run: npx --yes cwbridge@latest",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function main() {
|
|
125
|
+
if (process.platform !== "win32") {
|
|
126
|
+
console.error("CWBridge via npx currently supports Windows x64 only.");
|
|
127
|
+
process.exitCode = 1;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const executable = path.join(__dirname, "cwbridge.exe");
|
|
132
|
+
if (!fs.existsSync(executable)) {
|
|
133
|
+
console.error("CWBridge executable is missing from this npm package.");
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await reportAvailableUpdate();
|
|
139
|
+
const result = spawnSync(executable, process.argv.slice(2), {
|
|
140
|
+
stdio: "inherit",
|
|
141
|
+
windowsHide: false,
|
|
142
|
+
});
|
|
143
|
+
if (result.error) {
|
|
144
|
+
console.error(`Unable to start CWBridge: ${result.error.message}`);
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
process.exitCode = typeof result.status === "number" ? result.status : 1;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (require.main === module) {
|
|
152
|
+
main().catch((error) => {
|
|
153
|
+
console.error(`CWBridge launcher failed: ${error.message}`);
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
module.exports = { isNewerVersion, parseVersion };
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cwbridge",
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"description": "Windows launcher for the compiled CWBridge executable",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"private": false,
|
|
7
|
+
"os": [
|
|
8
|
+
"win32"
|
|
9
|
+
],
|
|
10
|
+
"cpu": [
|
|
11
|
+
"x64"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"cwbridge": "bin/cwbridge.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"bin/cwbridge.js",
|
|
21
|
+
"bin/cwbridge.exe",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
}
|
|
27
|
+
}
|