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.
Files changed (95) hide show
  1. package/CONTRIBUTING.md +250 -0
  2. package/LICENSE +21 -0
  3. package/README.md +653 -29
  4. package/dist/cli/commands/config.d.ts +35 -0
  5. package/dist/cli/commands/config.js +336 -0
  6. package/dist/cli/commands/info.d.ts +37 -0
  7. package/dist/cli/commands/info.js +98 -0
  8. package/dist/cli/commands/list.d.ts +26 -0
  9. package/dist/cli/commands/list.js +86 -0
  10. package/dist/cli/commands/logs.d.ts +46 -0
  11. package/dist/cli/commands/logs.js +292 -0
  12. package/dist/cli/commands/mcp.d.ts +5 -0
  13. package/dist/cli/commands/mcp.js +17 -0
  14. package/dist/cli/commands/refresh.d.ts +20 -0
  15. package/dist/cli/commands/refresh.js +139 -0
  16. package/dist/cli/commands/remove.d.ts +20 -0
  17. package/dist/cli/commands/remove.js +144 -0
  18. package/dist/cli/commands/restart.d.ts +25 -0
  19. package/dist/cli/commands/restart.js +177 -0
  20. package/dist/cli/commands/start.d.ts +37 -0
  21. package/dist/cli/commands/start.js +293 -0
  22. package/dist/cli/commands/stop.d.ts +20 -0
  23. package/dist/cli/commands/stop.js +108 -0
  24. package/dist/cli/index.d.ts +9 -0
  25. package/dist/cli/index.js +160 -0
  26. package/dist/cli/output/formatters.d.ts +117 -0
  27. package/dist/cli/output/formatters.js +454 -0
  28. package/dist/cli/output/json-formatter.d.ts +22 -0
  29. package/dist/cli/output/json-formatter.js +40 -0
  30. package/dist/index.d.ts +15 -0
  31. package/dist/index.js +25 -0
  32. package/dist/mcp/index.d.ts +14 -0
  33. package/dist/mcp/index.js +352 -0
  34. package/dist/mcp/resources/servers.d.ts +14 -0
  35. package/dist/mcp/resources/servers.js +128 -0
  36. package/dist/mcp/tools/config.d.ts +33 -0
  37. package/dist/mcp/tools/config.js +88 -0
  38. package/dist/mcp/tools/info.d.ts +36 -0
  39. package/dist/mcp/tools/info.js +65 -0
  40. package/dist/mcp/tools/list.d.ts +36 -0
  41. package/dist/mcp/tools/list.js +49 -0
  42. package/dist/mcp/tools/logs.d.ts +44 -0
  43. package/dist/mcp/tools/logs.js +55 -0
  44. package/dist/mcp/tools/refresh.d.ts +33 -0
  45. package/dist/mcp/tools/refresh.js +54 -0
  46. package/dist/mcp/tools/remove.d.ts +23 -0
  47. package/dist/mcp/tools/remove.js +43 -0
  48. package/dist/mcp/tools/restart.d.ts +23 -0
  49. package/dist/mcp/tools/restart.js +42 -0
  50. package/dist/mcp/tools/start.d.ts +38 -0
  51. package/dist/mcp/tools/start.js +73 -0
  52. package/dist/mcp/tools/stop.d.ts +23 -0
  53. package/dist/mcp/tools/stop.js +40 -0
  54. package/dist/services/config.service.d.ts +80 -0
  55. package/dist/services/config.service.js +227 -0
  56. package/dist/services/port.service.d.ts +82 -0
  57. package/dist/services/port.service.js +151 -0
  58. package/dist/services/process.service.d.ts +61 -0
  59. package/dist/services/process.service.js +220 -0
  60. package/dist/services/registry.service.d.ts +50 -0
  61. package/dist/services/registry.service.js +157 -0
  62. package/dist/types/config.d.ts +107 -0
  63. package/dist/types/config.js +44 -0
  64. package/dist/types/errors.d.ts +102 -0
  65. package/dist/types/errors.js +197 -0
  66. package/dist/types/pm2.d.ts +50 -0
  67. package/dist/types/pm2.js +4 -0
  68. package/dist/types/registry.d.ts +230 -0
  69. package/dist/types/registry.js +33 -0
  70. package/dist/utils/ci-detector.d.ts +31 -0
  71. package/dist/utils/ci-detector.js +68 -0
  72. package/dist/utils/config-drift.d.ts +71 -0
  73. package/dist/utils/config-drift.js +128 -0
  74. package/dist/utils/error-handler.d.ts +21 -0
  75. package/dist/utils/error-handler.js +38 -0
  76. package/dist/utils/log-follower.d.ts +10 -0
  77. package/dist/utils/log-follower.js +98 -0
  78. package/dist/utils/logger.d.ts +11 -0
  79. package/dist/utils/logger.js +24 -0
  80. package/dist/utils/names.d.ts +7 -0
  81. package/dist/utils/names.js +20 -0
  82. package/dist/utils/template.d.ts +88 -0
  83. package/dist/utils/template.js +180 -0
  84. package/dist/utils/time-parser.d.ts +19 -0
  85. package/dist/utils/time-parser.js +54 -0
  86. package/docs/ci-cd.md +408 -0
  87. package/docs/configuration.md +325 -0
  88. package/docs/mcp-integration.md +411 -0
  89. package/examples/basic-usage/README.md +187 -0
  90. package/examples/ci-github-actions/workflow.yml +195 -0
  91. package/examples/mcp-claude-code/README.md +213 -0
  92. package/examples/multi-server/README.md +270 -0
  93. package/examples/storybook/README.md +187 -0
  94. package/examples/vite-project/README.md +251 -0
  95. 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;