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,160 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { startAction } from "./commands/start.js";
|
|
3
|
+
import { stopAction } from "./commands/stop.js";
|
|
4
|
+
import { listAction } from "./commands/list.js";
|
|
5
|
+
import { infoAction } from "./commands/info.js";
|
|
6
|
+
import { logsAction } from "./commands/logs.js";
|
|
7
|
+
import { restartAction } from "./commands/restart.js";
|
|
8
|
+
import { removeAction } from "./commands/remove.js";
|
|
9
|
+
import { configAction } from "./commands/config.js";
|
|
10
|
+
import { mcpAction } from "./commands/mcp.js";
|
|
11
|
+
/**
|
|
12
|
+
* Create the CLI program
|
|
13
|
+
*/
|
|
14
|
+
export function createProgram() {
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program
|
|
17
|
+
.name("servherd")
|
|
18
|
+
.description("CLI tool and MCP server for managing development servers across projects")
|
|
19
|
+
.version("0.1.0")
|
|
20
|
+
.option("--json", "Output results as JSON")
|
|
21
|
+
.option("--ci", "Force CI mode behavior (sequential port allocation, no interactive prompts)")
|
|
22
|
+
.option("--no-ci", "Force non-CI mode behavior (overrides CI environment detection)");
|
|
23
|
+
// Start command
|
|
24
|
+
program
|
|
25
|
+
.command("start")
|
|
26
|
+
.description("Start a development server")
|
|
27
|
+
.argument("[command...]", "Command to run (use -- to separate from options)")
|
|
28
|
+
.option("-n, --name <name>", "Name for the server")
|
|
29
|
+
.option("-p, --port <port>", "Port for the server (overrides deterministic assignment)", parseInt)
|
|
30
|
+
.option("--protocol <protocol>", "Protocol to use (http or https)", (value) => {
|
|
31
|
+
if (value !== "http" && value !== "https") {
|
|
32
|
+
throw new Error("Protocol must be 'http' or 'https'");
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
})
|
|
36
|
+
.option("-t, --tag <tag...>", "Tags for the server")
|
|
37
|
+
.option("-d, --description <description>", "Description of the server")
|
|
38
|
+
.option("-e, --env <KEY=VALUE...>", "Environment variables (supports {{port}}, {{hostname}}, {{url}}, {{https-cert}}, {{https-key}} templates)")
|
|
39
|
+
.action(async (commandArgs, options, cmd) => {
|
|
40
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
41
|
+
await startAction(commandArgs, { ...options, json: globalOpts.json });
|
|
42
|
+
});
|
|
43
|
+
// Stop command
|
|
44
|
+
program
|
|
45
|
+
.command("stop")
|
|
46
|
+
.description("Stop a development server")
|
|
47
|
+
.argument("[name]", "Name of the server to stop")
|
|
48
|
+
.option("-a, --all", "Stop all servers")
|
|
49
|
+
.option("-t, --tag <tag>", "Stop servers with this tag")
|
|
50
|
+
.option("-f, --force", "Force stop using SIGKILL")
|
|
51
|
+
.action(async (name, options, cmd) => {
|
|
52
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
53
|
+
await stopAction(name, { ...options, json: globalOpts.json });
|
|
54
|
+
});
|
|
55
|
+
// List command
|
|
56
|
+
program
|
|
57
|
+
.command("list")
|
|
58
|
+
.alias("ls")
|
|
59
|
+
.description("List all managed servers")
|
|
60
|
+
.option("-r, --running", "Only show running servers")
|
|
61
|
+
.option("-s, --stopped", "Only show stopped servers")
|
|
62
|
+
.option("-t, --tag <tag>", "Filter by tag")
|
|
63
|
+
.option("-c, --cwd <path>", "Filter by working directory")
|
|
64
|
+
.option("--cmd <pattern>", "Filter by command pattern (glob syntax, e.g., '*storybook*')")
|
|
65
|
+
.action(async (options, cmd) => {
|
|
66
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
67
|
+
await listAction({ ...options, json: globalOpts.json });
|
|
68
|
+
});
|
|
69
|
+
// Info command
|
|
70
|
+
program
|
|
71
|
+
.command("info")
|
|
72
|
+
.description("Show detailed information about a server")
|
|
73
|
+
.argument("<name>", "Name of the server")
|
|
74
|
+
.action(async (name, options, cmd) => {
|
|
75
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
76
|
+
await infoAction(name, { json: globalOpts.json });
|
|
77
|
+
});
|
|
78
|
+
// Logs command
|
|
79
|
+
program
|
|
80
|
+
.command("logs")
|
|
81
|
+
.description("View server logs")
|
|
82
|
+
.argument("[name]", "Name of the server")
|
|
83
|
+
.option("-n, --lines <number>", "Number of lines to show (from end)", "50")
|
|
84
|
+
.option("-e, --error", "Show error logs instead of output logs")
|
|
85
|
+
.option("-f, --follow", "Follow logs in real-time")
|
|
86
|
+
.option("--since <time>", "Show logs since time (e.g., 1h, 30m, 2024-01-15)")
|
|
87
|
+
.option("--head <number>", "Show first N lines (instead of last)")
|
|
88
|
+
.option("--flush", "Clear logs instead of displaying")
|
|
89
|
+
.option("-a, --all", "Apply to all servers (with --flush)")
|
|
90
|
+
.action(async (name, options, cmd) => {
|
|
91
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
92
|
+
await logsAction(name, {
|
|
93
|
+
lines: options.lines ? parseInt(options.lines, 10) : undefined,
|
|
94
|
+
error: options.error,
|
|
95
|
+
follow: options.follow,
|
|
96
|
+
since: options.since,
|
|
97
|
+
head: options.head ? parseInt(options.head, 10) : undefined,
|
|
98
|
+
flush: options.flush,
|
|
99
|
+
all: options.all,
|
|
100
|
+
json: globalOpts.json,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
// Restart command
|
|
104
|
+
program
|
|
105
|
+
.command("restart")
|
|
106
|
+
.description("Restart a development server")
|
|
107
|
+
.argument("[name]", "Name of the server to restart")
|
|
108
|
+
.option("-a, --all", "Restart all servers")
|
|
109
|
+
.option("-t, --tag <tag>", "Restart servers with this tag")
|
|
110
|
+
.action(async (name, options, cmd) => {
|
|
111
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
112
|
+
await restartAction(name, { ...options, json: globalOpts.json });
|
|
113
|
+
});
|
|
114
|
+
// Remove command
|
|
115
|
+
program
|
|
116
|
+
.command("remove")
|
|
117
|
+
.alias("rm")
|
|
118
|
+
.description("Remove a server from the registry (stops it first)")
|
|
119
|
+
.argument("[name]", "Name of the server to remove")
|
|
120
|
+
.option("-a, --all", "Remove all servers")
|
|
121
|
+
.option("-t, --tag <tag>", "Remove servers with this tag")
|
|
122
|
+
.option("-f, --force", "Skip confirmation prompt")
|
|
123
|
+
.action(async (name, options, cmd) => {
|
|
124
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
125
|
+
await removeAction(name, { ...options, json: globalOpts.json });
|
|
126
|
+
});
|
|
127
|
+
// Config command
|
|
128
|
+
program
|
|
129
|
+
.command("config")
|
|
130
|
+
.description("View and modify configuration")
|
|
131
|
+
.option("-s, --show", "Show current configuration (default)")
|
|
132
|
+
.option("-g, --get <key>", "Get a specific configuration value")
|
|
133
|
+
.option("--set <key>", "Set a configuration value (use with --value)")
|
|
134
|
+
.option("--value <value>", "Value to set (use with --set)")
|
|
135
|
+
.option("-r, --reset", "Reset configuration to defaults")
|
|
136
|
+
.option("-f, --force", "Skip confirmation prompt for reset")
|
|
137
|
+
.option("--refresh <name>", "Refresh a specific server with config drift")
|
|
138
|
+
.option("--refresh-all", "Refresh all servers with config drift")
|
|
139
|
+
.option("-t, --tag <tag>", "Refresh servers with this tag (use with --refresh-all)")
|
|
140
|
+
.option("--dry-run", "Show what would be refreshed without making changes")
|
|
141
|
+
.action(async (options, cmd) => {
|
|
142
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
143
|
+
await configAction({ ...options, json: globalOpts.json });
|
|
144
|
+
});
|
|
145
|
+
// MCP command
|
|
146
|
+
program
|
|
147
|
+
.command("mcp")
|
|
148
|
+
.description("Start the MCP server (for use with Claude Code or other MCP clients)")
|
|
149
|
+
.action(async () => {
|
|
150
|
+
await mcpAction();
|
|
151
|
+
});
|
|
152
|
+
return program;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Run the CLI
|
|
156
|
+
*/
|
|
157
|
+
export async function runCLI(args = process.argv) {
|
|
158
|
+
const program = createProgram();
|
|
159
|
+
await program.parseAsync(args);
|
|
160
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { ServerEntry, ServerStatus } from "../../types/registry.js";
|
|
2
|
+
import type { InfoCommandResult } from "../commands/info.js";
|
|
3
|
+
import type { LogsCommandResult } from "../commands/logs.js";
|
|
4
|
+
/**
|
|
5
|
+
* Format server status with color
|
|
6
|
+
*/
|
|
7
|
+
export declare function formatStatus(status: ServerStatus): string;
|
|
8
|
+
/**
|
|
9
|
+
* Format URL for display
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatUrl(protocol: string, hostname: string, port: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* Format server name for display
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatName(name: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Create a table showing server list
|
|
18
|
+
*/
|
|
19
|
+
export interface ServerListItem {
|
|
20
|
+
server: ServerEntry;
|
|
21
|
+
status: ServerStatus;
|
|
22
|
+
hasDrift?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function formatServerListTable(servers: ServerListItem[]): string;
|
|
25
|
+
/**
|
|
26
|
+
* Format start result output
|
|
27
|
+
*/
|
|
28
|
+
export interface StartResult {
|
|
29
|
+
action: "started" | "existing" | "restarted" | "renamed";
|
|
30
|
+
server: ServerEntry;
|
|
31
|
+
status: ServerStatus;
|
|
32
|
+
previousName?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function formatStartResult(result: StartResult): string;
|
|
35
|
+
/**
|
|
36
|
+
* Format stop result output
|
|
37
|
+
*/
|
|
38
|
+
export interface StopResult {
|
|
39
|
+
name: string;
|
|
40
|
+
success: boolean;
|
|
41
|
+
message?: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function formatStopResult(results: StopResult[]): string;
|
|
44
|
+
/**
|
|
45
|
+
* Format error message
|
|
46
|
+
*/
|
|
47
|
+
export declare function formatError(message: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Format success message
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatSuccess(message: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Format info message
|
|
54
|
+
*/
|
|
55
|
+
export declare function formatInfo(message: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Format warning message
|
|
58
|
+
*/
|
|
59
|
+
export declare function formatWarning(message: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* Format server info as a boxed display
|
|
62
|
+
*/
|
|
63
|
+
export declare function formatServerInfo(info: InfoCommandResult): string;
|
|
64
|
+
/**
|
|
65
|
+
* Format logs output
|
|
66
|
+
*/
|
|
67
|
+
export declare function formatLogs(result: LogsCommandResult): string;
|
|
68
|
+
/**
|
|
69
|
+
* Format restart result output
|
|
70
|
+
*/
|
|
71
|
+
export interface RestartResult {
|
|
72
|
+
name: string;
|
|
73
|
+
success: boolean;
|
|
74
|
+
status?: ServerStatus;
|
|
75
|
+
message?: string;
|
|
76
|
+
configRefreshed?: boolean;
|
|
77
|
+
}
|
|
78
|
+
export declare function formatRestartResult(results: RestartResult[]): string;
|
|
79
|
+
/**
|
|
80
|
+
* Format refresh result output
|
|
81
|
+
*/
|
|
82
|
+
export interface RefreshResult {
|
|
83
|
+
name: string;
|
|
84
|
+
success: boolean;
|
|
85
|
+
status?: ServerStatus;
|
|
86
|
+
message?: string;
|
|
87
|
+
driftDetails?: string;
|
|
88
|
+
skipped?: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Format remove result output
|
|
92
|
+
*/
|
|
93
|
+
export interface RemoveResult {
|
|
94
|
+
name: string;
|
|
95
|
+
success: boolean;
|
|
96
|
+
cancelled?: boolean;
|
|
97
|
+
message?: string;
|
|
98
|
+
}
|
|
99
|
+
export declare function formatRemoveResult(results: RemoveResult[]): string;
|
|
100
|
+
/**
|
|
101
|
+
* Format config result output
|
|
102
|
+
*/
|
|
103
|
+
export interface ConfigResult {
|
|
104
|
+
config?: Record<string, unknown>;
|
|
105
|
+
configPath?: string | null;
|
|
106
|
+
globalConfigPath?: string;
|
|
107
|
+
key?: string;
|
|
108
|
+
value?: unknown;
|
|
109
|
+
updated?: boolean;
|
|
110
|
+
refreshMessage?: string;
|
|
111
|
+
reset?: boolean;
|
|
112
|
+
cancelled?: boolean;
|
|
113
|
+
refreshResults?: RefreshResult[];
|
|
114
|
+
dryRun?: boolean;
|
|
115
|
+
error?: string;
|
|
116
|
+
}
|
|
117
|
+
export declare function formatConfigResult(result: ConfigResult): string;
|