@telara-cli/cli 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/README.md +25 -0
- package/bin/telara +11 -0
- package/package.json +25 -0
- package/scripts/install.js +240 -0
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# @telera/cli
|
|
2
|
+
|
|
3
|
+
Install the Telara CLI via npm.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npm install -g @telera/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
telara login
|
|
15
|
+
telara setup
|
|
16
|
+
telara --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Alternative installation
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
macOS/Linux: curl -fsSL https://get.telara.ai/install.sh | sh
|
|
23
|
+
Windows: irm https://get.telara.ai/windows | iex
|
|
24
|
+
Homebrew: brew install telera-ai/tap/telara
|
|
25
|
+
```
|
package/bin/telara
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// This stub is replaced by the actual binary during postinstall (npm scripts/install.js).
|
|
3
|
+
// If this script is still running, the postinstall step did not complete successfully.
|
|
4
|
+
console.error('telara binary not found.');
|
|
5
|
+
console.error('The postinstall step may have failed. Try reinstalling:');
|
|
6
|
+
console.error(' npm install -g @telera/cli');
|
|
7
|
+
console.error('');
|
|
8
|
+
console.error('Or install manually:');
|
|
9
|
+
console.error(' macOS/Linux: curl -fsSL https://get.telara.ai/install.sh | sh');
|
|
10
|
+
console.error(' Windows: irm https://get.telara.ai/windows | iex');
|
|
11
|
+
process.exit(1);
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@telara-cli/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Telara CLI — manage your MCP configurations",
|
|
5
|
+
"homepage": "https://telara.ai",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/Telara-Labs/Telara-CLI"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"bin": {
|
|
12
|
+
"telara": "./bin/telara"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"postinstall": "node scripts/install.js"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=14"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"bin/",
|
|
22
|
+
"scripts/install.js",
|
|
23
|
+
"README.md"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
// Read version from package.json, allow override via env var
|
|
12
|
+
const pkg = require('../package.json');
|
|
13
|
+
const version = process.env.TELARA_VERSION || pkg.version;
|
|
14
|
+
|
|
15
|
+
const PRIMARY_BASE_URL = 'https://get.telara.ai/download';
|
|
16
|
+
const FALLBACK_BASE_URL = 'https://github.com/Telara-Labs/Telara-CLI/releases/download';
|
|
17
|
+
const MANUAL_INSTALL_URL = 'https://telara.ai/docs/cli/install';
|
|
18
|
+
|
|
19
|
+
function getPlatformInfo() {
|
|
20
|
+
const platform = process.platform;
|
|
21
|
+
const arch = process.arch;
|
|
22
|
+
|
|
23
|
+
let os_name;
|
|
24
|
+
switch (platform) {
|
|
25
|
+
case 'darwin':
|
|
26
|
+
os_name = 'darwin';
|
|
27
|
+
break;
|
|
28
|
+
case 'linux':
|
|
29
|
+
os_name = 'linux';
|
|
30
|
+
break;
|
|
31
|
+
case 'win32':
|
|
32
|
+
os_name = 'windows';
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Unsupported platform: ${platform}\n` +
|
|
37
|
+
`Please install manually: ${MANUAL_INSTALL_URL}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let arch_name;
|
|
42
|
+
switch (arch) {
|
|
43
|
+
case 'x64':
|
|
44
|
+
arch_name = 'amd64';
|
|
45
|
+
break;
|
|
46
|
+
case 'arm64':
|
|
47
|
+
arch_name = 'arm64';
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Unsupported architecture: ${arch}\n` +
|
|
52
|
+
`Please install manually: ${MANUAL_INSTALL_URL}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { os_name, arch_name };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getArchiveFilename(version, os_name, arch_name) {
|
|
60
|
+
// version field in package.json does not include the "v" prefix
|
|
61
|
+
// archive filenames use the bare version number (e.g. telara_0.1.0_darwin_amd64.tar.gz)
|
|
62
|
+
const bare_version = version.replace(/^v/, '');
|
|
63
|
+
|
|
64
|
+
if (os_name === 'windows') {
|
|
65
|
+
return `telara_${bare_version}_windows_${arch_name}.zip`;
|
|
66
|
+
}
|
|
67
|
+
return `telara_${bare_version}_${os_name}_${arch_name}.tar.gz`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getBinPath() {
|
|
71
|
+
const bin_dir = path.join(__dirname, '..', 'bin');
|
|
72
|
+
const bin_name = process.platform === 'win32' ? 'telara.exe' : 'telara';
|
|
73
|
+
return { bin_dir, bin_path: path.join(bin_dir, bin_name) };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isCorrectVersionInstalled(bin_path) {
|
|
77
|
+
if (!fs.existsSync(bin_path)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const output = execSync(`"${bin_path}" version`, {
|
|
82
|
+
timeout: 5000,
|
|
83
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
84
|
+
}).toString().trim();
|
|
85
|
+
const bare_version = version.replace(/^v/, '');
|
|
86
|
+
return output.includes(bare_version);
|
|
87
|
+
} catch (_) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function download(url, dest) {
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
const file = fs.createWriteStream(dest);
|
|
95
|
+
|
|
96
|
+
const request = (target_url) => {
|
|
97
|
+
https.get(target_url, (res) => {
|
|
98
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
99
|
+
// Follow redirect
|
|
100
|
+
file.close();
|
|
101
|
+
request(res.headers.location);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (res.statusCode !== 200) {
|
|
106
|
+
file.close();
|
|
107
|
+
fs.unlink(dest, () => {});
|
|
108
|
+
reject(new Error(`Download failed with HTTP ${res.statusCode} from ${target_url}`));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
res.pipe(file);
|
|
113
|
+
file.on('finish', () => {
|
|
114
|
+
file.close(resolve);
|
|
115
|
+
});
|
|
116
|
+
}).on('error', (err) => {
|
|
117
|
+
file.close();
|
|
118
|
+
fs.unlink(dest, () => {});
|
|
119
|
+
reject(err);
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
request(url);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function extractTarGz(archive_path, extract_dir) {
|
|
128
|
+
execSync(`tar -xzf "${archive_path}" -C "${extract_dir}"`, { stdio: 'inherit' });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function extractZip(archive_path, extract_dir) {
|
|
132
|
+
// Use PowerShell Expand-Archive on Windows
|
|
133
|
+
execSync(
|
|
134
|
+
`powershell -NoProfile -NonInteractive -Command "Expand-Archive -Force -Path '${archive_path}' -DestinationPath '${extract_dir}'"`,
|
|
135
|
+
{ stdio: 'inherit' }
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function downloadWithFallback(filename, version, dest) {
|
|
140
|
+
const tag = `v${version.replace(/^v/, '')}`;
|
|
141
|
+
const primary_url = `${PRIMARY_BASE_URL}/${tag}/${filename}`;
|
|
142
|
+
const fallback_url = `${FALLBACK_BASE_URL}/${tag}/${filename}`;
|
|
143
|
+
|
|
144
|
+
console.log(`Downloading telara v${version.replace(/^v/, '')}...`);
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
await download(primary_url, dest);
|
|
148
|
+
return;
|
|
149
|
+
} catch (primary_err) {
|
|
150
|
+
console.log(`Primary download failed (${primary_err.message}), trying fallback...`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
await download(fallback_url, dest);
|
|
155
|
+
} catch (fallback_err) {
|
|
156
|
+
throw new Error(
|
|
157
|
+
`Both download sources failed.\n` +
|
|
158
|
+
` Primary: ${primary_url}\n` +
|
|
159
|
+
` Fallback: ${fallback_url}\n` +
|
|
160
|
+
` Last error: ${fallback_err.message}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function main() {
|
|
166
|
+
const { os_name, arch_name } = getPlatformInfo();
|
|
167
|
+
const filename = getArchiveFilename(version, os_name, arch_name);
|
|
168
|
+
const { bin_dir, bin_path } = getBinPath();
|
|
169
|
+
|
|
170
|
+
// Skip download if correct version is already installed
|
|
171
|
+
if (isCorrectVersionInstalled(bin_path)) {
|
|
172
|
+
console.log(`telara v${version.replace(/^v/, '')} is already installed.`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Ensure bin directory exists
|
|
177
|
+
if (!fs.existsSync(bin_dir)) {
|
|
178
|
+
fs.mkdirSync(bin_dir, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const tmp_dir = fs.mkdtempSync(path.join(os.tmpdir(), 'telara-install-'));
|
|
182
|
+
const archive_path = path.join(tmp_dir, filename);
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
await downloadWithFallback(filename, version, archive_path);
|
|
186
|
+
|
|
187
|
+
console.log('Extracting...');
|
|
188
|
+
if (os_name === 'windows') {
|
|
189
|
+
extractZip(archive_path, tmp_dir);
|
|
190
|
+
} else {
|
|
191
|
+
extractTarGz(archive_path, tmp_dir);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const extracted_bin_name = os_name === 'windows' ? 'telara.exe' : 'telara';
|
|
195
|
+
const extracted_bin = path.join(tmp_dir, extracted_bin_name);
|
|
196
|
+
|
|
197
|
+
if (!fs.existsSync(extracted_bin)) {
|
|
198
|
+
throw new Error(`Expected binary not found after extraction: ${extracted_bin}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Move binary into place — use copy+delete to handle cross-filesystem moves
|
|
202
|
+
try {
|
|
203
|
+
fs.renameSync(extracted_bin, bin_path);
|
|
204
|
+
} catch (_) {
|
|
205
|
+
fs.copyFileSync(extracted_bin, bin_path);
|
|
206
|
+
fs.unlinkSync(extracted_bin);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Make executable on unix
|
|
210
|
+
if (os_name !== 'windows') {
|
|
211
|
+
fs.chmodSync(bin_path, 0o755);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log(`telara installed successfully.`);
|
|
215
|
+
console.log('');
|
|
216
|
+
console.log('Get started:');
|
|
217
|
+
console.log(' 1. Generate a token at https://app.telara.ai/settings?tab=developer');
|
|
218
|
+
console.log(' 2. telara login --token <your-token>');
|
|
219
|
+
console.log(' 3. telara setup claude-code');
|
|
220
|
+
} finally {
|
|
221
|
+
// Clean up temp directory
|
|
222
|
+
try {
|
|
223
|
+
fs.rmSync(tmp_dir, { recursive: true, force: true });
|
|
224
|
+
} catch (_) {
|
|
225
|
+
// Non-critical cleanup failure — ignore
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
main().catch((err) => {
|
|
231
|
+
console.error('');
|
|
232
|
+
console.error('telara install failed: ' + err.message);
|
|
233
|
+
console.error('');
|
|
234
|
+
console.error('Install manually:');
|
|
235
|
+
console.error(' macOS/Linux: curl -fsSL https://get.telara.ai/install.sh | sh');
|
|
236
|
+
console.error(' Windows: irm https://get.telara.ai/windows | iex');
|
|
237
|
+
console.error(' More options: ' + MANUAL_INSTALL_URL);
|
|
238
|
+
console.error('');
|
|
239
|
+
process.exit(1);
|
|
240
|
+
});
|