@dhruv2mars/offdex 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 +31 -0
- package/bin/install-lib.js +326 -0
- package/bin/install.js +31 -0
- package/bin/offdex-lib.js +21 -0
- package/bin/offdex.js +43 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @dhruv2mars/offdex
|
|
2
|
+
|
|
3
|
+
Install the Offdex bridge CLI globally:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g @dhruv2mars/offdex
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Then run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
offdex
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The package downloads the matching native runtime for your platform from GitHub Releases.
|
|
16
|
+
|
|
17
|
+
Supported targets:
|
|
18
|
+
|
|
19
|
+
- macOS `arm64`
|
|
20
|
+
- macOS `x64`
|
|
21
|
+
- Linux `arm64`
|
|
22
|
+
- Linux `x64`
|
|
23
|
+
- Windows `x64`
|
|
24
|
+
|
|
25
|
+
Common usage:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
offdex bridge --host 0.0.0.0 --port 42420
|
|
29
|
+
offdex bridge --control-plane-url https://control.offdex.app
|
|
30
|
+
offdex --help
|
|
31
|
+
```
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createWriteStream, existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { chmod, mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
import https from "node:https";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
const REPO = "Dhruv2mars/offdex";
|
|
10
|
+
const SUPPORTED_TARGETS = new Map([
|
|
11
|
+
["darwin:arm64", "bun-darwin-arm64"],
|
|
12
|
+
["darwin:x64", "bun-darwin-x64"],
|
|
13
|
+
["linux:arm64", "bun-linux-arm64"],
|
|
14
|
+
["linux:x64", "bun-linux-x64-baseline"],
|
|
15
|
+
["win32:x64", "bun-windows-x64-baseline"]
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export function supportedPlatformMatrix() {
|
|
19
|
+
return [...SUPPORTED_TARGETS.keys()].map((key) => {
|
|
20
|
+
const [platform, arch] = key.split(":");
|
|
21
|
+
return { platform, arch, target: SUPPORTED_TARGETS.get(key) };
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isSupportedPlatform(platform = process.platform, arch = process.arch) {
|
|
26
|
+
return SUPPORTED_TARGETS.has(`${platform}:${arch}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function supportedPlatformList() {
|
|
30
|
+
return supportedPlatformMatrix().map(({ platform, arch }) => `${platform}/${arch}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function targetForPlatform(platform = process.platform, arch = process.arch) {
|
|
34
|
+
const target = SUPPORTED_TARGETS.get(`${platform}:${arch}`);
|
|
35
|
+
if (!target) {
|
|
36
|
+
throw new Error(`unsupported_platform:${platform}-${arch}`);
|
|
37
|
+
}
|
|
38
|
+
return target;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function assertSupportedPlatform(platform = process.platform, arch = process.arch) {
|
|
42
|
+
if (isSupportedPlatform(platform, arch)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`unsupported_platform:${platform}-${arch}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function binNameForPlatform(platform = process.platform) {
|
|
49
|
+
return platform === "win32" ? "offdex.exe" : "offdex";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function assetNameFor(platform = process.platform, arch = process.arch) {
|
|
53
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
54
|
+
return `offdex-${platform}-${arch}${ext}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function checksumsAssetNameFor(platform = process.platform, arch = process.arch) {
|
|
58
|
+
return `checksums-${platform}-${arch}.txt`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function resolveInstallRoot(env = process.env, home = homedir()) {
|
|
62
|
+
return env.OFFDEX_INSTALL_ROOT || join(home, ".offdex");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function resolveInstallMetaPath(env = process.env, home = homedir()) {
|
|
66
|
+
return join(resolveInstallRoot(env, home), "install-meta.json");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function resolveInstalledBin(env = process.env, platform = process.platform, home = homedir()) {
|
|
70
|
+
return join(resolveInstallRoot(env, home), "bin", binNameForPlatform(platform));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function packageManagerHintFromEnv(env = process.env) {
|
|
74
|
+
const execPath = String(env.npm_execpath || "").toLowerCase();
|
|
75
|
+
if (execPath.includes("bun")) return "bun";
|
|
76
|
+
if (execPath.includes("pnpm")) return "pnpm";
|
|
77
|
+
if (execPath.includes("yarn")) return "yarn";
|
|
78
|
+
if (execPath.includes("npm")) return "npm";
|
|
79
|
+
|
|
80
|
+
const ua = String(env.npm_config_user_agent || "").toLowerCase();
|
|
81
|
+
if (ua.startsWith("bun/")) return "bun";
|
|
82
|
+
if (ua.startsWith("pnpm/")) return "pnpm";
|
|
83
|
+
if (ua.startsWith("yarn/")) return "yarn";
|
|
84
|
+
if (ua.startsWith("npm/")) return "npm";
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function shouldInstallBinary({ binExists, installedVersion, packageVersion }) {
|
|
90
|
+
if (!binExists) return true;
|
|
91
|
+
if (!packageVersion) return false;
|
|
92
|
+
return installedVersion !== packageVersion;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function resolvePackageVersion(packageJsonPath, env = process.env) {
|
|
96
|
+
try {
|
|
97
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
98
|
+
return typeof pkg.version === "string" && pkg.version.length > 0
|
|
99
|
+
? pkg.version
|
|
100
|
+
: (env.npm_package_version || "0.0.0");
|
|
101
|
+
} catch {
|
|
102
|
+
return env.npm_package_version || "0.0.0";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function isWorkspaceCheckout(packageJsonPath) {
|
|
107
|
+
try {
|
|
108
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
109
|
+
return pkg?.name === "offdex" && Array.isArray(pkg?.workspaces);
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function shouldSkipPackageInstall({
|
|
116
|
+
env = process.env,
|
|
117
|
+
packageRoot
|
|
118
|
+
}) {
|
|
119
|
+
if (env.OFFDEX_SKIP_INSTALL === "1") {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const workspaceRootPackageJson = join(packageRoot, "..", "..", "package.json");
|
|
124
|
+
return isWorkspaceCheckout(workspaceRootPackageJson);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function parseChecksumForAsset(text, asset) {
|
|
128
|
+
if (typeof text !== "string") return null;
|
|
129
|
+
for (const line of text.split(/\r?\n/)) {
|
|
130
|
+
const match = line.trim().match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
|
|
131
|
+
if (!match) continue;
|
|
132
|
+
const candidate = match[2].trim().replace(/^[.][/\\]/, "");
|
|
133
|
+
if (candidate !== asset) continue;
|
|
134
|
+
return match[1].toLowerCase();
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function requestProtocolFor(url) {
|
|
140
|
+
return new URL(url).protocol;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function requestText(url, redirects = 0) {
|
|
144
|
+
if (redirects > 5) {
|
|
145
|
+
throw new Error("too_many_redirects");
|
|
146
|
+
}
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
const transport = requestProtocolFor(url) === "http:" ? http : https;
|
|
149
|
+
const request = transport.get(
|
|
150
|
+
url,
|
|
151
|
+
{
|
|
152
|
+
agent: false,
|
|
153
|
+
headers: {
|
|
154
|
+
Connection: "close",
|
|
155
|
+
"User-Agent": "offdex-installer"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
(response) => {
|
|
159
|
+
if (
|
|
160
|
+
response.statusCode &&
|
|
161
|
+
response.statusCode >= 300 &&
|
|
162
|
+
response.statusCode < 400 &&
|
|
163
|
+
response.headers.location
|
|
164
|
+
) {
|
|
165
|
+
response.resume();
|
|
166
|
+
requestText(response.headers.location, redirects + 1).then(resolve, reject);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (response.statusCode !== 200) {
|
|
170
|
+
response.resume();
|
|
171
|
+
reject(new Error(`http ${response.statusCode}`));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
let data = "";
|
|
175
|
+
response.setEncoding("utf8");
|
|
176
|
+
response.on("data", (chunk) => {
|
|
177
|
+
data += chunk;
|
|
178
|
+
});
|
|
179
|
+
response.on("end", () => resolve(data));
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
request.on("error", reject);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function download(url, outputPath, redirects = 0) {
|
|
187
|
+
if (redirects > 5) {
|
|
188
|
+
throw new Error("too_many_redirects");
|
|
189
|
+
}
|
|
190
|
+
const partPath = `${outputPath}.part`;
|
|
191
|
+
return new Promise((resolve, reject) => {
|
|
192
|
+
const transport = requestProtocolFor(url) === "http:" ? http : https;
|
|
193
|
+
const request = transport.get(
|
|
194
|
+
url,
|
|
195
|
+
{
|
|
196
|
+
agent: false,
|
|
197
|
+
headers: {
|
|
198
|
+
Connection: "close",
|
|
199
|
+
"User-Agent": "offdex-installer"
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
(response) => {
|
|
203
|
+
if (
|
|
204
|
+
response.statusCode &&
|
|
205
|
+
response.statusCode >= 300 &&
|
|
206
|
+
response.statusCode < 400 &&
|
|
207
|
+
response.headers.location
|
|
208
|
+
) {
|
|
209
|
+
response.resume();
|
|
210
|
+
download(response.headers.location, outputPath, redirects + 1).then(resolve, reject);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (response.statusCode !== 200) {
|
|
214
|
+
response.resume();
|
|
215
|
+
reject(new Error(`http ${response.statusCode}`));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const file = createWriteStream(partPath);
|
|
219
|
+
file.on("error", async (error) => {
|
|
220
|
+
await rm(partPath, { force: true });
|
|
221
|
+
reject(error);
|
|
222
|
+
});
|
|
223
|
+
response.on("error", async (error) => {
|
|
224
|
+
await rm(partPath, { force: true });
|
|
225
|
+
reject(error);
|
|
226
|
+
});
|
|
227
|
+
response.pipe(file);
|
|
228
|
+
file.on("finish", () => {
|
|
229
|
+
file.close(async () => {
|
|
230
|
+
try {
|
|
231
|
+
await rename(partPath, outputPath);
|
|
232
|
+
resolve();
|
|
233
|
+
} catch (error) {
|
|
234
|
+
await rm(partPath, { force: true });
|
|
235
|
+
reject(error);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
request.on("error", async (error) => {
|
|
242
|
+
await rm(partPath, { force: true });
|
|
243
|
+
reject(error);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function installRuntime({
|
|
249
|
+
version,
|
|
250
|
+
env = process.env,
|
|
251
|
+
platform = process.platform,
|
|
252
|
+
arch = process.arch,
|
|
253
|
+
home = homedir(),
|
|
254
|
+
downloadFn = download,
|
|
255
|
+
requestTextFn = requestText
|
|
256
|
+
}) {
|
|
257
|
+
assertSupportedPlatform(platform, arch);
|
|
258
|
+
const installRoot = resolveInstallRoot(env, home);
|
|
259
|
+
const installBin = resolveInstalledBin(env, platform, home);
|
|
260
|
+
const installMeta = resolveInstallMetaPath(env, home);
|
|
261
|
+
const asset = assetNameFor(platform, arch);
|
|
262
|
+
const checksumsAsset = checksumsAssetNameFor(platform, arch);
|
|
263
|
+
const baseUrl = env.OFFDEX_RELEASE_BASE_URL
|
|
264
|
+
|| `https://github.com/${REPO}/releases/download/v${version}`;
|
|
265
|
+
|
|
266
|
+
await mkdir(join(installRoot, "bin"), { recursive: true });
|
|
267
|
+
|
|
268
|
+
let checksumsText;
|
|
269
|
+
try {
|
|
270
|
+
checksumsText = await requestTextFn(`${baseUrl}/${checksumsAsset}`);
|
|
271
|
+
} catch {
|
|
272
|
+
throw new Error(`failed_download:${checksumsAsset}`);
|
|
273
|
+
}
|
|
274
|
+
const expectedChecksum = parseChecksumForAsset(checksumsText, asset);
|
|
275
|
+
if (!expectedChecksum) {
|
|
276
|
+
throw new Error(`missing_checksum:${asset}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const tempPath = `${installBin}.download`;
|
|
280
|
+
try {
|
|
281
|
+
try {
|
|
282
|
+
await downloadFn(`${baseUrl}/${asset}`, tempPath);
|
|
283
|
+
} catch {
|
|
284
|
+
throw new Error(`failed_download:${asset}`);
|
|
285
|
+
}
|
|
286
|
+
const actualChecksum = createHash("sha256").update(readFileSync(tempPath)).digest("hex");
|
|
287
|
+
if (actualChecksum !== expectedChecksum) {
|
|
288
|
+
throw new Error(`checksum_mismatch:${asset}`);
|
|
289
|
+
}
|
|
290
|
+
if (platform !== "win32") {
|
|
291
|
+
await chmod(tempPath, 0o755);
|
|
292
|
+
}
|
|
293
|
+
await rm(installBin, { force: true });
|
|
294
|
+
await rename(tempPath, installBin);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
await rm(tempPath, { force: true });
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
if (platform !== "win32") {
|
|
300
|
+
await chmod(installBin, 0o755);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
await writeFile(
|
|
304
|
+
installMeta,
|
|
305
|
+
JSON.stringify(
|
|
306
|
+
{
|
|
307
|
+
packageManager: packageManagerHintFromEnv(env),
|
|
308
|
+
version
|
|
309
|
+
},
|
|
310
|
+
null,
|
|
311
|
+
2
|
|
312
|
+
)
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
return { asset, installBin, installRoot, version };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function readInstalledVersion(env = process.env, home = homedir()) {
|
|
319
|
+
const metaPath = resolveInstallMetaPath(env, home);
|
|
320
|
+
if (!existsSync(metaPath)) return null;
|
|
321
|
+
try {
|
|
322
|
+
return JSON.parse(readFileSync(metaPath, "utf8")).version || null;
|
|
323
|
+
} catch {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
package/bin/install.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
installRuntime,
|
|
6
|
+
resolvePackageVersion,
|
|
7
|
+
shouldSkipPackageInstall,
|
|
8
|
+
supportedPlatformList
|
|
9
|
+
} from "./install-lib.js";
|
|
10
|
+
|
|
11
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const packageRoot = join(here, "..");
|
|
13
|
+
const packageVersion = resolvePackageVersion(join(packageRoot, "package.json"), process.env);
|
|
14
|
+
|
|
15
|
+
if (shouldSkipPackageInstall({ env: process.env, packageRoot })) {
|
|
16
|
+
console.log("offdex: skipping native runtime install inside workspace checkout");
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
installRuntime({ version: packageVersion })
|
|
21
|
+
.then(({ installBin }) => {
|
|
22
|
+
console.log(`offdex: installed ${installBin}`);
|
|
23
|
+
})
|
|
24
|
+
.catch((error) => {
|
|
25
|
+
const message = error instanceof Error ? error.message : "unknown";
|
|
26
|
+
console.error(`offdex: install failed (${message})`);
|
|
27
|
+
if (typeof message === "string" && message.startsWith("unsupported_platform:")) {
|
|
28
|
+
console.error(`offdex: supported targets are ${supportedPlatformList().join(", ")}`);
|
|
29
|
+
}
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { readInstalledVersion, resolveInstalledBin, shouldInstallBinary } from "./install-lib.js";
|
|
5
|
+
|
|
6
|
+
export function resolvePackageBinDir(moduleUrl = import.meta.url) {
|
|
7
|
+
return dirname(fileURLToPath(moduleUrl));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function readPackageVersion(moduleUrl = import.meta.url) {
|
|
11
|
+
const binDir = resolvePackageBinDir(moduleUrl);
|
|
12
|
+
const packageJsonPath = join(binDir, "..", "package.json");
|
|
13
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
14
|
+
return pkg.version;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function installedVersion(env = process.env) {
|
|
18
|
+
return readInstalledVersion(env);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { resolveInstalledBin, shouldInstallBinary };
|
package/bin/offdex.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
installedVersion,
|
|
8
|
+
readPackageVersion,
|
|
9
|
+
resolveInstalledBin,
|
|
10
|
+
resolvePackageBinDir,
|
|
11
|
+
shouldInstallBinary
|
|
12
|
+
} from "./offdex-lib.js";
|
|
13
|
+
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const installedBin = resolveInstalledBin(process.env, process.platform);
|
|
16
|
+
const packageVersion = readPackageVersion();
|
|
17
|
+
const currentInstalledVersion = installedVersion(process.env);
|
|
18
|
+
|
|
19
|
+
if (
|
|
20
|
+
shouldInstallBinary({
|
|
21
|
+
binExists: existsSync(installedBin),
|
|
22
|
+
installedVersion: currentInstalledVersion,
|
|
23
|
+
packageVersion
|
|
24
|
+
})
|
|
25
|
+
) {
|
|
26
|
+
console.error("offdex: setting up native runtime...");
|
|
27
|
+
const here = resolvePackageBinDir(import.meta.url);
|
|
28
|
+
const installer = join(here, "install.js");
|
|
29
|
+
const install = spawnSync(process.execPath, [installer], {
|
|
30
|
+
stdio: "inherit",
|
|
31
|
+
env: process.env
|
|
32
|
+
});
|
|
33
|
+
if (install.status !== 0 || !existsSync(installedBin)) {
|
|
34
|
+
console.error("offdex: install missing. try reinstall: npm i -g @dhruv2mars/offdex");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const result = spawnSync(installedBin, args, {
|
|
40
|
+
stdio: "inherit",
|
|
41
|
+
env: process.env
|
|
42
|
+
});
|
|
43
|
+
process.exit(result.status ?? 1);
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dhruv2mars/offdex",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Codex mobile bridge CLI for Offdex",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"offdex": "bin/offdex.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"offdex": "node bin/offdex.js",
|
|
17
|
+
"postinstall": "node bin/install.js",
|
|
18
|
+
"build": "node -e \"process.exit(0)\"",
|
|
19
|
+
"check": "node --check bin/offdex.js && node --check bin/offdex-lib.js && node --check bin/install.js && node --check bin/install-lib.js && node --test test/*.test.js",
|
|
20
|
+
"test": "node --test test/*.test.js"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/Dhruv2mars/offdex#readme",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/Dhruv2mars/offdex.git",
|
|
26
|
+
"directory": "packages/npm"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/Dhruv2mars/offdex/issues"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"codex",
|
|
33
|
+
"offdex",
|
|
34
|
+
"cli",
|
|
35
|
+
"bridge"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"preferGlobal": true,
|
|
41
|
+
"license": "MIT"
|
|
42
|
+
}
|