codmir 0.1.0 → 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/README.md +20 -0
- package/dist/chunk-7HVQNURM.mjs +273 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +769 -0
- package/dist/cli/index.mjs +479 -0
- package/dist/index.mjs +3 -269
- package/package.json +13 -5
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CodmirClient
|
|
4
|
+
} from "../chunk-7HVQNURM.mjs";
|
|
5
|
+
|
|
6
|
+
// src/cli/index.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
|
|
9
|
+
// src/cli/utils/auth.ts
|
|
10
|
+
import http from "http";
|
|
11
|
+
import open from "open";
|
|
12
|
+
|
|
13
|
+
// src/cli/utils/config.ts
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import os from "os";
|
|
17
|
+
var CONFIG_DIR = path.join(os.homedir(), ".codmir");
|
|
18
|
+
var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
19
|
+
var PROJECT_CONFIG_FILE = ".codmir.json";
|
|
20
|
+
function ensureConfigDir() {
|
|
21
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
22
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function readConfig() {
|
|
26
|
+
ensureConfigDir();
|
|
27
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const data = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
32
|
+
return JSON.parse(data);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error("Error reading config:", error);
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function writeConfig(config) {
|
|
39
|
+
ensureConfigDir();
|
|
40
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
41
|
+
}
|
|
42
|
+
function clearConfig() {
|
|
43
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
44
|
+
fs.unlinkSync(CONFIG_FILE);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function readProjectConfig(cwd = process.cwd()) {
|
|
48
|
+
const configPath = path.join(cwd, PROJECT_CONFIG_FILE);
|
|
49
|
+
if (!fs.existsSync(configPath)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const data = fs.readFileSync(configPath, "utf-8");
|
|
54
|
+
return JSON.parse(data);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Error reading project config:", error);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function writeProjectConfig(config, cwd = process.cwd()) {
|
|
61
|
+
const configPath = path.join(cwd, PROJECT_CONFIG_FILE);
|
|
62
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
63
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
64
|
+
if (fs.existsSync(gitignorePath)) {
|
|
65
|
+
const gitignore = fs.readFileSync(gitignorePath, "utf-8");
|
|
66
|
+
if (!gitignore.includes(PROJECT_CONFIG_FILE)) {
|
|
67
|
+
fs.appendFileSync(gitignorePath, `
|
|
68
|
+
# codmir
|
|
69
|
+
${PROJECT_CONFIG_FILE}
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function getToken() {
|
|
75
|
+
const config = readConfig();
|
|
76
|
+
return config.token || null;
|
|
77
|
+
}
|
|
78
|
+
function isAuthenticated() {
|
|
79
|
+
const token = getToken();
|
|
80
|
+
return !!token;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/cli/utils/auth.ts
|
|
84
|
+
async function startOAuthFlow(baseUrl = "https://codmir.com") {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
const server = http.createServer(async (req, res) => {
|
|
87
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
88
|
+
if (url.pathname === "/callback") {
|
|
89
|
+
const token = url.searchParams.get("token");
|
|
90
|
+
const error = url.searchParams.get("error");
|
|
91
|
+
if (error) {
|
|
92
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
93
|
+
res.end(`
|
|
94
|
+
<html>
|
|
95
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
96
|
+
<h1>\u274C Authentication Failed</h1>
|
|
97
|
+
<p>${error}</p>
|
|
98
|
+
<p>You can close this window.</p>
|
|
99
|
+
</body>
|
|
100
|
+
</html>
|
|
101
|
+
`);
|
|
102
|
+
server.close();
|
|
103
|
+
reject(new Error(error));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!token) {
|
|
107
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
108
|
+
res.end(`
|
|
109
|
+
<html>
|
|
110
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
111
|
+
<h1>\u274C No token received</h1>
|
|
112
|
+
<p>You can close this window.</p>
|
|
113
|
+
</body>
|
|
114
|
+
</html>
|
|
115
|
+
`);
|
|
116
|
+
server.close();
|
|
117
|
+
reject(new Error("No token received"));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
121
|
+
res.end(`
|
|
122
|
+
<html>
|
|
123
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
124
|
+
<h1>\u2705 Authentication Successful!</h1>
|
|
125
|
+
<p>You can now close this window and return to your terminal.</p>
|
|
126
|
+
<script>setTimeout(() => window.close(), 2000);</script>
|
|
127
|
+
</body>
|
|
128
|
+
</html>
|
|
129
|
+
`);
|
|
130
|
+
try {
|
|
131
|
+
const userResponse = await fetch(`${baseUrl}/api/user/profile`, {
|
|
132
|
+
headers: {
|
|
133
|
+
"Authorization": `Bearer ${token}`
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
if (!userResponse.ok) {
|
|
137
|
+
throw new Error("Failed to fetch user info");
|
|
138
|
+
}
|
|
139
|
+
const user = await userResponse.json();
|
|
140
|
+
server.close();
|
|
141
|
+
resolve({
|
|
142
|
+
token,
|
|
143
|
+
user: {
|
|
144
|
+
id: user.id,
|
|
145
|
+
email: user.email,
|
|
146
|
+
name: user.name
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
} catch (error2) {
|
|
150
|
+
server.close();
|
|
151
|
+
reject(error2);
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
res.writeHead(404);
|
|
155
|
+
res.end("Not found");
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
const port = 3333;
|
|
159
|
+
server.listen(port, () => {
|
|
160
|
+
const authUrl = `${baseUrl}/cli/auth?port=${port}`;
|
|
161
|
+
console.log("\u{1F510} Opening browser for authentication...");
|
|
162
|
+
console.log(` If browser doesn't open, visit: ${authUrl}`);
|
|
163
|
+
open(authUrl).catch(() => {
|
|
164
|
+
console.log("\n\u26A0\uFE0F Could not open browser automatically.");
|
|
165
|
+
console.log(` Please visit: ${authUrl}`);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
setTimeout(() => {
|
|
169
|
+
server.close();
|
|
170
|
+
reject(new Error("Authentication timeout"));
|
|
171
|
+
}, 5 * 60 * 1e3);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
async function authenticateWithToken(token, baseUrl = "https://codmir.com") {
|
|
175
|
+
const response = await fetch(`${baseUrl}/api/user/profile`, {
|
|
176
|
+
headers: {
|
|
177
|
+
"Authorization": `Bearer ${token}`
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
throw new Error("Invalid token");
|
|
182
|
+
}
|
|
183
|
+
const user = await response.json();
|
|
184
|
+
return {
|
|
185
|
+
token,
|
|
186
|
+
user: {
|
|
187
|
+
id: user.id,
|
|
188
|
+
email: user.email,
|
|
189
|
+
name: user.name
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function saveAuth(authResult) {
|
|
194
|
+
const config = readConfig();
|
|
195
|
+
writeConfig({
|
|
196
|
+
...config,
|
|
197
|
+
token: authResult.token,
|
|
198
|
+
userId: authResult.user.id,
|
|
199
|
+
email: authResult.user.email,
|
|
200
|
+
name: authResult.user.name,
|
|
201
|
+
lastLogin: (/* @__PURE__ */ new Date()).toISOString()
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/cli/commands/login.ts
|
|
206
|
+
import chalk from "chalk";
|
|
207
|
+
async function loginCommand(options) {
|
|
208
|
+
if (isAuthenticated()) {
|
|
209
|
+
const config = readConfig();
|
|
210
|
+
console.log(chalk.yellow("\u26A0\uFE0F Already logged in as:"), chalk.bold(config.email));
|
|
211
|
+
console.log(chalk.dim(" Run"), chalk.cyan("codmir logout"), chalk.dim("to log out first"));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
console.log(chalk.bold("\u{1F680} codmir login"));
|
|
215
|
+
console.log(chalk.dim(" Authenticate to start using codmir CLI\n"));
|
|
216
|
+
try {
|
|
217
|
+
let authResult;
|
|
218
|
+
if (options.token) {
|
|
219
|
+
console.log(chalk.dim(" Authenticating with provided token..."));
|
|
220
|
+
authResult = await authenticateWithToken(options.token);
|
|
221
|
+
} else {
|
|
222
|
+
authResult = await startOAuthFlow();
|
|
223
|
+
}
|
|
224
|
+
saveAuth(authResult);
|
|
225
|
+
console.log(chalk.green("\n\u2705 Successfully logged in!"));
|
|
226
|
+
console.log(chalk.dim(" User:"), chalk.bold(authResult.user.name));
|
|
227
|
+
console.log(chalk.dim(" Email:"), chalk.bold(authResult.user.email));
|
|
228
|
+
console.log(chalk.dim("\n You can now use"), chalk.cyan("codmir link"), chalk.dim("to connect your project"));
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.error(chalk.red("\n\u274C Login failed:"), error instanceof Error ? error.message : "Unknown error");
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/cli/commands/link.ts
|
|
236
|
+
import chalk2 from "chalk";
|
|
237
|
+
import prompts from "prompts";
|
|
238
|
+
async function linkCommand(options) {
|
|
239
|
+
const token = getToken();
|
|
240
|
+
if (!token) {
|
|
241
|
+
console.error(chalk2.red("\u274C Not authenticated"));
|
|
242
|
+
console.log(chalk2.dim(" Run"), chalk2.cyan("codmir login"), chalk2.dim("first"));
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
console.log(chalk2.bold("\n\u{1F517} codmir link"));
|
|
246
|
+
console.log(chalk2.dim(" Link this directory to a codmir project\n"));
|
|
247
|
+
const existing = readProjectConfig();
|
|
248
|
+
if (existing) {
|
|
249
|
+
console.log(chalk2.yellow("\u26A0\uFE0F This directory is already linked to:"));
|
|
250
|
+
console.log(chalk2.dim(" Project ID:"), chalk2.bold(existing.projectId));
|
|
251
|
+
if (existing.projectName) {
|
|
252
|
+
console.log(chalk2.dim(" Project:"), chalk2.bold(existing.projectName));
|
|
253
|
+
}
|
|
254
|
+
const { overwrite } = await prompts({
|
|
255
|
+
type: "confirm",
|
|
256
|
+
name: "overwrite",
|
|
257
|
+
message: "Do you want to overwrite this configuration?",
|
|
258
|
+
initial: false
|
|
259
|
+
});
|
|
260
|
+
if (!overwrite) {
|
|
261
|
+
console.log(chalk2.dim(" Cancelled"));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
const client = new CodmirClient({
|
|
267
|
+
apiKey: token,
|
|
268
|
+
baseUrl: process.env.CODMIR_API_URL || "https://codmir.com"
|
|
269
|
+
});
|
|
270
|
+
let projectId = options.project;
|
|
271
|
+
let orgId = options.org;
|
|
272
|
+
console.log(chalk2.dim(" Fetching your projects...\n"));
|
|
273
|
+
const response = await fetch(`${process.env.CODMIR_API_URL || "https://codmir.com"}/api/projects`, {
|
|
274
|
+
headers: {
|
|
275
|
+
"Authorization": `Bearer ${token}`
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
if (!response.ok) {
|
|
279
|
+
throw new Error("Failed to fetch projects");
|
|
280
|
+
}
|
|
281
|
+
const projects = await response.json();
|
|
282
|
+
if (!projectId) {
|
|
283
|
+
if (projects.length === 0) {
|
|
284
|
+
console.log(chalk2.yellow("\u26A0\uFE0F You don't have any projects yet"));
|
|
285
|
+
console.log(chalk2.dim(" Create a project at"), chalk2.cyan("https://codmir.com"));
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
const { selectedProject } = await prompts({
|
|
289
|
+
type: "select",
|
|
290
|
+
name: "selectedProject",
|
|
291
|
+
message: "Select a project to link:",
|
|
292
|
+
choices: projects.map((p) => ({
|
|
293
|
+
title: `${p.name} (${p.key})`,
|
|
294
|
+
value: p.id,
|
|
295
|
+
description: p.description || void 0
|
|
296
|
+
}))
|
|
297
|
+
});
|
|
298
|
+
if (!selectedProject) {
|
|
299
|
+
console.log(chalk2.dim(" Cancelled"));
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
projectId = selectedProject;
|
|
303
|
+
}
|
|
304
|
+
const project = projects.find((p) => p.id === projectId);
|
|
305
|
+
if (!project) {
|
|
306
|
+
console.error(chalk2.red("\u274C Project not found"));
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
writeProjectConfig({
|
|
310
|
+
projectId: project.id,
|
|
311
|
+
organizationId: project.organizationId || orgId,
|
|
312
|
+
projectName: project.name
|
|
313
|
+
});
|
|
314
|
+
console.log(chalk2.green("\n\u2705 Successfully linked!"));
|
|
315
|
+
console.log(chalk2.dim(" Project:"), chalk2.bold(project.name));
|
|
316
|
+
console.log(chalk2.dim(" ID:"), chalk2.bold(project.id));
|
|
317
|
+
console.log(chalk2.dim("\n Configuration saved to"), chalk2.cyan(".codmir.json"));
|
|
318
|
+
console.log(chalk2.dim(" You can now use the codmir package in your project"));
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error(chalk2.red("\n\u274C Link failed:"), error instanceof Error ? error.message : "Unknown error");
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/cli/commands/whoami.ts
|
|
326
|
+
import chalk3 from "chalk";
|
|
327
|
+
async function whoamiCommand() {
|
|
328
|
+
if (!isAuthenticated()) {
|
|
329
|
+
console.error(chalk3.red("\u274C Not authenticated"));
|
|
330
|
+
console.log(chalk3.dim(" Run"), chalk3.cyan("codmir login"), chalk3.dim("first"));
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
const config = readConfig();
|
|
334
|
+
console.log(chalk3.bold("\n\u{1F464} Current User\n"));
|
|
335
|
+
console.log(chalk3.dim(" Name:"), chalk3.bold(config.name || "N/A"));
|
|
336
|
+
console.log(chalk3.dim(" Email:"), chalk3.bold(config.email || "N/A"));
|
|
337
|
+
console.log(chalk3.dim(" User ID:"), chalk3.dim(config.userId || "N/A"));
|
|
338
|
+
if (config.lastLogin) {
|
|
339
|
+
const lastLogin = new Date(config.lastLogin);
|
|
340
|
+
console.log(chalk3.dim(" Last Login:"), chalk3.dim(lastLogin.toLocaleString()));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/cli/commands/logout.ts
|
|
345
|
+
import chalk4 from "chalk";
|
|
346
|
+
async function logoutCommand() {
|
|
347
|
+
if (!isAuthenticated()) {
|
|
348
|
+
console.log(chalk4.yellow("\u26A0\uFE0F Not logged in"));
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const config = readConfig();
|
|
352
|
+
clearConfig();
|
|
353
|
+
console.log(chalk4.green("\u2705 Successfully logged out"));
|
|
354
|
+
if (config.email) {
|
|
355
|
+
console.log(chalk4.dim(" Goodbye,"), chalk4.bold(config.email));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/cli/commands/init.ts
|
|
360
|
+
import chalk5 from "chalk";
|
|
361
|
+
import fs2 from "fs";
|
|
362
|
+
import path2 from "path";
|
|
363
|
+
async function initCommand() {
|
|
364
|
+
console.log(chalk5.bold("\n\u{1F389} Initialize codmir\n"));
|
|
365
|
+
const config = readProjectConfig();
|
|
366
|
+
if (!config) {
|
|
367
|
+
console.log(chalk5.yellow("\u26A0\uFE0F Project not linked"));
|
|
368
|
+
console.log(chalk5.dim(" Run"), chalk5.cyan("codmir link"), chalk5.dim("to link this project first"));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
console.log(chalk5.dim(" Project:"), chalk5.bold(config.projectName || config.projectId));
|
|
372
|
+
const exampleFile = path2.join(process.cwd(), "codmir.example.ts");
|
|
373
|
+
if (fs2.existsSync(exampleFile)) {
|
|
374
|
+
console.log(chalk5.yellow("\n\u26A0\uFE0F codmir.example.ts already exists"));
|
|
375
|
+
} else {
|
|
376
|
+
const example = `import { CodmirClient } from 'codmir';
|
|
377
|
+
|
|
378
|
+
// Initialize the client
|
|
379
|
+
const codmir = new CodmirClient({
|
|
380
|
+
apiKey: process.env.CODMIR_API_KEY!, // Get token from: codmir login
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Example: Create a ticket
|
|
384
|
+
async function createTicket() {
|
|
385
|
+
const ticket = await codmir.tickets.create(
|
|
386
|
+
'${config.projectId}',
|
|
387
|
+
'board-id',
|
|
388
|
+
{
|
|
389
|
+
title: 'Example ticket',
|
|
390
|
+
description: 'Created from codmir package',
|
|
391
|
+
priority: 'HIGH',
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
console.log('Created ticket:', ticket.id);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Example: Create a test case
|
|
399
|
+
async function createTestCase() {
|
|
400
|
+
const testCase = await codmir.testCases.create('${config.projectId}', {
|
|
401
|
+
title: 'Example test case',
|
|
402
|
+
suiteId: 'suite-id',
|
|
403
|
+
template: 'steps',
|
|
404
|
+
steps: [
|
|
405
|
+
{ action: 'Do something', expected: 'It works' },
|
|
406
|
+
],
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
console.log('Created test case:', testCase.id);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Run examples
|
|
413
|
+
createTicket().catch(console.error);
|
|
414
|
+
createTestCase().catch(console.error);
|
|
415
|
+
`;
|
|
416
|
+
fs2.writeFileSync(exampleFile, example);
|
|
417
|
+
console.log(chalk5.green("\n\u2705 Created"), chalk5.cyan("codmir.example.ts"));
|
|
418
|
+
}
|
|
419
|
+
console.log(chalk5.bold("\n\u{1F4DA} Next Steps:\n"));
|
|
420
|
+
console.log(chalk5.dim(" 1."), "Install the package:", chalk5.cyan("npm install codmir"));
|
|
421
|
+
console.log(chalk5.dim(" 2."), "Get your API token from:", chalk5.cyan("https://codmir.com/settings/tokens"));
|
|
422
|
+
console.log(chalk5.dim(" 3."), "Set environment variable:", chalk5.cyan("CODMIR_API_KEY=your-token"));
|
|
423
|
+
console.log(chalk5.dim(" 4."), "Check", chalk5.cyan("codmir.example.ts"), "for usage examples");
|
|
424
|
+
console.log(chalk5.dim("\n \u{1F4D6} Documentation:"), chalk5.cyan("https://codmir.com/docs"));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/cli/commands/projects.ts
|
|
428
|
+
import chalk6 from "chalk";
|
|
429
|
+
async function projectsCommand() {
|
|
430
|
+
const token = getToken();
|
|
431
|
+
if (!token) {
|
|
432
|
+
console.error(chalk6.red("\u274C Not authenticated"));
|
|
433
|
+
console.log(chalk6.dim(" Run"), chalk6.cyan("codmir login"), chalk6.dim("first"));
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
console.log(chalk6.bold("\n\u{1F4C1} Your Projects\n"));
|
|
437
|
+
try {
|
|
438
|
+
const response = await fetch(`${process.env.CODMIR_API_URL || "https://codmir.com"}/api/projects`, {
|
|
439
|
+
headers: {
|
|
440
|
+
"Authorization": `Bearer ${token}`
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
if (!response.ok) {
|
|
444
|
+
throw new Error("Failed to fetch projects");
|
|
445
|
+
}
|
|
446
|
+
const projects = await response.json();
|
|
447
|
+
if (projects.length === 0) {
|
|
448
|
+
console.log(chalk6.yellow(" No projects found"));
|
|
449
|
+
console.log(chalk6.dim(" Create one at"), chalk6.cyan("https://codmir.com"));
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
projects.forEach((project, index) => {
|
|
453
|
+
console.log(chalk6.bold(` ${index + 1}. ${project.name}`), chalk6.dim(`(${project.key})`));
|
|
454
|
+
console.log(chalk6.dim(" ID:"), project.id);
|
|
455
|
+
if (project.description) {
|
|
456
|
+
console.log(chalk6.dim(" Description:"), project.description);
|
|
457
|
+
}
|
|
458
|
+
console.log("");
|
|
459
|
+
});
|
|
460
|
+
console.log(chalk6.dim(" Link a project:"), chalk6.cyan("codmir link"));
|
|
461
|
+
} catch (error) {
|
|
462
|
+
console.error(chalk6.red("\n\u274C Failed to fetch projects:"), error instanceof Error ? error.message : "Unknown error");
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/cli/index.ts
|
|
468
|
+
var program = new Command();
|
|
469
|
+
program.name("codmir").description("codmir CLI - the AI that prevents wasted engineering time").version("0.1.0");
|
|
470
|
+
program.command("login").description("Authenticate with codmir").option("--token <token>", "Use an API token directly").action(loginCommand);
|
|
471
|
+
program.command("link").description("Link current directory to a codmir project").option("--project <projectId>", "Project ID to link").option("--org <orgId>", "Organization ID").action(linkCommand);
|
|
472
|
+
program.command("whoami").description("Show current authenticated user").action(whoamiCommand);
|
|
473
|
+
program.command("logout").description("Log out of codmir").action(logoutCommand);
|
|
474
|
+
program.command("init").description("Initialize codmir in your project").action(initCommand);
|
|
475
|
+
program.command("projects").alias("ls").description("List your projects").action(projectsCommand);
|
|
476
|
+
program.parse(process.argv);
|
|
477
|
+
if (!process.argv.slice(2).length) {
|
|
478
|
+
program.outputHelp();
|
|
479
|
+
}
|