kern0-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -0
- package/README.npm.md +137 -0
- package/bin/kern0 +23 -0
- package/bin/kern0-mcp +2 -0
- package/lib/cli/api.ts +208 -0
- package/lib/cli/auth-flow.ts +155 -0
- package/lib/cli/commands/auth.ts +126 -0
- package/lib/cli/commands/customers.ts +68 -0
- package/lib/cli/commands/environments.ts +74 -0
- package/lib/cli/commands/org.ts +130 -0
- package/lib/cli/commands/tokens.ts +124 -0
- package/lib/cli/config.ts +257 -0
- package/lib/cli/index.ts +222 -0
- package/lib/mcp/api-client.ts +76 -0
- package/lib/mcp/resources.ts +55 -0
- package/lib/mcp/server.ts +216 -0
- package/lib/mcp/tools.ts +288 -0
- package/package.json +76 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setCurrentOrg,
|
|
3
|
+
deleteToken,
|
|
4
|
+
setUser,
|
|
5
|
+
setAuth,
|
|
6
|
+
setOrganizations,
|
|
7
|
+
printContext,
|
|
8
|
+
isAuthenticated,
|
|
9
|
+
getUser,
|
|
10
|
+
} from "../config";
|
|
11
|
+
import {
|
|
12
|
+
initializeDeviceFlow,
|
|
13
|
+
pollForToken,
|
|
14
|
+
completeLogin,
|
|
15
|
+
} from "../auth-flow";
|
|
16
|
+
import open from "open";
|
|
17
|
+
|
|
18
|
+
export async function login(): Promise<void> {
|
|
19
|
+
console.log("\nš Kern0 Login\n");
|
|
20
|
+
|
|
21
|
+
// Check if already authenticated
|
|
22
|
+
if (isAuthenticated()) {
|
|
23
|
+
const user = getUser();
|
|
24
|
+
console.log(`Already authenticated as ${user?.email}.\n`);
|
|
25
|
+
console.log(`Run 'kern0 logout' if you want to log in as a different user.\n`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
console.log("ā³ Initializing device flow...\n");
|
|
31
|
+
|
|
32
|
+
// Step 1: Initialize device flow
|
|
33
|
+
const deviceFlow = await initializeDeviceFlow();
|
|
34
|
+
|
|
35
|
+
console.log("ā
Device code generated!\n");
|
|
36
|
+
console.log("š± To authenticate:\n");
|
|
37
|
+
console.log(` 1. Go to: ${deviceFlow.verification_uri}`);
|
|
38
|
+
console.log(` 2. Enter code: ${deviceFlow.user_code}\n`);
|
|
39
|
+
console.log("ā³ Waiting for approval in browser...\n");
|
|
40
|
+
|
|
41
|
+
// Try to open browser automatically
|
|
42
|
+
try {
|
|
43
|
+
await open(deviceFlow.verification_uri);
|
|
44
|
+
console.log("š Browser opened automatically\n");
|
|
45
|
+
} catch {
|
|
46
|
+
console.log("š Copy the link above to open in your browser\n");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Step 2: Poll for token
|
|
50
|
+
const tokenResponse = await pollForToken(deviceFlow.device_code);
|
|
51
|
+
|
|
52
|
+
console.log("ā
Approved!\n");
|
|
53
|
+
console.log("ā³ Completing login...\n");
|
|
54
|
+
|
|
55
|
+
// Step 3: Complete login (fetch user, orgs, save config)
|
|
56
|
+
const { user, organizations } = await completeLogin(
|
|
57
|
+
tokenResponse.access_token,
|
|
58
|
+
tokenResponse.expires_in,
|
|
59
|
+
tokenResponse.user,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
console.log("ā
Login successful!\n");
|
|
63
|
+
console.log(`š¤ User: ${user.email}\n`);
|
|
64
|
+
|
|
65
|
+
// Step 4: Select organization
|
|
66
|
+
if (organizations.length === 0) {
|
|
67
|
+
console.log("ā ļø No organizations found. Create one with 'kern0 org create'.\n");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (organizations.length === 1) {
|
|
72
|
+
// Auto-select if only one org
|
|
73
|
+
setCurrentOrg(organizations[0].id);
|
|
74
|
+
console.log(`š¢ Organization: ${organizations[0].name}\n`);
|
|
75
|
+
} else {
|
|
76
|
+
// Show org selection
|
|
77
|
+
console.log("š¢ Available Organizations:\n");
|
|
78
|
+
organizations.forEach((org, index) => {
|
|
79
|
+
console.log(` ${index + 1}. ${org.name}`);
|
|
80
|
+
});
|
|
81
|
+
console.log(
|
|
82
|
+
`\nš Run 'kern0 org select <orgId>' to switch organizations\n`,
|
|
83
|
+
);
|
|
84
|
+
console.log(`Selecting first organization: ${organizations[0].name}\n`);
|
|
85
|
+
setCurrentOrg(organizations[0].id);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log("š Ready to use Kern0!\n");
|
|
89
|
+
printContext();
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error instanceof Error) {
|
|
92
|
+
console.error("ā Login failed:", error.message);
|
|
93
|
+
}
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function logout(): Promise<void> {
|
|
99
|
+
console.log("\nš Logging out...\n");
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Clear credentials
|
|
103
|
+
deleteToken();
|
|
104
|
+
|
|
105
|
+
// Clear auth state
|
|
106
|
+
setAuth({
|
|
107
|
+
device_token: "",
|
|
108
|
+
expires_at: 0,
|
|
109
|
+
token_type: "device",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Clear user
|
|
113
|
+
setUser({ id: "", email: "" });
|
|
114
|
+
|
|
115
|
+
// Clear organizations
|
|
116
|
+
setOrganizations([]);
|
|
117
|
+
|
|
118
|
+
console.log("ā
Logged out successfully\n");
|
|
119
|
+
console.log("Run 'kern0 login' to authenticate again\n");
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (error instanceof Error) {
|
|
122
|
+
console.error("ā Logout failed:", error.message);
|
|
123
|
+
}
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireAuthentication,
|
|
3
|
+
requireOrganization,
|
|
4
|
+
getCurrentCustomer,
|
|
5
|
+
setCurrentCustomer,
|
|
6
|
+
printContext,
|
|
7
|
+
} from "../config";
|
|
8
|
+
import {
|
|
9
|
+
listCustomers,
|
|
10
|
+
} from "../api";
|
|
11
|
+
|
|
12
|
+
export async function listCustomersCmd(): Promise<void> {
|
|
13
|
+
try {
|
|
14
|
+
requireAuthentication();
|
|
15
|
+
const orgId = requireOrganization();
|
|
16
|
+
|
|
17
|
+
console.log("\nš¢ Customers:\n");
|
|
18
|
+
|
|
19
|
+
const customers = await listCustomers(orgId);
|
|
20
|
+
const currentCustomerId = getCurrentCustomer();
|
|
21
|
+
|
|
22
|
+
if (customers.length === 0) {
|
|
23
|
+
console.log(" No customers found.\n");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(" ID Name");
|
|
28
|
+
console.log(" " + "ā".repeat(50));
|
|
29
|
+
|
|
30
|
+
customers.forEach(customer => {
|
|
31
|
+
const marker = customer.id === currentCustomerId ? "* " : " ";
|
|
32
|
+
const idDisplay = customer.id.padEnd(25);
|
|
33
|
+
console.log(` ${marker}${idDisplay} ${customer.name}`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.log("");
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (error instanceof Error) {
|
|
39
|
+
console.error("Error:", error.message);
|
|
40
|
+
}
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function switchCustomerCmd(customerId: string): Promise<void> {
|
|
46
|
+
try {
|
|
47
|
+
requireAuthentication();
|
|
48
|
+
const orgId = requireOrganization();
|
|
49
|
+
|
|
50
|
+
const customers = await listCustomers(orgId);
|
|
51
|
+
const customer = customers.find(c => c.id === customerId);
|
|
52
|
+
|
|
53
|
+
if (!customer) {
|
|
54
|
+
console.error(`\nā Customer "${customerId}" not found in current organization.\n`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setCurrentCustomer(customerId);
|
|
59
|
+
|
|
60
|
+
console.log(`\nā
Switched to: ${customer.name}\n`);
|
|
61
|
+
printContext();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error instanceof Error) {
|
|
64
|
+
console.error("Error:", error.message);
|
|
65
|
+
}
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireAuthentication,
|
|
3
|
+
requireOrganization,
|
|
4
|
+
} from "../config";
|
|
5
|
+
import { apiCall } from "../api";
|
|
6
|
+
|
|
7
|
+
export interface Environment {
|
|
8
|
+
id: string;
|
|
9
|
+
customerId: string;
|
|
10
|
+
customerName: string;
|
|
11
|
+
label: string;
|
|
12
|
+
status: string;
|
|
13
|
+
product: string;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function listEnvironments(): Promise<void> {
|
|
18
|
+
try {
|
|
19
|
+
requireAuthentication();
|
|
20
|
+
const orgId = requireOrganization();
|
|
21
|
+
|
|
22
|
+
console.log("\nšļø Environments:\n");
|
|
23
|
+
|
|
24
|
+
const response = await apiCall<{ environments: Environment[] }>(
|
|
25
|
+
"GET",
|
|
26
|
+
`/api/v1/orgs/${orgId}/environments`,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const environments = response.environments || [];
|
|
30
|
+
|
|
31
|
+
if (environments.length === 0) {
|
|
32
|
+
console.log(" No environments found.\n");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(" Label Status Customer Product Updated");
|
|
37
|
+
console.log(" " + "ā".repeat(100));
|
|
38
|
+
|
|
39
|
+
environments.forEach((env) => {
|
|
40
|
+
const label = env.label.substring(0, 28).padEnd(28);
|
|
41
|
+
const status = (env.status || "unknown").substring(0, 12).padEnd(12);
|
|
42
|
+
const customer = (env.customerName || env.customerId)
|
|
43
|
+
.substring(0, 24)
|
|
44
|
+
.padEnd(24);
|
|
45
|
+
const product = (env.product || "unknown").substring(0, 8).padEnd(8);
|
|
46
|
+
const updated = new Date(env.updatedAt).toLocaleDateString();
|
|
47
|
+
|
|
48
|
+
console.log(` ${label}${status}${customer}${product}${updated}`);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
console.log("");
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error instanceof Error) {
|
|
54
|
+
console.error("Error:", error.message);
|
|
55
|
+
}
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function getEnvironmentInfo(envId: string): Promise<void> {
|
|
61
|
+
try {
|
|
62
|
+
requireAuthentication();
|
|
63
|
+
const orgId = requireOrganization();
|
|
64
|
+
|
|
65
|
+
// TODO: Implement env info endpoint
|
|
66
|
+
console.error("Not yet implemented");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error instanceof Error) {
|
|
70
|
+
console.error("Error:", error.message);
|
|
71
|
+
}
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireAuthentication,
|
|
3
|
+
requireOrganization,
|
|
4
|
+
getCurrentOrg,
|
|
5
|
+
setCurrentOrg,
|
|
6
|
+
getOrganizations,
|
|
7
|
+
setOrganizations,
|
|
8
|
+
addOrganization,
|
|
9
|
+
printContext,
|
|
10
|
+
} from "../config";
|
|
11
|
+
import {
|
|
12
|
+
listOrganizations,
|
|
13
|
+
getOrganization,
|
|
14
|
+
createOrganization as apiCreateOrganization,
|
|
15
|
+
} from "../api";
|
|
16
|
+
|
|
17
|
+
export async function listOrgs(): Promise<void> {
|
|
18
|
+
try {
|
|
19
|
+
requireAuthentication();
|
|
20
|
+
|
|
21
|
+
console.log("\nš¦ Your Organizations:\n");
|
|
22
|
+
|
|
23
|
+
const orgs = await listOrganizations();
|
|
24
|
+
const currentOrgId = getCurrentOrg();
|
|
25
|
+
|
|
26
|
+
if (orgs.length === 0) {
|
|
27
|
+
console.log(" No organizations found. Run `kern0 org create` to create one.\n");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Update config with fetched orgs
|
|
32
|
+
const orgData = orgs.map(o => ({ id: o.id, name: o.name }));
|
|
33
|
+
setOrganizations(orgData);
|
|
34
|
+
|
|
35
|
+
orgs.forEach(org => {
|
|
36
|
+
if (!org.id || !org.name) return; // Skip orgs without proper data
|
|
37
|
+
const marker = org.id === currentOrgId ? "* " : " ";
|
|
38
|
+
const type = org.type === "msp" ? " (MSP)" : "";
|
|
39
|
+
console.log(` ${marker}${org.id.substring(0, 8)}... ${org.name}${type}`);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
console.log("");
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error instanceof Error) {
|
|
45
|
+
console.error("Error:", error.message);
|
|
46
|
+
}
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function selectOrg(orgId: string): Promise<void> {
|
|
52
|
+
try {
|
|
53
|
+
requireAuthentication();
|
|
54
|
+
|
|
55
|
+
// Verify org exists by fetching it
|
|
56
|
+
const org = await getOrganization(orgId);
|
|
57
|
+
|
|
58
|
+
// Update current org
|
|
59
|
+
setCurrentOrg(orgId);
|
|
60
|
+
|
|
61
|
+
// Add to organizations list if not already there
|
|
62
|
+
addOrganization({ id: org.id, name: org.name });
|
|
63
|
+
|
|
64
|
+
console.log(`\nā
Switched to: ${org.name}\n`);
|
|
65
|
+
printContext();
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (error instanceof Error) {
|
|
68
|
+
console.error("Error:", error.message);
|
|
69
|
+
}
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function getOrgInfo(): Promise<void> {
|
|
75
|
+
try {
|
|
76
|
+
requireAuthentication();
|
|
77
|
+
const orgId = requireOrganization();
|
|
78
|
+
|
|
79
|
+
const org = await getOrganization(orgId);
|
|
80
|
+
|
|
81
|
+
console.log("\nš Organization Details:\n");
|
|
82
|
+
console.log(` ID: ${org.id}`);
|
|
83
|
+
console.log(` Name: ${org.name}`);
|
|
84
|
+
console.log(` Type: ${org.type === "msp" ? "MSP (Managed Services Provider)" : "Direct"}`);
|
|
85
|
+
console.log(` Created: ${new Date(org.createdAt).toLocaleDateString()}`);
|
|
86
|
+
console.log("");
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (error instanceof Error) {
|
|
89
|
+
console.error("Error:", error.message);
|
|
90
|
+
}
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function createOrg(
|
|
96
|
+
name: string,
|
|
97
|
+
type: "direct" | "msp" = "direct",
|
|
98
|
+
): Promise<void> {
|
|
99
|
+
try {
|
|
100
|
+
requireAuthentication();
|
|
101
|
+
|
|
102
|
+
console.log(`\nā³ Creating organization "${name}"...`);
|
|
103
|
+
|
|
104
|
+
const org = await apiCreateOrganization(name, type);
|
|
105
|
+
|
|
106
|
+
// Add to organizations list
|
|
107
|
+
addOrganization({ id: org.id, name: org.name });
|
|
108
|
+
|
|
109
|
+
// Set as current org if it's the first one
|
|
110
|
+
const currentOrgId = getCurrentOrg();
|
|
111
|
+
if (!currentOrgId) {
|
|
112
|
+
setCurrentOrg(org.id);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(`ā
Organization created!\n`);
|
|
116
|
+
console.log(` ID: ${org.id}`);
|
|
117
|
+
console.log(` Name: ${org.name}`);
|
|
118
|
+
console.log(` Type: ${org.type}\n`);
|
|
119
|
+
|
|
120
|
+
if (!currentOrgId) {
|
|
121
|
+
console.log(`š Set as current organization\n`);
|
|
122
|
+
printContext();
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (error instanceof Error) {
|
|
126
|
+
console.error("Error:", error.message);
|
|
127
|
+
}
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
requireAuthentication,
|
|
3
|
+
requireOrganization,
|
|
4
|
+
} from "../config";
|
|
5
|
+
import {
|
|
6
|
+
listTokens,
|
|
7
|
+
createToken,
|
|
8
|
+
revokeToken,
|
|
9
|
+
} from "../api";
|
|
10
|
+
|
|
11
|
+
export async function listTokensCmd(): Promise<void> {
|
|
12
|
+
try {
|
|
13
|
+
requireAuthentication();
|
|
14
|
+
const orgId = requireOrganization();
|
|
15
|
+
|
|
16
|
+
console.log("\nš API Tokens:\n");
|
|
17
|
+
|
|
18
|
+
const tokens = await listTokens(orgId);
|
|
19
|
+
|
|
20
|
+
if (tokens.length === 0) {
|
|
21
|
+
console.log(" No tokens found. Run `kern0 tokens create` to create one.\n");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log(" ID Name Scopes Expires");
|
|
26
|
+
console.log(" " + "ā".repeat(95));
|
|
27
|
+
|
|
28
|
+
tokens.forEach(token => {
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
const expiresIn = token.expiresAt - now;
|
|
31
|
+
const daysLeft = Math.ceil(expiresIn / (1000 * 60 * 60 * 24));
|
|
32
|
+
|
|
33
|
+
let expireStatus = `${daysLeft}d`;
|
|
34
|
+
if (expiresIn < 0) {
|
|
35
|
+
expireStatus = "EXPIRED";
|
|
36
|
+
} else if (daysLeft <= 7) {
|
|
37
|
+
expireStatus = `${daysLeft}d ā ļø`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const scopes = token.scopes.join(", ");
|
|
41
|
+
const id = token.id.substring(0, 10);
|
|
42
|
+
const nameDisplay = token.name.substring(0, 26).padEnd(26);
|
|
43
|
+
const scopesDisplay = scopes.substring(0, 28).padEnd(28);
|
|
44
|
+
|
|
45
|
+
console.log(` ${id} ${nameDisplay} ${scopesDisplay} ${expireStatus}`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
console.log("");
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error instanceof Error) {
|
|
51
|
+
console.error("Error:", error.message);
|
|
52
|
+
}
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function createTokenCmd(
|
|
58
|
+
name: string,
|
|
59
|
+
scopes?: string[],
|
|
60
|
+
customerIds?: string[],
|
|
61
|
+
expiresIn?: number,
|
|
62
|
+
): Promise<void> {
|
|
63
|
+
try {
|
|
64
|
+
requireAuthentication();
|
|
65
|
+
const orgId = requireOrganization();
|
|
66
|
+
|
|
67
|
+
console.log(`\nā³ Creating token "${name}"...`);
|
|
68
|
+
|
|
69
|
+
const defaultScopes = scopes || ["read:readiness", "read:dependencies"];
|
|
70
|
+
const defaultExpires = expiresIn || 2592000; // 30 days
|
|
71
|
+
|
|
72
|
+
const token = await createToken(
|
|
73
|
+
orgId,
|
|
74
|
+
name,
|
|
75
|
+
defaultScopes,
|
|
76
|
+
customerIds,
|
|
77
|
+
defaultExpires,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
console.log(`\nā
Token created!\n`);
|
|
81
|
+
console.log(` ID: ${token.id}`);
|
|
82
|
+
console.log(` Name: ${token.name}`);
|
|
83
|
+
console.log(` Scopes: ${token.scopes.join(", ")}`);
|
|
84
|
+
console.log(` Expires: ${new Date(token.expiresAt).toLocaleDateString()}`);
|
|
85
|
+
console.log("");
|
|
86
|
+
console.log(" ā ļø Token value (shown only once):");
|
|
87
|
+
console.log(` ${token.token}\n`);
|
|
88
|
+
console.log(" š¾ Save this token somewhere safe. You won't be able to see it again!\n");
|
|
89
|
+
|
|
90
|
+
// Try to copy to clipboard if available
|
|
91
|
+
try {
|
|
92
|
+
const clipboardy = require("clipboardy");
|
|
93
|
+
if (clipboardy) {
|
|
94
|
+
clipboardy.writeSync(token.token);
|
|
95
|
+
console.log(" š Token copied to clipboard\n");
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
// Clipboard not available, that's ok
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error instanceof Error) {
|
|
102
|
+
console.error("Error:", error.message);
|
|
103
|
+
}
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function revokeTokenCmd(tokenId: string): Promise<void> {
|
|
109
|
+
try {
|
|
110
|
+
requireAuthentication();
|
|
111
|
+
const orgId = requireOrganization();
|
|
112
|
+
|
|
113
|
+
console.log(`\nā³ Revoking token ${tokenId.substring(0, 10)}...`);
|
|
114
|
+
|
|
115
|
+
await revokeToken(orgId, tokenId);
|
|
116
|
+
|
|
117
|
+
console.log(`ā
Token revoked!\n`);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (error instanceof Error) {
|
|
120
|
+
console.error("Error:", error.message);
|
|
121
|
+
}
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
}
|