@nimblebrain/mpak 0.0.1-beta.1
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/.env.example +13 -0
- package/CLAUDE.md +117 -0
- package/LICENSE +201 -0
- package/README.md +206 -0
- package/dist/commands/packages/pull.d.ts +11 -0
- package/dist/commands/packages/pull.d.ts.map +1 -0
- package/dist/commands/packages/pull.js +72 -0
- package/dist/commands/packages/pull.js.map +1 -0
- package/dist/commands/packages/search.d.ts +12 -0
- package/dist/commands/packages/search.d.ts.map +1 -0
- package/dist/commands/packages/search.js +63 -0
- package/dist/commands/packages/search.js.map +1 -0
- package/dist/commands/packages/show.d.ts +8 -0
- package/dist/commands/packages/show.d.ts.map +1 -0
- package/dist/commands/packages/show.js +121 -0
- package/dist/commands/packages/show.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api/registry-client.d.ts +63 -0
- package/dist/lib/api/registry-client.d.ts.map +1 -0
- package/dist/lib/api/registry-client.js +167 -0
- package/dist/lib/api/registry-client.js.map +1 -0
- package/dist/program.d.ts +8 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +69 -0
- package/dist/program.js.map +1 -0
- package/dist/utils/config-manager.d.ts +23 -0
- package/dist/utils/config-manager.d.ts.map +1 -0
- package/dist/utils/config-manager.js +65 -0
- package/dist/utils/config-manager.js.map +1 -0
- package/dist/utils/errors.d.ts +12 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +27 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/version.d.ts +5 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +19 -0
- package/dist/utils/version.js.map +1 -0
- package/eslint.config.js +63 -0
- package/package.json +48 -0
- package/src/commands/packages/pull.ts +96 -0
- package/src/commands/packages/search.ts +83 -0
- package/src/commands/packages/show.ts +138 -0
- package/src/index.ts +15 -0
- package/src/lib/api/registry-client.ts +223 -0
- package/src/lib/api/schema.d.ts +548 -0
- package/src/program.test.ts +24 -0
- package/src/program.ts +76 -0
- package/src/utils/config-manager.test.ts +60 -0
- package/src/utils/config-manager.ts +81 -0
- package/src/utils/errors.test.ts +25 -0
- package/src/utils/errors.ts +33 -0
- package/src/utils/version.test.ts +16 -0
- package/src/utils/version.ts +18 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration manager for CLI settings in ~/.mpak/config.json
|
|
6
|
+
*/
|
|
7
|
+
export class ConfigManager {
|
|
8
|
+
configDir;
|
|
9
|
+
configFile;
|
|
10
|
+
config = null;
|
|
11
|
+
constructor() {
|
|
12
|
+
this.configDir = join(homedir(), '.mpak');
|
|
13
|
+
this.configFile = join(this.configDir, 'config.json');
|
|
14
|
+
this.ensureConfigDir();
|
|
15
|
+
}
|
|
16
|
+
ensureConfigDir() {
|
|
17
|
+
if (!existsSync(this.configDir)) {
|
|
18
|
+
mkdirSync(this.configDir, { recursive: true, mode: 0o700 });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
loadConfig() {
|
|
22
|
+
if (this.config) {
|
|
23
|
+
return this.config;
|
|
24
|
+
}
|
|
25
|
+
if (!existsSync(this.configFile)) {
|
|
26
|
+
this.config = {
|
|
27
|
+
version: '1.0.0',
|
|
28
|
+
lastUpdated: new Date().toISOString(),
|
|
29
|
+
};
|
|
30
|
+
this.saveConfig();
|
|
31
|
+
return this.config;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const configJson = readFileSync(this.configFile, 'utf8');
|
|
35
|
+
this.config = JSON.parse(configJson);
|
|
36
|
+
return this.config;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
this.config = {
|
|
40
|
+
version: '1.0.0',
|
|
41
|
+
lastUpdated: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
this.saveConfig();
|
|
44
|
+
return this.config;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
saveConfig() {
|
|
48
|
+
if (!this.config) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.config.lastUpdated = new Date().toISOString();
|
|
52
|
+
const configJson = JSON.stringify(this.config, null, 2);
|
|
53
|
+
writeFileSync(this.configFile, configJson, { mode: 0o600 });
|
|
54
|
+
}
|
|
55
|
+
setRegistryUrl(url) {
|
|
56
|
+
const config = this.loadConfig();
|
|
57
|
+
config.registryUrl = url;
|
|
58
|
+
this.saveConfig();
|
|
59
|
+
}
|
|
60
|
+
getRegistryUrl() {
|
|
61
|
+
const config = this.loadConfig();
|
|
62
|
+
return config.registryUrl || process.env.MPAK_REGISTRY_URL || 'https://api.mpak.dev';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=config-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/utils/config-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B;;GAEG;AACH,MAAM,OAAO,aAAa;IAChB,SAAS,CAAS;IAClB,UAAU,CAAS;IACnB,MAAM,GAAsB,IAAI,CAAC;IAEzC;QACE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACtD,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,MAAM,GAAG;gBACZ,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACtC,CAAC;YACF,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAe,CAAC;YACnD,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,MAAM,GAAG;gBACZ,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACtC,CAAC;YACF,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;IACH,CAAC;IAEO,UAAU;QAChB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACnD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACxD,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,cAAc,CAAC,GAAW;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QACjC,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC;QACzB,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,cAAc;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,sBAAsB,CAAC;IACvF,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error class for CLI errors
|
|
3
|
+
*/
|
|
4
|
+
export declare class CLIError extends Error {
|
|
5
|
+
readonly exitCode: number;
|
|
6
|
+
constructor(message: string, exitCode?: number);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Handles errors gracefully and exits with appropriate code
|
|
10
|
+
*/
|
|
11
|
+
export declare function handleError(error: unknown): never;
|
|
12
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBAG/B,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAU;CAMvB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAajD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom error class for CLI errors
|
|
3
|
+
*/
|
|
4
|
+
export class CLIError extends Error {
|
|
5
|
+
exitCode;
|
|
6
|
+
constructor(message, exitCode = 1) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'CLIError';
|
|
9
|
+
this.exitCode = exitCode;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Handles errors gracefully and exits with appropriate code
|
|
14
|
+
*/
|
|
15
|
+
export function handleError(error) {
|
|
16
|
+
if (error instanceof CLIError) {
|
|
17
|
+
console.error(`Error: ${error.message}`);
|
|
18
|
+
process.exit(error.exitCode);
|
|
19
|
+
}
|
|
20
|
+
if (error instanceof Error) {
|
|
21
|
+
console.error(`Error: ${error.message}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
console.error('An unexpected error occurred');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjB,QAAQ,CAAS;IAEjC,YACE,OAAe,EACf,WAAmB,CAAC;QAEpB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/utils/version.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAUnC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Gets the current version from package.json
|
|
6
|
+
*/
|
|
7
|
+
export function getVersion() {
|
|
8
|
+
try {
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
const packageJsonPath = join(__dirname, '../../package.json');
|
|
12
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
13
|
+
return packageJson.version;
|
|
14
|
+
}
|
|
15
|
+
catch (_error) {
|
|
16
|
+
return 'unknown';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/utils/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC;;GAEG;AACH,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACtC,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;QAC9D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;QACvE,OAAO,WAAW,CAAC,OAAO,CAAC;IAC7B,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
import typescript from '@typescript-eslint/eslint-plugin';
|
|
3
|
+
import typescriptParser from '@typescript-eslint/parser';
|
|
4
|
+
|
|
5
|
+
export default [
|
|
6
|
+
js.configs.recommended,
|
|
7
|
+
{
|
|
8
|
+
files: ['src/**/*.ts'],
|
|
9
|
+
languageOptions: {
|
|
10
|
+
parser: typescriptParser,
|
|
11
|
+
parserOptions: {
|
|
12
|
+
ecmaVersion: 2022,
|
|
13
|
+
sourceType: 'module'
|
|
14
|
+
},
|
|
15
|
+
globals: {
|
|
16
|
+
console: 'readonly',
|
|
17
|
+
process: 'readonly',
|
|
18
|
+
Buffer: 'readonly',
|
|
19
|
+
__dirname: 'readonly',
|
|
20
|
+
__filename: 'readonly',
|
|
21
|
+
global: 'readonly',
|
|
22
|
+
fetch: 'readonly',
|
|
23
|
+
setTimeout: 'readonly',
|
|
24
|
+
setInterval: 'readonly',
|
|
25
|
+
clearTimeout: 'readonly',
|
|
26
|
+
clearInterval: 'readonly',
|
|
27
|
+
setImmediate: 'readonly',
|
|
28
|
+
Blob: 'readonly',
|
|
29
|
+
FormData: 'readonly',
|
|
30
|
+
URLSearchParams: 'readonly',
|
|
31
|
+
NodeJS: 'readonly'
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
plugins: {
|
|
35
|
+
'@typescript-eslint': typescript
|
|
36
|
+
},
|
|
37
|
+
rules: {
|
|
38
|
+
// TypeScript specific rules
|
|
39
|
+
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'all', caughtErrorsIgnorePattern: '^_' }],
|
|
40
|
+
'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'all', caughtErrorsIgnorePattern: '^_' }],
|
|
41
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
42
|
+
|
|
43
|
+
// General JavaScript rules
|
|
44
|
+
'no-console': 'off', // CLI needs console output
|
|
45
|
+
'prefer-const': 'error',
|
|
46
|
+
'no-var': 'error',
|
|
47
|
+
'eqeqeq': 'error',
|
|
48
|
+
'no-trailing-spaces': 'error',
|
|
49
|
+
'no-multiple-empty-lines': ['error', { max: 2 }],
|
|
50
|
+
|
|
51
|
+
// Import/export rules
|
|
52
|
+
'no-duplicate-imports': 'error'
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
ignores: [
|
|
57
|
+
'dist/**',
|
|
58
|
+
'node_modules/**',
|
|
59
|
+
'*.js',
|
|
60
|
+
'examples/**'
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nimblebrain/mpak",
|
|
3
|
+
"version": "0.0.1-beta.1",
|
|
4
|
+
"description": "CLI for downloading MCPB bundles from the mpak registry",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mpak": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"generate:types": "openapi-typescript http://localhost:3200/documentation/json -o src/lib/api/schema.d.ts",
|
|
14
|
+
"lint": "eslint src",
|
|
15
|
+
"lint:fix": "eslint src --fix",
|
|
16
|
+
"prepublishOnly": "npm run build",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"test": "vitest",
|
|
19
|
+
"test:ui": "vitest --ui",
|
|
20
|
+
"test:coverage": "vitest --coverage",
|
|
21
|
+
"typecheck": "tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"mcpb",
|
|
26
|
+
"anthropic",
|
|
27
|
+
"cli",
|
|
28
|
+
"package-manager"
|
|
29
|
+
],
|
|
30
|
+
"author": "NimbleBrain Inc",
|
|
31
|
+
"license": "Apache-2.0",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"commander": "^14.0.2",
|
|
34
|
+
"dotenv": "^17.2.3"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^25.0.3",
|
|
38
|
+
"@vitest/ui": "^4.0.16",
|
|
39
|
+
"openapi-typescript": "^7.10.1",
|
|
40
|
+
"tsx": "^4.21.0",
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"vitest": "^4.0.16"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"registry": "https://registry.npmjs.org/"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import { RegistryClient } from '../../lib/api/registry-client.js';
|
|
3
|
+
|
|
4
|
+
export interface PullOptions {
|
|
5
|
+
output?: string;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
os?: string;
|
|
8
|
+
arch?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse package specification into name and version
|
|
13
|
+
* Examples:
|
|
14
|
+
* @scope/name -> { name: '@scope/name', version: undefined }
|
|
15
|
+
* @scope/name@1.0.0 -> { name: '@scope/name', version: '1.0.0' }
|
|
16
|
+
*/
|
|
17
|
+
function parsePackageSpec(spec: string): { name: string; version?: string } {
|
|
18
|
+
// Find the last @ which separates version from package name
|
|
19
|
+
// Package names start with @, so we need to find the second @
|
|
20
|
+
const lastAtIndex = spec.lastIndexOf('@');
|
|
21
|
+
|
|
22
|
+
if (lastAtIndex <= 0) {
|
|
23
|
+
// No version specified or invalid format
|
|
24
|
+
return { name: spec };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const name = spec.substring(0, lastAtIndex);
|
|
28
|
+
const version = spec.substring(lastAtIndex + 1);
|
|
29
|
+
|
|
30
|
+
// Validate that the name still starts with @
|
|
31
|
+
if (!name.startsWith('@')) {
|
|
32
|
+
// This means the @ was part of the package name, not a version separator
|
|
33
|
+
return { name: spec };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { name, version };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Pull (download) a package from the registry
|
|
41
|
+
*/
|
|
42
|
+
export async function handlePull(
|
|
43
|
+
packageSpec: string,
|
|
44
|
+
options: PullOptions = {}
|
|
45
|
+
): Promise<void> {
|
|
46
|
+
try {
|
|
47
|
+
const { name, version } = parsePackageSpec(packageSpec);
|
|
48
|
+
|
|
49
|
+
const client = new RegistryClient();
|
|
50
|
+
|
|
51
|
+
// Detect platform (or use explicit overrides)
|
|
52
|
+
const detectedPlatform = RegistryClient.detectPlatform();
|
|
53
|
+
const platform = {
|
|
54
|
+
os: options.os || detectedPlatform.os,
|
|
55
|
+
arch: options.arch || detectedPlatform.arch,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
console.log(`=> Fetching ${version ? `${name}@${version}` : `${name} (latest)`}...`);
|
|
59
|
+
console.log(` Platform: ${platform.os}-${platform.arch}`);
|
|
60
|
+
|
|
61
|
+
// Get download info with platform
|
|
62
|
+
const downloadInfo = await client.getDownloadInfo(name, version, platform);
|
|
63
|
+
|
|
64
|
+
if (options.json) {
|
|
65
|
+
console.log(JSON.stringify(downloadInfo, null, 2));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const bundle = downloadInfo.bundle;
|
|
70
|
+
console.log(` Version: ${bundle.version}`);
|
|
71
|
+
console.log(` Artifact: ${bundle.platform.os}-${bundle.platform.arch}`);
|
|
72
|
+
console.log(` Size: ${(bundle.size / (1024 * 1024)).toFixed(2)} MB`);
|
|
73
|
+
|
|
74
|
+
// Determine output filename (include platform in name)
|
|
75
|
+
const platformSuffix = `${bundle.platform.os}-${bundle.platform.arch}`;
|
|
76
|
+
const defaultFilename = `${name.replace('@', '').replace('/', '-')}-${bundle.version}-${platformSuffix}.mcpb`;
|
|
77
|
+
const outputPath = options.output
|
|
78
|
+
? resolve(options.output)
|
|
79
|
+
: resolve(defaultFilename);
|
|
80
|
+
|
|
81
|
+
console.log(`\n=> Downloading to ${outputPath}...`);
|
|
82
|
+
|
|
83
|
+
// Download the bundle
|
|
84
|
+
await client.downloadBundle(downloadInfo.url, outputPath);
|
|
85
|
+
|
|
86
|
+
console.log(`\n=> Bundle downloaded successfully!`);
|
|
87
|
+
console.log(` File: ${outputPath}`);
|
|
88
|
+
console.log(` SHA256: ${bundle.sha256.substring(0, 16)}...`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error('\n=> Failed to pull bundle');
|
|
91
|
+
if (error instanceof Error) {
|
|
92
|
+
console.error(` ${error.message}`);
|
|
93
|
+
}
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { RegistryClient } from '../../lib/api/registry-client.js';
|
|
2
|
+
|
|
3
|
+
export interface SearchOptions {
|
|
4
|
+
type?: string;
|
|
5
|
+
sort?: 'downloads' | 'recent' | 'name';
|
|
6
|
+
limit?: number;
|
|
7
|
+
offset?: number;
|
|
8
|
+
json?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Search bundles (v1 API)
|
|
13
|
+
*/
|
|
14
|
+
export async function handleSearch(
|
|
15
|
+
query: string,
|
|
16
|
+
options: SearchOptions = {}
|
|
17
|
+
): Promise<void> {
|
|
18
|
+
try {
|
|
19
|
+
const client = new RegistryClient();
|
|
20
|
+
const result = await client.searchBundles(query, {
|
|
21
|
+
type: options.type,
|
|
22
|
+
sort: options.sort,
|
|
23
|
+
limit: options.limit,
|
|
24
|
+
offset: options.offset,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (options.json) {
|
|
28
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (result.bundles.length === 0) {
|
|
33
|
+
console.log(`\nNo bundles found for "${query}"`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(`\nFound ${result.total} bundle(s) for "${query}":\n`);
|
|
38
|
+
|
|
39
|
+
for (const bundle of result.bundles) {
|
|
40
|
+
const verified = bundle.verified ? '✓' : ' ';
|
|
41
|
+
const provenance = bundle.provenance ? '🔒' : '';
|
|
42
|
+
|
|
43
|
+
console.log(`${verified} ${bundle.name} v${bundle.latest_version} ${provenance}`);
|
|
44
|
+
|
|
45
|
+
if (bundle.description) {
|
|
46
|
+
console.log(` ${bundle.description}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const details = [];
|
|
50
|
+
if (bundle.downloads) details.push(`${bundle.downloads} downloads`);
|
|
51
|
+
if (bundle.server_type) details.push(bundle.server_type);
|
|
52
|
+
if (bundle.author?.name) details.push(`by ${bundle.author.name}`);
|
|
53
|
+
|
|
54
|
+
if (details.length > 0) {
|
|
55
|
+
console.log(` ${details.join(' • ')}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (bundle.tools && bundle.tools.length > 0) {
|
|
59
|
+
const toolNames = bundle.tools.slice(0, 3).map((t) => t.name);
|
|
60
|
+
const toolsDisplay =
|
|
61
|
+
bundle.tools.length > 3
|
|
62
|
+
? `${toolNames.join(', ')} +${bundle.tools.length - 3} more`
|
|
63
|
+
: toolNames.join(', ');
|
|
64
|
+
console.log(` Tools: ${toolsDisplay}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (result.pagination.has_more) {
|
|
71
|
+
const nextOffset = (options.offset || 0) + (options.limit || 20);
|
|
72
|
+
console.log(`More results available. Use --offset ${nextOffset} to see more.`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`Use "mpak show <bundle>" for more details`);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('=> Failed to search bundles');
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
console.error(` ${error.message}`);
|
|
80
|
+
}
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { RegistryClient } from '../../lib/api/registry-client.js';
|
|
2
|
+
|
|
3
|
+
export interface ShowOptions {
|
|
4
|
+
json?: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Show detailed information about a bundle (v1 API)
|
|
9
|
+
*/
|
|
10
|
+
export async function handleShow(
|
|
11
|
+
packageName: string,
|
|
12
|
+
options: ShowOptions = {}
|
|
13
|
+
): Promise<void> {
|
|
14
|
+
try {
|
|
15
|
+
const client = new RegistryClient();
|
|
16
|
+
|
|
17
|
+
// Fetch bundle details and versions in parallel
|
|
18
|
+
const [bundle, versionsInfo] = await Promise.all([
|
|
19
|
+
client.getBundle(packageName),
|
|
20
|
+
client.getVersions(packageName),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
if (options.json) {
|
|
24
|
+
console.log(JSON.stringify({ ...bundle, versions_detail: versionsInfo.versions }, null, 2));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Header
|
|
29
|
+
const verified = bundle.verified ? '✓ ' : '';
|
|
30
|
+
const provenance = bundle.provenance ? '🔒 ' : '';
|
|
31
|
+
console.log(`\n${verified}${provenance}${bundle.display_name || bundle.name} v${bundle.latest_version}\n`);
|
|
32
|
+
|
|
33
|
+
// Description
|
|
34
|
+
if (bundle.description) {
|
|
35
|
+
console.log(bundle.description);
|
|
36
|
+
console.log();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Basic info
|
|
40
|
+
console.log('Bundle Information:');
|
|
41
|
+
console.log(` Name: ${bundle.name}`);
|
|
42
|
+
if (bundle.author?.name) {
|
|
43
|
+
console.log(` Author: ${bundle.author.name}`);
|
|
44
|
+
}
|
|
45
|
+
if (bundle.server_type) {
|
|
46
|
+
console.log(` Type: ${bundle.server_type}`);
|
|
47
|
+
}
|
|
48
|
+
if (bundle.license) {
|
|
49
|
+
console.log(` License: ${bundle.license}`);
|
|
50
|
+
}
|
|
51
|
+
if (bundle.homepage) {
|
|
52
|
+
console.log(` Homepage: ${bundle.homepage}`);
|
|
53
|
+
}
|
|
54
|
+
console.log();
|
|
55
|
+
|
|
56
|
+
// Provenance info
|
|
57
|
+
if (bundle.provenance) {
|
|
58
|
+
console.log('Provenance:');
|
|
59
|
+
console.log(` Repository: ${bundle.provenance.repository}`);
|
|
60
|
+
console.log(` Commit: ${bundle.provenance.sha.substring(0, 12)}`);
|
|
61
|
+
console.log(` Provider: ${bundle.provenance.provider}`);
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Stats
|
|
66
|
+
console.log('Statistics:');
|
|
67
|
+
console.log(` Downloads: ${bundle.downloads.toLocaleString()}`);
|
|
68
|
+
console.log(` Published: ${new Date(bundle.published_at).toLocaleDateString()}`);
|
|
69
|
+
console.log();
|
|
70
|
+
|
|
71
|
+
// GitHub info
|
|
72
|
+
if (bundle.github) {
|
|
73
|
+
console.log('GitHub:');
|
|
74
|
+
console.log(` Repository: ${bundle.github.repo}`);
|
|
75
|
+
if (bundle.github.stars != null) console.log(` Stars: ${bundle.github.stars}`);
|
|
76
|
+
if (bundle.github.forks != null) console.log(` Forks: ${bundle.github.forks}`);
|
|
77
|
+
if (bundle.github.watchers != null) console.log(` Watchers: ${bundle.github.watchers}`);
|
|
78
|
+
console.log();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Tools
|
|
82
|
+
if (bundle.tools && bundle.tools.length > 0) {
|
|
83
|
+
console.log(`Tools (${bundle.tools.length}):`);
|
|
84
|
+
for (const tool of bundle.tools) {
|
|
85
|
+
console.log(` - ${tool.name}`);
|
|
86
|
+
if (tool.description) {
|
|
87
|
+
console.log(` ${tool.description}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
console.log();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Versions with platforms
|
|
94
|
+
if (versionsInfo.versions && versionsInfo.versions.length > 0) {
|
|
95
|
+
console.log(`Versions (${versionsInfo.versions.length}):`);
|
|
96
|
+
const recentVersions = versionsInfo.versions.slice(0, 5);
|
|
97
|
+
for (const version of recentVersions) {
|
|
98
|
+
const date = new Date(version.published_at).toLocaleDateString();
|
|
99
|
+
const downloads = version.downloads.toLocaleString();
|
|
100
|
+
const isLatest = version.version === versionsInfo.latest ? ' (latest)' : '';
|
|
101
|
+
const provTag = version.provenance ? ' 🔒' : '';
|
|
102
|
+
|
|
103
|
+
// Format platforms
|
|
104
|
+
const platformStrs = version.platforms.map((p) => `${p.os}-${p.arch}`);
|
|
105
|
+
const platformsDisplay = platformStrs.length > 0 ? ` [${platformStrs.join(', ')}]` : '';
|
|
106
|
+
|
|
107
|
+
console.log(` ${version.version}${isLatest}${provTag} - ${date} - ${downloads} downloads${platformsDisplay}`);
|
|
108
|
+
}
|
|
109
|
+
if (versionsInfo.versions.length > 5) {
|
|
110
|
+
console.log(` ... and ${versionsInfo.versions.length - 5} more`);
|
|
111
|
+
}
|
|
112
|
+
console.log();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Available platforms for latest version
|
|
116
|
+
const latestVersion = versionsInfo.versions.find((v) => v.version === versionsInfo.latest);
|
|
117
|
+
if (latestVersion && latestVersion.platforms.length > 0) {
|
|
118
|
+
console.log('Available Platforms:');
|
|
119
|
+
for (const platform of latestVersion.platforms) {
|
|
120
|
+
console.log(` - ${platform.os}-${platform.arch}`);
|
|
121
|
+
}
|
|
122
|
+
console.log();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Install instructions
|
|
126
|
+
console.log('Install:');
|
|
127
|
+
console.log(` mpak install ${bundle.name}`);
|
|
128
|
+
console.log();
|
|
129
|
+
console.log('Pull (download only):');
|
|
130
|
+
console.log(` mpak pull ${bundle.name}`);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error('=> Failed to get bundle details');
|
|
133
|
+
if (error instanceof Error) {
|
|
134
|
+
console.error(` ${error.message}`);
|
|
135
|
+
}
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { config } from 'dotenv';
|
|
4
|
+
import { createProgram } from './program.js';
|
|
5
|
+
import { handleError } from './utils/errors.js';
|
|
6
|
+
|
|
7
|
+
// Load environment variables from .env file
|
|
8
|
+
config();
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
const program = createProgram();
|
|
12
|
+
await program.parseAsync(process.argv);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
main().catch(handleError);
|