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,216 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Server,
|
|
3
|
+
} from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import {
|
|
5
|
+
StdioServerTransport,
|
|
6
|
+
} from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import {
|
|
8
|
+
ListResourcesRequestSchema,
|
|
9
|
+
ReadResourceRequestSchema,
|
|
10
|
+
ListToolsRequestSchema,
|
|
11
|
+
CallToolRequestSchema,
|
|
12
|
+
ErrorCode,
|
|
13
|
+
McpError,
|
|
14
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
15
|
+
import { fetchAPI } from "./api-client.js";
|
|
16
|
+
import {
|
|
17
|
+
getOrganizationResources,
|
|
18
|
+
getEnvironmentResources,
|
|
19
|
+
getAuditLogResources,
|
|
20
|
+
getTokenResources,
|
|
21
|
+
} from "./resources.js";
|
|
22
|
+
import {
|
|
23
|
+
listTools,
|
|
24
|
+
executeTool,
|
|
25
|
+
} from "./tools.js";
|
|
26
|
+
|
|
27
|
+
const server = new Server({
|
|
28
|
+
name: "kernzero-mcp",
|
|
29
|
+
version: "1.0.0",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// List available resources
|
|
33
|
+
server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
|
|
34
|
+
try {
|
|
35
|
+
const apiUrl = process.env.KERN0_API_URL || "https://staging.kernzero.com";
|
|
36
|
+
const token = process.env.KERN0_TOKEN;
|
|
37
|
+
|
|
38
|
+
if (!token) {
|
|
39
|
+
throw new McpError(
|
|
40
|
+
ErrorCode.InvalidRequest,
|
|
41
|
+
"KERN0_TOKEN environment variable is required"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fetch authenticated user and accessible orgs
|
|
46
|
+
const me = await fetchAPI("/api/v1/auth/me", token, apiUrl);
|
|
47
|
+
if (!me.authenticated) {
|
|
48
|
+
throw new McpError(ErrorCode.InvalidRequest, "Authentication failed");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const userId = me.userId;
|
|
52
|
+
const resources = [];
|
|
53
|
+
|
|
54
|
+
// Get organizations
|
|
55
|
+
const orgsResponse = await fetchAPI(
|
|
56
|
+
"/api/v1/orgs",
|
|
57
|
+
token,
|
|
58
|
+
apiUrl
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (Array.isArray(orgsResponse)) {
|
|
62
|
+
for (const org of orgsResponse) {
|
|
63
|
+
resources.push(...getOrganizationResources(org));
|
|
64
|
+
resources.push(...getEnvironmentResources(org.id));
|
|
65
|
+
resources.push(...getAuditLogResources(org.id));
|
|
66
|
+
resources.push(...getTokenResources(org.id));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { resources };
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new McpError(
|
|
73
|
+
ErrorCode.InternalError,
|
|
74
|
+
`Failed to list resources: ${error}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Read resource
|
|
80
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
81
|
+
try {
|
|
82
|
+
const apiUrl = process.env.KERN0_API_URL || "https://staging.kernzero.com";
|
|
83
|
+
const token = process.env.KERN0_TOKEN;
|
|
84
|
+
|
|
85
|
+
if (!token) {
|
|
86
|
+
throw new McpError(
|
|
87
|
+
ErrorCode.InvalidRequest,
|
|
88
|
+
"KERN0_TOKEN environment variable is required"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const uri = request.params.uri;
|
|
93
|
+
|
|
94
|
+
// Parse URI format: kernzero://resource-type/id
|
|
95
|
+
const match = uri.match(/kernzero:\/\/([^/]+)\/(.+)/);
|
|
96
|
+
if (!match) {
|
|
97
|
+
throw new McpError(ErrorCode.InvalidRequest, `Invalid URI format: ${uri}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const [, resourceType, resourceId] = match;
|
|
101
|
+
|
|
102
|
+
let content = "";
|
|
103
|
+
switch (resourceType) {
|
|
104
|
+
case "organization": {
|
|
105
|
+
const org = await fetchAPI(
|
|
106
|
+
`/api/v1/orgs/${resourceId}`,
|
|
107
|
+
token,
|
|
108
|
+
apiUrl
|
|
109
|
+
);
|
|
110
|
+
content = JSON.stringify(org, null, 2);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case "environments": {
|
|
115
|
+
const envs = await fetchAPI(
|
|
116
|
+
`/api/v1/orgs/${resourceId}/environments`,
|
|
117
|
+
token,
|
|
118
|
+
apiUrl
|
|
119
|
+
);
|
|
120
|
+
content = JSON.stringify(envs, null, 2);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case "tokens": {
|
|
125
|
+
const tokens = await fetchAPI(
|
|
126
|
+
`/api/v1/orgs/${resourceId}/tokens`,
|
|
127
|
+
token,
|
|
128
|
+
apiUrl
|
|
129
|
+
);
|
|
130
|
+
content = JSON.stringify(tokens, null, 2);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
case "audit-logs": {
|
|
135
|
+
const logs = await fetchAPI(
|
|
136
|
+
`/api/v1/orgs/${resourceId}/audit-logs`,
|
|
137
|
+
token,
|
|
138
|
+
apiUrl
|
|
139
|
+
);
|
|
140
|
+
content = JSON.stringify(logs, null, 2);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
default:
|
|
145
|
+
throw new McpError(
|
|
146
|
+
ErrorCode.InvalidRequest,
|
|
147
|
+
`Unknown resource type: ${resourceType}`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
contents: [
|
|
153
|
+
{
|
|
154
|
+
uri,
|
|
155
|
+
mimeType: "application/json",
|
|
156
|
+
text: content,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new McpError(
|
|
162
|
+
ErrorCode.InternalError,
|
|
163
|
+
`Failed to read resource: ${error}`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// List tools
|
|
169
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
170
|
+
return { tools: listTools() };
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Call tool
|
|
174
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
175
|
+
try {
|
|
176
|
+
const apiUrl = process.env.KERN0_API_URL || "https://staging.kernzero.com";
|
|
177
|
+
const token = process.env.KERN0_TOKEN;
|
|
178
|
+
|
|
179
|
+
if (!token) {
|
|
180
|
+
throw new McpError(
|
|
181
|
+
ErrorCode.InvalidRequest,
|
|
182
|
+
"KERN0_TOKEN environment variable is required"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const result = await executeTool(
|
|
187
|
+
request.params.name,
|
|
188
|
+
request.params.arguments || {},
|
|
189
|
+
token,
|
|
190
|
+
apiUrl
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
content: [
|
|
195
|
+
{
|
|
196
|
+
type: "text",
|
|
197
|
+
text: JSON.stringify(result, null, 2),
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
} catch (error) {
|
|
202
|
+
throw new McpError(
|
|
203
|
+
ErrorCode.InternalError,
|
|
204
|
+
`Tool execution failed: ${error}`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Start server
|
|
210
|
+
async function main() {
|
|
211
|
+
const transport = new StdioServerTransport();
|
|
212
|
+
await server.connect(transport);
|
|
213
|
+
console.error("KernZero MCP server started");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
main().catch(console.error);
|
package/lib/mcp/tools.ts
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool definitions for KernZero
|
|
3
|
+
* Tools represent actions that Claude can execute
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import { fetchAPI, callAPI } from "./api-client.js";
|
|
8
|
+
|
|
9
|
+
export function listTools(): Tool[] {
|
|
10
|
+
return [
|
|
11
|
+
{
|
|
12
|
+
name: "get_user_context",
|
|
13
|
+
description: "Get current authenticated user and organization context",
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {},
|
|
17
|
+
required: [],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: "list_organizations",
|
|
23
|
+
description: "List all organizations the user has access to",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {},
|
|
27
|
+
required: [],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
name: "get_organization_details",
|
|
33
|
+
description: "Get detailed information about a specific organization",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
orgId: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Organization ID",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: ["orgId"],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
name: "list_environments",
|
|
48
|
+
description:
|
|
49
|
+
"List all monitored environments in an organization with their status",
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
orgId: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Organization ID",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ["orgId"],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
{
|
|
63
|
+
name: "get_environment_status",
|
|
64
|
+
description: "Get detailed status and health metrics for an environment",
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
orgId: {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "Organization ID",
|
|
71
|
+
},
|
|
72
|
+
envId: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "Environment ID",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
required: ["orgId", "envId"],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
{
|
|
82
|
+
name: "list_audit_logs",
|
|
83
|
+
description:
|
|
84
|
+
"Get audit trail showing who performed what actions and when",
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: "object",
|
|
87
|
+
properties: {
|
|
88
|
+
orgId: {
|
|
89
|
+
type: "string",
|
|
90
|
+
description: "Organization ID",
|
|
91
|
+
},
|
|
92
|
+
limit: {
|
|
93
|
+
type: "number",
|
|
94
|
+
description: "Number of logs to retrieve (default: 50)",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
required: ["orgId"],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
{
|
|
102
|
+
name: "list_tokens",
|
|
103
|
+
description: "List Personal Access Tokens and their scopes",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: {
|
|
107
|
+
orgId: {
|
|
108
|
+
type: "string",
|
|
109
|
+
description: "Organization ID",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
required: ["orgId"],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
{
|
|
117
|
+
name: "analyze_readiness",
|
|
118
|
+
description:
|
|
119
|
+
"Analyze recovery readiness for an organization based on environment status",
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: "object",
|
|
122
|
+
properties: {
|
|
123
|
+
orgId: {
|
|
124
|
+
type: "string",
|
|
125
|
+
description: "Organization ID to analyze",
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
required: ["orgId"],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
name: "check_backup_health",
|
|
134
|
+
description: "Check backup health status for all environments",
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: "object",
|
|
137
|
+
properties: {
|
|
138
|
+
orgId: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Organization ID",
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
required: ["orgId"],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function executeTool(
|
|
150
|
+
name: string,
|
|
151
|
+
args: Record<string, any>,
|
|
152
|
+
token: string,
|
|
153
|
+
apiUrl: string
|
|
154
|
+
): Promise<any> {
|
|
155
|
+
switch (name) {
|
|
156
|
+
case "get_user_context": {
|
|
157
|
+
return await fetchAPI("/api/v1/auth/me", token, apiUrl);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
case "list_organizations": {
|
|
161
|
+
const orgs = await fetchAPI("/api/v1/orgs", token, apiUrl);
|
|
162
|
+
return Array.isArray(orgs) ? orgs : orgs.organizations || [];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
case "get_organization_details": {
|
|
166
|
+
if (!args.orgId) throw new Error("orgId is required");
|
|
167
|
+
return await fetchAPI(`/api/v1/orgs/${args.orgId}`, token, apiUrl);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
case "list_environments": {
|
|
171
|
+
if (!args.orgId) throw new Error("orgId is required");
|
|
172
|
+
return await fetchAPI(
|
|
173
|
+
`/api/v1/orgs/${args.orgId}/environments`,
|
|
174
|
+
token,
|
|
175
|
+
apiUrl
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
case "get_environment_status": {
|
|
180
|
+
if (!args.orgId || !args.envId)
|
|
181
|
+
throw new Error("orgId and envId are required");
|
|
182
|
+
const envs = await fetchAPI(
|
|
183
|
+
`/api/v1/orgs/${args.orgId}/environments`,
|
|
184
|
+
token,
|
|
185
|
+
apiUrl
|
|
186
|
+
);
|
|
187
|
+
const env = (envs.environments || []).find(
|
|
188
|
+
(e: any) => e.id === args.envId
|
|
189
|
+
);
|
|
190
|
+
return env || { error: "Environment not found" };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case "list_audit_logs": {
|
|
194
|
+
if (!args.orgId) throw new Error("orgId is required");
|
|
195
|
+
return await fetchAPI(
|
|
196
|
+
`/api/v1/orgs/${args.orgId}/audit-logs?limit=${args.limit || 50}`,
|
|
197
|
+
token,
|
|
198
|
+
apiUrl
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
case "list_tokens": {
|
|
203
|
+
if (!args.orgId) throw new Error("orgId is required");
|
|
204
|
+
return await fetchAPI(`/api/v1/orgs/${args.orgId}/tokens`, token, apiUrl);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case "analyze_readiness": {
|
|
208
|
+
if (!args.orgId) throw new Error("orgId is required");
|
|
209
|
+
const envs = await fetchAPI(
|
|
210
|
+
`/api/v1/orgs/${args.orgId}/environments`,
|
|
211
|
+
token,
|
|
212
|
+
apiUrl
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const environments = envs.environments || [];
|
|
216
|
+
const onlineCount = environments.filter(
|
|
217
|
+
(e: any) => e.status === "online"
|
|
218
|
+
).length;
|
|
219
|
+
const offlineCount = environments.filter(
|
|
220
|
+
(e: any) => e.status === "offline"
|
|
221
|
+
).length;
|
|
222
|
+
const validBackups = environments.filter(
|
|
223
|
+
(e: any) => e.hasValidBackup || e.hasValidBackupByPeer
|
|
224
|
+
).length;
|
|
225
|
+
|
|
226
|
+
const readiness = environments.length > 0
|
|
227
|
+
? Math.round(
|
|
228
|
+
((validBackups / environments.length) * 100 +
|
|
229
|
+
(onlineCount / Math.max(1, environments.length)) * 50) / 1.5
|
|
230
|
+
)
|
|
231
|
+
: 0;
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
totalEnvironments: environments.length,
|
|
235
|
+
onlineCount,
|
|
236
|
+
offlineCount,
|
|
237
|
+
validBackupsCount: validBackups,
|
|
238
|
+
readinessPercentage: readiness,
|
|
239
|
+
assessment:
|
|
240
|
+
readiness >= 80
|
|
241
|
+
? "READY - High recovery readiness"
|
|
242
|
+
: readiness >= 60
|
|
243
|
+
? "PARTIAL - Some gaps to address"
|
|
244
|
+
: readiness >= 40
|
|
245
|
+
? "AT RISK - Multiple issues"
|
|
246
|
+
: "CRITICAL - Immediate attention needed",
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
case "check_backup_health": {
|
|
251
|
+
if (!args.orgId) throw new Error("orgId is required");
|
|
252
|
+
const envs = await fetchAPI(
|
|
253
|
+
`/api/v1/orgs/${args.orgId}/environments`,
|
|
254
|
+
token,
|
|
255
|
+
apiUrl
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const environments = envs.environments || [];
|
|
259
|
+
const backupStatus = environments.map((env: any) => ({
|
|
260
|
+
environment: env.label,
|
|
261
|
+
status: env.status,
|
|
262
|
+
hasValidBackup: env.hasValidBackup,
|
|
263
|
+
hasValidBackupByPeer: env.hasValidBackupByPeer,
|
|
264
|
+
lastBackupTime: env.lastBackupTime,
|
|
265
|
+
backupHealth:
|
|
266
|
+
env.hasValidBackup || env.hasValidBackupByPeer ? "✅ HEALTHY" : "⚠️ NEEDS BACKUP",
|
|
267
|
+
}));
|
|
268
|
+
|
|
269
|
+
const unhealthy = backupStatus.filter(
|
|
270
|
+
(b: any) => b.backupHealth !== "✅ HEALTHY"
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
totalEnvironments: environments.length,
|
|
275
|
+
healthyBackups: backupStatus.length - unhealthy.length,
|
|
276
|
+
unhealthyBackups: unhealthy.length,
|
|
277
|
+
details: backupStatus,
|
|
278
|
+
summary:
|
|
279
|
+
unhealthy.length === 0
|
|
280
|
+
? "All backups are healthy"
|
|
281
|
+
: `${unhealthy.length} environment(s) need backup attention`,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
default:
|
|
286
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kern0-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "KernZero CLI - Recovery control plane management from the terminal",
|
|
5
|
+
"private": false,
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "KernZero",
|
|
8
|
+
"homepage": "https://kernzero.com",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/santorres/kern0.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"recovery",
|
|
15
|
+
"cli",
|
|
16
|
+
"backup",
|
|
17
|
+
"disaster-recovery",
|
|
18
|
+
"active-directory",
|
|
19
|
+
"zero-trust",
|
|
20
|
+
"passkeys",
|
|
21
|
+
"device-flow"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"kern0": "./bin/kern0",
|
|
25
|
+
"kern0-mcp": "./bin/kern0-mcp"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"lib/cli",
|
|
29
|
+
"lib/mcp",
|
|
30
|
+
"bin",
|
|
31
|
+
"package.json"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"type": "module",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"dev": "next dev",
|
|
39
|
+
"build": "next build",
|
|
40
|
+
"start": "next start",
|
|
41
|
+
"lint": "eslint",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"firebase:init": "node scripts/bootstrap.mjs",
|
|
44
|
+
"firebase:probe": "node scripts/probe.mjs",
|
|
45
|
+
"seed:v1": "node scripts/seed-v1-samples.mjs",
|
|
46
|
+
"patch:iroh": "node scripts/patch-iroh.mjs",
|
|
47
|
+
"postinstall": "node scripts/patch-iroh.mjs"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@aws-sdk/client-s3": "^3.1127.0",
|
|
51
|
+
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
52
|
+
"@number0/iroh": "^1.1.0",
|
|
53
|
+
"@simplewebauthn/browser": "^14.0.0",
|
|
54
|
+
"@simplewebauthn/server": "^14.0.3",
|
|
55
|
+
"@types/qrcode": "^1.5.6",
|
|
56
|
+
"firebase": "^12.18.0",
|
|
57
|
+
"firebase-admin": "^14.5.0",
|
|
58
|
+
"lucide-react": "^1.41.0",
|
|
59
|
+
"next": "16.3.4",
|
|
60
|
+
"open": "^10.0.0",
|
|
61
|
+
"qrcode": "^1.5.4",
|
|
62
|
+
"react": "19.2.8",
|
|
63
|
+
"react-dom": "19.2.8"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@tailwindcss/postcss": "^4",
|
|
67
|
+
"@types/node": "^20",
|
|
68
|
+
"@types/react": "^19",
|
|
69
|
+
"@types/react-dom": "^19",
|
|
70
|
+
"eslint": "^9",
|
|
71
|
+
"eslint-config-next": "16.3.4",
|
|
72
|
+
"tailwindcss": "^4",
|
|
73
|
+
"tsx": "^4.23.15",
|
|
74
|
+
"typescript": "^5"
|
|
75
|
+
}
|
|
76
|
+
}
|