@apono-io/apono-mcp 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.
- package/bin/run.js +38 -0
- package/lib/download.js +203 -0
- package/lib/paths.js +17 -0
- package/package.json +31 -0
package/bin/run.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { execFileSync } = require("child_process");
|
|
6
|
+
const { ensureBinary } = require("../lib/download");
|
|
7
|
+
|
|
8
|
+
// Commands that should be passed directly to the CLI (not prefixed with "mcp")
|
|
9
|
+
const DIRECT_COMMANDS = ["login", "logout", "version"];
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
try {
|
|
13
|
+
const binaryPath = await ensureBinary();
|
|
14
|
+
const userArgs = process.argv.slice(2);
|
|
15
|
+
|
|
16
|
+
let args;
|
|
17
|
+
if (userArgs.length > 0 && DIRECT_COMMANDS.includes(userArgs[0])) {
|
|
18
|
+
// Pass directly: apono login, apono logout, etc.
|
|
19
|
+
args = userArgs;
|
|
20
|
+
} else {
|
|
21
|
+
// Default: apono mcp <args>
|
|
22
|
+
args = ["mcp", ...userArgs];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
execFileSync(binaryPath, args, {
|
|
26
|
+
stdio: "inherit",
|
|
27
|
+
env: process.env,
|
|
28
|
+
});
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.status != null) {
|
|
31
|
+
process.exit(err.status);
|
|
32
|
+
}
|
|
33
|
+
process.stderr.write(`Error: ${err.message}\n`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
main();
|
package/lib/download.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const https = require("https");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const os = require("os");
|
|
7
|
+
const { execFileSync } = require("child_process");
|
|
8
|
+
const { BINARY_DIR, BINARY_NAME, BINARY_PATH, RELEASES_BASE_URL } = require("./paths");
|
|
9
|
+
|
|
10
|
+
function getPlatform() {
|
|
11
|
+
const platform = process.platform;
|
|
12
|
+
switch (platform) {
|
|
13
|
+
case "darwin":
|
|
14
|
+
return "darwin";
|
|
15
|
+
case "linux":
|
|
16
|
+
return "linux";
|
|
17
|
+
case "win32":
|
|
18
|
+
return "windows";
|
|
19
|
+
default:
|
|
20
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getArch() {
|
|
25
|
+
const arch = process.arch;
|
|
26
|
+
switch (arch) {
|
|
27
|
+
case "x64":
|
|
28
|
+
return "amd64";
|
|
29
|
+
case "arm64":
|
|
30
|
+
return "arm64";
|
|
31
|
+
case "arm":
|
|
32
|
+
return "armv7";
|
|
33
|
+
default:
|
|
34
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getArchiveExtension() {
|
|
39
|
+
return process.platform === "win32" ? "zip" : "tar.gz";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function httpsGet(url) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
https
|
|
45
|
+
.get(url, { headers: { "User-Agent": "apono-mcp-npm" } }, (res) => {
|
|
46
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
47
|
+
return httpsGet(res.headers.location).then(resolve, reject);
|
|
48
|
+
}
|
|
49
|
+
if (res.statusCode !== 200) {
|
|
50
|
+
res.resume();
|
|
51
|
+
return reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
|
|
52
|
+
}
|
|
53
|
+
const chunks = [];
|
|
54
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
55
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
56
|
+
res.on("error", reject);
|
|
57
|
+
})
|
|
58
|
+
.on("error", reject);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getInstalledVersion() {
|
|
63
|
+
try {
|
|
64
|
+
if (!fs.existsSync(BINARY_PATH)) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const output = execFileSync(BINARY_PATH, ["--version"], {
|
|
68
|
+
encoding: "utf8",
|
|
69
|
+
timeout: 10000,
|
|
70
|
+
}).trim();
|
|
71
|
+
const match = output.match(/v?(\d+\.\d+\.\d+\S*)/);
|
|
72
|
+
return match ? match[1] : null;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function getLatestVersion() {
|
|
79
|
+
const url = `${RELEASES_BASE_URL}/cli/latest.txt`;
|
|
80
|
+
const data = await httpsGet(url);
|
|
81
|
+
return data.toString("utf8").trim();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function downloadToFile(url, dest) {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
https
|
|
87
|
+
.get(url, { headers: { "User-Agent": "apono-mcp-npm" } }, (res) => {
|
|
88
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
89
|
+
return downloadToFile(res.headers.location, dest).then(resolve, reject);
|
|
90
|
+
}
|
|
91
|
+
if (res.statusCode !== 200) {
|
|
92
|
+
res.resume();
|
|
93
|
+
return reject(new Error(`HTTP ${res.statusCode} downloading ${url}`));
|
|
94
|
+
}
|
|
95
|
+
const file = fs.createWriteStream(dest);
|
|
96
|
+
res.pipe(file);
|
|
97
|
+
file.on("finish", () => file.close(resolve));
|
|
98
|
+
file.on("error", reject);
|
|
99
|
+
})
|
|
100
|
+
.on("error", reject);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function extractTarGz(archivePath, destDir) {
|
|
105
|
+
execFileSync("tar", ["xzf", archivePath, "-C", destDir], { timeout: 60000 });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function extractZip(archivePath, destDir) {
|
|
109
|
+
if (process.platform === "win32") {
|
|
110
|
+
execFileSync("powershell", [
|
|
111
|
+
"-Command",
|
|
112
|
+
`Expand-Archive -Force -Path '${archivePath}' -DestinationPath '${destDir}'`,
|
|
113
|
+
], { timeout: 60000 });
|
|
114
|
+
} else {
|
|
115
|
+
execFileSync("unzip", ["-o", archivePath, "-d", destDir], { timeout: 60000 });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
120
|
+
const LAST_CHECK_FILE = path.join(BINARY_DIR, ".last-update-check");
|
|
121
|
+
|
|
122
|
+
function shouldCheckForUpdate() {
|
|
123
|
+
try {
|
|
124
|
+
if (!fs.existsSync(LAST_CHECK_FILE)) return true;
|
|
125
|
+
const lastCheck = parseInt(fs.readFileSync(LAST_CHECK_FILE, "utf8"), 10);
|
|
126
|
+
return Date.now() - lastCheck > UPDATE_CHECK_INTERVAL_MS;
|
|
127
|
+
} catch {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function recordUpdateCheck() {
|
|
133
|
+
try {
|
|
134
|
+
fs.mkdirSync(BINARY_DIR, { recursive: true });
|
|
135
|
+
fs.writeFileSync(LAST_CHECK_FILE, String(Date.now()));
|
|
136
|
+
} catch {
|
|
137
|
+
// ignore
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function ensureBinary() {
|
|
142
|
+
const installedVersion = getInstalledVersion();
|
|
143
|
+
|
|
144
|
+
// If binary exists and we checked recently, skip the network call
|
|
145
|
+
if (installedVersion && !shouldCheckForUpdate()) {
|
|
146
|
+
return BINARY_PATH;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let latestVersion;
|
|
150
|
+
try {
|
|
151
|
+
latestVersion = await getLatestVersion();
|
|
152
|
+
recordUpdateCheck();
|
|
153
|
+
} catch (err) {
|
|
154
|
+
if (installedVersion) {
|
|
155
|
+
process.stderr.write(`Warning: Could not check for updates: ${err.message}\n`);
|
|
156
|
+
return BINARY_PATH;
|
|
157
|
+
}
|
|
158
|
+
throw new Error(`Failed to fetch latest version: ${err.message}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (installedVersion === latestVersion) {
|
|
162
|
+
return BINARY_PATH;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const platformStr = getPlatform();
|
|
166
|
+
const archStr = getArch();
|
|
167
|
+
const ext = getArchiveExtension();
|
|
168
|
+
const archiveName = `apono-agentic-cli_${latestVersion}_${platformStr}_${archStr}.${ext}`;
|
|
169
|
+
const downloadUrl = `${RELEASES_BASE_URL}/cli/v${latestVersion}/${archiveName}`;
|
|
170
|
+
|
|
171
|
+
process.stderr.write(`Downloading Apono CLI v${latestVersion}...\n`);
|
|
172
|
+
|
|
173
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "apono-"));
|
|
174
|
+
const archivePath = path.join(tmpDir, archiveName);
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
await downloadToFile(downloadUrl, archivePath);
|
|
178
|
+
|
|
179
|
+
fs.mkdirSync(BINARY_DIR, { recursive: true });
|
|
180
|
+
|
|
181
|
+
if (ext === "zip") {
|
|
182
|
+
extractZip(archivePath, tmpDir);
|
|
183
|
+
} else {
|
|
184
|
+
extractTarGz(archivePath, tmpDir);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const extractedBinary = path.join(tmpDir, BINARY_NAME);
|
|
188
|
+
if (!fs.existsSync(extractedBinary)) {
|
|
189
|
+
throw new Error(`Binary not found in archive at ${extractedBinary}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
fs.copyFileSync(extractedBinary, BINARY_PATH);
|
|
193
|
+
fs.chmodSync(BINARY_PATH, 0o755);
|
|
194
|
+
|
|
195
|
+
process.stderr.write(`Apono CLI v${latestVersion} installed to ${BINARY_PATH}\n`);
|
|
196
|
+
} finally {
|
|
197
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return BINARY_PATH;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = { ensureBinary };
|
package/lib/paths.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
|
|
6
|
+
const RELEASES_BASE_URL = "https://apono-agentic-releases.s3.amazonaws.com";
|
|
7
|
+
|
|
8
|
+
const BINARY_DIR = path.join(os.homedir(), ".apono", "bin");
|
|
9
|
+
const BINARY_NAME = process.platform === "win32" ? "apono.exe" : "apono";
|
|
10
|
+
const BINARY_PATH = path.join(BINARY_DIR, BINARY_NAME);
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
BINARY_DIR,
|
|
14
|
+
BINARY_NAME,
|
|
15
|
+
BINARY_PATH,
|
|
16
|
+
RELEASES_BASE_URL,
|
|
17
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@apono-io/apono-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Apono MCP server for Claude Code, Claude Desktop, and Cursor",
|
|
5
|
+
"bin": {
|
|
6
|
+
"apono-mcp": "./bin/run.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"lib/"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public",
|
|
14
|
+
"registry": "https://registry.npmjs.org/"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"apono",
|
|
18
|
+
"mcp",
|
|
19
|
+
"claude",
|
|
20
|
+
"cursor",
|
|
21
|
+
"access-management"
|
|
22
|
+
],
|
|
23
|
+
"license": "Apache-2.0",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/apono-io/apono-cli-agentic.git"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=16"
|
|
30
|
+
}
|
|
31
|
+
}
|