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 CHANGED
@@ -1,33 +1,46 @@
1
1
  # Argon CLI
2
2
 
3
- The official CLI for Argon — Git for MongoDB: branch, time-travel, merge and undo with real drivers and versioned history underneath.
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
- # Enable time travel
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
- # Create instant branch
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
- # Time travel queries
24
- argon time-travel info --time "1h ago"
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
- Full documentation: https://github.com/argon-lab/argon
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.1",
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": ">=14.0.0"
41
+ "node": ">=18.0.0"
41
42
  },
42
43
  "os": [
43
44
  "darwin",
@@ -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 { execSync } = require('child_process');
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 platform = `${process.platform}-${process.arch}`;
21
- if (!PLATFORM_MAP[platform]) {
22
- throw new Error(`Unsupported platform: ${platform}`);
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
- async function install() {
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 platform = getPlatform();
59
- const binaryName = getBinaryName();
60
- const assetName = process.platform === 'win32' ? `argon-${platform}.exe` : `argon-${platform}`;
61
- const downloadUrl = `https://github.com/argon-lab/argon/releases/download/v${version}/${assetName}`;
62
-
63
- const binDir = path.join(__dirname, '..', 'bin');
64
- const binaryPath = path.join(binDir, binaryName);
65
-
66
- // Create bin directory
67
- if (!fs.existsSync(binDir)) {
68
- fs.mkdirSync(binDir, { recursive: true });
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
- console.log(`Downloading Argon CLI v${version} for ${platform}...`);
72
- console.log(`From: ${downloadUrl}`);
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
- // Verify installation
82
- try {
83
- const output = execSync(`"${binaryPath}" --version`, { encoding: 'utf8' });
84
- console.log('✅ Argon CLI installed successfully!');
85
- console.log(output.trim());
86
- } catch (e) {
87
- console.error('⚠️ Binary downloaded but verification failed');
88
- console.error('Please check if the binary is working correctly');
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
- } catch (error) {
92
- console.error('Failed to install Argon CLI:', error.message);
93
- console.error('\nYou can manually download from:');
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
- // Run installation
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 };