@mandors/cli 0.0.14 → 0.0.17
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 +104 -4
- package/{npm/binaries → binaries}/darwin-arm64/mandor +0 -0
- package/binaries/darwin-arm64.tar.gz +0 -0
- package/{npm/binaries → binaries}/linux-arm64/mandor +0 -0
- package/binaries/linux-arm64.tar.gz +0 -0
- package/npm/lib/install.js +73 -119
- package/npm/lib/resolve.js +84 -73
- package/npm/scripts/build.js +68 -86
- package/package.json +2 -4
- package/scripts/install.sh +105 -0
- package/npm/binaries/darwin-arm64.tar.gz +0 -0
- package/npm/binaries/linux-arm64.tar.gz +0 -0
package/README.md
CHANGED
|
@@ -69,20 +69,120 @@ Mandor provides:
|
|
|
69
69
|
|
|
70
70
|
## Installation
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
Mandor can be installed via curl, npm, or built from source.
|
|
73
73
|
|
|
74
|
+
### Option 1: curl (Recommended for macOS/Linux)
|
|
75
|
+
|
|
76
|
+
The fastest way to install Mandor on macOS or Linux:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Install latest stable version (to ~/.local/bin)
|
|
80
|
+
curl -fsSL https://raw.githubusercontent.com/sanxzy/mandor/main/scripts/install.sh | sh
|
|
81
|
+
|
|
82
|
+
# Install to custom directory
|
|
83
|
+
curl -fsSL https://raw.githubusercontent.com/sanxzy/mandor/main/scripts/install.sh | sh -s -- --prefix /usr/local/bin
|
|
84
|
+
|
|
85
|
+
# Install latest prerelease (beta versions)
|
|
86
|
+
curl -fsSL https://raw.githubusercontent.com/sanxzy/mandor/main/scripts/install.sh | sh -s -- --prerelease
|
|
87
|
+
|
|
88
|
+
# Install specific version
|
|
89
|
+
curl -fsSL https://raw.githubusercontent.com/sanxzy/mandor/main/scripts/install.sh | sh -s -- --version v0.0.16
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Default install location:** `$HOME/.local/bin/mandor`
|
|
93
|
+
|
|
94
|
+
**Add to PATH:**
|
|
95
|
+
```bash
|
|
96
|
+
# For bash
|
|
97
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc
|
|
98
|
+
|
|
99
|
+
# For zsh
|
|
100
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Verify installation:**
|
|
104
|
+
```bash
|
|
105
|
+
mandor --help
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Option 2: NPM (Cross-platform)
|
|
109
|
+
|
|
110
|
+
Install via npm for macOS, Linux, or Windows:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Install globally
|
|
114
|
+
npm install -g @mandor/cli
|
|
115
|
+
|
|
116
|
+
# Verify installation
|
|
117
|
+
mandor --help
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Or use npx to run without installing:**
|
|
74
121
|
```bash
|
|
75
|
-
|
|
122
|
+
npx @mandor/cli init "My Project"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Programmatic usage:**
|
|
126
|
+
```javascript
|
|
127
|
+
const mandor = require('@mandor/cli');
|
|
128
|
+
|
|
129
|
+
const cli = new mandor.Mandor({ json: true, cwd: '/project/path' });
|
|
130
|
+
await cli.init('My Project');
|
|
131
|
+
await cli.projectCreate('api', { name: 'API Service' });
|
|
132
|
+
const tasks = await cli.taskList({ project: 'api', status: 'pending' });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Option 3: From Source
|
|
136
|
+
|
|
137
|
+
Build from Go source code:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Clone repository
|
|
141
|
+
git clone https://github.com/sanxzy/mandor.git
|
|
76
142
|
cd mandor
|
|
143
|
+
|
|
144
|
+
# Build binary
|
|
77
145
|
go build -o build/mandor ./cmd/mandor
|
|
146
|
+
|
|
147
|
+
# Install to system
|
|
78
148
|
sudo mv build/mandor /usr/local/bin/
|
|
149
|
+
|
|
150
|
+
# Verify
|
|
151
|
+
mandor --help
|
|
79
152
|
```
|
|
80
153
|
|
|
81
|
-
###
|
|
154
|
+
### Platform Support
|
|
155
|
+
|
|
156
|
+
| Method | macOS | Linux | Windows |
|
|
157
|
+
|--------|-------|-------|---------|
|
|
158
|
+
| curl | ✅ arm64, x64 | ✅ arm64, x64 | ❌ |
|
|
159
|
+
| npm | ✅ arm64, x64 | ✅ arm64, x64 | ✅ arm64, x64 |
|
|
160
|
+
| Source | ✅ | ✅ | ✅ |
|
|
161
|
+
|
|
162
|
+
### Troubleshooting
|
|
163
|
+
|
|
164
|
+
**curl: command not found**
|
|
165
|
+
Install curl: `brew install curl` (macOS) or `sudo apt install curl` (Linux)
|
|
166
|
+
|
|
167
|
+
**mandor: command not found**
|
|
168
|
+
Ensure the install directory is in your PATH (see above)
|
|
82
169
|
|
|
170
|
+
**Permission denied**
|
|
83
171
|
```bash
|
|
84
|
-
|
|
172
|
+
# Fix permissions for ~/.local/bin
|
|
173
|
+
chmod +x ~/.local/bin/mandor
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**NPM permission errors**
|
|
177
|
+
```bash
|
|
178
|
+
# Use npx (no install)
|
|
85
179
|
npx @mandor/cli init "My Project"
|
|
180
|
+
|
|
181
|
+
# Or fix npm global permissions
|
|
182
|
+
mkdir -p ~/.npm-global
|
|
183
|
+
npm config set prefix '~/.npm-global'
|
|
184
|
+
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
|
|
185
|
+
source ~/.bashrc
|
|
86
186
|
```
|
|
87
187
|
|
|
88
188
|
---
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/npm/lib/install.js
CHANGED
|
@@ -1,154 +1,108 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Post-install hook for Mandor CLI
|
|
3
|
-
* @description
|
|
4
|
-
* @version 0.0.
|
|
3
|
+
* @description Downloads binary from GitHub releases during npm install
|
|
4
|
+
* @version 0.0.3
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const { downloadBinary, getCurrentPlatform } = require('./download');
|
|
8
7
|
const fs = require('fs');
|
|
9
8
|
const path = require('path');
|
|
10
9
|
const os = require('os');
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
|
|
12
|
+
const REPO = 'sanxzy/mandor';
|
|
13
|
+
const GITHUB_API = 'https://api.github.com';
|
|
14
|
+
const CACHE_DIR = path.join(os.homedir(), '.mandor', 'bin');
|
|
15
|
+
|
|
16
|
+
function getPlatform() {
|
|
17
|
+
const platform = os.platform();
|
|
18
|
+
const arch = os.arch();
|
|
19
|
+
const platformMap = { darwin: 'darwin', linux: 'linux', win32: 'win32' };
|
|
20
|
+
const archMap = { x64: 'x64', arm64: 'arm64', amd64: 'x64', aarch64: 'arm64' };
|
|
21
|
+
return {
|
|
22
|
+
platform: platformMap[platform] || platform,
|
|
23
|
+
arch: archMap[arch] || arch
|
|
24
|
+
};
|
|
25
|
+
}
|
|
11
26
|
|
|
12
|
-
|
|
13
|
-
const
|
|
27
|
+
async function getLatestVersion(prerelease = false) {
|
|
28
|
+
const url = prerelease
|
|
29
|
+
? `${GITHUB_API}/repos/${REPO}/releases`
|
|
30
|
+
: `${GITHUB_API}/repos/${REPO}/releases/latest`;
|
|
14
31
|
|
|
15
|
-
|
|
16
|
-
|
|
32
|
+
const response = await fetch(url);
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Failed to fetch releases: ${response.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
const data = await response.json();
|
|
37
|
+
const tagName = Array.isArray(data) ? data[0].tag_name : data.tag_name;
|
|
38
|
+
return tagName.replace(/^v/, '');
|
|
39
|
+
}
|
|
17
40
|
|
|
18
|
-
/**
|
|
19
|
-
* Installs the Mandor binary for the current platform
|
|
20
|
-
* @async
|
|
21
|
-
* @param {Object} [options] - Installation options
|
|
22
|
-
* @param {string} [options.version] - Version to install (default: 'latest')
|
|
23
|
-
* @returns {Promise<string>} Path to the installed binary
|
|
24
|
-
* @throws {Error} If download fails
|
|
25
|
-
* @example
|
|
26
|
-
* // Called automatically by npm postinstall
|
|
27
|
-
* await install();
|
|
28
|
-
*/
|
|
29
41
|
async function install(options = {}) {
|
|
42
|
+
const { platform, arch } = getPlatform();
|
|
30
43
|
const version = options.version || 'latest';
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
const prerelease = options.prerelease || false;
|
|
45
|
+
const osArch = `${platform}-${arch}`;
|
|
46
|
+
const assetName = `${osArch}.tar.gz`;
|
|
34
47
|
|
|
35
|
-
|
|
48
|
+
console.log('Mandor Installer');
|
|
49
|
+
console.log('================');
|
|
50
|
+
console.log(`OS: ${osArch}`);
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
binaryPath = bundledPath;
|
|
42
|
-
} else {
|
|
43
|
-
// Download from GitHub releases
|
|
44
|
-
binaryPath = await downloadBinary(version, platform, arch);
|
|
52
|
+
let installVersion = version;
|
|
53
|
+
if (version === 'latest') {
|
|
54
|
+
console.log(`Fetching latest ${prerelease ? 'prerelease' : 'release'}...`);
|
|
55
|
+
installVersion = await getLatestVersion(prerelease);
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
console.log(
|
|
58
|
+
console.log(`Version: ${installVersion}`);
|
|
59
|
+
console.log('');
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
const cachePath = path.join(CACHE_DIR, installVersion, osArch);
|
|
62
|
+
const binaryPath = path.join(cachePath, platform === 'win32' ? 'mandor.exe' : 'mandor');
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* @param {string} arch - Target architecture
|
|
56
|
-
* @returns {string|null} Path to binary or null if not bundled
|
|
57
|
-
*/
|
|
58
|
-
function useBundledBinary(platform, arch) {
|
|
59
|
-
const osArch = `${platform}-${arch}`;
|
|
60
|
-
const tarball = path.join(BUNDLE_DIR, `${osArch}.tar.gz`);
|
|
61
|
-
const version = 'latest';
|
|
62
|
-
const cacheDir = path.join(os.homedir(), '.mandor', 'bin', `${version}-${osArch}`);
|
|
63
|
-
const dest = path.join(cacheDir, 'mandor');
|
|
64
|
-
|
|
65
|
-
console.log(`DEBUG: Looking for binary for ${osArch}`);
|
|
66
|
-
console.log(`DEBUG: BUNDLE_DIR: ${BUNDLE_DIR}`);
|
|
67
|
-
console.log(`DEBUG: Files in BUNDLE_DIR: ${fs.readdirSync(BUNDLE_DIR).join(', ')}`);
|
|
68
|
-
|
|
69
|
-
// First check if binary already exists in cache
|
|
70
|
-
if (fs.existsSync(dest)) {
|
|
71
|
-
console.log(`DEBUG: Using cached binary: ${dest}`);
|
|
72
|
-
return dest;
|
|
64
|
+
if (fs.existsSync(binaryPath)) {
|
|
65
|
+
console.log(`Using cached binary: ${binaryPath}`);
|
|
66
|
+
return binaryPath;
|
|
73
67
|
}
|
|
74
68
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
69
|
+
console.log('Downloading from GitHub releases...');
|
|
70
|
+
const downloadUrl = `https://github.com/${REPO}/releases/download/v${installVersion}/${assetName}`;
|
|
71
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mandor-install-'));
|
|
72
|
+
const tarball = path.join(tempDir, assetName);
|
|
80
73
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
74
|
+
const response = await fetch(downloadUrl);
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
77
|
+
throw new Error(`Download failed: ${response.statusText} (${downloadUrl})`);
|
|
85
78
|
}
|
|
86
79
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
} catch (e) {
|
|
94
|
-
console.log(`DEBUG: Failed to extract tarball: ${e.message}`);
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
80
|
+
const file = fs.createWriteStream(tarball);
|
|
81
|
+
await new Promise((resolve, reject) => {
|
|
82
|
+
response.body.pipe(file);
|
|
83
|
+
file.on('finish', resolve);
|
|
84
|
+
file.on('error', reject);
|
|
85
|
+
});
|
|
98
86
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
* @returns {number} Number of files removed
|
|
102
|
-
* @example
|
|
103
|
-
* const removed = cleanupCache();
|
|
104
|
-
* console.log(`Removed ${removed} old binary files`);
|
|
105
|
-
*/
|
|
106
|
-
function cleanupCache() {
|
|
107
|
-
if (!fs.existsSync(CACHE_DIR)) return 0;
|
|
108
|
-
|
|
109
|
-
const files = fs.readdirSync(CACHE_DIR);
|
|
110
|
-
let removed = 0;
|
|
111
|
-
|
|
112
|
-
for (const file of files) {
|
|
113
|
-
const filePath = path.join(CACHE_DIR, file);
|
|
114
|
-
const stats = fs.statSync(filePath);
|
|
115
|
-
|
|
116
|
-
// Remove files older than 30 days
|
|
117
|
-
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
|
|
118
|
-
if (stats.mtimeMs < thirtyDaysAgo) {
|
|
119
|
-
fs.unlinkSync(filePath);
|
|
120
|
-
removed++;
|
|
121
|
-
}
|
|
87
|
+
if (!fs.existsSync(cachePath)) {
|
|
88
|
+
fs.mkdirSync(cachePath, { recursive: true });
|
|
122
89
|
}
|
|
123
90
|
|
|
124
|
-
|
|
125
|
-
|
|
91
|
+
execSync(`tar -xzf "${tarball}" -C "${cachePath}"`, { stdio: 'inherit' });
|
|
92
|
+
fs.chmodSync(binaryPath, '755');
|
|
126
93
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
* const version = getInstalledVersion();
|
|
132
|
-
* if (version) { console.log(`Using Mandor ${version}`); }
|
|
133
|
-
*/
|
|
134
|
-
function getInstalledVersion() {
|
|
135
|
-
const versionPath = path.join(CACHE_DIR, 'version.txt');
|
|
136
|
-
if (fs.existsSync(versionPath)) {
|
|
137
|
-
return fs.readFileSync(versionPath, 'utf-8').trim();
|
|
138
|
-
}
|
|
139
|
-
return null;
|
|
94
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
95
|
+
|
|
96
|
+
console.log(`Installed: ${binaryPath}`);
|
|
97
|
+
return binaryPath;
|
|
140
98
|
}
|
|
141
99
|
|
|
142
|
-
// Run install on postinstall
|
|
143
100
|
if (require.main === module || process.env.npm_lifecycle_event === 'postinstall') {
|
|
144
|
-
|
|
101
|
+
const prerelease = process.argv.includes('--prerelease') || process.argv.includes('-p');
|
|
102
|
+
install({ prerelease }).catch(error => {
|
|
145
103
|
console.error('Failed to install Mandor:', error.message);
|
|
146
104
|
process.exit(1);
|
|
147
105
|
});
|
|
148
106
|
}
|
|
149
107
|
|
|
150
|
-
module.exports = {
|
|
151
|
-
install,
|
|
152
|
-
cleanupCache,
|
|
153
|
-
getInstalledVersion
|
|
154
|
-
};
|
|
108
|
+
module.exports = { install, getLatestVersion, getPlatform };
|
package/npm/lib/resolve.js
CHANGED
|
@@ -1,59 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Version resolution module for Mandor CLI
|
|
3
3
|
* @description Resolves the correct binary path based on version and platform
|
|
4
|
-
* @version 0.0.
|
|
4
|
+
* @version 0.0.2
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const os = require('os');
|
|
10
|
-
const { downloadBinary, getCurrentPlatform
|
|
10
|
+
const { downloadBinary, getCurrentPlatform } = require('./download');
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const REPO = 'sanxzy/mandor';
|
|
13
|
+
const GITHUB_API = 'https://api.github.com';
|
|
13
14
|
const DEFAULT_VERSION = 'latest';
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
-
* Resolves the binary path for the requested version
|
|
17
|
-
* @async
|
|
18
|
-
* @param {Object} [options] - Resolution options
|
|
19
|
-
* @param {string} [options.version] - Requested version (default: 'latest')
|
|
20
|
-
* @param {boolean} [options.forceDownload] - Force re-download even if cached
|
|
21
|
-
* @returns {Promise<string>} Path to the Mandor binary
|
|
22
|
-
* @throws {Error} If binary cannot be resolved or downloaded
|
|
23
|
-
* @example
|
|
24
|
-
* const binaryPath = await resolve({ version: '1.0.0' });
|
|
25
|
-
* console.log(`Using: ${binaryPath}`);
|
|
26
|
-
*/
|
|
27
|
-
async function resolve(options = {}) {
|
|
28
|
-
const version = options.version || DEFAULT_VERSION;
|
|
29
|
-
const { platform, arch } = getCurrentPlatform();
|
|
30
|
-
|
|
31
|
-
// Check cache first
|
|
32
|
-
const cachedPath = getCachedBinary(version, platform, arch);
|
|
33
|
-
if (cachedPath && !options.forceDownload) {
|
|
34
|
-
return cachedPath;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Download if not cached
|
|
38
|
-
const binaryPath = await downloadBinary(version, platform, arch);
|
|
39
|
-
cacheBinary(binaryPath, version, platform, arch);
|
|
40
|
-
|
|
41
|
-
return binaryPath;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Gets the cached binary path for a specific version
|
|
46
|
-
* @param {string} version - Version to look for
|
|
47
|
-
* @param {string} platform - Target platform
|
|
48
|
-
* @param {string} arch - Target architecture
|
|
49
|
-
* @returns {string|null} Path to cached binary or null
|
|
50
|
-
* @example
|
|
51
|
-
* const cached = getCachedBinary('1.0.0', 'darwin', 'x64');
|
|
52
|
-
*/
|
|
53
16
|
function getCachedBinary(version, platform, arch) {
|
|
54
|
-
const
|
|
17
|
+
const osArch = `${platform}-${arch}`;
|
|
55
18
|
const binaryName = platform === 'win32' ? 'mandor.exe' : 'mandor';
|
|
56
|
-
const binaryPath = path.join(
|
|
19
|
+
const binaryPath = path.join(os.homedir(), '.mandor', 'bin', version, osArch, binaryName);
|
|
57
20
|
|
|
58
21
|
if (fs.existsSync(binaryPath)) {
|
|
59
22
|
return binaryPath;
|
|
@@ -61,18 +24,23 @@ function getCachedBinary(version, platform, arch) {
|
|
|
61
24
|
return null;
|
|
62
25
|
}
|
|
63
26
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
27
|
+
async function getLatestVersion(prerelease = false) {
|
|
28
|
+
const url = prerelease
|
|
29
|
+
? `${GITHUB_API}/repos/${REPO}/releases`
|
|
30
|
+
: `${GITHUB_API}/repos/${REPO}/releases/latest`;
|
|
31
|
+
|
|
32
|
+
const response = await fetch(url);
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Failed to fetch releases: ${response.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
const data = await response.json();
|
|
37
|
+
const tagName = Array.isArray(data) ? data[0].tag_name : data.tag_name;
|
|
38
|
+
return tagName.replace(/^v/, '');
|
|
39
|
+
}
|
|
40
|
+
|
|
74
41
|
function cacheBinary(binaryPath, version, platform, arch) {
|
|
75
|
-
const
|
|
42
|
+
const osArch = `${platform}-${arch}`;
|
|
43
|
+
const cacheDir = path.join(os.homedir(), '.mandor', 'bin', version, osArch);
|
|
76
44
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
77
45
|
|
|
78
46
|
const binaryName = platform === 'win32' ? 'mandor.exe' : 'mandor';
|
|
@@ -82,13 +50,54 @@ function cacheBinary(binaryPath, version, platform, arch) {
|
|
|
82
50
|
fs.chmodSync(destPath, '755');
|
|
83
51
|
}
|
|
84
52
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
53
|
+
async function resolve(options = {}) {
|
|
54
|
+
const version = options.version || DEFAULT_VERSION;
|
|
55
|
+
const { platform, arch } = getCurrentPlatform();
|
|
56
|
+
const prerelease = options.prerelease || false;
|
|
57
|
+
|
|
58
|
+
let resolveVersion = version;
|
|
59
|
+
if (version === 'latest') {
|
|
60
|
+
resolveVersion = await getLatestVersion(prerelease);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const cachedPath = getCachedBinary(resolveVersion, platform, arch);
|
|
64
|
+
if (cachedPath && !options.forceDownload) {
|
|
65
|
+
return cachedPath;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const osArch = `${platform}-${arch}`;
|
|
69
|
+
const downloadUrl = `https://github.com/${REPO}/releases/download/v${resolveVersion}/${osArch}.tar.gz`;
|
|
70
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mandor-download-'));
|
|
71
|
+
const tarball = path.join(tempDir, `${osArch}.tar.gz`);
|
|
72
|
+
|
|
73
|
+
console.log(`Downloading Mandor ${resolveVersion}...`);
|
|
74
|
+
|
|
75
|
+
const response = await fetch(downloadUrl);
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
78
|
+
throw new Error(`Download failed: ${response.statusText}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const file = fs.createWriteStream(tarball);
|
|
82
|
+
await new Promise((resolve, reject) => {
|
|
83
|
+
response.body.pipe(file);
|
|
84
|
+
file.on('finish', resolve);
|
|
85
|
+
file.on('error', reject);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const binaryName = platform === 'win32' ? 'mandor.exe' : 'mandor';
|
|
89
|
+
const cacheDir = path.join(os.homedir(), '.mandor', 'bin', resolveVersion, osArch);
|
|
90
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
91
|
+
|
|
92
|
+
const { execSync } = require('child_process');
|
|
93
|
+
execSync(`tar -xzf "${tarball}" -C "${cacheDir}"`, { stdio: 'pipe' });
|
|
94
|
+
fs.chmodSync(path.join(cacheDir, binaryName), '755');
|
|
95
|
+
|
|
96
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
97
|
+
|
|
98
|
+
return path.join(cacheDir, binaryName);
|
|
99
|
+
}
|
|
100
|
+
|
|
92
101
|
function listCachedBinaries() {
|
|
93
102
|
const cacheDir = path.join(os.homedir(), '.mandor', 'bin');
|
|
94
103
|
|
|
@@ -100,23 +109,24 @@ function listCachedBinaries() {
|
|
|
100
109
|
const entries = fs.readdirSync(cacheDir);
|
|
101
110
|
|
|
102
111
|
for (const entry of entries) {
|
|
103
|
-
const
|
|
104
|
-
if (fs.statSync(
|
|
105
|
-
const
|
|
106
|
-
|
|
112
|
+
const versionPath = path.join(cacheDir, entry);
|
|
113
|
+
if (fs.statSync(versionPath).isDirectory()) {
|
|
114
|
+
const subEntries = fs.readdirSync(versionPath);
|
|
115
|
+
for (const subEntry of subEntries) {
|
|
116
|
+
const subPath = path.join(versionPath, subEntry);
|
|
117
|
+
if (fs.statSync(subPath).isDirectory()) {
|
|
118
|
+
const parts = subEntry.split('-');
|
|
119
|
+
const arch = parts.pop();
|
|
120
|
+
const platform = parts.join('-');
|
|
121
|
+
versions.push({ version: entry, platform, arch, path: subPath });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
107
124
|
}
|
|
108
125
|
}
|
|
109
126
|
|
|
110
127
|
return versions;
|
|
111
128
|
}
|
|
112
129
|
|
|
113
|
-
/**
|
|
114
|
-
* Clears all cached binaries
|
|
115
|
-
* @returns {number} Number of binaries removed
|
|
116
|
-
* @example
|
|
117
|
-
* const removed = clearCache();
|
|
118
|
-
* console.log(`Cleared ${removed} cached binaries`);
|
|
119
|
-
*/
|
|
120
130
|
function clearCache() {
|
|
121
131
|
const cacheDir = path.join(os.homedir(), '.mandor', 'bin');
|
|
122
132
|
|
|
@@ -144,5 +154,6 @@ module.exports = {
|
|
|
144
154
|
cacheBinary,
|
|
145
155
|
listCachedBinaries,
|
|
146
156
|
clearCache,
|
|
147
|
-
DEFAULT_VERSION
|
|
157
|
+
DEFAULT_VERSION,
|
|
158
|
+
getLatestVersion
|
|
148
159
|
};
|
package/npm/scripts/build.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Cross-platform build script for Mandor binaries
|
|
3
3
|
* @description Compiles Go binaries for all supported platforms and creates distribution archives
|
|
4
|
-
* @version 0.0.
|
|
4
|
+
* @version 0.0.2
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { execSync } = require('child_process');
|
|
@@ -9,13 +9,11 @@ const fs = require('fs');
|
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const os = require('os');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
12
|
+
const ROOT_DIR = path.join(__dirname, '..', '..');
|
|
13
|
+
const BINARIES_DIR = path.join(ROOT_DIR, 'binaries');
|
|
14
|
+
const NPM_DIR = path.join(ROOT_DIR, 'npm');
|
|
15
|
+
const NPM_BINARIES_DIR = path.join(NPM_DIR, 'binaries');
|
|
17
16
|
|
|
18
|
-
/** @type {PlatformConfig[]} All supported platform configurations */
|
|
19
17
|
const PLATFORMS = [
|
|
20
18
|
{ os: 'darwin', arch: 'x64' },
|
|
21
19
|
{ os: 'darwin', arch: 'arm64' },
|
|
@@ -25,118 +23,105 @@ const PLATFORMS = [
|
|
|
25
23
|
{ os: 'win32', arch: 'arm64' }
|
|
26
24
|
];
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
* Builds the Mandor binary for a specific platform
|
|
30
|
-
* @param {PlatformConfig} platform - Platform configuration
|
|
31
|
-
* @param {string} sourceDir - Source directory containing Go code
|
|
32
|
-
* @returns {string|null} Path to the compiled binary, or null if unsupported
|
|
33
|
-
* @example
|
|
34
|
-
* const binaryPath = buildForPlatform({ os: 'darwin', arch: 'arm64' }, './cmd/mandor');
|
|
35
|
-
*/
|
|
36
|
-
function buildForPlatform(platform, sourceDir) {
|
|
26
|
+
function buildForPlatform(platform) {
|
|
37
27
|
const { os, arch } = platform;
|
|
38
|
-
const outputDir = path.join(
|
|
39
|
-
const
|
|
28
|
+
const outputDir = path.join(BINARIES_DIR, `${os}-${arch}`);
|
|
29
|
+
const binaryName = os === 'win32' ? 'mandor.exe' : 'mandor';
|
|
30
|
+
const outputPath = path.join(outputDir, binaryName);
|
|
40
31
|
|
|
41
32
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
42
33
|
|
|
43
34
|
console.log(`Building for ${os}-${arch}...`);
|
|
44
35
|
|
|
45
36
|
try {
|
|
46
|
-
execSync(`GOOS=${os} GOARCH=${arch} go build -o "${outputPath}"
|
|
37
|
+
execSync(`GOOS=${os} GOARCH=${arch} go build -o "${outputPath}" ./cmd/mandor`, {
|
|
47
38
|
stdio: 'pipe',
|
|
48
39
|
shell: process.platform === 'win32'
|
|
49
40
|
});
|
|
50
41
|
return outputPath;
|
|
51
42
|
} catch (error) {
|
|
52
|
-
// Check stderr for unsupported pair message
|
|
53
43
|
const stderr = error.stderr ? error.stderr.toString() : '';
|
|
54
44
|
if (stderr.includes('unsupported GOOS/GOARCH pair')) {
|
|
55
|
-
console.log(`
|
|
45
|
+
console.log(` Not supported on this system`);
|
|
56
46
|
return null;
|
|
57
47
|
}
|
|
58
|
-
console.error(`
|
|
48
|
+
console.error(` Build failed:`, error.message);
|
|
59
49
|
throw error;
|
|
60
50
|
}
|
|
61
51
|
}
|
|
62
52
|
|
|
63
|
-
/**
|
|
64
|
-
* Creates a distribution archive for a platform
|
|
65
|
-
* @param {PlatformConfig} platform - Platform configuration
|
|
66
|
-
* @returns {string|null} Path to the archive file, or null if binary doesn't exist
|
|
67
|
-
* @example
|
|
68
|
-
* const archivePath = createArchive({ os: 'linux', arch: 'x64' });
|
|
69
|
-
*/
|
|
70
53
|
function createArchive(platform) {
|
|
71
54
|
const { os, arch } = platform;
|
|
72
|
-
const sourceDir = path.join(
|
|
55
|
+
const sourceDir = path.join(BINARIES_DIR, `${os}-${arch}`);
|
|
56
|
+
const binaryName = os === 'win32' ? 'mandor.exe' : 'mandor';
|
|
73
57
|
|
|
74
|
-
|
|
75
|
-
const binaryExists = fs.existsSync(path.join(sourceDir, os === 'win32' ? 'mandor.exe' : 'mandor'));
|
|
76
|
-
if (!binaryExists) {
|
|
58
|
+
if (!fs.existsSync(path.join(sourceDir, binaryName))) {
|
|
77
59
|
return null;
|
|
78
60
|
}
|
|
79
61
|
|
|
80
|
-
const archivePath = path.join(
|
|
62
|
+
const archivePath = path.join(BINARIES_DIR, `${os}-${arch}.tar.gz`);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
execSync(`tar -czf "${archivePath}" -C "${sourceDir}" .`, {
|
|
66
|
+
stdio: 'pipe',
|
|
67
|
+
shell: true
|
|
68
|
+
});
|
|
69
|
+
return archivePath;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(` Failed to create archive:`, error.message);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
81
75
|
|
|
82
|
-
|
|
76
|
+
function copyToNpm() {
|
|
77
|
+
fs.mkdirSync(NPM_BINARIES_DIR, { recursive: true });
|
|
83
78
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
79
|
+
for (const platform of PLATFORMS) {
|
|
80
|
+
const { os, arch } = platform;
|
|
81
|
+
const archivePath = path.join(BINARIES_DIR, `${os}-${arch}.tar.gz`);
|
|
82
|
+
const destPath = path.join(NPM_BINARIES_DIR, `${os}-${arch}.tar.gz`);
|
|
88
83
|
|
|
89
|
-
|
|
84
|
+
if (fs.existsSync(archivePath)) {
|
|
85
|
+
fs.cpSync(archivePath, destPath);
|
|
86
|
+
console.log(`Copied ${os}-${arch}.tar.gz to npm/`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const results = [];
|
|
102
|
-
const unsupported = [];
|
|
91
|
+
function cleanBuildDirs() {
|
|
92
|
+
if (fs.existsSync(BINARIES_DIR)) {
|
|
93
|
+
for (const entry of fs.readdirSync(BINARIES_DIR)) {
|
|
94
|
+
if (entry.endsWith('.tar.gz')) {
|
|
95
|
+
fs.unlinkSync(path.join(BINARIES_DIR, entry));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
103
100
|
|
|
101
|
+
function mainBuild() {
|
|
104
102
|
console.log(`Running on: ${os.platform()}/${os.arch()}`);
|
|
105
103
|
console.log('Building cross-platform binaries...\n');
|
|
106
104
|
|
|
107
|
-
|
|
105
|
+
cleanBuildDirs();
|
|
106
|
+
fs.mkdirSync(NPM_BINARIES_DIR, { recursive: true });
|
|
107
|
+
|
|
108
|
+
const results = [];
|
|
109
|
+
const unsupported = [];
|
|
110
|
+
|
|
108
111
|
for (const platform of PLATFORMS) {
|
|
109
|
-
const binaryPath = buildForPlatform(platform
|
|
112
|
+
const binaryPath = buildForPlatform(platform);
|
|
110
113
|
if (binaryPath) {
|
|
111
|
-
results.push({
|
|
112
|
-
|
|
113
|
-
arch: platform.arch,
|
|
114
|
-
binaryPath,
|
|
115
|
-
status: 'built'
|
|
116
|
-
});
|
|
117
|
-
console.log(` ✓ Built successfully\n`);
|
|
114
|
+
results.push({ platform: platform.os, arch: platform.arch, binaryPath, status: 'built' });
|
|
115
|
+
console.log(` Built successfully\n`);
|
|
118
116
|
} else {
|
|
119
|
-
unsupported.push({
|
|
120
|
-
platform: platform.os,
|
|
121
|
-
arch: platform.arch,
|
|
122
|
-
status: 'unsupported'
|
|
123
|
-
});
|
|
117
|
+
unsupported.push({ platform: platform.os, arch: platform.arch, status: 'unsupported' });
|
|
124
118
|
}
|
|
125
119
|
}
|
|
126
120
|
|
|
127
|
-
// Create archives for successfully built platforms
|
|
128
|
-
const archiveResults = [];
|
|
129
121
|
for (const platform of PLATFORMS) {
|
|
130
122
|
const archivePath = createArchive(platform);
|
|
131
123
|
if (archivePath) {
|
|
132
124
|
const stats = fs.statSync(archivePath);
|
|
133
|
-
archiveResults.push({
|
|
134
|
-
platform: platform.os,
|
|
135
|
-
arch: platform.arch,
|
|
136
|
-
archivePath,
|
|
137
|
-
archiveSize: stats.size
|
|
138
|
-
});
|
|
139
|
-
|
|
140
125
|
const existing = results.find(r => r.platform === platform.os && r.arch === platform.arch);
|
|
141
126
|
if (existing) {
|
|
142
127
|
existing.archivePath = archivePath;
|
|
@@ -145,10 +130,12 @@ function mainBuild() {
|
|
|
145
130
|
}
|
|
146
131
|
}
|
|
147
132
|
|
|
148
|
-
|
|
133
|
+
console.log('Copying archives to npm/binaries/ for npm package...');
|
|
134
|
+
copyToNpm();
|
|
135
|
+
|
|
149
136
|
console.log('─'.repeat(50));
|
|
150
137
|
console.log(`Build complete!`);
|
|
151
|
-
console.log(` Built: ${
|
|
138
|
+
console.log(` Built: ${results.length} platforms`);
|
|
152
139
|
console.log(` Unsupported: ${unsupported.length} platforms`);
|
|
153
140
|
|
|
154
141
|
if (unsupported.length > 0) {
|
|
@@ -158,10 +145,12 @@ function mainBuild() {
|
|
|
158
145
|
});
|
|
159
146
|
}
|
|
160
147
|
|
|
161
|
-
|
|
148
|
+
console.log(`\nArchives location: ${BINARIES_DIR}/`);
|
|
149
|
+
console.log(`NPM package binaries: ${NPM_BINARIES_DIR}/`);
|
|
150
|
+
|
|
151
|
+
return results;
|
|
162
152
|
}
|
|
163
153
|
|
|
164
|
-
// Run if executed directly
|
|
165
154
|
if (require.main === module) {
|
|
166
155
|
const results = mainBuild();
|
|
167
156
|
if (results.length > 0) {
|
|
@@ -170,14 +159,7 @@ if (require.main === module) {
|
|
|
170
159
|
Platform: `${r.platform}/${r.arch}`,
|
|
171
160
|
'Archive Size': `${(r.archiveSize / 1024).toFixed(1)} KB`
|
|
172
161
|
})));
|
|
173
|
-
|
|
174
|
-
console.log('Archives location: npm/binaries/');
|
|
175
162
|
}
|
|
176
163
|
}
|
|
177
164
|
|
|
178
|
-
module.exports = {
|
|
179
|
-
PLATFORMS,
|
|
180
|
-
buildForPlatform,
|
|
181
|
-
createArchive,
|
|
182
|
-
mainBuild
|
|
183
|
-
};
|
|
165
|
+
module.exports = { PLATFORMS, buildForPlatform, createArchive, mainBuild, copyToNpm };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mandors/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "Event-based task manager CLI for AI agent workflows",
|
|
5
5
|
"main": "npm/lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,9 +19,7 @@
|
|
|
19
19
|
"node": ">=16.0.0"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
|
-
"postinstall": "node npm/lib/install.js",
|
|
23
|
-
"prepublishOnly": "npm run build",
|
|
24
|
-
"preversion": "npm run build",
|
|
22
|
+
"postinstall": "node npm/lib/install.js --prerelease",
|
|
25
23
|
"build": "node npm/scripts/build.js",
|
|
26
24
|
"build:darwin:x64": "GOOS=darwin GOARCH=x64 go build -o binaries/darwin-x64/mandor ./cmd/mandor",
|
|
27
25
|
"build:darwin:arm64": "GOOS=darwin GOARCH=arm64 go build -o binaries/darwin-arm64/mandor ./cmd/mandor",
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
#
|
|
3
|
+
# install.sh - Install Mandor CLI
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# curl -fsSL https://raw.githubusercontent.com/sanxzy/mandor/main/scripts/install.sh | sh
|
|
7
|
+
# curl -fsSL https://raw.githubusercontent.com/sanxzy/mandor/main/scripts/install.sh | sh -s -- --help
|
|
8
|
+
#
|
|
9
|
+
# Options:
|
|
10
|
+
# --prefix DIR Install prefix (default: $HOME/.local)
|
|
11
|
+
# --version VER Install specific version (default: latest)
|
|
12
|
+
# --prerelease Install latest prerelease
|
|
13
|
+
# --help Show this help
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
set -e
|
|
17
|
+
|
|
18
|
+
REPO="sanxzy/mandor"
|
|
19
|
+
INSTALL_DIR="${HOME}/.local/bin"
|
|
20
|
+
VERSION="latest"
|
|
21
|
+
PRERELEASE=""
|
|
22
|
+
TAG="latest"
|
|
23
|
+
|
|
24
|
+
while [ $# -gt 0 ]; do
|
|
25
|
+
case "$1" in
|
|
26
|
+
--prefix)
|
|
27
|
+
INSTALL_DIR="$2"
|
|
28
|
+
shift 2
|
|
29
|
+
;;
|
|
30
|
+
--version)
|
|
31
|
+
VERSION="$2"
|
|
32
|
+
shift 2
|
|
33
|
+
;;
|
|
34
|
+
--prerelease)
|
|
35
|
+
PRERELEASE="1"
|
|
36
|
+
TAG="latest"
|
|
37
|
+
shift
|
|
38
|
+
;;
|
|
39
|
+
--help|-h)
|
|
40
|
+
head -20 "$0"
|
|
41
|
+
exit 0
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
|
47
|
+
ARCH=$(uname -m)
|
|
48
|
+
case "$ARCH" in
|
|
49
|
+
x86_64|amd64) ARCH="x64" ;;
|
|
50
|
+
arm64|aarch64) ARCH="arm64" ;;
|
|
51
|
+
esac
|
|
52
|
+
|
|
53
|
+
case "$OS" in
|
|
54
|
+
darwin) ;;
|
|
55
|
+
linux) ;;
|
|
56
|
+
*)
|
|
57
|
+
echo "Unsupported OS: $OS"
|
|
58
|
+
exit 1
|
|
59
|
+
;;
|
|
60
|
+
esac
|
|
61
|
+
|
|
62
|
+
echo "Mandor Installer"
|
|
63
|
+
echo "================"
|
|
64
|
+
echo "OS: $OS-$ARCH"
|
|
65
|
+
|
|
66
|
+
if [ "$VERSION" = "latest" ]; then
|
|
67
|
+
if [ -n "$PRERELEASE" ]; then
|
|
68
|
+
echo "Fetching latest prerelease..."
|
|
69
|
+
VERSION=$(curl -s "https://api.github.com/repos/${REPO}/releases" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -1)
|
|
70
|
+
else
|
|
71
|
+
echo "Fetching latest release..."
|
|
72
|
+
VERSION=$(curl -s "https://api.github.com/repos/${REPO}/releases/latest" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p')
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
echo "Version: $VERSION"
|
|
77
|
+
echo "Install dir: $INSTALL_DIR"
|
|
78
|
+
echo ""
|
|
79
|
+
|
|
80
|
+
ASSET_NAME="${OS}-${ARCH}.tar.gz"
|
|
81
|
+
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${VERSION}/${ASSET_NAME}"
|
|
82
|
+
TEMP_DIR=$(mktemp -d)
|
|
83
|
+
TARFILE="${TEMP_DIR}/${ASSET_NAME}"
|
|
84
|
+
|
|
85
|
+
echo "Downloading ${ASSET_NAME}..."
|
|
86
|
+
if ! curl -fsSL -o "$TARFILE" "$DOWNLOAD_URL"; then
|
|
87
|
+
echo "Download failed: $DOWNLOAD_URL"
|
|
88
|
+
rm -rf "$TEMP_DIR"
|
|
89
|
+
exit 1
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
echo "Extracting..."
|
|
93
|
+
mkdir -p "$INSTALL_DIR"
|
|
94
|
+
tar -xzf "$TARFILE" -C "$INSTALL_DIR"
|
|
95
|
+
chmod 755 "${INSTALL_DIR}/mandor"
|
|
96
|
+
|
|
97
|
+
rm -rf "$TEMP_DIR"
|
|
98
|
+
|
|
99
|
+
echo ""
|
|
100
|
+
echo "Installed: ${INSTALL_DIR}/mandor"
|
|
101
|
+
echo ""
|
|
102
|
+
|
|
103
|
+
if [ -d "$HOME/.local/bin" ]; then
|
|
104
|
+
echo "Add to PATH: export PATH=\"\$HOME/.local/bin:\$PATH\""
|
|
105
|
+
fi
|
|
Binary file
|
|
Binary file
|