@dean0x/mino 0.1.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.
Files changed (3) hide show
  1. package/bin/mino.js +132 -0
  2. package/install.js +214 -0
  3. package/package.json +42 -0
package/bin/mino.js ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("child_process");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+
7
+ const BINARY_NAME = process.platform === "win32" ? "mino.exe" : "mino";
8
+
9
+ /**
10
+ * Platform-specific package mapping
11
+ */
12
+ const PLATFORM_PACKAGES = {
13
+ "darwin-x64": "@dean0x/mino-darwin-x64",
14
+ "darwin-arm64": "@dean0x/mino-darwin-arm64",
15
+ "linux-x64": "@dean0x/mino-linux-x64",
16
+ "linux-arm64": "@dean0x/mino-linux-arm64",
17
+ };
18
+
19
+ /**
20
+ * Get the platform key for the current system
21
+ * @returns {string | null}
22
+ */
23
+ function getPlatformKey() {
24
+ const platform = process.platform;
25
+ const arch = process.arch;
26
+
27
+ if (platform === "darwin" && arch === "x64") return "darwin-x64";
28
+ if (platform === "darwin" && arch === "arm64") return "darwin-arm64";
29
+ if (platform === "linux" && arch === "x64") return "linux-x64";
30
+ if (platform === "linux" && arch === "arm64") return "linux-arm64";
31
+
32
+ return null;
33
+ }
34
+
35
+ /**
36
+ * Try to resolve binary from optional dependencies
37
+ * @returns {string | null}
38
+ */
39
+ function resolveBinaryFromOptionalDeps() {
40
+ const platformKey = getPlatformKey();
41
+ if (!platformKey) return null;
42
+
43
+ const packageName = PLATFORM_PACKAGES[platformKey];
44
+ if (!packageName) return null;
45
+
46
+ try {
47
+ const packagePath = require.resolve(`${packageName}/package.json`);
48
+ const packageDir = path.dirname(packagePath);
49
+ const binaryPath = path.join(packageDir, "bin", BINARY_NAME);
50
+
51
+ if (fs.existsSync(binaryPath)) {
52
+ return binaryPath;
53
+ }
54
+ } catch {
55
+ // Package not installed (expected on non-matching platforms)
56
+ }
57
+
58
+ return null;
59
+ }
60
+
61
+ /**
62
+ * Try to resolve binary from postinstall fallback location
63
+ * @returns {string | null}
64
+ */
65
+ function resolveBinaryFromFallback() {
66
+ const fallbackPath = path.join(__dirname, "..", ".binary", BINARY_NAME);
67
+ if (fs.existsSync(fallbackPath)) {
68
+ return fallbackPath;
69
+ }
70
+ return null;
71
+ }
72
+
73
+ /**
74
+ * Find the mino binary
75
+ * @returns {string}
76
+ */
77
+ function findBinary() {
78
+ // Try optional dependencies first (fastest path)
79
+ const optionalDepsBinary = resolveBinaryFromOptionalDeps();
80
+ if (optionalDepsBinary) {
81
+ return optionalDepsBinary;
82
+ }
83
+
84
+ // Try postinstall fallback location
85
+ const fallbackBinary = resolveBinaryFromFallback();
86
+ if (fallbackBinary) {
87
+ return fallbackBinary;
88
+ }
89
+
90
+ // No binary found
91
+ const platformKey = getPlatformKey();
92
+ if (!platformKey) {
93
+ console.error(
94
+ `Error: Unsupported platform: ${process.platform}-${process.arch}`
95
+ );
96
+ console.error("Mino supports: darwin-x64, darwin-arm64, linux-x64, linux-arm64");
97
+ process.exit(1);
98
+ }
99
+
100
+ console.error("Error: Could not find mino binary.");
101
+ console.error("");
102
+ console.error("This usually means:");
103
+ console.error(" 1. npm failed to install the platform-specific package");
104
+ console.error(" 2. The postinstall script failed to download the binary");
105
+ console.error("");
106
+ console.error("Try reinstalling: npm install -g @dean0x/mino");
107
+ process.exit(1);
108
+ }
109
+
110
+ /**
111
+ * Main entry point
112
+ */
113
+ function main() {
114
+ const binaryPath = findBinary();
115
+ const args = process.argv.slice(2);
116
+
117
+ const child = spawn(binaryPath, args, {
118
+ stdio: "inherit",
119
+ env: process.env,
120
+ });
121
+
122
+ child.on("error", (err) => {
123
+ console.error(`Failed to execute mino: ${err.message}`);
124
+ process.exit(1);
125
+ });
126
+
127
+ child.on("close", (code) => {
128
+ process.exit(code ?? 0);
129
+ });
130
+ }
131
+
132
+ main();
package/install.js ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Postinstall fallback script for @dean0x/mino
5
+ *
6
+ * This script runs after npm install. If the platform-specific optional
7
+ * dependency was installed successfully, this script does nothing.
8
+ * Otherwise, it downloads the correct binary from the npm registry.
9
+ */
10
+
11
+ const https = require("https");
12
+ const fs = require("fs");
13
+ const path = require("path");
14
+ const zlib = require("zlib");
15
+ const { execSync } = require("child_process");
16
+
17
+ const BINARY_NAME = "mino";
18
+ const PACKAGE_VERSION = require("./package.json").version;
19
+
20
+ /**
21
+ * Platform-specific package mapping
22
+ */
23
+ const PLATFORM_PACKAGES = {
24
+ "darwin-x64": "@dean0x/mino-darwin-x64",
25
+ "darwin-arm64": "@dean0x/mino-darwin-arm64",
26
+ "linux-x64": "@dean0x/mino-linux-x64",
27
+ "linux-arm64": "@dean0x/mino-linux-arm64",
28
+ };
29
+
30
+ /**
31
+ * Get the platform key for the current system
32
+ * @returns {string | null}
33
+ */
34
+ function getPlatformKey() {
35
+ const platform = process.platform;
36
+ const arch = process.arch;
37
+
38
+ if (platform === "darwin" && arch === "x64") return "darwin-x64";
39
+ if (platform === "darwin" && arch === "arm64") return "darwin-arm64";
40
+ if (platform === "linux" && arch === "x64") return "linux-x64";
41
+ if (platform === "linux" && arch === "arm64") return "linux-arm64";
42
+
43
+ return null;
44
+ }
45
+
46
+ /**
47
+ * Check if binary is already available from optional dependencies
48
+ * @returns {boolean}
49
+ */
50
+ function binaryAlreadyAvailable() {
51
+ const platformKey = getPlatformKey();
52
+ if (!platformKey) return false;
53
+
54
+ const packageName = PLATFORM_PACKAGES[platformKey];
55
+ if (!packageName) return false;
56
+
57
+ try {
58
+ const packagePath = require.resolve(`${packageName}/package.json`);
59
+ const packageDir = path.dirname(packagePath);
60
+ const binaryPath = path.join(packageDir, "bin", BINARY_NAME);
61
+ return fs.existsSync(binaryPath);
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Download file from URL with redirect handling
69
+ * @param {string} url
70
+ * @returns {Promise<Buffer>}
71
+ */
72
+ function download(url) {
73
+ return new Promise((resolve, reject) => {
74
+ const request = https.get(url, (response) => {
75
+ // Handle redirects
76
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
77
+ download(response.headers.location).then(resolve).catch(reject);
78
+ return;
79
+ }
80
+
81
+ if (response.statusCode !== 200) {
82
+ reject(new Error(`HTTP ${response.statusCode}: ${url}`));
83
+ return;
84
+ }
85
+
86
+ const chunks = [];
87
+ response.on("data", (chunk) => chunks.push(chunk));
88
+ response.on("end", () => resolve(Buffer.concat(chunks)));
89
+ response.on("error", reject);
90
+ });
91
+
92
+ request.on("error", reject);
93
+ request.setTimeout(60000, () => {
94
+ request.destroy();
95
+ reject(new Error("Download timeout"));
96
+ });
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Extract tarball and find the binary
102
+ * @param {Buffer} tarballBuffer
103
+ * @returns {Buffer}
104
+ */
105
+ function extractBinaryFromTarball(tarballBuffer) {
106
+ // Decompress gzip
107
+ const tarBuffer = zlib.gunzipSync(tarballBuffer);
108
+
109
+ // Simple tar extraction - find the binary file
110
+ // tar format: 512-byte headers followed by file content
111
+ let offset = 0;
112
+ while (offset < tarBuffer.length) {
113
+ // Read header
114
+ const header = tarBuffer.subarray(offset, offset + 512);
115
+
116
+ // Check for end of archive (empty header)
117
+ if (header.every((b) => b === 0)) break;
118
+
119
+ // Extract filename (first 100 bytes, null-terminated)
120
+ let nameEnd = 0;
121
+ while (nameEnd < 100 && header[nameEnd] !== 0) nameEnd++;
122
+ const name = header.subarray(0, nameEnd).toString("utf8");
123
+
124
+ // Extract file size (octal string at offset 124, 12 bytes)
125
+ const sizeStr = header.subarray(124, 136).toString("utf8").trim();
126
+ const size = parseInt(sizeStr, 8) || 0;
127
+
128
+ offset += 512; // Move past header
129
+
130
+ // Check if this is the binary we're looking for
131
+ if (name.endsWith(`/bin/${BINARY_NAME}`) || name === `bin/${BINARY_NAME}`) {
132
+ return tarBuffer.subarray(offset, offset + size);
133
+ }
134
+
135
+ // Skip to next file (rounded up to 512-byte boundary)
136
+ offset += Math.ceil(size / 512) * 512;
137
+ }
138
+
139
+ throw new Error(`Binary not found in tarball`);
140
+ }
141
+
142
+ /**
143
+ * Download and install the binary as fallback
144
+ */
145
+ async function installFallback() {
146
+ const platformKey = getPlatformKey();
147
+ if (!platformKey) {
148
+ console.log(`Unsupported platform: ${process.platform}-${process.arch}`);
149
+ console.log("Skipping postinstall - binary must be installed manually.");
150
+ return;
151
+ }
152
+
153
+ const packageName = PLATFORM_PACKAGES[platformKey];
154
+
155
+ console.log(`Downloading ${packageName}@${PACKAGE_VERSION}...`);
156
+
157
+ // Get package metadata from npm registry
158
+ const registryUrl = `https://registry.npmjs.org/${packageName}/${PACKAGE_VERSION}`;
159
+ const metadataBuffer = await download(registryUrl);
160
+ const metadata = JSON.parse(metadataBuffer.toString("utf8"));
161
+
162
+ const tarballUrl = metadata.dist.tarball;
163
+ if (!tarballUrl) {
164
+ throw new Error("Could not find tarball URL in package metadata");
165
+ }
166
+
167
+ // Download tarball
168
+ console.log("Extracting binary...");
169
+ const tarballBuffer = await download(tarballUrl);
170
+
171
+ // Extract binary
172
+ const binaryBuffer = extractBinaryFromTarball(tarballBuffer);
173
+
174
+ // Write binary to local directory
175
+ const binaryDir = path.join(__dirname, ".binary");
176
+ const binaryPath = path.join(binaryDir, BINARY_NAME);
177
+
178
+ fs.mkdirSync(binaryDir, { recursive: true });
179
+ fs.writeFileSync(binaryPath, binaryBuffer, { mode: 0o755 });
180
+
181
+ console.log(`Installed ${BINARY_NAME} to ${binaryPath}`);
182
+ }
183
+
184
+ /**
185
+ * Main entry point
186
+ */
187
+ async function main() {
188
+ // Skip if running in CI or if explicitly disabled
189
+ if (process.env.MINO_SKIP_INSTALL) {
190
+ console.log("MINO_SKIP_INSTALL is set, skipping postinstall");
191
+ return;
192
+ }
193
+
194
+ // Skip if binary already available from optional dependencies
195
+ if (binaryAlreadyAvailable()) {
196
+ console.log("Binary available from optional dependency, skipping fallback download");
197
+ return;
198
+ }
199
+
200
+ try {
201
+ await installFallback();
202
+ } catch (error) {
203
+ // Don't fail the install - npm will have already printed warnings about
204
+ // optional dependencies. The user can still install manually.
205
+ console.error(`Warning: Failed to download fallback binary: ${error.message}`);
206
+ console.error("You may need to install the binary manually.");
207
+ }
208
+ }
209
+
210
+ main().catch((error) => {
211
+ console.error(error);
212
+ // Exit 0 so npm install doesn't fail
213
+ process.exit(0);
214
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@dean0x/mino",
3
+ "version": "0.1.0",
4
+ "description": "Secure AI agent sandbox using rootless containers",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/dean0x/mino"
9
+ },
10
+ "homepage": "https://github.com/dean0x/mino#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/dean0x/mino/issues"
13
+ },
14
+ "keywords": [
15
+ "sandbox",
16
+ "container",
17
+ "podman",
18
+ "security",
19
+ "ai",
20
+ "agent",
21
+ "cli"
22
+ ],
23
+ "bin": {
24
+ "mino": "./bin/mino.js"
25
+ },
26
+ "files": [
27
+ "bin",
28
+ "install.js"
29
+ ],
30
+ "optionalDependencies": {
31
+ "@dean0x/mino-darwin-x64": "0.1.0",
32
+ "@dean0x/mino-darwin-arm64": "0.1.0",
33
+ "@dean0x/mino-linux-x64": "0.1.0",
34
+ "@dean0x/mino-linux-arm64": "0.1.0"
35
+ },
36
+ "scripts": {
37
+ "postinstall": "node install.js"
38
+ },
39
+ "engines": {
40
+ "node": ">=16"
41
+ }
42
+ }