@kandiforge/spectacle 0.45.8 → 0.45.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/package.json +2 -2
- package/scripts/install.js +137 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kandiforge/spectacle",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.17",
|
|
4
4
|
"description": "Spectacle server and CLI tools for KandiForge ecosystem",
|
|
5
5
|
"author": "Abstract Class Consulting Inc.",
|
|
6
6
|
"license": "PROPRIETARY",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"development"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@kandiforge/kandi-cli": "
|
|
24
|
+
"@kandiforge/kandi-cli": "^0.45.7"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"postinstall": "node scripts/install.js",
|
package/scripts/install.js
CHANGED
|
@@ -6,13 +6,58 @@ const https = require('https');
|
|
|
6
6
|
const { execSync } = require('child_process');
|
|
7
7
|
|
|
8
8
|
// This version MUST match package.json
|
|
9
|
-
const RELEASE_VERSION = '0.45.
|
|
9
|
+
const RELEASE_VERSION = '0.45.17';
|
|
10
10
|
const GITHUB_REPO = 'KandiForge/distribution';
|
|
11
|
+
const PACKAGE_NAME = '@kandiforge/spectacle';
|
|
12
|
+
|
|
13
|
+
// ANSI color codes
|
|
14
|
+
const colors = {
|
|
15
|
+
reset: '\x1b[0m',
|
|
16
|
+
bold: '\x1b[1m',
|
|
17
|
+
dim: '\x1b[2m',
|
|
18
|
+
cyan: '\x1b[36m',
|
|
19
|
+
green: '\x1b[32m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
blue: '\x1b[34m',
|
|
22
|
+
magenta: '\x1b[35m',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function log(message) {
|
|
26
|
+
console.log(message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function logStep(step, message) {
|
|
30
|
+
log(`${colors.cyan}[${step}]${colors.reset} ${message}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function logSuccess(message) {
|
|
34
|
+
log(`${colors.green}OK${colors.reset} ${message}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function logInfo(message) {
|
|
38
|
+
log(`${colors.dim} ${message}${colors.reset}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getPlatformName(platform) {
|
|
42
|
+
const names = {
|
|
43
|
+
darwin: 'macOS',
|
|
44
|
+
linux: 'Linux',
|
|
45
|
+
win32: 'Windows',
|
|
46
|
+
};
|
|
47
|
+
return names[platform] || platform;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getArchName(arch) {
|
|
51
|
+
const names = {
|
|
52
|
+
arm64: 'Apple Silicon',
|
|
53
|
+
x64: 'x64',
|
|
54
|
+
ia32: 'x86',
|
|
55
|
+
};
|
|
56
|
+
return names[arch] || arch;
|
|
57
|
+
}
|
|
11
58
|
|
|
12
59
|
function downloadFile(url, destPath) {
|
|
13
60
|
return new Promise((resolve, reject) => {
|
|
14
|
-
console.log(`Downloading from: ${url}`);
|
|
15
|
-
|
|
16
61
|
https.get(url, (response) => {
|
|
17
62
|
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
18
63
|
// Follow redirect
|
|
@@ -57,6 +102,15 @@ function extractZip(zipPath, destDir) {
|
|
|
57
102
|
async function install() {
|
|
58
103
|
try {
|
|
59
104
|
const platform = process.platform;
|
|
105
|
+
const arch = process.arch;
|
|
106
|
+
|
|
107
|
+
// Print installation header
|
|
108
|
+
log('');
|
|
109
|
+
log(`${colors.bold}${colors.cyan}Spectacle${colors.reset} ${colors.dim}v${RELEASE_VERSION}${colors.reset}`);
|
|
110
|
+
log(`${colors.dim}Server and CLI tools for KandiForge ecosystem${colors.reset}`);
|
|
111
|
+
log('');
|
|
112
|
+
|
|
113
|
+
logStep('1/4', `Detecting platform: ${colors.bold}${getPlatformName(platform)}${colors.reset} (${getArchName(arch)})`);
|
|
60
114
|
|
|
61
115
|
// Determine archive name and binary extension based on platform
|
|
62
116
|
let fileName, isZip, ext;
|
|
@@ -101,7 +155,6 @@ async function install() {
|
|
|
101
155
|
}
|
|
102
156
|
|
|
103
157
|
// Clean bin directory - remove any existing binaries to avoid mixing platforms
|
|
104
|
-
console.log('Cleaning bin directory...');
|
|
105
158
|
const existingFiles = fs.readdirSync(binDir);
|
|
106
159
|
for (const file of existingFiles) {
|
|
107
160
|
// Skip .gitkeep or other dotfiles
|
|
@@ -110,20 +163,19 @@ async function install() {
|
|
|
110
163
|
try {
|
|
111
164
|
fs.unlinkSync(filePath);
|
|
112
165
|
} catch (e) {
|
|
113
|
-
|
|
166
|
+
// Ignore cleanup errors
|
|
114
167
|
}
|
|
115
168
|
}
|
|
116
169
|
|
|
117
|
-
|
|
118
|
-
|
|
170
|
+
logStep('2/4', 'Downloading binaries from GitHub releases...');
|
|
171
|
+
logInfo(downloadUrl);
|
|
119
172
|
await downloadFile(downloadUrl, tempFile);
|
|
120
173
|
|
|
121
|
-
|
|
122
|
-
console.log('Extracting binaries...');
|
|
174
|
+
logStep('3/4', 'Extracting binaries...');
|
|
123
175
|
if (isZip) {
|
|
124
176
|
extractZip(tempFile, binDir);
|
|
125
177
|
} else {
|
|
126
|
-
execSync(`tar -xzf "${tempFile}" -C "${binDir}"`, { stdio: '
|
|
178
|
+
execSync(`tar -xzf "${tempFile}" -C "${binDir}"`, { stdio: 'pipe' });
|
|
127
179
|
}
|
|
128
180
|
|
|
129
181
|
// Clean up archive
|
|
@@ -148,8 +200,6 @@ async function install() {
|
|
|
148
200
|
}
|
|
149
201
|
|
|
150
202
|
installedBinaries.push({ name: baseName, path: destPath });
|
|
151
|
-
} else {
|
|
152
|
-
console.warn(`Warning: ${srcName} not found in archive`);
|
|
153
203
|
}
|
|
154
204
|
}
|
|
155
205
|
|
|
@@ -162,15 +212,85 @@ async function install() {
|
|
|
162
212
|
throw new Error('No binaries were found in the archive');
|
|
163
213
|
}
|
|
164
214
|
|
|
165
|
-
|
|
166
|
-
|
|
215
|
+
logStep('4/4', 'Setting up CLI commands...');
|
|
216
|
+
|
|
217
|
+
// Create symlinks in npm global bin directory
|
|
218
|
+
// npm doesn't create symlinks for binaries that don't exist at install time,
|
|
219
|
+
// so we need to create them manually after downloading
|
|
220
|
+
let symlinkSuccess = false;
|
|
221
|
+
try {
|
|
222
|
+
// Get npm prefix and construct bin path (npm bin -g is deprecated)
|
|
223
|
+
const npmPrefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
|
|
224
|
+
const npmGlobalBin = path.join(npmPrefix, 'bin');
|
|
225
|
+
|
|
226
|
+
for (const binary of installedBinaries) {
|
|
227
|
+
const binaryName = `${binary.name}${ext}`;
|
|
228
|
+
const sourcePath = binary.path;
|
|
229
|
+
const linkPath = path.join(npmGlobalBin, binaryName);
|
|
230
|
+
|
|
231
|
+
if (fs.existsSync(sourcePath)) {
|
|
232
|
+
// Remove existing symlink if it exists
|
|
233
|
+
try {
|
|
234
|
+
if (fs.existsSync(linkPath) || fs.lstatSync(linkPath).isSymbolicLink()) {
|
|
235
|
+
fs.unlinkSync(linkPath);
|
|
236
|
+
}
|
|
237
|
+
} catch (e) {
|
|
238
|
+
// Ignore if file doesn't exist
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Create symlink
|
|
242
|
+
if (platform === 'win32') {
|
|
243
|
+
// On Windows, copy the file instead of symlinking (requires admin for symlinks)
|
|
244
|
+
fs.copyFileSync(sourcePath, linkPath);
|
|
245
|
+
} else {
|
|
246
|
+
fs.symlinkSync(sourcePath, linkPath);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
symlinkSuccess = true;
|
|
251
|
+
} catch (symlinkError) {
|
|
252
|
+
// Symlink creation failed - will show path instructions below
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Print success message
|
|
256
|
+
log('');
|
|
257
|
+
log(`${colors.green}Installation complete!${colors.reset}`);
|
|
258
|
+
log('');
|
|
259
|
+
log(`${colors.bold}Installed ${installedBinaries.length} binaries:${colors.reset}`);
|
|
167
260
|
for (const binary of installedBinaries) {
|
|
168
|
-
|
|
261
|
+
log(` ${colors.cyan}${binary.name}${colors.reset}`);
|
|
169
262
|
}
|
|
170
263
|
|
|
264
|
+
// Print next steps
|
|
265
|
+
log('');
|
|
266
|
+
log(`${colors.bold}Next steps:${colors.reset}`);
|
|
267
|
+
log(` ${colors.dim}1.${colors.reset} Start the Spectacle server:`);
|
|
268
|
+
log(` ${colors.cyan}kandi-spectacle serve${colors.reset}`);
|
|
269
|
+
log('');
|
|
270
|
+
log(` ${colors.dim}2.${colors.reset} Or use individual CLI tools:`);
|
|
271
|
+
log(` ${colors.cyan}kandi-plan --help${colors.reset} ${colors.dim}# Planning and specs${colors.reset}`);
|
|
272
|
+
log(` ${colors.cyan}kandi-gpt --help${colors.reset} ${colors.dim}# GPT integration${colors.reset}`);
|
|
273
|
+
log(` ${colors.cyan}kandi-deploy --help${colors.reset} ${colors.dim}# Deployment automation${colors.reset}`);
|
|
274
|
+
log('');
|
|
275
|
+
|
|
276
|
+
if (!symlinkSuccess) {
|
|
277
|
+
log(`${colors.yellow}Note:${colors.reset} Commands may not be in your PATH.`);
|
|
278
|
+
log(`Add this to your shell profile:`);
|
|
279
|
+
log(` ${colors.dim}export PATH="${binDir}:$PATH"${colors.reset}`);
|
|
280
|
+
log('');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
log(`${colors.dim}Documentation: https://docs.kandiforge.com${colors.reset}`);
|
|
284
|
+
log('');
|
|
285
|
+
|
|
171
286
|
} catch (error) {
|
|
172
|
-
|
|
173
|
-
|
|
287
|
+
log('');
|
|
288
|
+
log(`${colors.bold}Installation failed${colors.reset}`);
|
|
289
|
+
log(`${colors.dim}${error.message}${colors.reset}`);
|
|
290
|
+
log('');
|
|
291
|
+
log(`Please report this issue at:`);
|
|
292
|
+
log(`${colors.cyan}https://github.com/KandiForge/apps/issues${colors.reset}`);
|
|
293
|
+
log('');
|
|
174
294
|
process.exit(1);
|
|
175
295
|
}
|
|
176
296
|
}
|