servherd 0.0.1 → 1.0.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/CONTRIBUTING.md +250 -0
- package/LICENSE +21 -0
- package/README.md +653 -29
- package/dist/cli/commands/config.d.ts +35 -0
- package/dist/cli/commands/config.js +336 -0
- package/dist/cli/commands/info.d.ts +37 -0
- package/dist/cli/commands/info.js +98 -0
- package/dist/cli/commands/list.d.ts +26 -0
- package/dist/cli/commands/list.js +86 -0
- package/dist/cli/commands/logs.d.ts +46 -0
- package/dist/cli/commands/logs.js +292 -0
- package/dist/cli/commands/mcp.d.ts +5 -0
- package/dist/cli/commands/mcp.js +17 -0
- package/dist/cli/commands/refresh.d.ts +20 -0
- package/dist/cli/commands/refresh.js +139 -0
- package/dist/cli/commands/remove.d.ts +20 -0
- package/dist/cli/commands/remove.js +144 -0
- package/dist/cli/commands/restart.d.ts +25 -0
- package/dist/cli/commands/restart.js +177 -0
- package/dist/cli/commands/start.d.ts +37 -0
- package/dist/cli/commands/start.js +293 -0
- package/dist/cli/commands/stop.d.ts +20 -0
- package/dist/cli/commands/stop.js +108 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.js +160 -0
- package/dist/cli/output/formatters.d.ts +117 -0
- package/dist/cli/output/formatters.js +454 -0
- package/dist/cli/output/json-formatter.d.ts +22 -0
- package/dist/cli/output/json-formatter.js +40 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +25 -0
- package/dist/mcp/index.d.ts +14 -0
- package/dist/mcp/index.js +352 -0
- package/dist/mcp/resources/servers.d.ts +14 -0
- package/dist/mcp/resources/servers.js +128 -0
- package/dist/mcp/tools/config.d.ts +33 -0
- package/dist/mcp/tools/config.js +88 -0
- package/dist/mcp/tools/info.d.ts +36 -0
- package/dist/mcp/tools/info.js +65 -0
- package/dist/mcp/tools/list.d.ts +36 -0
- package/dist/mcp/tools/list.js +49 -0
- package/dist/mcp/tools/logs.d.ts +44 -0
- package/dist/mcp/tools/logs.js +55 -0
- package/dist/mcp/tools/refresh.d.ts +33 -0
- package/dist/mcp/tools/refresh.js +54 -0
- package/dist/mcp/tools/remove.d.ts +23 -0
- package/dist/mcp/tools/remove.js +43 -0
- package/dist/mcp/tools/restart.d.ts +23 -0
- package/dist/mcp/tools/restart.js +42 -0
- package/dist/mcp/tools/start.d.ts +38 -0
- package/dist/mcp/tools/start.js +73 -0
- package/dist/mcp/tools/stop.d.ts +23 -0
- package/dist/mcp/tools/stop.js +40 -0
- package/dist/services/config.service.d.ts +80 -0
- package/dist/services/config.service.js +227 -0
- package/dist/services/port.service.d.ts +82 -0
- package/dist/services/port.service.js +151 -0
- package/dist/services/process.service.d.ts +61 -0
- package/dist/services/process.service.js +220 -0
- package/dist/services/registry.service.d.ts +50 -0
- package/dist/services/registry.service.js +157 -0
- package/dist/types/config.d.ts +107 -0
- package/dist/types/config.js +44 -0
- package/dist/types/errors.d.ts +102 -0
- package/dist/types/errors.js +197 -0
- package/dist/types/pm2.d.ts +50 -0
- package/dist/types/pm2.js +4 -0
- package/dist/types/registry.d.ts +230 -0
- package/dist/types/registry.js +33 -0
- package/dist/utils/ci-detector.d.ts +31 -0
- package/dist/utils/ci-detector.js +68 -0
- package/dist/utils/config-drift.d.ts +71 -0
- package/dist/utils/config-drift.js +128 -0
- package/dist/utils/error-handler.d.ts +21 -0
- package/dist/utils/error-handler.js +38 -0
- package/dist/utils/log-follower.d.ts +10 -0
- package/dist/utils/log-follower.js +98 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.js +24 -0
- package/dist/utils/names.d.ts +7 -0
- package/dist/utils/names.js +20 -0
- package/dist/utils/template.d.ts +88 -0
- package/dist/utils/template.js +180 -0
- package/dist/utils/time-parser.d.ts +19 -0
- package/dist/utils/time-parser.js +54 -0
- package/docs/ci-cd.md +408 -0
- package/docs/configuration.md +325 -0
- package/docs/mcp-integration.md +411 -0
- package/examples/basic-usage/README.md +187 -0
- package/examples/ci-github-actions/workflow.yml +195 -0
- package/examples/mcp-claude-code/README.md +213 -0
- package/examples/multi-server/README.md +270 -0
- package/examples/storybook/README.md +187 -0
- package/examples/vite-project/README.md +251 -0
- package/package.json +123 -6
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error codes for servherd operations.
|
|
3
|
+
* Grouped by category:
|
|
4
|
+
* - 1xxx: Server-related errors
|
|
5
|
+
* - 2xxx: Port-related errors
|
|
6
|
+
* - 3xxx: PM2/Process-related errors
|
|
7
|
+
* - 4xxx: Configuration errors
|
|
8
|
+
* - 5xxx: Registry errors
|
|
9
|
+
* - 6xxx: Template errors
|
|
10
|
+
* - 7xxx: Command/CLI errors
|
|
11
|
+
* - 9xxx: Unknown/Other errors
|
|
12
|
+
*/
|
|
13
|
+
export var ServherdErrorCode;
|
|
14
|
+
(function (ServherdErrorCode) {
|
|
15
|
+
// Server errors (1xxx)
|
|
16
|
+
ServherdErrorCode[ServherdErrorCode["SERVER_NOT_FOUND"] = 1001] = "SERVER_NOT_FOUND";
|
|
17
|
+
ServherdErrorCode[ServherdErrorCode["SERVER_ALREADY_EXISTS"] = 1002] = "SERVER_ALREADY_EXISTS";
|
|
18
|
+
ServherdErrorCode[ServherdErrorCode["SERVER_NOT_RUNNING"] = 1003] = "SERVER_NOT_RUNNING";
|
|
19
|
+
ServherdErrorCode[ServherdErrorCode["SERVER_ALREADY_RUNNING"] = 1004] = "SERVER_ALREADY_RUNNING";
|
|
20
|
+
// Port errors (2xxx)
|
|
21
|
+
ServherdErrorCode[ServherdErrorCode["PORT_UNAVAILABLE"] = 2001] = "PORT_UNAVAILABLE";
|
|
22
|
+
ServherdErrorCode[ServherdErrorCode["PORT_OUT_OF_RANGE"] = 2002] = "PORT_OUT_OF_RANGE";
|
|
23
|
+
ServherdErrorCode[ServherdErrorCode["PORT_ALLOCATION_FAILED"] = 2003] = "PORT_ALLOCATION_FAILED";
|
|
24
|
+
// PM2/Process errors (3xxx)
|
|
25
|
+
ServherdErrorCode[ServherdErrorCode["PM2_CONNECTION_FAILED"] = 3001] = "PM2_CONNECTION_FAILED";
|
|
26
|
+
ServherdErrorCode[ServherdErrorCode["PM2_START_FAILED"] = 3002] = "PM2_START_FAILED";
|
|
27
|
+
ServherdErrorCode[ServherdErrorCode["PM2_STOP_FAILED"] = 3003] = "PM2_STOP_FAILED";
|
|
28
|
+
ServherdErrorCode[ServherdErrorCode["PM2_DELETE_FAILED"] = 3004] = "PM2_DELETE_FAILED";
|
|
29
|
+
ServherdErrorCode[ServherdErrorCode["PM2_RESTART_FAILED"] = 3005] = "PM2_RESTART_FAILED";
|
|
30
|
+
ServherdErrorCode[ServherdErrorCode["PM2_DESCRIBE_FAILED"] = 3006] = "PM2_DESCRIBE_FAILED";
|
|
31
|
+
// Configuration errors (4xxx)
|
|
32
|
+
ServherdErrorCode[ServherdErrorCode["CONFIG_LOAD_FAILED"] = 4001] = "CONFIG_LOAD_FAILED";
|
|
33
|
+
ServherdErrorCode[ServherdErrorCode["CONFIG_SAVE_FAILED"] = 4002] = "CONFIG_SAVE_FAILED";
|
|
34
|
+
ServherdErrorCode[ServherdErrorCode["CONFIG_INVALID"] = 4003] = "CONFIG_INVALID";
|
|
35
|
+
ServherdErrorCode[ServherdErrorCode["CONFIG_KEY_NOT_FOUND"] = 4004] = "CONFIG_KEY_NOT_FOUND";
|
|
36
|
+
ServherdErrorCode[ServherdErrorCode["CONFIG_VALIDATION_FAILED"] = 4005] = "CONFIG_VALIDATION_FAILED";
|
|
37
|
+
// Registry errors (5xxx)
|
|
38
|
+
ServherdErrorCode[ServherdErrorCode["REGISTRY_LOAD_FAILED"] = 5001] = "REGISTRY_LOAD_FAILED";
|
|
39
|
+
ServherdErrorCode[ServherdErrorCode["REGISTRY_SAVE_FAILED"] = 5002] = "REGISTRY_SAVE_FAILED";
|
|
40
|
+
ServherdErrorCode[ServherdErrorCode["REGISTRY_CORRUPT"] = 5003] = "REGISTRY_CORRUPT";
|
|
41
|
+
// Template errors (6xxx)
|
|
42
|
+
ServherdErrorCode[ServherdErrorCode["TEMPLATE_INVALID"] = 6001] = "TEMPLATE_INVALID";
|
|
43
|
+
ServherdErrorCode[ServherdErrorCode["TEMPLATE_MISSING_VARIABLE"] = 6002] = "TEMPLATE_MISSING_VARIABLE";
|
|
44
|
+
// Command/CLI errors (7xxx)
|
|
45
|
+
ServherdErrorCode[ServherdErrorCode["COMMAND_INVALID"] = 7001] = "COMMAND_INVALID";
|
|
46
|
+
ServherdErrorCode[ServherdErrorCode["COMMAND_MISSING_ARGUMENT"] = 7002] = "COMMAND_MISSING_ARGUMENT";
|
|
47
|
+
ServherdErrorCode[ServherdErrorCode["COMMAND_CONFLICT"] = 7003] = "COMMAND_CONFLICT";
|
|
48
|
+
ServherdErrorCode[ServherdErrorCode["INTERACTIVE_NOT_AVAILABLE"] = 7004] = "INTERACTIVE_NOT_AVAILABLE";
|
|
49
|
+
// Unknown/Other errors (9xxx)
|
|
50
|
+
ServherdErrorCode[ServherdErrorCode["UNKNOWN_ERROR"] = 9999] = "UNKNOWN_ERROR";
|
|
51
|
+
})(ServherdErrorCode || (ServherdErrorCode = {}));
|
|
52
|
+
/**
|
|
53
|
+
* Map of error codes to their string names for display purposes.
|
|
54
|
+
*/
|
|
55
|
+
const ERROR_CODE_NAMES = {
|
|
56
|
+
[ServherdErrorCode.SERVER_NOT_FOUND]: "SERVER_NOT_FOUND",
|
|
57
|
+
[ServherdErrorCode.SERVER_ALREADY_EXISTS]: "SERVER_ALREADY_EXISTS",
|
|
58
|
+
[ServherdErrorCode.SERVER_NOT_RUNNING]: "SERVER_NOT_RUNNING",
|
|
59
|
+
[ServherdErrorCode.SERVER_ALREADY_RUNNING]: "SERVER_ALREADY_RUNNING",
|
|
60
|
+
[ServherdErrorCode.PORT_UNAVAILABLE]: "PORT_UNAVAILABLE",
|
|
61
|
+
[ServherdErrorCode.PORT_OUT_OF_RANGE]: "PORT_OUT_OF_RANGE",
|
|
62
|
+
[ServherdErrorCode.PORT_ALLOCATION_FAILED]: "PORT_ALLOCATION_FAILED",
|
|
63
|
+
[ServherdErrorCode.PM2_CONNECTION_FAILED]: "PM2_CONNECTION_FAILED",
|
|
64
|
+
[ServherdErrorCode.PM2_START_FAILED]: "PM2_START_FAILED",
|
|
65
|
+
[ServherdErrorCode.PM2_STOP_FAILED]: "PM2_STOP_FAILED",
|
|
66
|
+
[ServherdErrorCode.PM2_DELETE_FAILED]: "PM2_DELETE_FAILED",
|
|
67
|
+
[ServherdErrorCode.PM2_RESTART_FAILED]: "PM2_RESTART_FAILED",
|
|
68
|
+
[ServherdErrorCode.PM2_DESCRIBE_FAILED]: "PM2_DESCRIBE_FAILED",
|
|
69
|
+
[ServherdErrorCode.CONFIG_LOAD_FAILED]: "CONFIG_LOAD_FAILED",
|
|
70
|
+
[ServherdErrorCode.CONFIG_SAVE_FAILED]: "CONFIG_SAVE_FAILED",
|
|
71
|
+
[ServherdErrorCode.CONFIG_INVALID]: "CONFIG_INVALID",
|
|
72
|
+
[ServherdErrorCode.CONFIG_KEY_NOT_FOUND]: "CONFIG_KEY_NOT_FOUND",
|
|
73
|
+
[ServherdErrorCode.CONFIG_VALIDATION_FAILED]: "CONFIG_VALIDATION_FAILED",
|
|
74
|
+
[ServherdErrorCode.REGISTRY_LOAD_FAILED]: "REGISTRY_LOAD_FAILED",
|
|
75
|
+
[ServherdErrorCode.REGISTRY_SAVE_FAILED]: "REGISTRY_SAVE_FAILED",
|
|
76
|
+
[ServherdErrorCode.REGISTRY_CORRUPT]: "REGISTRY_CORRUPT",
|
|
77
|
+
[ServherdErrorCode.TEMPLATE_INVALID]: "TEMPLATE_INVALID",
|
|
78
|
+
[ServherdErrorCode.TEMPLATE_MISSING_VARIABLE]: "TEMPLATE_MISSING_VARIABLE",
|
|
79
|
+
[ServherdErrorCode.COMMAND_INVALID]: "COMMAND_INVALID",
|
|
80
|
+
[ServherdErrorCode.COMMAND_MISSING_ARGUMENT]: "COMMAND_MISSING_ARGUMENT",
|
|
81
|
+
[ServherdErrorCode.COMMAND_CONFLICT]: "COMMAND_CONFLICT",
|
|
82
|
+
[ServherdErrorCode.INTERACTIVE_NOT_AVAILABLE]: "INTERACTIVE_NOT_AVAILABLE",
|
|
83
|
+
[ServherdErrorCode.UNKNOWN_ERROR]: "UNKNOWN_ERROR",
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Custom error class for servherd operations.
|
|
87
|
+
* Includes error codes for programmatic handling and optional details.
|
|
88
|
+
*/
|
|
89
|
+
export class ServherdError extends Error {
|
|
90
|
+
code;
|
|
91
|
+
details;
|
|
92
|
+
constructor(code, message, details) {
|
|
93
|
+
super(message);
|
|
94
|
+
this.name = "ServherdError";
|
|
95
|
+
this.code = code;
|
|
96
|
+
this.details = details;
|
|
97
|
+
// Maintains proper stack trace for where our error was thrown (V8 only)
|
|
98
|
+
if (Error.captureStackTrace) {
|
|
99
|
+
Error.captureStackTrace(this, ServherdError);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Serialize error to JSON for logging or API responses.
|
|
104
|
+
*/
|
|
105
|
+
toJSON() {
|
|
106
|
+
return {
|
|
107
|
+
name: this.name,
|
|
108
|
+
code: this.code,
|
|
109
|
+
message: this.message,
|
|
110
|
+
details: this.details,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the string name of the error code.
|
|
115
|
+
*/
|
|
116
|
+
getCodeName() {
|
|
117
|
+
return ERROR_CODE_NAMES[this.code] || "UNKNOWN_ERROR";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Type guard to check if an error is a ServherdError.
|
|
122
|
+
*/
|
|
123
|
+
export function isServherdError(error) {
|
|
124
|
+
return error instanceof ServherdError;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Format an error for CLI display.
|
|
128
|
+
* Includes color codes and proper formatting for terminal output.
|
|
129
|
+
*/
|
|
130
|
+
export function formatErrorForCLI(error) {
|
|
131
|
+
if (isServherdError(error)) {
|
|
132
|
+
let message = `\x1b[31mError [${error.code}]: ${error.message}\x1b[0m`;
|
|
133
|
+
if (error.details) {
|
|
134
|
+
if (error.details.stderr) {
|
|
135
|
+
message += `\n\x1b[90m${error.details.stderr}\x1b[0m`;
|
|
136
|
+
}
|
|
137
|
+
if (error.details.stdout) {
|
|
138
|
+
message += `\n\x1b[90m${error.details.stdout}\x1b[0m`;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return message;
|
|
142
|
+
}
|
|
143
|
+
if (error instanceof Error) {
|
|
144
|
+
return `\x1b[31mError: ${error.message}\x1b[0m`;
|
|
145
|
+
}
|
|
146
|
+
return "\x1b[31mUnknown error occurred\x1b[0m";
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Format an error for MCP tool response.
|
|
150
|
+
* Returns a structured error response compatible with MCP protocol.
|
|
151
|
+
*/
|
|
152
|
+
export function formatErrorForMCP(error) {
|
|
153
|
+
if (isServherdError(error)) {
|
|
154
|
+
return {
|
|
155
|
+
isError: true,
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: "text",
|
|
159
|
+
text: JSON.stringify({
|
|
160
|
+
error: error.getCodeName(),
|
|
161
|
+
code: error.code,
|
|
162
|
+
message: error.message,
|
|
163
|
+
details: error.details,
|
|
164
|
+
}, null, 2),
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (error instanceof Error) {
|
|
170
|
+
return {
|
|
171
|
+
isError: true,
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: JSON.stringify({
|
|
176
|
+
error: "UNKNOWN_ERROR",
|
|
177
|
+
code: ServherdErrorCode.UNKNOWN_ERROR,
|
|
178
|
+
message: error.message,
|
|
179
|
+
}, null, 2),
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
isError: true,
|
|
186
|
+
content: [
|
|
187
|
+
{
|
|
188
|
+
type: "text",
|
|
189
|
+
text: JSON.stringify({
|
|
190
|
+
error: "UNKNOWN_ERROR",
|
|
191
|
+
code: ServherdErrorCode.UNKNOWN_ERROR,
|
|
192
|
+
message: "An unknown error occurred",
|
|
193
|
+
}, null, 2),
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PM2-related type definitions
|
|
3
|
+
*/
|
|
4
|
+
export interface PM2StartOptions {
|
|
5
|
+
name: string;
|
|
6
|
+
script: string;
|
|
7
|
+
args?: string[];
|
|
8
|
+
cwd?: string;
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
instances?: number;
|
|
11
|
+
autorestart?: boolean;
|
|
12
|
+
watch?: boolean;
|
|
13
|
+
max_memory_restart?: string | number;
|
|
14
|
+
output?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
/** Format for log timestamps (moment.js format, e.g., "YYYY-MM-DD HH:mm:ss Z") */
|
|
17
|
+
log_date_format?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PM2ProcessEnv {
|
|
20
|
+
status: "online" | "stopped" | "errored" | "stopping" | "launching" | "one-launch-status";
|
|
21
|
+
pm_id: number;
|
|
22
|
+
name: string;
|
|
23
|
+
pm_uptime: number;
|
|
24
|
+
created_at: number;
|
|
25
|
+
restart_time: number;
|
|
26
|
+
unstable_restarts: number;
|
|
27
|
+
pm_cwd: string;
|
|
28
|
+
pm_exec_path: string;
|
|
29
|
+
exec_mode: "fork" | "cluster";
|
|
30
|
+
node_args: string[];
|
|
31
|
+
pm_out_log_path: string;
|
|
32
|
+
pm_err_log_path: string;
|
|
33
|
+
pm_pid_path: string;
|
|
34
|
+
env: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
export interface PM2Monit {
|
|
37
|
+
memory: number;
|
|
38
|
+
cpu: number;
|
|
39
|
+
}
|
|
40
|
+
export interface PM2ProcessDescription {
|
|
41
|
+
pid: number;
|
|
42
|
+
name: string;
|
|
43
|
+
pm2_env: PM2ProcessEnv;
|
|
44
|
+
monit?: PM2Monit;
|
|
45
|
+
}
|
|
46
|
+
export interface PM2Process {
|
|
47
|
+
name: string;
|
|
48
|
+
pm_id: number;
|
|
49
|
+
}
|
|
50
|
+
export type PM2Callback<T> = (err: Error | null, result?: T) => void;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const ServerStatusSchema: z.ZodEnum<["online", "stopped", "errored", "unknown"]>;
|
|
3
|
+
/**
|
|
4
|
+
* Snapshot of config values used when server was started.
|
|
5
|
+
* Used to detect config drift.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ConfigSnapshotSchema: z.ZodObject<{
|
|
8
|
+
hostname: z.ZodOptional<z.ZodString>;
|
|
9
|
+
httpsCert: z.ZodOptional<z.ZodString>;
|
|
10
|
+
httpsKey: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
hostname?: string | undefined;
|
|
13
|
+
httpsCert?: string | undefined;
|
|
14
|
+
httpsKey?: string | undefined;
|
|
15
|
+
}, {
|
|
16
|
+
hostname?: string | undefined;
|
|
17
|
+
httpsCert?: string | undefined;
|
|
18
|
+
httpsKey?: string | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
export type ConfigSnapshot = z.infer<typeof ConfigSnapshotSchema>;
|
|
21
|
+
export declare const ServerEntrySchema: z.ZodObject<{
|
|
22
|
+
id: z.ZodString;
|
|
23
|
+
name: z.ZodString;
|
|
24
|
+
command: z.ZodString;
|
|
25
|
+
resolvedCommand: z.ZodString;
|
|
26
|
+
cwd: z.ZodString;
|
|
27
|
+
port: z.ZodNumber;
|
|
28
|
+
protocol: z.ZodEnum<["http", "https"]>;
|
|
29
|
+
hostname: z.ZodString;
|
|
30
|
+
env: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
31
|
+
createdAt: z.ZodString;
|
|
32
|
+
pm2Name: z.ZodString;
|
|
33
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
34
|
+
description: z.ZodOptional<z.ZodString>;
|
|
35
|
+
usedConfigKeys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
36
|
+
configSnapshot: z.ZodOptional<z.ZodObject<{
|
|
37
|
+
hostname: z.ZodOptional<z.ZodString>;
|
|
38
|
+
httpsCert: z.ZodOptional<z.ZodString>;
|
|
39
|
+
httpsKey: z.ZodOptional<z.ZodString>;
|
|
40
|
+
}, "strip", z.ZodTypeAny, {
|
|
41
|
+
hostname?: string | undefined;
|
|
42
|
+
httpsCert?: string | undefined;
|
|
43
|
+
httpsKey?: string | undefined;
|
|
44
|
+
}, {
|
|
45
|
+
hostname?: string | undefined;
|
|
46
|
+
httpsCert?: string | undefined;
|
|
47
|
+
httpsKey?: string | undefined;
|
|
48
|
+
}>>;
|
|
49
|
+
}, "strip", z.ZodTypeAny, {
|
|
50
|
+
hostname: string;
|
|
51
|
+
protocol: "http" | "https";
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
command: string;
|
|
55
|
+
resolvedCommand: string;
|
|
56
|
+
cwd: string;
|
|
57
|
+
port: number;
|
|
58
|
+
env: Record<string, string>;
|
|
59
|
+
createdAt: string;
|
|
60
|
+
pm2Name: string;
|
|
61
|
+
tags?: string[] | undefined;
|
|
62
|
+
description?: string | undefined;
|
|
63
|
+
usedConfigKeys?: string[] | undefined;
|
|
64
|
+
configSnapshot?: {
|
|
65
|
+
hostname?: string | undefined;
|
|
66
|
+
httpsCert?: string | undefined;
|
|
67
|
+
httpsKey?: string | undefined;
|
|
68
|
+
} | undefined;
|
|
69
|
+
}, {
|
|
70
|
+
hostname: string;
|
|
71
|
+
protocol: "http" | "https";
|
|
72
|
+
id: string;
|
|
73
|
+
name: string;
|
|
74
|
+
command: string;
|
|
75
|
+
resolvedCommand: string;
|
|
76
|
+
cwd: string;
|
|
77
|
+
port: number;
|
|
78
|
+
env: Record<string, string>;
|
|
79
|
+
createdAt: string;
|
|
80
|
+
pm2Name: string;
|
|
81
|
+
tags?: string[] | undefined;
|
|
82
|
+
description?: string | undefined;
|
|
83
|
+
usedConfigKeys?: string[] | undefined;
|
|
84
|
+
configSnapshot?: {
|
|
85
|
+
hostname?: string | undefined;
|
|
86
|
+
httpsCert?: string | undefined;
|
|
87
|
+
httpsKey?: string | undefined;
|
|
88
|
+
} | undefined;
|
|
89
|
+
}>;
|
|
90
|
+
export declare const RegistrySchema: z.ZodObject<{
|
|
91
|
+
version: z.ZodString;
|
|
92
|
+
servers: z.ZodArray<z.ZodObject<{
|
|
93
|
+
id: z.ZodString;
|
|
94
|
+
name: z.ZodString;
|
|
95
|
+
command: z.ZodString;
|
|
96
|
+
resolvedCommand: z.ZodString;
|
|
97
|
+
cwd: z.ZodString;
|
|
98
|
+
port: z.ZodNumber;
|
|
99
|
+
protocol: z.ZodEnum<["http", "https"]>;
|
|
100
|
+
hostname: z.ZodString;
|
|
101
|
+
env: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
102
|
+
createdAt: z.ZodString;
|
|
103
|
+
pm2Name: z.ZodString;
|
|
104
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
105
|
+
description: z.ZodOptional<z.ZodString>;
|
|
106
|
+
usedConfigKeys: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
107
|
+
configSnapshot: z.ZodOptional<z.ZodObject<{
|
|
108
|
+
hostname: z.ZodOptional<z.ZodString>;
|
|
109
|
+
httpsCert: z.ZodOptional<z.ZodString>;
|
|
110
|
+
httpsKey: z.ZodOptional<z.ZodString>;
|
|
111
|
+
}, "strip", z.ZodTypeAny, {
|
|
112
|
+
hostname?: string | undefined;
|
|
113
|
+
httpsCert?: string | undefined;
|
|
114
|
+
httpsKey?: string | undefined;
|
|
115
|
+
}, {
|
|
116
|
+
hostname?: string | undefined;
|
|
117
|
+
httpsCert?: string | undefined;
|
|
118
|
+
httpsKey?: string | undefined;
|
|
119
|
+
}>>;
|
|
120
|
+
}, "strip", z.ZodTypeAny, {
|
|
121
|
+
hostname: string;
|
|
122
|
+
protocol: "http" | "https";
|
|
123
|
+
id: string;
|
|
124
|
+
name: string;
|
|
125
|
+
command: string;
|
|
126
|
+
resolvedCommand: string;
|
|
127
|
+
cwd: string;
|
|
128
|
+
port: number;
|
|
129
|
+
env: Record<string, string>;
|
|
130
|
+
createdAt: string;
|
|
131
|
+
pm2Name: string;
|
|
132
|
+
tags?: string[] | undefined;
|
|
133
|
+
description?: string | undefined;
|
|
134
|
+
usedConfigKeys?: string[] | undefined;
|
|
135
|
+
configSnapshot?: {
|
|
136
|
+
hostname?: string | undefined;
|
|
137
|
+
httpsCert?: string | undefined;
|
|
138
|
+
httpsKey?: string | undefined;
|
|
139
|
+
} | undefined;
|
|
140
|
+
}, {
|
|
141
|
+
hostname: string;
|
|
142
|
+
protocol: "http" | "https";
|
|
143
|
+
id: string;
|
|
144
|
+
name: string;
|
|
145
|
+
command: string;
|
|
146
|
+
resolvedCommand: string;
|
|
147
|
+
cwd: string;
|
|
148
|
+
port: number;
|
|
149
|
+
env: Record<string, string>;
|
|
150
|
+
createdAt: string;
|
|
151
|
+
pm2Name: string;
|
|
152
|
+
tags?: string[] | undefined;
|
|
153
|
+
description?: string | undefined;
|
|
154
|
+
usedConfigKeys?: string[] | undefined;
|
|
155
|
+
configSnapshot?: {
|
|
156
|
+
hostname?: string | undefined;
|
|
157
|
+
httpsCert?: string | undefined;
|
|
158
|
+
httpsKey?: string | undefined;
|
|
159
|
+
} | undefined;
|
|
160
|
+
}>, "many">;
|
|
161
|
+
}, "strip", z.ZodTypeAny, {
|
|
162
|
+
version: string;
|
|
163
|
+
servers: {
|
|
164
|
+
hostname: string;
|
|
165
|
+
protocol: "http" | "https";
|
|
166
|
+
id: string;
|
|
167
|
+
name: string;
|
|
168
|
+
command: string;
|
|
169
|
+
resolvedCommand: string;
|
|
170
|
+
cwd: string;
|
|
171
|
+
port: number;
|
|
172
|
+
env: Record<string, string>;
|
|
173
|
+
createdAt: string;
|
|
174
|
+
pm2Name: string;
|
|
175
|
+
tags?: string[] | undefined;
|
|
176
|
+
description?: string | undefined;
|
|
177
|
+
usedConfigKeys?: string[] | undefined;
|
|
178
|
+
configSnapshot?: {
|
|
179
|
+
hostname?: string | undefined;
|
|
180
|
+
httpsCert?: string | undefined;
|
|
181
|
+
httpsKey?: string | undefined;
|
|
182
|
+
} | undefined;
|
|
183
|
+
}[];
|
|
184
|
+
}, {
|
|
185
|
+
version: string;
|
|
186
|
+
servers: {
|
|
187
|
+
hostname: string;
|
|
188
|
+
protocol: "http" | "https";
|
|
189
|
+
id: string;
|
|
190
|
+
name: string;
|
|
191
|
+
command: string;
|
|
192
|
+
resolvedCommand: string;
|
|
193
|
+
cwd: string;
|
|
194
|
+
port: number;
|
|
195
|
+
env: Record<string, string>;
|
|
196
|
+
createdAt: string;
|
|
197
|
+
pm2Name: string;
|
|
198
|
+
tags?: string[] | undefined;
|
|
199
|
+
description?: string | undefined;
|
|
200
|
+
usedConfigKeys?: string[] | undefined;
|
|
201
|
+
configSnapshot?: {
|
|
202
|
+
hostname?: string | undefined;
|
|
203
|
+
httpsCert?: string | undefined;
|
|
204
|
+
httpsKey?: string | undefined;
|
|
205
|
+
} | undefined;
|
|
206
|
+
}[];
|
|
207
|
+
}>;
|
|
208
|
+
export type ServerStatus = z.infer<typeof ServerStatusSchema>;
|
|
209
|
+
export type ServerEntry = z.infer<typeof ServerEntrySchema>;
|
|
210
|
+
export type Registry = z.infer<typeof RegistrySchema>;
|
|
211
|
+
export interface ServerFilter {
|
|
212
|
+
name?: string;
|
|
213
|
+
tag?: string;
|
|
214
|
+
cwd?: string;
|
|
215
|
+
cmd?: string;
|
|
216
|
+
running?: boolean;
|
|
217
|
+
}
|
|
218
|
+
export interface AddServerOptions {
|
|
219
|
+
command: string;
|
|
220
|
+
cwd: string;
|
|
221
|
+
port: number;
|
|
222
|
+
name?: string;
|
|
223
|
+
protocol?: "http" | "https";
|
|
224
|
+
hostname?: string;
|
|
225
|
+
env?: Record<string, string>;
|
|
226
|
+
tags?: string[];
|
|
227
|
+
description?: string;
|
|
228
|
+
usedConfigKeys?: string[];
|
|
229
|
+
configSnapshot?: ConfigSnapshot;
|
|
230
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const ServerStatusSchema = z.enum(["online", "stopped", "errored", "unknown"]);
|
|
3
|
+
/**
|
|
4
|
+
* Snapshot of config values used when server was started.
|
|
5
|
+
* Used to detect config drift.
|
|
6
|
+
*/
|
|
7
|
+
export const ConfigSnapshotSchema = z.object({
|
|
8
|
+
hostname: z.string().optional(),
|
|
9
|
+
httpsCert: z.string().optional(),
|
|
10
|
+
httpsKey: z.string().optional(),
|
|
11
|
+
});
|
|
12
|
+
export const ServerEntrySchema = z.object({
|
|
13
|
+
id: z.string(),
|
|
14
|
+
name: z.string(),
|
|
15
|
+
command: z.string(),
|
|
16
|
+
resolvedCommand: z.string(),
|
|
17
|
+
cwd: z.string(),
|
|
18
|
+
port: z.number().int().min(1).max(65535),
|
|
19
|
+
protocol: z.enum(["http", "https"]),
|
|
20
|
+
hostname: z.string(),
|
|
21
|
+
env: z.record(z.string()),
|
|
22
|
+
createdAt: z.string(),
|
|
23
|
+
pm2Name: z.string(),
|
|
24
|
+
tags: z.array(z.string()).optional(),
|
|
25
|
+
description: z.string().optional(),
|
|
26
|
+
// Config tracking for drift detection
|
|
27
|
+
usedConfigKeys: z.array(z.string()).optional(),
|
|
28
|
+
configSnapshot: ConfigSnapshotSchema.optional(),
|
|
29
|
+
});
|
|
30
|
+
export const RegistrySchema = z.object({
|
|
31
|
+
version: z.string(),
|
|
32
|
+
servers: z.array(ServerEntrySchema),
|
|
33
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI Environment Detector
|
|
3
|
+
* Detects various CI/CD environments and provides information about them.
|
|
4
|
+
*/
|
|
5
|
+
export interface CIInfo {
|
|
6
|
+
isCI: boolean;
|
|
7
|
+
name: string | null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Options for CI detection that can be passed from CLI flags
|
|
11
|
+
*/
|
|
12
|
+
export interface CIModeOptions {
|
|
13
|
+
ci?: boolean;
|
|
14
|
+
noCi?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare class CIDetector {
|
|
17
|
+
/**
|
|
18
|
+
* Check if running in a CI environment
|
|
19
|
+
* @param options Optional CLI flags to override environment detection
|
|
20
|
+
*/
|
|
21
|
+
static isCI(options?: CIModeOptions): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Get the name of the CI environment
|
|
24
|
+
* Returns null if not running in CI
|
|
25
|
+
*/
|
|
26
|
+
static getCIName(): string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Get complete CI information
|
|
29
|
+
*/
|
|
30
|
+
static getInfo(): CIInfo;
|
|
31
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CI Environment Detector
|
|
3
|
+
* Detects various CI/CD environments and provides information about them.
|
|
4
|
+
*/
|
|
5
|
+
const CI_ENVIRONMENTS = [
|
|
6
|
+
{ name: "GitHub Actions", envVar: "GITHUB_ACTIONS" },
|
|
7
|
+
{ name: "GitLab CI", envVar: "GITLAB_CI" },
|
|
8
|
+
{ name: "CircleCI", envVar: "CIRCLECI" },
|
|
9
|
+
{ name: "Travis CI", envVar: "TRAVIS" },
|
|
10
|
+
{ name: "Jenkins", envVar: "JENKINS_URL" },
|
|
11
|
+
{ name: "Buildkite", envVar: "BUILDKITE" },
|
|
12
|
+
{ name: "Azure Pipelines", envVar: "AZURE_PIPELINES" },
|
|
13
|
+
{ name: "TeamCity", envVar: "TEAMCITY_VERSION" },
|
|
14
|
+
];
|
|
15
|
+
export class CIDetector {
|
|
16
|
+
/**
|
|
17
|
+
* Check if running in a CI environment
|
|
18
|
+
* @param options Optional CLI flags to override environment detection
|
|
19
|
+
*/
|
|
20
|
+
static isCI(options) {
|
|
21
|
+
// Explicit --no-ci flag takes highest precedence
|
|
22
|
+
if (options?.noCi) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
// Explicit --ci flag takes precedence over environment detection
|
|
26
|
+
if (options?.ci) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
// Fall back to environment variable detection
|
|
30
|
+
// Check for generic CI environment variable
|
|
31
|
+
if (process.env.CI) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
// Check for specific CI environments
|
|
35
|
+
for (const ci of CI_ENVIRONMENTS) {
|
|
36
|
+
if (process.env[ci.envVar]) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get the name of the CI environment
|
|
44
|
+
* Returns null if not running in CI
|
|
45
|
+
*/
|
|
46
|
+
static getCIName() {
|
|
47
|
+
// Check for specific CI environments first
|
|
48
|
+
for (const ci of CI_ENVIRONMENTS) {
|
|
49
|
+
if (process.env[ci.envVar]) {
|
|
50
|
+
return ci.name;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Check for generic CI environment variable
|
|
54
|
+
if (process.env.CI) {
|
|
55
|
+
return "Unknown CI";
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get complete CI information
|
|
61
|
+
*/
|
|
62
|
+
static getInfo() {
|
|
63
|
+
return {
|
|
64
|
+
isCI: this.isCI(),
|
|
65
|
+
name: this.getCIName(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for detecting configuration drift in servers.
|
|
3
|
+
* Drift occurs when config values have changed since a server was started.
|
|
4
|
+
*/
|
|
5
|
+
import type { GlobalConfig } from "../types/config.js";
|
|
6
|
+
import type { ServerEntry, ConfigSnapshot } from "../types/registry.js";
|
|
7
|
+
/**
|
|
8
|
+
* Information about a drifted config value
|
|
9
|
+
*/
|
|
10
|
+
export interface DriftedValue {
|
|
11
|
+
/** The config key that has drifted */
|
|
12
|
+
configKey: string;
|
|
13
|
+
/** The template variable name */
|
|
14
|
+
templateVar: string;
|
|
15
|
+
/** The value when server was started */
|
|
16
|
+
startedWith: string | undefined;
|
|
17
|
+
/** The current config value */
|
|
18
|
+
currentValue: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Result of drift detection for a server
|
|
22
|
+
*/
|
|
23
|
+
export interface DriftResult {
|
|
24
|
+
/** Whether the server has config drift */
|
|
25
|
+
hasDrift: boolean;
|
|
26
|
+
/** List of drifted values */
|
|
27
|
+
driftedValues: DriftedValue[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extract the config keys used by a command template
|
|
31
|
+
* @param command - The command template string
|
|
32
|
+
* @returns Array of config keys used (e.g., ["hostname", "httpsCert"])
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractUsedConfigKeys(command: string): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Create a config snapshot containing only the values used by a server
|
|
37
|
+
* @param config - Current global config
|
|
38
|
+
* @param usedConfigKeys - Config keys used by the server
|
|
39
|
+
* @returns Config snapshot with only relevant values
|
|
40
|
+
*/
|
|
41
|
+
export declare function createConfigSnapshot(config: GlobalConfig, usedConfigKeys: string[]): ConfigSnapshot;
|
|
42
|
+
/**
|
|
43
|
+
* Detect config drift for a server
|
|
44
|
+
* @param server - Server entry from registry
|
|
45
|
+
* @param currentConfig - Current global config
|
|
46
|
+
* @returns Drift detection result
|
|
47
|
+
*/
|
|
48
|
+
export declare function detectDrift(server: ServerEntry, currentConfig: GlobalConfig): DriftResult;
|
|
49
|
+
/**
|
|
50
|
+
* Find all servers that use a specific config key
|
|
51
|
+
* @param servers - Array of server entries
|
|
52
|
+
* @param configKey - The config key to search for
|
|
53
|
+
* @returns Array of servers using that config key
|
|
54
|
+
*/
|
|
55
|
+
export declare function findServersUsingConfigKey(servers: ServerEntry[], configKey: string): ServerEntry[];
|
|
56
|
+
/**
|
|
57
|
+
* Find all servers with config drift
|
|
58
|
+
* @param servers - Array of server entries
|
|
59
|
+
* @param currentConfig - Current global config
|
|
60
|
+
* @returns Array of servers with drift and their drift details
|
|
61
|
+
*/
|
|
62
|
+
export declare function findServersWithDrift(servers: ServerEntry[], currentConfig: GlobalConfig): Array<{
|
|
63
|
+
server: ServerEntry;
|
|
64
|
+
drift: DriftResult;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Format drift information for display
|
|
68
|
+
* @param drift - Drift result to format
|
|
69
|
+
* @returns Formatted string describing the drift
|
|
70
|
+
*/
|
|
71
|
+
export declare function formatDrift(drift: DriftResult): string;
|