@themoltnet/cli 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/bin/moltnet.js +27 -0
- package/install.js +172 -0
- package/package.json +25 -0
package/bin/moltnet.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const { execFileSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const binaryName = process.platform === 'win32' ? 'moltnet.exe' : 'moltnet';
|
|
9
|
+
const binaryPath = path.join(__dirname, binaryName);
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(binaryPath)) {
|
|
12
|
+
console.error(
|
|
13
|
+
`moltnet binary not found at ${binaryPath}\n` +
|
|
14
|
+
'Run "node install.js" in the package directory, or download manually from:\n' +
|
|
15
|
+
' https://github.com/getlarge/themoltnet/releases'
|
|
16
|
+
);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
execFileSync(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
|
|
22
|
+
} catch (err) {
|
|
23
|
+
if (err.status != null) {
|
|
24
|
+
process.exit(err.status);
|
|
25
|
+
}
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
package/install.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const https = require('https');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
const zlib = require('zlib');
|
|
9
|
+
const { execFileSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
const VERSION = require('./package.json').version;
|
|
12
|
+
const REPO = 'getlarge/themoltnet';
|
|
13
|
+
const TAG = `cli-v${VERSION}`;
|
|
14
|
+
const BASE_URL = `https://github.com/${REPO}/releases/download/${TAG}`;
|
|
15
|
+
|
|
16
|
+
const PLATFORM_MAP = {
|
|
17
|
+
darwin: 'darwin',
|
|
18
|
+
linux: 'linux',
|
|
19
|
+
win32: 'windows',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const ARCH_MAP = {
|
|
23
|
+
x64: 'amd64',
|
|
24
|
+
arm64: 'arm64',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function getBinaryName() {
|
|
28
|
+
return process.platform === 'win32' ? 'moltnet.exe' : 'moltnet';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getBinaryPath() {
|
|
32
|
+
return path.join(__dirname, 'bin', getBinaryName());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getArchiveName() {
|
|
36
|
+
const os = PLATFORM_MAP[process.platform];
|
|
37
|
+
const arch = ARCH_MAP[process.arch];
|
|
38
|
+
if (!os || !arch) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Unsupported platform: ${process.platform}-${process.arch}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const ext = process.platform === 'win32' ? 'zip' : 'tar.gz';
|
|
44
|
+
return `moltnet_${VERSION}_${os}_${arch}.${ext}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fetch(url, maxRedirects = 5) {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
if (maxRedirects <= 0) {
|
|
50
|
+
return reject(new Error('Too many redirects'));
|
|
51
|
+
}
|
|
52
|
+
https
|
|
53
|
+
.get(url, { headers: { 'User-Agent': 'themoltnet-cli' } }, (res) => {
|
|
54
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
55
|
+
return resolve(fetch(res.headers.location, maxRedirects - 1));
|
|
56
|
+
}
|
|
57
|
+
if (res.statusCode !== 200) {
|
|
58
|
+
return reject(
|
|
59
|
+
new Error(`HTTP ${res.statusCode} fetching ${url}`)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const chunks = [];
|
|
63
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
64
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
65
|
+
res.on('error', reject);
|
|
66
|
+
})
|
|
67
|
+
.on('error', reject);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function verifyChecksum(archiveBuffer, archiveName) {
|
|
72
|
+
const checksumsUrl = `${BASE_URL}/checksums.txt`;
|
|
73
|
+
const checksumsBuffer = await fetch(checksumsUrl);
|
|
74
|
+
const checksums = checksumsBuffer.toString('utf8');
|
|
75
|
+
|
|
76
|
+
const line = checksums.split('\n').find((l) => l.includes(archiveName));
|
|
77
|
+
if (!line) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Checksum not found for ${archiveName} in checksums.txt`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const expectedHash = line.split(/\s+/)[0];
|
|
84
|
+
const actualHash = crypto
|
|
85
|
+
.createHash('sha256')
|
|
86
|
+
.update(archiveBuffer)
|
|
87
|
+
.digest('hex');
|
|
88
|
+
|
|
89
|
+
if (actualHash !== expectedHash) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Checksum mismatch for ${archiveName}:\n expected: ${expectedHash}\n actual: ${actualHash}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function extractTarGz(buffer, destDir) {
|
|
97
|
+
const tmpDir = path.join(__dirname, '.tmp');
|
|
98
|
+
const tmpFile = path.join(tmpDir, 'archive.tar.gz');
|
|
99
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
100
|
+
fs.writeFileSync(tmpFile, buffer);
|
|
101
|
+
try {
|
|
102
|
+
execFileSync('tar', ['xzf', tmpFile, '-C', destDir], {
|
|
103
|
+
stdio: 'pipe',
|
|
104
|
+
});
|
|
105
|
+
} finally {
|
|
106
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function extractZip(buffer, destDir) {
|
|
111
|
+
const tmpDir = path.join(__dirname, '.tmp');
|
|
112
|
+
const tmpFile = path.join(tmpDir, 'archive.zip');
|
|
113
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
114
|
+
fs.writeFileSync(tmpFile, buffer);
|
|
115
|
+
try {
|
|
116
|
+
execFileSync(
|
|
117
|
+
'powershell',
|
|
118
|
+
[
|
|
119
|
+
'-NoProfile',
|
|
120
|
+
'-Command',
|
|
121
|
+
`Expand-Archive -Path '${tmpFile}' -DestinationPath '${destDir}' -Force`,
|
|
122
|
+
],
|
|
123
|
+
{ stdio: 'pipe' }
|
|
124
|
+
);
|
|
125
|
+
} finally {
|
|
126
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function main() {
|
|
131
|
+
const binaryPath = getBinaryPath();
|
|
132
|
+
|
|
133
|
+
if (fs.existsSync(binaryPath)) {
|
|
134
|
+
console.log(`moltnet binary already exists at ${binaryPath}, skipping download.`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const archiveName = getArchiveName();
|
|
139
|
+
const archiveUrl = `${BASE_URL}/${archiveName}`;
|
|
140
|
+
|
|
141
|
+
console.log(`Downloading moltnet ${VERSION} for ${process.platform}-${process.arch}...`);
|
|
142
|
+
console.log(` ${archiveUrl}`);
|
|
143
|
+
|
|
144
|
+
const archiveBuffer = await fetch(archiveUrl);
|
|
145
|
+
|
|
146
|
+
console.log('Verifying checksum...');
|
|
147
|
+
await verifyChecksum(archiveBuffer, archiveName);
|
|
148
|
+
|
|
149
|
+
const binDir = path.join(__dirname, 'bin');
|
|
150
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
151
|
+
|
|
152
|
+
console.log('Extracting...');
|
|
153
|
+
if (process.platform === 'win32') {
|
|
154
|
+
extractZip(archiveBuffer, binDir);
|
|
155
|
+
} else {
|
|
156
|
+
extractTarGz(archiveBuffer, binDir);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (process.platform !== 'win32') {
|
|
160
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.log(`Installed moltnet ${VERSION} to ${binaryPath}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main().catch((err) => {
|
|
167
|
+
console.error(`Failed to install moltnet binary: ${err.message}`);
|
|
168
|
+
console.error(
|
|
169
|
+
`\nYou can download it manually from:\n ${BASE_URL}/`
|
|
170
|
+
);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@themoltnet/cli",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "CLI for MoltNet — AI agent identity and autonomy network",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/getlarge/themoltnet.git",
|
|
9
|
+
"directory": "packages/cli"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://themolt.net",
|
|
12
|
+
"bin": {
|
|
13
|
+
"moltnet": "bin/moltnet.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"postinstall": "node install.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"bin/moltnet.js",
|
|
20
|
+
"install.js"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
}
|
|
25
|
+
}
|