@keplog/cli 0.2.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/LICENSE +21 -0
- package/README.md +495 -0
- package/bin/keplog +2 -0
- package/dist/commands/delete.d.ts +3 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +158 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +131 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/issues.d.ts +3 -0
- package/dist/commands/issues.d.ts.map +1 -0
- package/dist/commands/issues.js +543 -0
- package/dist/commands/issues.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +104 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/releases.d.ts +3 -0
- package/dist/commands/releases.d.ts.map +1 -0
- package/dist/commands/releases.js +100 -0
- package/dist/commands/releases.js.map +1 -0
- package/dist/commands/upload.d.ts +3 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +76 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +57 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +155 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/uploader.d.ts +11 -0
- package/dist/lib/uploader.d.ts.map +1 -0
- package/dist/lib/uploader.js +171 -0
- package/dist/lib/uploader.js.map +1 -0
- package/jest.config.js +16 -0
- package/package.json +58 -0
- package/src/commands/delete.ts +186 -0
- package/src/commands/init.ts +137 -0
- package/src/commands/issues.ts +695 -0
- package/src/commands/list.ts +124 -0
- package/src/commands/releases.ts +122 -0
- package/src/commands/upload.ts +76 -0
- package/src/index.ts +31 -0
- package/src/lib/config.ts +138 -0
- package/src/lib/uploader.ts +168 -0
- package/tests/README.md +380 -0
- package/tests/config.test.ts +397 -0
- package/tests/uploader.test.ts +524 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.uploadSourceMaps = uploadSourceMaps;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const glob_1 = require("glob");
|
|
43
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
44
|
+
const axios_1 = __importDefault(require("axios"));
|
|
45
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
46
|
+
const ora_1 = __importDefault(require("ora"));
|
|
47
|
+
async function uploadSourceMaps(options) {
|
|
48
|
+
const { release, filePatterns, projectId, apiKey, apiUrl, verbose } = options;
|
|
49
|
+
console.log(chalk_1.default.bold.cyan('\nš¦ Keplog Source Map Uploader\n'));
|
|
50
|
+
console.log(chalk_1.default.gray(`Release: ${release}`));
|
|
51
|
+
console.log(chalk_1.default.gray(`Project ID: ${projectId}`));
|
|
52
|
+
console.log(chalk_1.default.gray(`API URL: ${apiUrl}\n`));
|
|
53
|
+
// Find all matching files
|
|
54
|
+
const spinner = (0, ora_1.default)('Finding source map files...').start();
|
|
55
|
+
const allFiles = [];
|
|
56
|
+
for (const pattern of filePatterns) {
|
|
57
|
+
try {
|
|
58
|
+
const matches = await (0, glob_1.glob)(pattern, { nodir: true });
|
|
59
|
+
if (verbose) {
|
|
60
|
+
spinner.info(`Pattern "${pattern}" matched ${matches.length} file(s)`);
|
|
61
|
+
}
|
|
62
|
+
allFiles.push(...matches);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
spinner.fail(`Invalid glob pattern: ${pattern}`);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Remove duplicates
|
|
70
|
+
const uniqueFiles = [...new Set(allFiles)];
|
|
71
|
+
if (uniqueFiles.length === 0) {
|
|
72
|
+
spinner.fail('No source map files found');
|
|
73
|
+
console.log(chalk_1.default.yellow('\nā ļø No files matched the specified patterns'));
|
|
74
|
+
console.log(chalk_1.default.gray('\nTip: Make sure your patterns are correct:'));
|
|
75
|
+
console.log(chalk_1.default.gray(' --files="dist/**/*.map"'));
|
|
76
|
+
console.log(chalk_1.default.gray(' --files="build/*.map"'));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
spinner.succeed(`Found ${uniqueFiles.length} source map file(s)`);
|
|
80
|
+
// Filter only .map files
|
|
81
|
+
const mapFiles = uniqueFiles.filter(file => file.endsWith('.map'));
|
|
82
|
+
if (mapFiles.length === 0) {
|
|
83
|
+
console.log(chalk_1.default.yellow('\nā ļø No .map files found'));
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
if (mapFiles.length !== uniqueFiles.length) {
|
|
87
|
+
console.log(chalk_1.default.yellow(`\nā ļø Skipped ${uniqueFiles.length - mapFiles.length} non-.map file(s)`));
|
|
88
|
+
}
|
|
89
|
+
// Display files to upload
|
|
90
|
+
if (verbose) {
|
|
91
|
+
console.log(chalk_1.default.cyan('\nš Files to upload:'));
|
|
92
|
+
mapFiles.forEach((file, index) => {
|
|
93
|
+
const stats = fs.statSync(file);
|
|
94
|
+
const size = formatFileSize(stats.size);
|
|
95
|
+
console.log(chalk_1.default.gray(` ${index + 1}. ${file} (${size})`));
|
|
96
|
+
});
|
|
97
|
+
console.log();
|
|
98
|
+
}
|
|
99
|
+
// Create form data
|
|
100
|
+
const uploadSpinner = (0, ora_1.default)(`Uploading ${mapFiles.length} file(s) to Keplog...`).start();
|
|
101
|
+
try {
|
|
102
|
+
const formData = new form_data_1.default();
|
|
103
|
+
formData.append('release', release);
|
|
104
|
+
// Add all files
|
|
105
|
+
for (const filePath of mapFiles) {
|
|
106
|
+
const fileName = path.basename(filePath);
|
|
107
|
+
const fileStream = fs.createReadStream(filePath);
|
|
108
|
+
formData.append('files', fileStream, fileName);
|
|
109
|
+
}
|
|
110
|
+
// Upload to API
|
|
111
|
+
const url = `${apiUrl}/api/v1/cli/projects/${projectId}/sourcemaps`;
|
|
112
|
+
const response = await axios_1.default.post(url, formData, {
|
|
113
|
+
headers: {
|
|
114
|
+
'X-API-Key': apiKey,
|
|
115
|
+
...formData.getHeaders(),
|
|
116
|
+
},
|
|
117
|
+
maxContentLength: Infinity,
|
|
118
|
+
maxBodyLength: Infinity,
|
|
119
|
+
});
|
|
120
|
+
const data = response.data;
|
|
121
|
+
uploadSpinner.succeed('Upload complete!');
|
|
122
|
+
// Display results
|
|
123
|
+
console.log(chalk_1.default.bold.green('\nā
Upload Complete!\n'));
|
|
124
|
+
console.log(chalk_1.default.gray(`Release: ${data.release}`));
|
|
125
|
+
console.log(chalk_1.default.gray(`Uploaded: ${data.count} file(s)\n`));
|
|
126
|
+
if (data.uploaded && data.uploaded.length > 0) {
|
|
127
|
+
console.log(chalk_1.default.cyan('š Successfully uploaded:'));
|
|
128
|
+
data.uploaded.forEach(filename => {
|
|
129
|
+
console.log(chalk_1.default.green(` ā ${filename}`));
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (data.errors && data.errors.length > 0) {
|
|
133
|
+
console.log(chalk_1.default.yellow('\nā ļø Errors:'));
|
|
134
|
+
data.errors.forEach(error => {
|
|
135
|
+
console.log(chalk_1.default.red(` ā ${error}`));
|
|
136
|
+
});
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
console.log(chalk_1.default.cyan(`\nš” Source maps will be used automatically when processing errors for release ${data.release}\n`));
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
uploadSpinner.fail('Upload failed');
|
|
143
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
144
|
+
if (error.code === 'ENOTFOUND') {
|
|
145
|
+
throw new Error(`Could not connect to ${apiUrl}. Please check your internet connection.`);
|
|
146
|
+
}
|
|
147
|
+
else if (error.code === 'ECONNREFUSED') {
|
|
148
|
+
throw new Error(`Connection refused to ${apiUrl}. Please check the API URL.`);
|
|
149
|
+
}
|
|
150
|
+
else if (error.response) {
|
|
151
|
+
// Server responded with error
|
|
152
|
+
const data = error.response.data;
|
|
153
|
+
throw new Error(data.error || `HTTP ${error.response.status}: ${error.response.statusText}`);
|
|
154
|
+
}
|
|
155
|
+
else if (error.request) {
|
|
156
|
+
// Request made but no response
|
|
157
|
+
throw new Error(`No response from server. Please check your internet connection.`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function formatFileSize(bytes) {
|
|
164
|
+
if (bytes === 0)
|
|
165
|
+
return '0 B';
|
|
166
|
+
const k = 1024;
|
|
167
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
168
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
169
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=uploader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploader.js","sourceRoot":"","sources":["../../src/lib/uploader.ts"],"names":[],"mappings":"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"}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
preset: 'ts-jest',
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
roots: ['<rootDir>/src', '<rootDir>/tests'],
|
|
5
|
+
testMatch: ['**/__tests__/**/*.ts', '**/*.test.ts', '**/*.spec.ts'],
|
|
6
|
+
moduleFileExtensions: ['ts', 'js', 'json'],
|
|
7
|
+
collectCoverageFrom: [
|
|
8
|
+
'src/**/*.ts',
|
|
9
|
+
'!src/**/*.d.ts',
|
|
10
|
+
'!src/index.ts', // Main entry point, tested via integration
|
|
11
|
+
],
|
|
12
|
+
coverageDirectory: 'coverage',
|
|
13
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
14
|
+
verbose: true,
|
|
15
|
+
testTimeout: 10000,
|
|
16
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@keplog/cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Official Keplog CLI for uploading source maps and managing projects",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"keplog": "./bin/keplog"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsc --watch",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"test:verbose": "jest --verbose"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"keplog",
|
|
20
|
+
"error-tracking",
|
|
21
|
+
"source-maps",
|
|
22
|
+
"cli",
|
|
23
|
+
"monitoring"
|
|
24
|
+
],
|
|
25
|
+
"author": "Keplog Team",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=16.0.0"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"axios": "^1.6.5",
|
|
32
|
+
"chalk": "^4.1.2",
|
|
33
|
+
"commander": "^11.1.0",
|
|
34
|
+
"dotenv": "^16.3.1",
|
|
35
|
+
"form-data": "^4.0.0",
|
|
36
|
+
"glob": "^10.3.10",
|
|
37
|
+
"ora": "^5.4.1",
|
|
38
|
+
"prompts": "^2.4.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/form-data": "^2.2.1",
|
|
42
|
+
"@types/jest": "^30.0.0",
|
|
43
|
+
"@types/node": "^20.10.5",
|
|
44
|
+
"@types/prompts": "^2.4.9",
|
|
45
|
+
"axios-mock-adapter": "^2.1.0",
|
|
46
|
+
"jest": "^30.2.0",
|
|
47
|
+
"ts-jest": "^29.4.6",
|
|
48
|
+
"typescript": "^5.3.3"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "https://github.com/keplog/keplog-cli.git"
|
|
53
|
+
},
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://github.com/keplog/keplog-cli/issues"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://keplog.com"
|
|
58
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import prompts from 'prompts';
|
|
5
|
+
import { ConfigManager } from '../lib/config.js';
|
|
6
|
+
|
|
7
|
+
interface SourceMap {
|
|
8
|
+
Filename: string;
|
|
9
|
+
Size: number;
|
|
10
|
+
UploadedAt: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ListResponse {
|
|
14
|
+
source_maps: SourceMap[];
|
|
15
|
+
release: string;
|
|
16
|
+
count: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const deleteCommand = new Command('delete')
|
|
20
|
+
.description('Delete source maps for a specific release')
|
|
21
|
+
.option('-r, --release <version>', 'Release version')
|
|
22
|
+
.option('-f, --file <filename>', 'Specific file to delete (optional - omit to delete all)')
|
|
23
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
24
|
+
.option('-p, --project-id <id>', 'Project ID (overrides config)')
|
|
25
|
+
.option('-k, --api-key <key>', 'API key (overrides config)')
|
|
26
|
+
.option('-u, --api-url <url>', 'API URL (overrides config)')
|
|
27
|
+
.action(async (options) => {
|
|
28
|
+
try {
|
|
29
|
+
// Read config from file (priority: local > global > env)
|
|
30
|
+
const config = ConfigManager.getConfig();
|
|
31
|
+
|
|
32
|
+
const release = options.release || process.env.KEPLOG_RELEASE;
|
|
33
|
+
const filename = options.file;
|
|
34
|
+
const skipConfirm = options.yes || false;
|
|
35
|
+
const projectId = options.projectId || config.projectId;
|
|
36
|
+
const apiKey = options.apiKey || config.apiKey;
|
|
37
|
+
const apiUrl = options.apiUrl || config.apiUrl || 'https://api.keplog.com';
|
|
38
|
+
|
|
39
|
+
// Validate required parameters
|
|
40
|
+
if (!projectId) {
|
|
41
|
+
console.error(chalk.red('\nā Error: Project ID is required\n'));
|
|
42
|
+
console.log('Options:');
|
|
43
|
+
console.log(' 1. Run: keplog init (recommended)');
|
|
44
|
+
console.log(' 2. Use flag: --project-id=<your-project-id>');
|
|
45
|
+
console.log(' 3. Set env: KEPLOG_PROJECT_ID=<your-project-id>\n');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!apiKey) {
|
|
50
|
+
console.error(chalk.red('\nā Error: API key is required\n'));
|
|
51
|
+
console.log('Options:');
|
|
52
|
+
console.log(' 1. Run: keplog init (recommended)');
|
|
53
|
+
console.log(' 2. Use flag: --api-key=<your-api-key>');
|
|
54
|
+
console.log(' 3. Set env: KEPLOG_API_KEY=<your-api-key>\n');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!release) {
|
|
59
|
+
console.error(chalk.red('\nā Error: Release version is required\n'));
|
|
60
|
+
console.log('Options:');
|
|
61
|
+
console.log(' 1. Use flag: --release=v1.0.0');
|
|
62
|
+
console.log(' 2. Set env: KEPLOG_RELEASE=v1.0.0\n');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(chalk.bold.red('\nšļø Keplog Source Maps - Delete\n'));
|
|
67
|
+
console.log(`Release: ${chalk.yellow(release)}`);
|
|
68
|
+
console.log(`Project ID: ${chalk.gray(projectId)}`);
|
|
69
|
+
|
|
70
|
+
if (filename) {
|
|
71
|
+
console.log(`File: ${chalk.yellow(filename)}\n`);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(chalk.yellow('Target: All source maps for this release\n'));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If deleting all files, fetch and show the list first
|
|
77
|
+
let filesToDelete: string[] = [];
|
|
78
|
+
if (!filename) {
|
|
79
|
+
const spinner = ora('Fetching source maps...').start();
|
|
80
|
+
|
|
81
|
+
const listUrl = `${apiUrl}/api/v1/cli/projects/${projectId}/sourcemaps?release=${encodeURIComponent(release)}`;
|
|
82
|
+
const listResponse = await fetch(listUrl, {
|
|
83
|
+
method: 'GET',
|
|
84
|
+
headers: {
|
|
85
|
+
'X-API-Key': apiKey,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (!listResponse.ok) {
|
|
90
|
+
const error = await listResponse.json() as any;
|
|
91
|
+
spinner.fail(chalk.red('Failed to fetch source maps'));
|
|
92
|
+
console.error(chalk.red(`\nā Error: ${error.error || 'Unknown error'}\n`));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const data = await listResponse.json() as ListResponse;
|
|
97
|
+
spinner.succeed(`Found ${data.count} source map${data.count !== 1 ? 's' : ''}`);
|
|
98
|
+
|
|
99
|
+
if (data.count === 0) {
|
|
100
|
+
console.log(chalk.yellow('\nNo source maps found for this release.\n'));
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
filesToDelete = data.source_maps.map(f => f.Filename);
|
|
105
|
+
|
|
106
|
+
console.log(chalk.gray('\nFiles to be deleted:'));
|
|
107
|
+
for (const file of data.source_maps) {
|
|
108
|
+
console.log(` ${chalk.red('ā')} ${file.Filename}`);
|
|
109
|
+
}
|
|
110
|
+
console.log('');
|
|
111
|
+
} else {
|
|
112
|
+
filesToDelete = [filename];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Confirmation prompt (unless --yes flag is used)
|
|
116
|
+
if (!skipConfirm) {
|
|
117
|
+
const message = filename
|
|
118
|
+
? `Are you sure you want to delete ${chalk.yellow(filename)} from release ${chalk.yellow(release)}?`
|
|
119
|
+
: `Are you sure you want to delete ${chalk.red.bold('ALL')} ${filesToDelete.length} source maps from release ${chalk.yellow(release)}?`;
|
|
120
|
+
|
|
121
|
+
const response = await prompts({
|
|
122
|
+
type: 'confirm',
|
|
123
|
+
name: 'confirmed',
|
|
124
|
+
message,
|
|
125
|
+
initial: false,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (!response.confirmed) {
|
|
129
|
+
console.log(chalk.gray('\nDeletion cancelled.\n'));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Delete files
|
|
135
|
+
let deletedCount = 0;
|
|
136
|
+
let failedCount = 0;
|
|
137
|
+
const errors: string[] = [];
|
|
138
|
+
|
|
139
|
+
console.log(chalk.gray('\nDeleting source maps...\n'));
|
|
140
|
+
|
|
141
|
+
for (const file of filesToDelete) {
|
|
142
|
+
const spinner = ora(`Deleting ${file}...`).start();
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const deleteUrl = `${apiUrl}/api/v1/cli/projects/${projectId}/sourcemaps/${encodeURIComponent(file)}?release=${encodeURIComponent(release)}`;
|
|
146
|
+
const response = await fetch(deleteUrl, {
|
|
147
|
+
method: 'DELETE',
|
|
148
|
+
headers: {
|
|
149
|
+
'X-API-Key': apiKey,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
const error = await response.json() as any;
|
|
155
|
+
throw new Error(error.error || 'Unknown error');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
spinner.succeed(chalk.green(`Deleted ${file}`));
|
|
159
|
+
deletedCount++;
|
|
160
|
+
} catch (error: any) {
|
|
161
|
+
spinner.fail(chalk.red(`Failed to delete ${file}`));
|
|
162
|
+
errors.push(`${file}: ${error.message}`);
|
|
163
|
+
failedCount++;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Summary
|
|
168
|
+
console.log(chalk.bold('\nā
Deletion Complete!\n'));
|
|
169
|
+
console.log(`Release: ${chalk.yellow(release)}`);
|
|
170
|
+
console.log(`Deleted: ${chalk.green(deletedCount)} file${deletedCount !== 1 ? 's' : ''}`);
|
|
171
|
+
|
|
172
|
+
if (failedCount > 0) {
|
|
173
|
+
console.log(`Failed: ${chalk.red(failedCount)} file${failedCount !== 1 ? 's' : ''}\n`);
|
|
174
|
+
console.log(chalk.red.bold('ā ļø Errors:'));
|
|
175
|
+
for (const error of errors) {
|
|
176
|
+
console.log(` ${chalk.red('ā')} ${error}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log('');
|
|
181
|
+
|
|
182
|
+
} catch (error: any) {
|
|
183
|
+
console.error(chalk.red(`\nā Error: ${error.message}\n`));
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import prompts from 'prompts';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { ConfigManager } from '../lib/config';
|
|
5
|
+
|
|
6
|
+
export const initCommand = new Command('init')
|
|
7
|
+
.description('Initialize Keplog configuration for the current project')
|
|
8
|
+
.option('-g, --global', 'Save configuration globally (in ~/.keplogrc)')
|
|
9
|
+
.option('-f, --force', 'Overwrite existing configuration')
|
|
10
|
+
.action(async (options) => {
|
|
11
|
+
try {
|
|
12
|
+
const isGlobal = options.global || false;
|
|
13
|
+
const force = options.force || false;
|
|
14
|
+
|
|
15
|
+
console.log(chalk.bold.cyan('\nš Keplog CLI Configuration\n'));
|
|
16
|
+
|
|
17
|
+
// Check if config already exists
|
|
18
|
+
const hasLocal = ConfigManager.hasLocalConfig();
|
|
19
|
+
const hasGlobal = ConfigManager.hasGlobalConfig();
|
|
20
|
+
|
|
21
|
+
if (!force) {
|
|
22
|
+
if (isGlobal && hasGlobal) {
|
|
23
|
+
console.log(chalk.yellow('ā ļø Global configuration already exists at ~/.keplogrc'));
|
|
24
|
+
const { overwrite } = await prompts({
|
|
25
|
+
type: 'confirm',
|
|
26
|
+
name: 'overwrite',
|
|
27
|
+
message: 'Do you want to overwrite it?',
|
|
28
|
+
initial: false,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!overwrite) {
|
|
32
|
+
console.log(chalk.gray('\nConfiguration cancelled.'));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
} else if (!isGlobal && hasLocal) {
|
|
36
|
+
const configPath = ConfigManager.getLocalConfigPath();
|
|
37
|
+
console.log(chalk.yellow(`ā ļø Configuration already exists at ${configPath}`));
|
|
38
|
+
const { overwrite } = await prompts({
|
|
39
|
+
type: 'confirm',
|
|
40
|
+
name: 'overwrite',
|
|
41
|
+
message: 'Do you want to overwrite it?',
|
|
42
|
+
initial: false,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (!overwrite) {
|
|
46
|
+
console.log(chalk.gray('\nConfiguration cancelled.'));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Read existing config (if any)
|
|
53
|
+
const existingConfig = ConfigManager.readConfig();
|
|
54
|
+
|
|
55
|
+
// Interactive prompts
|
|
56
|
+
console.log(chalk.gray('Get your credentials from: Project Settings ā General ā Project Credentials\n'));
|
|
57
|
+
|
|
58
|
+
const responses = await prompts([
|
|
59
|
+
{
|
|
60
|
+
type: 'text',
|
|
61
|
+
name: 'projectId',
|
|
62
|
+
message: 'Project ID:',
|
|
63
|
+
initial: existingConfig.projectId || '',
|
|
64
|
+
validate: (value: string) => value.trim().length > 0 || 'Project ID is required',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: 'password',
|
|
68
|
+
name: 'apiKey',
|
|
69
|
+
message: 'API Key:',
|
|
70
|
+
initial: existingConfig.apiKey || '',
|
|
71
|
+
validate: (value: string) => value.trim().length > 0 || 'API Key is required',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'text',
|
|
75
|
+
name: 'apiUrl',
|
|
76
|
+
message: 'API URL (optional):',
|
|
77
|
+
initial: existingConfig.apiUrl || 'https://api.keplog.com',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: 'text',
|
|
81
|
+
name: 'projectName',
|
|
82
|
+
message: 'Project name (optional):',
|
|
83
|
+
initial: existingConfig.projectName || '',
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
// Check if user cancelled
|
|
88
|
+
if (!responses.projectId || !responses.apiKey) {
|
|
89
|
+
console.log(chalk.gray('\nConfiguration cancelled.'));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Prepare config
|
|
94
|
+
const config = {
|
|
95
|
+
projectId: responses.projectId.trim(),
|
|
96
|
+
apiKey: responses.apiKey.trim(),
|
|
97
|
+
apiUrl: responses.apiUrl?.trim() || 'https://api.keplog.com',
|
|
98
|
+
projectName: responses.projectName?.trim() || undefined,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Save config
|
|
102
|
+
if (isGlobal) {
|
|
103
|
+
ConfigManager.writeGlobalConfig(config);
|
|
104
|
+
console.log(chalk.green('\nā
Configuration saved globally to ~/.keplogrc'));
|
|
105
|
+
} else {
|
|
106
|
+
ConfigManager.writeLocalConfig(config);
|
|
107
|
+
console.log(chalk.green('\nā
Configuration saved to .keplog.json'));
|
|
108
|
+
console.log(chalk.gray('\nTip: Add .keplog.json to .gitignore to keep credentials secret'));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Display saved config
|
|
112
|
+
console.log(chalk.cyan('\nš Saved configuration:'));
|
|
113
|
+
console.log(chalk.gray(` Project ID: ${config.projectId}`));
|
|
114
|
+
console.log(chalk.gray(` API Key: ${'*'.repeat(Math.min(config.apiKey.length, 20))}...`));
|
|
115
|
+
console.log(chalk.gray(` API URL: ${config.apiUrl}`));
|
|
116
|
+
if (config.projectName) {
|
|
117
|
+
console.log(chalk.gray(` Project Name: ${config.projectName}`));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log(chalk.cyan('\nš” Next steps:'));
|
|
121
|
+
console.log(chalk.gray(' 1. Upload source maps: keplog upload --release=v1.0.0 --files="dist/**/*.map"'));
|
|
122
|
+
console.log(chalk.gray(' 2. View help: keplog upload --help'));
|
|
123
|
+
console.log();
|
|
124
|
+
|
|
125
|
+
} catch (error: any) {
|
|
126
|
+
if (error.message === 'canceled') {
|
|
127
|
+
console.log(chalk.gray('\n\nConfiguration cancelled.'));
|
|
128
|
+
process.exit(0);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.error(chalk.red(`\nā Error: ${error.message}`));
|
|
132
|
+
if (error.stack && process.env.DEBUG) {
|
|
133
|
+
console.error(chalk.gray(error.stack));
|
|
134
|
+
}
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
});
|