@myclaude-cli/cli 0.3.0-alpha
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/dev.js +5 -0
- package/bin/myclaude.js +20 -0
- package/dist/commands/help.d.ts +11 -0
- package/dist/commands/help.js +221 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/info.d.ts +12 -0
- package/dist/commands/info.js +87 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/install.d.ts +14 -0
- package/dist/commands/install.js +113 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +9 -0
- package/dist/commands/list.js +41 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.d.ts +9 -0
- package/dist/commands/login.js +83 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +9 -0
- package/dist/commands/logout.js +25 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/publish.d.ts +10 -0
- package/dist/commands/publish.js +229 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/search.d.ts +18 -0
- package/dist/commands/search.js +108 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.js +84 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/uninstall.d.ts +14 -0
- package/dist/commands/uninstall.js +98 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/validate.d.ts +9 -0
- package/dist/commands/validate.js +144 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/whoami.d.ts +9 -0
- package/dist/commands/whoami.js +52 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/api.d.ts +18 -0
- package/dist/core/api.js +135 -0
- package/dist/core/api.js.map +1 -0
- package/dist/core/auth.d.ts +30 -0
- package/dist/core/auth.js +71 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/browser-auth.d.ts +24 -0
- package/dist/core/browser-auth.js +155 -0
- package/dist/core/browser-auth.js.map +1 -0
- package/dist/core/install.d.ts +24 -0
- package/dist/core/install.js +191 -0
- package/dist/core/install.js.map +1 -0
- package/dist/core/lockfile.d.ts +18 -0
- package/dist/core/lockfile.js +41 -0
- package/dist/core/lockfile.js.map +1 -0
- package/dist/core/manifest.d.ts +41 -0
- package/dist/core/manifest.js +203 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/packer.d.ts +25 -0
- package/dist/core/packer.js +169 -0
- package/dist/core/packer.js.map +1 -0
- package/dist/core/paths.d.ts +2 -0
- package/dist/core/paths.js +50 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/secret-scan.d.ts +17 -0
- package/dist/core/secret-scan.js +73 -0
- package/dist/core/secret-scan.js.map +1 -0
- package/dist/ui/exit-codes.d.ts +91 -0
- package/dist/ui/exit-codes.js +51 -0
- package/dist/ui/exit-codes.js.map +1 -0
- package/dist/ui/format.d.ts +12 -0
- package/dist/ui/format.js +44 -0
- package/dist/ui/format.js.map +1 -0
- package/dist/ui/index.d.ts +13 -0
- package/dist/ui/index.js +11 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/layout.d.ts +28 -0
- package/dist/ui/layout.js +161 -0
- package/dist/ui/layout.js.map +1 -0
- package/dist/ui/table.d.ts +10 -0
- package/dist/ui/table.js +42 -0
- package/dist/ui/table.js.map +1 -0
- package/dist/ui/theme.d.ts +46 -0
- package/dist/ui/theme.js +180 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.js +44 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/keychain.d.ts +3 -0
- package/dist/utils/keychain.js +53 -0
- package/dist/utils/keychain.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface BrowserAuthResult {
|
|
2
|
+
idToken: string;
|
|
3
|
+
refreshToken: string;
|
|
4
|
+
uid: string;
|
|
5
|
+
email: string;
|
|
6
|
+
username: string;
|
|
7
|
+
displayName: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Browser-based authentication flow (localhost callback).
|
|
11
|
+
*
|
|
12
|
+
* 1. Start HTTP server on 127.0.0.1:random-port
|
|
13
|
+
* 2. Open browser to myclaude.sh/cli/auth?port=PORT&state=STATE
|
|
14
|
+
* 3. User authenticates in browser (existing Firebase Auth)
|
|
15
|
+
* 4. Browser POSTs tokens to localhost:PORT/callback
|
|
16
|
+
* 5. Return tokens to caller
|
|
17
|
+
*
|
|
18
|
+
* Security:
|
|
19
|
+
* - Server binds to 127.0.0.1 only (no remote access)
|
|
20
|
+
* - Random state param prevents CSRF
|
|
21
|
+
* - Server shuts down after first successful callback
|
|
22
|
+
* - 5 minute timeout
|
|
23
|
+
*/
|
|
24
|
+
export declare function browserAuth(): Promise<BrowserAuthResult>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { exec } from 'node:child_process';
|
|
4
|
+
import { platform } from 'node:os';
|
|
5
|
+
import { getBaseUrl } from './api.js';
|
|
6
|
+
const TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
7
|
+
/**
|
|
8
|
+
* Browser-based authentication flow (localhost callback).
|
|
9
|
+
*
|
|
10
|
+
* 1. Start HTTP server on 127.0.0.1:random-port
|
|
11
|
+
* 2. Open browser to myclaude.sh/cli/auth?port=PORT&state=STATE
|
|
12
|
+
* 3. User authenticates in browser (existing Firebase Auth)
|
|
13
|
+
* 4. Browser POSTs tokens to localhost:PORT/callback
|
|
14
|
+
* 5. Return tokens to caller
|
|
15
|
+
*
|
|
16
|
+
* Security:
|
|
17
|
+
* - Server binds to 127.0.0.1 only (no remote access)
|
|
18
|
+
* - Random state param prevents CSRF
|
|
19
|
+
* - Server shuts down after first successful callback
|
|
20
|
+
* - 5 minute timeout
|
|
21
|
+
*/
|
|
22
|
+
export async function browserAuth() {
|
|
23
|
+
const state = randomBytes(32).toString('hex');
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
let resolved = false;
|
|
26
|
+
const server = createServer((req, res) => {
|
|
27
|
+
// CORS preflight for browser POST
|
|
28
|
+
if (req.method === 'OPTIONS') {
|
|
29
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
30
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
31
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
32
|
+
res.setHeader('Access-Control-Max-Age', '86400');
|
|
33
|
+
res.writeHead(204);
|
|
34
|
+
res.end();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (req.method === 'POST' && (req.url === '/callback' || req.url?.startsWith('/callback'))) {
|
|
38
|
+
let body = '';
|
|
39
|
+
req.on('data', (chunk) => { body += chunk.toString(); });
|
|
40
|
+
req.on('end', () => {
|
|
41
|
+
try {
|
|
42
|
+
// Support both JSON and form-urlencoded (form submit bypasses CORS)
|
|
43
|
+
const contentType = req.headers['content-type'] || '';
|
|
44
|
+
let data;
|
|
45
|
+
if (contentType.includes('application/json')) {
|
|
46
|
+
data = JSON.parse(body);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Parse URL-encoded form data
|
|
50
|
+
data = Object.fromEntries(new URLSearchParams(body));
|
|
51
|
+
}
|
|
52
|
+
// Verify state to prevent CSRF
|
|
53
|
+
if (data.state !== state) {
|
|
54
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
55
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
56
|
+
res.end(JSON.stringify({ error: 'Invalid state parameter' }));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!data.idToken || !data.refreshToken || !data.uid) {
|
|
60
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
61
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
62
|
+
res.end(JSON.stringify({ error: 'Missing required token fields' }));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Send success page
|
|
66
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
67
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
68
|
+
res.end(successPage());
|
|
69
|
+
resolved = true;
|
|
70
|
+
server.close();
|
|
71
|
+
resolve({
|
|
72
|
+
displayName: data.displayName || '',
|
|
73
|
+
email: data.email || '',
|
|
74
|
+
idToken: data.idToken,
|
|
75
|
+
refreshToken: data.refreshToken,
|
|
76
|
+
uid: data.uid,
|
|
77
|
+
username: data.username || '',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
82
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
83
|
+
res.end(JSON.stringify({ error: 'Invalid request body' }));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
res.writeHead(404);
|
|
89
|
+
res.end('Not found');
|
|
90
|
+
});
|
|
91
|
+
// Bind to localhost (use 'localhost' not '127.0.0.1' for browser CORS compat on Windows)
|
|
92
|
+
server.listen(0, 'localhost', () => {
|
|
93
|
+
const { port } = server.address();
|
|
94
|
+
const authUrl = `${getBaseUrl()}/cli/auth?port=${port}&state=${state}`;
|
|
95
|
+
openBrowser(authUrl);
|
|
96
|
+
});
|
|
97
|
+
// Timeout
|
|
98
|
+
const timer = setTimeout(() => {
|
|
99
|
+
if (!resolved) {
|
|
100
|
+
resolved = true;
|
|
101
|
+
server.close();
|
|
102
|
+
reject(new Error('Authentication timed out after 5 minutes. Run: myclaude login'));
|
|
103
|
+
}
|
|
104
|
+
}, TIMEOUT_MS);
|
|
105
|
+
server.on('close', () => clearTimeout(timer));
|
|
106
|
+
server.on('error', (err) => {
|
|
107
|
+
if (!resolved) {
|
|
108
|
+
resolved = true;
|
|
109
|
+
clearTimeout(timer);
|
|
110
|
+
reject(new Error(`Failed to start auth server: ${err.message}`));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function openBrowser(url) {
|
|
116
|
+
const os = platform();
|
|
117
|
+
let cmd;
|
|
118
|
+
if (os === 'win32') {
|
|
119
|
+
cmd = `start "" "${url}"`;
|
|
120
|
+
}
|
|
121
|
+
else if (os === 'darwin') {
|
|
122
|
+
cmd = `open "${url}"`;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
cmd = `xdg-open "${url}"`;
|
|
126
|
+
}
|
|
127
|
+
exec(cmd, (err) => {
|
|
128
|
+
if (err) {
|
|
129
|
+
// If browser can't open, show URL for manual copy
|
|
130
|
+
process.stderr.write(`\n Open this URL in your browser:\n ${url}\n\n`);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function successPage() {
|
|
135
|
+
return `<!DOCTYPE html>
|
|
136
|
+
<html>
|
|
137
|
+
<head>
|
|
138
|
+
<meta charset="utf-8">
|
|
139
|
+
<title>MyClaude CLI — Authenticated</title>
|
|
140
|
+
<style>
|
|
141
|
+
body { font-family: -apple-system, system-ui, sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #0a0a0a; color: #e5e5e5; }
|
|
142
|
+
.card { text-align: center; padding: 3rem; border: 1px solid #333; border-radius: 12px; max-width: 400px; }
|
|
143
|
+
h1 { font-size: 1.5rem; margin-bottom: 0.5rem; color: #c4704b; }
|
|
144
|
+
p { color: #999; font-size: 0.9rem; }
|
|
145
|
+
</style>
|
|
146
|
+
</head>
|
|
147
|
+
<body>
|
|
148
|
+
<div class="card">
|
|
149
|
+
<h1>Authenticated</h1>
|
|
150
|
+
<p>You can close this tab and return to your terminal.</p>
|
|
151
|
+
</div>
|
|
152
|
+
</body>
|
|
153
|
+
</html>`;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=browser-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-auth.js","sourceRoot":"","sources":["../../src/core/browser-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface InstallResult {
|
|
2
|
+
slug: string;
|
|
3
|
+
version: string;
|
|
4
|
+
type: string;
|
|
5
|
+
location: string;
|
|
6
|
+
checksum: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function validateSlug(slug: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Download and install a product from the MyClaude marketplace.
|
|
11
|
+
*
|
|
12
|
+
* Security defenses:
|
|
13
|
+
* - Slug validated with regex (no path traversal)
|
|
14
|
+
* - fileName from server validated (no path separators)
|
|
15
|
+
* - Download URL must be HTTPS
|
|
16
|
+
* - Download size capped at 50 MB
|
|
17
|
+
* - Product type validated against known set
|
|
18
|
+
* - Zip paths validated + resolved path must stay within target
|
|
19
|
+
* - Rollback: temp dir cleaned on failure
|
|
20
|
+
*/
|
|
21
|
+
export declare function installProduct(productId: string, slug: string, type: string, price: number, options?: {
|
|
22
|
+
force?: boolean;
|
|
23
|
+
global?: boolean;
|
|
24
|
+
}): Promise<InstallResult>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, posix, resolve, sep } from 'node:path';
|
|
4
|
+
import { apiPost } from './api.js';
|
|
5
|
+
import { addEntry } from './lockfile.js';
|
|
6
|
+
import { getInstallPath } from './paths.js';
|
|
7
|
+
// Validate slug: lowercase alphanumeric + hyphens only
|
|
8
|
+
const SLUG_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;
|
|
9
|
+
// Validate fileName from server: no path separators, no traversal
|
|
10
|
+
const SAFE_FILENAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
11
|
+
// Allowed download URL prefixes (R2 + Firebase Storage)
|
|
12
|
+
const ALLOWED_DOWNLOAD_PREFIXES = [
|
|
13
|
+
'https://', // All HTTPS origins are accepted for signed URLs
|
|
14
|
+
];
|
|
15
|
+
// Maximum download size: 50 MB
|
|
16
|
+
const MAX_DOWNLOAD_BYTES = 50 * 1024 * 1024;
|
|
17
|
+
// Valid product types from the marketplace
|
|
18
|
+
const VALID_TYPES = new Set([
|
|
19
|
+
'skills', 'squads', 'agents', 'workflows', 'design-systems',
|
|
20
|
+
'claude-md', 'prompts', 'applications', 'systems',
|
|
21
|
+
]);
|
|
22
|
+
export function validateSlug(slug) {
|
|
23
|
+
if (!slug || slug.length > 128) {
|
|
24
|
+
throw new Error(`Invalid product slug: too ${slug ? 'long' : 'short'}`);
|
|
25
|
+
}
|
|
26
|
+
if (!SLUG_RE.test(slug)) {
|
|
27
|
+
throw new Error(`Invalid product slug "${slug}". Slugs must be lowercase alphanumeric with hyphens.`);
|
|
28
|
+
}
|
|
29
|
+
if (slug.includes('--') || slug.includes('..')) {
|
|
30
|
+
throw new Error(`Invalid product slug "${slug}". Contains invalid sequence.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function validateFileName(name) {
|
|
34
|
+
if (!name || !SAFE_FILENAME_RE.test(name) || name.includes('..') || name.length > 255) {
|
|
35
|
+
throw new Error(`Invalid file name from server: "${name}"`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function validateDownloadUrl(url) {
|
|
39
|
+
if (!url || !url.startsWith('https://')) {
|
|
40
|
+
throw new Error(`Security: download URL must be HTTPS. Got: ${url?.slice(0, 50)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function validateType(type) {
|
|
44
|
+
if (!VALID_TYPES.has(type)) {
|
|
45
|
+
throw new Error(`Unknown product type: "${type}"`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Download and install a product from the MyClaude marketplace.
|
|
50
|
+
*
|
|
51
|
+
* Security defenses:
|
|
52
|
+
* - Slug validated with regex (no path traversal)
|
|
53
|
+
* - fileName from server validated (no path separators)
|
|
54
|
+
* - Download URL must be HTTPS
|
|
55
|
+
* - Download size capped at 50 MB
|
|
56
|
+
* - Product type validated against known set
|
|
57
|
+
* - Zip paths validated + resolved path must stay within target
|
|
58
|
+
* - Rollback: temp dir cleaned on failure
|
|
59
|
+
*/
|
|
60
|
+
export async function installProduct(productId, slug, type, price, options = {}) {
|
|
61
|
+
validateSlug(slug);
|
|
62
|
+
validateType(type);
|
|
63
|
+
// Get signed download URL
|
|
64
|
+
const downloadRes = await apiPost('/api/products/download', { productId }, { auth: true });
|
|
65
|
+
if (!downloadRes.ok) {
|
|
66
|
+
const err = await downloadRes.json().catch(() => ({}));
|
|
67
|
+
if (downloadRes.status === 403) {
|
|
68
|
+
throw new Error('Purchase required. Paid product install available in Beta.');
|
|
69
|
+
}
|
|
70
|
+
throw new Error(err.error || `Download failed: ${downloadRes.status}`);
|
|
71
|
+
}
|
|
72
|
+
const { url, fileName } = await downloadRes.json();
|
|
73
|
+
validateDownloadUrl(url);
|
|
74
|
+
validateFileName(fileName);
|
|
75
|
+
// Download the file
|
|
76
|
+
const fileRes = await fetch(url);
|
|
77
|
+
if (!fileRes.ok || !fileRes.body) {
|
|
78
|
+
throw new Error(`File download failed: ${fileRes.status}`);
|
|
79
|
+
}
|
|
80
|
+
// Check Content-Length before downloading
|
|
81
|
+
const contentLength = Number(fileRes.headers.get('content-length') || 0);
|
|
82
|
+
if (contentLength > MAX_DOWNLOAD_BYTES) {
|
|
83
|
+
throw new Error(`File too large: ${(contentLength / 1024 / 1024).toFixed(1)} MB (max 50 MB)`);
|
|
84
|
+
}
|
|
85
|
+
// Determine install location
|
|
86
|
+
const location = getInstallPath(slug, type, options.global || false);
|
|
87
|
+
const isZip = fileName.endsWith('.zip');
|
|
88
|
+
const isClaude = type === 'claude-md';
|
|
89
|
+
// Download to buffer, compute checksum (with size guard)
|
|
90
|
+
const chunks = [];
|
|
91
|
+
let totalBytes = 0;
|
|
92
|
+
const reader = fileRes.body.getReader();
|
|
93
|
+
while (true) {
|
|
94
|
+
const { done, value } = await reader.read();
|
|
95
|
+
if (done)
|
|
96
|
+
break;
|
|
97
|
+
totalBytes += value.byteLength;
|
|
98
|
+
if (totalBytes > MAX_DOWNLOAD_BYTES) {
|
|
99
|
+
throw new Error(`File too large: exceeded 50 MB limit during download`);
|
|
100
|
+
}
|
|
101
|
+
chunks.push(Buffer.from(value));
|
|
102
|
+
}
|
|
103
|
+
const fileBuffer = Buffer.concat(chunks);
|
|
104
|
+
const checksum = 'sha256:' + createHash('sha256').update(fileBuffer).digest('hex');
|
|
105
|
+
// Create target directory (clean first if --force)
|
|
106
|
+
try {
|
|
107
|
+
if (isClaude) {
|
|
108
|
+
mkdirSync(dirname(location), { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
if (options.force && existsSync(location)) {
|
|
112
|
+
rmSync(location, { force: true, recursive: true });
|
|
113
|
+
}
|
|
114
|
+
mkdirSync(location, { recursive: true });
|
|
115
|
+
}
|
|
116
|
+
if (isZip) {
|
|
117
|
+
const extractTarget = isClaude ? dirname(location) : location;
|
|
118
|
+
await extractZip(fileBuffer, extractTarget);
|
|
119
|
+
}
|
|
120
|
+
else if (isClaude) {
|
|
121
|
+
writeFileSync(location, fileBuffer);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
writeFileSync(join(location, fileName), fileBuffer);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
// Rollback: clean up partially created directory on failure
|
|
129
|
+
if (!isClaude && existsSync(location)) {
|
|
130
|
+
try {
|
|
131
|
+
rmSync(location, { force: true, recursive: true });
|
|
132
|
+
}
|
|
133
|
+
catch { /* best effort */ }
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
const version = '1.0.0'; // Alpha placeholder — version from product metadata in Beta
|
|
138
|
+
addEntry(slug, {
|
|
139
|
+
checksum,
|
|
140
|
+
installedAt: new Date().toISOString(),
|
|
141
|
+
location,
|
|
142
|
+
paid: price > 0,
|
|
143
|
+
productId,
|
|
144
|
+
type,
|
|
145
|
+
version,
|
|
146
|
+
});
|
|
147
|
+
return { checksum, location, slug, type, version };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Extract zip contents with security validation (DC23).
|
|
151
|
+
* Defense in depth:
|
|
152
|
+
* 1. Reject paths with "..", absolute paths, >255 chars
|
|
153
|
+
* 2. Resolve final path and verify it stays within targetDir
|
|
154
|
+
* 3. Reject UNC paths (Windows \\server\share)
|
|
155
|
+
*/
|
|
156
|
+
async function extractZip(buffer, targetDir) {
|
|
157
|
+
const { default: JSZip } = await import('jszip');
|
|
158
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
159
|
+
const resolvedTarget = resolve(targetDir);
|
|
160
|
+
for (const [entryPath, entry] of Object.entries(zip.files)) {
|
|
161
|
+
validateZipPath(entryPath);
|
|
162
|
+
// Canonical zip-slip defense: resolve and verify containment
|
|
163
|
+
const fullPath = resolve(join(targetDir, entryPath));
|
|
164
|
+
if (!fullPath.startsWith(resolvedTarget + sep) && fullPath !== resolvedTarget) {
|
|
165
|
+
throw new Error(`Security: resolved path escapes target directory: ${entryPath}`);
|
|
166
|
+
}
|
|
167
|
+
if (entry.dir) {
|
|
168
|
+
mkdirSync(fullPath, { recursive: true });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
172
|
+
const content = await entry.async('nodebuffer');
|
|
173
|
+
writeFileSync(fullPath, content);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function validateZipPath(entryPath) {
|
|
177
|
+
if (!entryPath || entryPath.trim() === '') {
|
|
178
|
+
throw new Error('Invalid zip: empty file path');
|
|
179
|
+
}
|
|
180
|
+
if (entryPath.includes('..')) {
|
|
181
|
+
throw new Error(`Security: zip contains path traversal: ${entryPath}`);
|
|
182
|
+
}
|
|
183
|
+
// Reject absolute paths: Unix, Windows drive, UNC
|
|
184
|
+
if (posix.isAbsolute(entryPath) || /^[A-Za-z]:/.test(entryPath) || entryPath.startsWith('\\\\')) {
|
|
185
|
+
throw new Error(`Security: zip contains absolute path: ${entryPath}`);
|
|
186
|
+
}
|
|
187
|
+
if (entryPath.length > 255) {
|
|
188
|
+
throw new Error(`Security: zip path exceeds 255 characters: ${entryPath.slice(0, 50)}...`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/core/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface LockfileEntry {
|
|
2
|
+
version: string;
|
|
3
|
+
type: string;
|
|
4
|
+
productId: string;
|
|
5
|
+
location: string;
|
|
6
|
+
checksum: string;
|
|
7
|
+
installedAt: string;
|
|
8
|
+
paid: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface Lockfile {
|
|
11
|
+
lockfileVersion: number;
|
|
12
|
+
installed: Record<string, LockfileEntry>;
|
|
13
|
+
}
|
|
14
|
+
export declare function readLockfile(): Lockfile;
|
|
15
|
+
export declare function writeLockfile(lockfile: Lockfile): void;
|
|
16
|
+
export declare function isInstalled(slug: string): LockfileEntry | null;
|
|
17
|
+
export declare function addEntry(slug: string, entry: LockfileEntry): void;
|
|
18
|
+
export declare function removeEntry(slug: string): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const LOCKFILE_NAME = 'myclaude-lock.json';
|
|
4
|
+
function getLockfilePath() {
|
|
5
|
+
return join(process.cwd(), LOCKFILE_NAME);
|
|
6
|
+
}
|
|
7
|
+
export function readLockfile() {
|
|
8
|
+
const path = getLockfilePath();
|
|
9
|
+
try {
|
|
10
|
+
if (!existsSync(path)) {
|
|
11
|
+
return { installed: {}, lockfileVersion: 1 };
|
|
12
|
+
}
|
|
13
|
+
const raw = readFileSync(path, 'utf-8');
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return { installed: {}, lockfileVersion: 1 };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function writeLockfile(lockfile) {
|
|
21
|
+
const path = getLockfilePath();
|
|
22
|
+
writeFileSync(path, JSON.stringify(lockfile, null, 2) + '\n');
|
|
23
|
+
}
|
|
24
|
+
export function isInstalled(slug) {
|
|
25
|
+
const lockfile = readLockfile();
|
|
26
|
+
return lockfile.installed[slug] || null;
|
|
27
|
+
}
|
|
28
|
+
export function addEntry(slug, entry) {
|
|
29
|
+
const lockfile = readLockfile();
|
|
30
|
+
lockfile.installed[slug] = entry;
|
|
31
|
+
writeLockfile(lockfile);
|
|
32
|
+
}
|
|
33
|
+
export function removeEntry(slug) {
|
|
34
|
+
const lockfile = readLockfile();
|
|
35
|
+
if (!lockfile.installed[slug])
|
|
36
|
+
return false;
|
|
37
|
+
delete lockfile.installed[slug];
|
|
38
|
+
writeLockfile(lockfile);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=lockfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/core/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,SAAS,CAAA;AAC/D,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAiB9B,MAAM,aAAa,GAAG,oBAAoB,CAAA;AAE1C,SAAS,eAAe;IACtB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAA;AAC3C,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,IAAI,GAAG,eAAe,EAAE,CAAA;IAC9B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,OAAO,EAAC,SAAS,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,EAAC,CAAA;QAC5C,CAAC;QAED,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACvC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa,CAAA;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAC,SAAS,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,EAAC,CAAA;IAC5C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAkB;IAC9C,MAAM,IAAI,GAAG,eAAe,EAAE,CAAA;IAC9B,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;IAC/B,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,KAAoB;IACzD,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;IAC/B,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;IAChC,aAAa,CAAC,QAAQ,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAA;IAC/B,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAC3C,OAAO,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC/B,aAAa,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,IAAI,CAAA;AACb,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vault.yaml manifest parser and validator.
|
|
3
|
+
* Source of truth for CLI publishing (DC11).
|
|
4
|
+
*/
|
|
5
|
+
declare const VALID_TYPES: readonly ["skill", "agent", "squad", "workflow", "design-system", "claude-md", "prompt", "application", "system"];
|
|
6
|
+
type ProductType = typeof VALID_TYPES[number];
|
|
7
|
+
export interface Manifest {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
type: ProductType;
|
|
11
|
+
description: string;
|
|
12
|
+
price: number;
|
|
13
|
+
license: string;
|
|
14
|
+
tags: string[];
|
|
15
|
+
entry: string;
|
|
16
|
+
readme: string;
|
|
17
|
+
category: string;
|
|
18
|
+
displayName: string;
|
|
19
|
+
mcsLevel: number;
|
|
20
|
+
language: string;
|
|
21
|
+
longDescription: string;
|
|
22
|
+
installTarget: string;
|
|
23
|
+
compatibility: {
|
|
24
|
+
claudeCode: string;
|
|
25
|
+
};
|
|
26
|
+
dependencies: {
|
|
27
|
+
myclaude: string[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface ValidationIssue {
|
|
31
|
+
field: string;
|
|
32
|
+
message: string;
|
|
33
|
+
blocker: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function readManifest(dir?: string): {
|
|
36
|
+
manifest: Manifest | null;
|
|
37
|
+
issues: ValidationIssue[];
|
|
38
|
+
};
|
|
39
|
+
/** Read README.md contents for marketplace listing */
|
|
40
|
+
export declare function readReadmeContents(dir?: string, readmePath?: string): string;
|
|
41
|
+
export {};
|