@mbc-cqrs-serverless/cli 1.0.26 → 1.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.
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.VERSION_FILE_NAME = void 0;
|
|
6
|
+
exports.VERSION_CACHE_TTL_MS = exports.VERSION_CACHE_FILE_NAME = exports.VERSION_FILE_NAME = void 0;
|
|
7
7
|
exports.getSkillsSourcePath = getSkillsSourcePath;
|
|
8
8
|
exports.getPersonalSkillsPath = getPersonalSkillsPath;
|
|
9
9
|
exports.getProjectSkillsPath = getProjectSkillsPath;
|
|
@@ -11,7 +11,9 @@ exports.copySkills = copySkills;
|
|
|
11
11
|
exports.getInstalledVersion = getInstalledVersion;
|
|
12
12
|
exports.getPackageVersion = getPackageVersion;
|
|
13
13
|
exports.writeVersionFile = writeVersionFile;
|
|
14
|
+
exports.getLatestVersionFromRegistry = getLatestVersionFromRegistry;
|
|
14
15
|
exports.default = installSkillsAction;
|
|
16
|
+
const child_process_1 = require("child_process");
|
|
15
17
|
const fs_1 = require("fs");
|
|
16
18
|
const os_1 = __importDefault(require("os"));
|
|
17
19
|
const path_1 = __importDefault(require("path"));
|
|
@@ -20,6 +22,18 @@ const ui_1 = require("../ui");
|
|
|
20
22
|
* Version file name for tracking installed skills version
|
|
21
23
|
*/
|
|
22
24
|
exports.VERSION_FILE_NAME = '.mbc-skills-version';
|
|
25
|
+
/**
|
|
26
|
+
* Cache file name for storing latest version from npm registry
|
|
27
|
+
*/
|
|
28
|
+
exports.VERSION_CACHE_FILE_NAME = '.mbc-version-cache.json';
|
|
29
|
+
/**
|
|
30
|
+
* Cache TTL in milliseconds (24 hours)
|
|
31
|
+
*/
|
|
32
|
+
exports.VERSION_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
33
|
+
/**
|
|
34
|
+
* npm package name for mcp-server
|
|
35
|
+
*/
|
|
36
|
+
const MCP_SERVER_PACKAGE = '@mbc-cqrs-serverless/mcp-server';
|
|
23
37
|
/**
|
|
24
38
|
* Get the path to the mcp-server skills source directory
|
|
25
39
|
*/
|
|
@@ -86,7 +100,9 @@ function copySkills(sourcePath, destPath) {
|
|
|
86
100
|
*/
|
|
87
101
|
function listSkills(sourcePath) {
|
|
88
102
|
const entries = (0, fs_1.readdirSync)(sourcePath, { withFileTypes: true });
|
|
89
|
-
return entries
|
|
103
|
+
return entries
|
|
104
|
+
.filter((entry) => entry.isDirectory())
|
|
105
|
+
.map((entry) => entry.name);
|
|
90
106
|
}
|
|
91
107
|
/**
|
|
92
108
|
* Get the installed version of skills from the version file
|
|
@@ -130,6 +146,61 @@ function writeVersionFile(destPath, version) {
|
|
|
130
146
|
const versionFilePath = path_1.default.join(destPath, exports.VERSION_FILE_NAME);
|
|
131
147
|
(0, fs_1.writeFileSync)(versionFilePath, version, 'utf-8');
|
|
132
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Get the latest package version from npm registry with caching
|
|
151
|
+
* @param destPath - The destination path where cache file is stored
|
|
152
|
+
* @param forceRefresh - Force refresh from npm registry, ignoring cache
|
|
153
|
+
* @returns The latest version or null if not available
|
|
154
|
+
*/
|
|
155
|
+
function getLatestVersionFromRegistry(destPath, forceRefresh = false) {
|
|
156
|
+
const cacheFilePath = path_1.default.join(destPath, exports.VERSION_CACHE_FILE_NAME);
|
|
157
|
+
// Check cache first (unless force refresh)
|
|
158
|
+
if (!forceRefresh && (0, fs_1.existsSync)(cacheFilePath)) {
|
|
159
|
+
try {
|
|
160
|
+
const cache = JSON.parse((0, fs_1.readFileSync)(cacheFilePath, 'utf-8'));
|
|
161
|
+
const cacheAge = Date.now() - new Date(cache.checkedAt).getTime();
|
|
162
|
+
if (cacheAge < exports.VERSION_CACHE_TTL_MS) {
|
|
163
|
+
// Cache is still valid
|
|
164
|
+
return cache.version;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Cache read failed, continue to fetch from registry
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Fetch from npm registry
|
|
172
|
+
try {
|
|
173
|
+
const version = (0, child_process_1.execSync)(`npm view ${MCP_SERVER_PACKAGE} version`, {
|
|
174
|
+
encoding: 'utf-8',
|
|
175
|
+
timeout: 10000,
|
|
176
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
177
|
+
}).trim();
|
|
178
|
+
// Ensure destination directory exists before writing cache
|
|
179
|
+
if (!(0, fs_1.existsSync)(destPath)) {
|
|
180
|
+
(0, fs_1.mkdirSync)(destPath, { recursive: true });
|
|
181
|
+
}
|
|
182
|
+
// Save to cache
|
|
183
|
+
const cache = {
|
|
184
|
+
version,
|
|
185
|
+
checkedAt: new Date().toISOString(),
|
|
186
|
+
};
|
|
187
|
+
(0, fs_1.writeFileSync)(cacheFilePath, JSON.stringify(cache, null, 2), 'utf-8');
|
|
188
|
+
return version;
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// Offline or error - try to use expired cache as fallback
|
|
192
|
+
if ((0, fs_1.existsSync)(cacheFilePath)) {
|
|
193
|
+
try {
|
|
194
|
+
const cache = JSON.parse((0, fs_1.readFileSync)(cacheFilePath, 'utf-8'));
|
|
195
|
+
return cache.version;
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
133
204
|
/**
|
|
134
205
|
* Install Claude Code skills for MBC CQRS Serverless
|
|
135
206
|
*/
|
|
@@ -170,23 +241,24 @@ async function installSkillsAction(options, command) {
|
|
|
170
241
|
// Check mode - compare versions without installing
|
|
171
242
|
if (check) {
|
|
172
243
|
const installedVersion = getInstalledVersion(destPath);
|
|
173
|
-
|
|
244
|
+
// Get latest version from npm registry (with 24h cache)
|
|
245
|
+
const latestVersion = getLatestVersionFromRegistry(destPath, force);
|
|
174
246
|
if (!installedVersion) {
|
|
175
247
|
ui_1.logger.warn('Skills are not installed.');
|
|
176
|
-
ui_1.logger.info(`Available version: ${
|
|
248
|
+
ui_1.logger.info(`Available version: ${latestVersion || 'unknown'}`);
|
|
177
249
|
ui_1.logger.info('Run `mbc install-skills` to install.');
|
|
178
250
|
return;
|
|
179
251
|
}
|
|
180
|
-
if (!
|
|
181
|
-
ui_1.logger.warn('Could not determine
|
|
252
|
+
if (!latestVersion) {
|
|
253
|
+
ui_1.logger.warn('Could not determine latest version. Check your network connection.');
|
|
182
254
|
ui_1.logger.info(`Installed version: ${installedVersion}`);
|
|
183
255
|
return;
|
|
184
256
|
}
|
|
185
|
-
if (installedVersion ===
|
|
257
|
+
if (installedVersion === latestVersion) {
|
|
186
258
|
ui_1.logger.success(`Skills are up to date (${installedVersion}).`);
|
|
187
259
|
}
|
|
188
260
|
else {
|
|
189
|
-
ui_1.logger.warn(`Update available: ${installedVersion} → ${
|
|
261
|
+
ui_1.logger.warn(`Update available: ${installedVersion} → ${latestVersion}`);
|
|
190
262
|
ui_1.logger.info('Run `mbc install-skills --force` to update.');
|
|
191
263
|
}
|
|
192
264
|
return;
|
|
@@ -203,8 +275,10 @@ async function installSkillsAction(options, command) {
|
|
|
203
275
|
// Copy skills
|
|
204
276
|
ui_1.logger.title('install', `Installing skills to ${destPath}`);
|
|
205
277
|
const copiedSkills = copySkills(sourcePath, destPath);
|
|
278
|
+
// Get version from npm registry (preferred) or fall back to local package.json
|
|
279
|
+
const latestVersion = getLatestVersionFromRegistry(destPath, true);
|
|
280
|
+
const packageVersion = latestVersion || getPackageVersion(sourcePath);
|
|
206
281
|
// Write version file
|
|
207
|
-
const packageVersion = getPackageVersion(sourcePath);
|
|
208
282
|
if (packageVersion) {
|
|
209
283
|
writeVersionFile(destPath, packageVersion);
|
|
210
284
|
}
|
|
@@ -39,7 +39,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
const install_skills_action_1 = __importStar(require("./install-skills.action"));
|
|
40
40
|
jest.mock('fs');
|
|
41
41
|
jest.mock('os');
|
|
42
|
+
jest.mock('child_process');
|
|
42
43
|
const fs_1 = require("fs");
|
|
44
|
+
const child_process_1 = require("child_process");
|
|
43
45
|
const os_1 = __importDefault(require("os"));
|
|
44
46
|
const mockExistsSync = fs_1.existsSync;
|
|
45
47
|
const mockMkdirSync = fs_1.mkdirSync;
|
|
@@ -47,6 +49,7 @@ const mockCpSync = fs_1.cpSync;
|
|
|
47
49
|
const mockReaddirSync = fs_1.readdirSync;
|
|
48
50
|
const mockReadFileSync = fs_1.readFileSync;
|
|
49
51
|
const mockWriteFileSync = fs_1.writeFileSync;
|
|
52
|
+
const mockExecSync = child_process_1.execSync;
|
|
50
53
|
describe('Install Skills Action', () => {
|
|
51
54
|
const mockCommand = {
|
|
52
55
|
name: () => 'install-skills',
|
|
@@ -55,6 +58,8 @@ describe('Install Skills Action', () => {
|
|
|
55
58
|
beforeEach(() => {
|
|
56
59
|
jest.clearAllMocks();
|
|
57
60
|
os_1.default.homedir.mockReturnValue('/home/user');
|
|
61
|
+
// Default mock for npm registry - returns a test version
|
|
62
|
+
mockExecSync.mockReturnValue('1.0.25\n');
|
|
58
63
|
});
|
|
59
64
|
describe('getSkillsSourcePath', () => {
|
|
60
65
|
it('should return the path to mcp-server skills directory', () => {
|
|
@@ -278,6 +283,81 @@ describe('Install Skills Action', () => {
|
|
|
278
283
|
expect(mockWriteFileSync).toHaveBeenCalledWith(expect.stringContaining(install_skills_action_1.VERSION_FILE_NAME), '1.0.25', 'utf-8');
|
|
279
284
|
});
|
|
280
285
|
});
|
|
286
|
+
describe('getLatestVersionFromRegistry', () => {
|
|
287
|
+
it('should fetch version from npm registry', () => {
|
|
288
|
+
mockExistsSync.mockReturnValue(false);
|
|
289
|
+
mockExecSync.mockReturnValue('1.0.26\n');
|
|
290
|
+
const version = (0, install_skills_action_1.getLatestVersionFromRegistry)('/dest/skills');
|
|
291
|
+
expect(version).toBe('1.0.26');
|
|
292
|
+
expect(mockExecSync).toHaveBeenCalledWith('npm view @mbc-cqrs-serverless/mcp-server version', expect.objectContaining({ encoding: 'utf-8', timeout: 10000 }));
|
|
293
|
+
});
|
|
294
|
+
it('should use cached version if cache is valid', () => {
|
|
295
|
+
const cacheData = {
|
|
296
|
+
version: '1.0.25',
|
|
297
|
+
checkedAt: new Date().toISOString(), // Fresh cache
|
|
298
|
+
};
|
|
299
|
+
mockExistsSync.mockReturnValue(true);
|
|
300
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(cacheData));
|
|
301
|
+
const version = (0, install_skills_action_1.getLatestVersionFromRegistry)('/dest/skills');
|
|
302
|
+
expect(version).toBe('1.0.25');
|
|
303
|
+
expect(mockExecSync).not.toHaveBeenCalled();
|
|
304
|
+
});
|
|
305
|
+
it('should fetch from registry if cache is expired', () => {
|
|
306
|
+
const cacheData = {
|
|
307
|
+
version: '1.0.24',
|
|
308
|
+
checkedAt: new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString(), // 25 hours ago
|
|
309
|
+
};
|
|
310
|
+
mockExistsSync.mockReturnValue(true);
|
|
311
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(cacheData));
|
|
312
|
+
mockExecSync.mockReturnValue('1.0.26\n');
|
|
313
|
+
const version = (0, install_skills_action_1.getLatestVersionFromRegistry)('/dest/skills');
|
|
314
|
+
expect(version).toBe('1.0.26');
|
|
315
|
+
expect(mockExecSync).toHaveBeenCalled();
|
|
316
|
+
});
|
|
317
|
+
it('should force refresh when forceRefresh is true', () => {
|
|
318
|
+
const cacheData = {
|
|
319
|
+
version: '1.0.24',
|
|
320
|
+
checkedAt: new Date().toISOString(), // Fresh cache
|
|
321
|
+
};
|
|
322
|
+
mockExistsSync.mockReturnValue(true);
|
|
323
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(cacheData));
|
|
324
|
+
mockExecSync.mockReturnValue('1.0.26\n');
|
|
325
|
+
const version = (0, install_skills_action_1.getLatestVersionFromRegistry)('/dest/skills', true);
|
|
326
|
+
expect(version).toBe('1.0.26');
|
|
327
|
+
expect(mockExecSync).toHaveBeenCalled();
|
|
328
|
+
});
|
|
329
|
+
it('should use expired cache as fallback when offline', () => {
|
|
330
|
+
const cacheData = {
|
|
331
|
+
version: '1.0.24',
|
|
332
|
+
checkedAt: new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString(),
|
|
333
|
+
};
|
|
334
|
+
mockExistsSync.mockReturnValue(true);
|
|
335
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(cacheData));
|
|
336
|
+
mockExecSync.mockImplementation(() => {
|
|
337
|
+
throw new Error('Network error');
|
|
338
|
+
});
|
|
339
|
+
const version = (0, install_skills_action_1.getLatestVersionFromRegistry)('/dest/skills');
|
|
340
|
+
expect(version).toBe('1.0.24');
|
|
341
|
+
});
|
|
342
|
+
it('should return null when offline and no cache exists', () => {
|
|
343
|
+
mockExistsSync.mockReturnValue(false);
|
|
344
|
+
mockExecSync.mockImplementation(() => {
|
|
345
|
+
throw new Error('Network error');
|
|
346
|
+
});
|
|
347
|
+
const version = (0, install_skills_action_1.getLatestVersionFromRegistry)('/dest/skills');
|
|
348
|
+
expect(version).toBeNull();
|
|
349
|
+
});
|
|
350
|
+
it('should save version to cache after fetching', () => {
|
|
351
|
+
mockExistsSync.mockReturnValue(true);
|
|
352
|
+
mockExecSync.mockReturnValue('1.0.26\n');
|
|
353
|
+
// Make cache read fail to trigger fetch
|
|
354
|
+
mockReadFileSync.mockImplementation(() => {
|
|
355
|
+
throw new Error('No cache');
|
|
356
|
+
});
|
|
357
|
+
(0, install_skills_action_1.getLatestVersionFromRegistry)('/dest/skills');
|
|
358
|
+
expect(mockWriteFileSync).toHaveBeenCalledWith(expect.stringContaining(install_skills_action_1.VERSION_CACHE_FILE_NAME), expect.stringContaining('"version": "1.0.26"'), 'utf-8');
|
|
359
|
+
});
|
|
360
|
+
});
|
|
281
361
|
describe('Check option', () => {
|
|
282
362
|
beforeEach(() => {
|
|
283
363
|
mockReaddirSync.mockReturnValue([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mbc-cqrs-serverless/cli",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "a CLI to get started with MBC CQRS serverless framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mbc",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"@faker-js/faker": "^8.3.1",
|
|
59
59
|
"copyfiles": "^2.4.1"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "f5ce26f50fa1d446a29a5a1771ca107ce426e7bc"
|
|
62
62
|
}
|