argonctl 2.0.1 → 2.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.
- package/README.md +23 -10
- package/package.json +7 -6
- package/scripts/install.js +63 -78
package/README.md
CHANGED
|
@@ -1,33 +1,46 @@
|
|
|
1
1
|
# Argon CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Argon creates versioned MongoDB sandboxes for branches, time travel, merges, and undo with native MongoDB drivers.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
Requires Node.js 18 or newer. The installer downloads the matching release binary for macOS or Linux (x64/arm64), or Windows (x64).
|
|
8
|
+
|
|
7
9
|
```bash
|
|
8
10
|
npm install -g argonctl
|
|
11
|
+
argon --version
|
|
9
12
|
```
|
|
10
13
|
|
|
11
14
|
## Usage
|
|
12
15
|
|
|
16
|
+
Use MongoDB 7 or newer configured as a replica set. A standalone MongoDB server cannot provide the change streams and transactions Argon requires.
|
|
17
|
+
|
|
13
18
|
```bash
|
|
14
|
-
|
|
15
|
-
export ENABLE_WAL=true
|
|
19
|
+
export MONGODB_URI='mongodb://localhost:27017/?replicaSet=rs0'
|
|
16
20
|
|
|
17
|
-
# Create project
|
|
18
21
|
argon projects create my-app
|
|
22
|
+
argon branches create feature-x --project my-app
|
|
23
|
+
argon time-travel info --project my-app --branch feature-x
|
|
24
|
+
|
|
25
|
+
# Start the MCP server for an MCP client.
|
|
26
|
+
argon mcp
|
|
27
|
+
```
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
argon branches create feature-x
|
|
29
|
+
Native writes require the API server, MCP server, or a running `argon ingest` process to capture history. `time-travel info` lists available history; use `time-travel query --project my-app --branch feature-x --lsn <LSN> --collection <collection>` to read a retained version.
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
## Building from source
|
|
32
|
+
|
|
33
|
+
Use the Go version declared in the repository's `go.mod` files. The CLI and API are separate Go modules; run their build commands from those directories:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
(cd cli && go build -o argon .)
|
|
37
|
+
(cd api && go run .)
|
|
25
38
|
```
|
|
26
39
|
|
|
27
40
|
## Documentation
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
See the [project documentation](https://github.com/argon-lab/argon) for setup, capture lifecycle, and supported operations.
|
|
30
43
|
|
|
31
44
|
## License
|
|
32
45
|
|
|
33
|
-
MIT
|
|
46
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "argonctl",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"mcpName": "io.github.argon-lab/argon",
|
|
5
5
|
"description": "Git for MongoDB: branch, time-travel, merge and undo - CLI",
|
|
6
6
|
"keywords": [
|
|
@@ -22,22 +22,23 @@
|
|
|
22
22
|
},
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"author": "Argon Lab",
|
|
25
|
-
"main": "index.js",
|
|
26
25
|
"bin": {
|
|
27
26
|
"argon": "bin/argon.js",
|
|
28
27
|
"argonctl": "bin/argon.js"
|
|
29
28
|
},
|
|
30
29
|
"files": [
|
|
31
|
-
"bin/",
|
|
32
|
-
"scripts/",
|
|
30
|
+
"bin/argon.js",
|
|
31
|
+
"scripts/install.js",
|
|
32
|
+
"scripts/uninstall.js",
|
|
33
33
|
"README.md"
|
|
34
34
|
],
|
|
35
35
|
"scripts": {
|
|
36
36
|
"postinstall": "node scripts/install.js",
|
|
37
|
-
"preuninstall": "node scripts/uninstall.js"
|
|
37
|
+
"preuninstall": "node scripts/uninstall.js",
|
|
38
|
+
"test": "node --test scripts/install.test.js"
|
|
38
39
|
},
|
|
39
40
|
"engines": {
|
|
40
|
-
"node": ">=
|
|
41
|
+
"node": ">=18.0.0"
|
|
41
42
|
},
|
|
42
43
|
"os": [
|
|
43
44
|
"darwin",
|
package/scripts/install.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
2
3
|
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const https = require('https');
|
|
6
|
-
const {
|
|
7
|
-
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const https = require('node:https');
|
|
7
|
+
const { pipeline } = require('node:stream/promises');
|
|
8
|
+
const { execFileSync } = require('node:child_process');
|
|
8
9
|
const version = require('../package.json').version;
|
|
9
10
|
|
|
10
|
-
// Platform mapping
|
|
11
11
|
const PLATFORM_MAP = {
|
|
12
12
|
'darwin-x64': 'darwin-amd64',
|
|
13
13
|
'darwin-arm64': 'darwin-arm64',
|
|
@@ -16,85 +16,70 @@ const PLATFORM_MAP = {
|
|
|
16
16
|
'win32-x64': 'windows-amd64',
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
function getPlatform() {
|
|
20
|
-
const
|
|
21
|
-
if (!PLATFORM_MAP[
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
return PLATFORM_MAP[platform];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getBinaryName() {
|
|
28
|
-
// The wrapper bin/argon.js execs this; distinct from the wrapper name.
|
|
29
|
-
return process.platform === 'win32' ? 'argon-bin.exe' : 'argon-bin';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function downloadBinary(url, dest) {
|
|
33
|
-
return new Promise((resolve, reject) => {
|
|
34
|
-
const file = fs.createWriteStream(dest);
|
|
35
|
-
https.get(url, (response) => {
|
|
36
|
-
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
37
|
-
// Follow redirect
|
|
38
|
-
https.get(response.headers.location, (redirectResponse) => {
|
|
39
|
-
redirectResponse.pipe(file);
|
|
40
|
-
file.on('finish', () => {
|
|
41
|
-
file.close(resolve);
|
|
42
|
-
});
|
|
43
|
-
}).on('error', reject);
|
|
44
|
-
} else if (response.statusCode === 200) {
|
|
45
|
-
response.pipe(file);
|
|
46
|
-
file.on('finish', () => {
|
|
47
|
-
file.close(resolve);
|
|
48
|
-
});
|
|
49
|
-
} else {
|
|
50
|
-
reject(new Error(`Failed to download: ${response.statusCode}`));
|
|
51
|
-
}
|
|
52
|
-
}).on('error', reject);
|
|
53
|
-
});
|
|
19
|
+
function getPlatform(platform = process.platform, arch = process.arch) {
|
|
20
|
+
const key = `${platform}-${arch}`;
|
|
21
|
+
if (!PLATFORM_MAP[key]) throw new Error(`Unsupported platform: ${key}`);
|
|
22
|
+
return PLATFORM_MAP[key];
|
|
54
23
|
}
|
|
55
24
|
|
|
56
|
-
|
|
25
|
+
// Only a successful final HTTP response reaches the file. GitHub's CDN may
|
|
26
|
+
// redirect more than once; reject loops, insecure redirects, and error pages.
|
|
27
|
+
async function downloadBinary(url, destination, get = https.get, redirects = 5) {
|
|
57
28
|
try {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
29
|
+
const parsed = new URL(url);
|
|
30
|
+
if (parsed.protocol !== 'https:') throw new Error('Downloads must use HTTPS');
|
|
31
|
+
const response = await new Promise((resolve, reject) => {
|
|
32
|
+
const request = get(parsed, resolve);
|
|
33
|
+
request.on('error', reject);
|
|
34
|
+
request.setTimeout(30000, () => request.destroy(new Error('Download timed out')));
|
|
35
|
+
});
|
|
36
|
+
if ([301, 302, 303, 307, 308].includes(response.statusCode)) {
|
|
37
|
+
response.resume();
|
|
38
|
+
if (redirects === 0 || !response.headers.location) throw new Error('Invalid or excessive download redirects');
|
|
39
|
+
return await downloadBinary(new URL(response.headers.location, parsed).href, destination, get, redirects - 1);
|
|
69
40
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
await downloadBinary(downloadUrl, binaryPath);
|
|
75
|
-
|
|
76
|
-
// Make binary executable on Unix-like systems
|
|
77
|
-
if (process.platform !== 'win32') {
|
|
78
|
-
fs.chmodSync(binaryPath, '755');
|
|
41
|
+
if (response.statusCode !== 200) {
|
|
42
|
+
response.resume();
|
|
43
|
+
throw new Error(`Download failed: HTTP ${response.statusCode}`);
|
|
79
44
|
}
|
|
45
|
+
await pipeline(response, fs.createWriteStream(destination));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
fs.rmSync(destination, { force: true });
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
80
51
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
52
|
+
async function install() {
|
|
53
|
+
const platform = getPlatform();
|
|
54
|
+
const suffix = process.platform === 'win32' ? '.exe' : '';
|
|
55
|
+
const assetName = `argon-${platform}${suffix}`;
|
|
56
|
+
const url = `https://github.com/argon-lab/argon/releases/download/v${version}/${assetName}`;
|
|
57
|
+
const binDir = path.join(__dirname, '..', 'bin');
|
|
58
|
+
const binaryPath = path.join(binDir, `argon-bin${suffix}`);
|
|
59
|
+
const temporaryPath = `${binaryPath}.${process.pid}.tmp${suffix}`;
|
|
60
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
61
|
+
console.log(`Downloading Argon CLI v${version} for ${platform}...`);
|
|
62
|
+
try {
|
|
63
|
+
await downloadBinary(url, temporaryPath);
|
|
64
|
+
if (process.platform !== 'win32') fs.chmodSync(temporaryPath, 0o755);
|
|
65
|
+
const output = execFileSync(temporaryPath, ['--version'], { encoding: 'utf8', timeout: 30000 });
|
|
66
|
+
const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
67
|
+
if (!new RegExp(`(?:^|\\s)v?${escaped}(?:\\s|$)`).test(output)) {
|
|
68
|
+
throw new Error(`Binary reported an unexpected version: ${output.trim()}`);
|
|
89
69
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.error('https://github.com/argon-lab/argon/releases');
|
|
95
|
-
process.exit(1);
|
|
70
|
+
fs.renameSync(temporaryPath, binaryPath);
|
|
71
|
+
console.log(`Argon CLI installed successfully: ${output.trim()}`);
|
|
72
|
+
} finally {
|
|
73
|
+
fs.rmSync(temporaryPath, { force: true });
|
|
96
74
|
}
|
|
97
75
|
}
|
|
98
76
|
|
|
99
|
-
|
|
100
|
-
install()
|
|
77
|
+
if (require.main === module) {
|
|
78
|
+
install().catch(error => {
|
|
79
|
+
console.error(`Failed to install Argon CLI: ${error.message}`);
|
|
80
|
+
console.error('Manual downloads: https://github.com/argon-lab/argon/releases');
|
|
81
|
+
process.exitCode = 1;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = { downloadBinary, getPlatform };
|