@restforgejs/mcp-server 1.2.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/LICENSE.md +21 -0
- package/README.md +149 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/env-parser.d.ts +30 -0
- package/dist/lib/env-parser.js +150 -0
- package/dist/lib/env-parser.js.map +1 -0
- package/dist/lib/exec.d.ts +29 -0
- package/dist/lib/exec.js +38 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +220 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/codegen/create-dashboard.d.ts +2 -0
- package/dist/tools/codegen/create-dashboard.js +256 -0
- package/dist/tools/codegen/create-dashboard.js.map +1 -0
- package/dist/tools/codegen/create-endpoint.d.ts +2 -0
- package/dist/tools/codegen/create-endpoint.js +263 -0
- package/dist/tools/codegen/create-endpoint.js.map +1 -0
- package/dist/tools/codegen/dbschema-generate-ddl.d.ts +2 -0
- package/dist/tools/codegen/dbschema-generate-ddl.js +187 -0
- package/dist/tools/codegen/dbschema-generate-ddl.js.map +1 -0
- package/dist/tools/codegen/dbschema-init.d.ts +2 -0
- package/dist/tools/codegen/dbschema-init.js +158 -0
- package/dist/tools/codegen/dbschema-init.js.map +1 -0
- package/dist/tools/codegen/dbschema-introspect.d.ts +2 -0
- package/dist/tools/codegen/dbschema-introspect.js +241 -0
- package/dist/tools/codegen/dbschema-introspect.js.map +1 -0
- package/dist/tools/codegen/dbschema-migrate.d.ts +2 -0
- package/dist/tools/codegen/dbschema-migrate.js +219 -0
- package/dist/tools/codegen/dbschema-migrate.js.map +1 -0
- package/dist/tools/codegen/dbschema-models.d.ts +2 -0
- package/dist/tools/codegen/dbschema-models.js +146 -0
- package/dist/tools/codegen/dbschema-models.js.map +1 -0
- package/dist/tools/codegen/dbschema-validate.d.ts +2 -0
- package/dist/tools/codegen/dbschema-validate.js +153 -0
- package/dist/tools/codegen/dbschema-validate.js.map +1 -0
- package/dist/tools/codegen/describe-table.d.ts +2 -0
- package/dist/tools/codegen/describe-table.js +259 -0
- package/dist/tools/codegen/describe-table.js.map +1 -0
- package/dist/tools/codegen/diff-payload.d.ts +2 -0
- package/dist/tools/codegen/diff-payload.js +165 -0
- package/dist/tools/codegen/diff-payload.js.map +1 -0
- package/dist/tools/codegen/generate-payload.d.ts +2 -0
- package/dist/tools/codegen/generate-payload.js +145 -0
- package/dist/tools/codegen/generate-payload.js.map +1 -0
- package/dist/tools/codegen/get-dashboard-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-dashboard-catalog.js +213 -0
- package/dist/tools/codegen/get-dashboard-catalog.js.map +1 -0
- package/dist/tools/codegen/get-dbschema-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-dbschema-catalog.js +244 -0
- package/dist/tools/codegen/get-dbschema-catalog.js.map +1 -0
- package/dist/tools/codegen/get-field-validation-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-field-validation-catalog.js +186 -0
- package/dist/tools/codegen/get-field-validation-catalog.js.map +1 -0
- package/dist/tools/codegen/get-query-declarative-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-query-declarative-catalog.js +200 -0
- package/dist/tools/codegen/get-query-declarative-catalog.js.map +1 -0
- package/dist/tools/codegen/index.d.ts +2 -0
- package/dist/tools/codegen/index.js +43 -0
- package/dist/tools/codegen/index.js.map +1 -0
- package/dist/tools/codegen/list-tables.d.ts +2 -0
- package/dist/tools/codegen/list-tables.js +220 -0
- package/dist/tools/codegen/list-tables.js.map +1 -0
- package/dist/tools/codegen/sync-payload.d.ts +2 -0
- package/dist/tools/codegen/sync-payload.js +177 -0
- package/dist/tools/codegen/sync-payload.js.map +1 -0
- package/dist/tools/codegen/validate-dashboard-payload.d.ts +2 -0
- package/dist/tools/codegen/validate-dashboard-payload.js +239 -0
- package/dist/tools/codegen/validate-dashboard-payload.js.map +1 -0
- package/dist/tools/codegen/validate-payload.d.ts +2 -0
- package/dist/tools/codegen/validate-payload.js +166 -0
- package/dist/tools/codegen/validate-payload.js.map +1 -0
- package/dist/tools/codegen/validate-sql.d.ts +2 -0
- package/dist/tools/codegen/validate-sql.js +270 -0
- package/dist/tools/codegen/validate-sql.js.map +1 -0
- package/dist/tools/health/index.d.ts +2 -0
- package/dist/tools/health/index.js +5 -0
- package/dist/tools/health/index.js.map +1 -0
- package/dist/tools/health/ping.d.ts +2 -0
- package/dist/tools/health/ping.js +68 -0
- package/dist/tools/health/ping.js.map +1 -0
- package/dist/tools/runtime/check-launcher-exists.d.ts +2 -0
- package/dist/tools/runtime/check-launcher-exists.js +111 -0
- package/dist/tools/runtime/check-launcher-exists.js.map +1 -0
- package/dist/tools/runtime/check-status.d.ts +2 -0
- package/dist/tools/runtime/check-status.js +417 -0
- package/dist/tools/runtime/check-status.js.map +1 -0
- package/dist/tools/runtime/detect-config.d.ts +2 -0
- package/dist/tools/runtime/detect-config.js +130 -0
- package/dist/tools/runtime/detect-config.js.map +1 -0
- package/dist/tools/runtime/detect-project.d.ts +2 -0
- package/dist/tools/runtime/detect-project.js +132 -0
- package/dist/tools/runtime/detect-project.js.map +1 -0
- package/dist/tools/runtime/generate-launcher.d.ts +2 -0
- package/dist/tools/runtime/generate-launcher.js +438 -0
- package/dist/tools/runtime/generate-launcher.js.map +1 -0
- package/dist/tools/runtime/index.d.ts +2 -0
- package/dist/tools/runtime/index.js +15 -0
- package/dist/tools/runtime/index.js.map +1 -0
- package/dist/tools/runtime/validate-preflight.d.ts +2 -0
- package/dist/tools/runtime/validate-preflight.js +209 -0
- package/dist/tools/runtime/validate-preflight.js.map +1 -0
- package/dist/tools/setup/create-folder.d.ts +2 -0
- package/dist/tools/setup/create-folder.js +138 -0
- package/dist/tools/setup/create-folder.js.map +1 -0
- package/dist/tools/setup/get-config-schema.d.ts +2 -0
- package/dist/tools/setup/get-config-schema.js +158 -0
- package/dist/tools/setup/get-config-schema.js.map +1 -0
- package/dist/tools/setup/get-init-template.d.ts +2 -0
- package/dist/tools/setup/get-init-template.js +130 -0
- package/dist/tools/setup/get-init-template.js.map +1 -0
- package/dist/tools/setup/index.d.ts +2 -0
- package/dist/tools/setup/index.js +21 -0
- package/dist/tools/setup/index.js.map +1 -0
- package/dist/tools/setup/init-config.d.ts +2 -0
- package/dist/tools/setup/init-config.js +120 -0
- package/dist/tools/setup/init-config.js.map +1 -0
- package/dist/tools/setup/install-package.d.ts +2 -0
- package/dist/tools/setup/install-package.js +133 -0
- package/dist/tools/setup/install-package.js.map +1 -0
- package/dist/tools/setup/read-env.d.ts +2 -0
- package/dist/tools/setup/read-env.js +138 -0
- package/dist/tools/setup/read-env.js.map +1 -0
- package/dist/tools/setup/update-env.d.ts +2 -0
- package/dist/tools/setup/update-env.js +176 -0
- package/dist/tools/setup/update-env.js.map +1 -0
- package/dist/tools/setup/validate-config.d.ts +2 -0
- package/dist/tools/setup/validate-config.js +138 -0
- package/dist/tools/setup/validate-config.js.map +1 -0
- package/dist/tools/setup/write-env.d.ts +2 -0
- package/dist/tools/setup/write-env.js +168 -0
- package/dist/tools/setup/write-env.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { access, readFile } from 'node:fs/promises';
|
|
3
|
+
import { resolve, join } from 'node:path';
|
|
4
|
+
import { request as httpRequest } from 'node:http';
|
|
5
|
+
import { createServer as createNetServer } from 'node:net';
|
|
6
|
+
import { execProcess } from '../../lib/exec.js';
|
|
7
|
+
function isProcessAlive(pid) {
|
|
8
|
+
try {
|
|
9
|
+
process.kill(pid, 0);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
const e = err;
|
|
14
|
+
if (e.code === 'ESRCH')
|
|
15
|
+
return false;
|
|
16
|
+
if (e.code === 'EPERM')
|
|
17
|
+
return true;
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function isPortInUse(port, host = '127.0.0.1') {
|
|
22
|
+
return new Promise((resolveFn) => {
|
|
23
|
+
const tester = createNetServer()
|
|
24
|
+
.once('error', () => resolveFn(true))
|
|
25
|
+
.once('listening', () => {
|
|
26
|
+
tester.close(() => resolveFn(false));
|
|
27
|
+
})
|
|
28
|
+
.listen(port, host);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function probeHttp(url, timeoutMs) {
|
|
32
|
+
return new Promise((resolveFn) => {
|
|
33
|
+
const start = Date.now();
|
|
34
|
+
let settled = false;
|
|
35
|
+
const finish = (val) => {
|
|
36
|
+
if (settled)
|
|
37
|
+
return;
|
|
38
|
+
settled = true;
|
|
39
|
+
resolveFn(val);
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
const req = httpRequest(url, { method: 'GET' }, (res) => {
|
|
43
|
+
const elapsed = Date.now() - start;
|
|
44
|
+
res.resume();
|
|
45
|
+
res.on('end', () => finish({ status_code: res.statusCode ?? null, response_time_ms: elapsed, error: null }));
|
|
46
|
+
res.on('error', (err) => finish({ status_code: null, response_time_ms: elapsed, error: err.message }));
|
|
47
|
+
});
|
|
48
|
+
req.on('error', (err) => finish({ status_code: null, response_time_ms: null, error: err.message }));
|
|
49
|
+
req.setTimeout(timeoutMs, () => {
|
|
50
|
+
req.destroy(new Error('timeout'));
|
|
51
|
+
});
|
|
52
|
+
req.end();
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
56
|
+
finish({ status_code: null, response_time_ms: null, error: msg });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function parsePm2Apps(stdout) {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(stdout);
|
|
63
|
+
if (Array.isArray(parsed))
|
|
64
|
+
return parsed;
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function findProjectApp(apps, projectName) {
|
|
72
|
+
return apps.find((a) => a.name === projectName) ?? null;
|
|
73
|
+
}
|
|
74
|
+
export function registerRuntimeCheckStatus(server) {
|
|
75
|
+
server.registerTool('runtime_check_status', {
|
|
76
|
+
title: 'Check RESTForge Server Status',
|
|
77
|
+
description: `Check whether the RESTForge Server is currently running. Inspects (in order): (1) .restforge/server.pid file (legacy launchers), (2) port-based detection — tries to bind to the configured port; if the bind fails the port is in use and a host-mode server is presumed running, (3) PM2 process list via 'pm2 jlist', (4) optional HTTP health endpoint probe.
|
|
78
|
+
|
|
79
|
+
USE WHEN:
|
|
80
|
+
- The user asks "is the server running?", "apakah server jalan?", "cek status server", "server saya kenapa tidak respond"
|
|
81
|
+
- After running the launcher (server-start.bat or equivalent) — to confirm the server actually came up
|
|
82
|
+
- Before invoking other tools that talk to the running server — to verify it's reachable
|
|
83
|
+
- The user reports the server appears to be down — to diagnose stale PID file vs PM2 app issue vs not started
|
|
84
|
+
|
|
85
|
+
DO NOT USE FOR:
|
|
86
|
+
- Generating launcher files -> use 'runtime_generate_launcher'
|
|
87
|
+
- Validating preflight before starting -> use 'runtime_validate_preflight'
|
|
88
|
+
- Starting or stopping the server -> the user runs server-start.bat / server-stop.bat themselves
|
|
89
|
+
- Calling specific business endpoints to test functionality -> out of scope; this only checks server liveness
|
|
90
|
+
|
|
91
|
+
Detection modes:
|
|
92
|
+
- 'auto' (default): try host mode first (PID file then port-based), fall back to pm2 mode
|
|
93
|
+
- 'host': only inspect .restforge/server.pid and the port (skip PM2 entirely)
|
|
94
|
+
- 'pm2': only inspect 'pm2 jlist' output (skip PID file and port). Requires 'project' parameter to locate the right app.
|
|
95
|
+
|
|
96
|
+
Note on host-mode detection:
|
|
97
|
+
- Current launcher scripts (generated by 'runtime_generate_launcher') do NOT write .restforge/server.pid. Detection therefore relies primarily on the port-based bind probe — pass 'port' to enable it. The PID-file path is still inspected for backward compatibility with launchers generated by older versions.
|
|
98
|
+
- The port probe binds locally on host_address (default 127.0.0.1). If the server binds to a different interface, pass host_address accordingly. A port shown 'in use' confirms a process is listening, but does not guarantee it is the RESTForge server.
|
|
99
|
+
|
|
100
|
+
Optional HTTP health probe: if 'health_path' is set, the tool performs an HTTP GET against http://<host_address>:<port><health_path> and reports status code + response time. Without health_path, only process liveness is checked.
|
|
101
|
+
|
|
102
|
+
Preconditions:
|
|
103
|
+
- The cwd folder must exist.
|
|
104
|
+
- For host mode port-based detection: 'port' must be provided.
|
|
105
|
+
- For pm2 mode: PM2 must be installed and running on the user machine.
|
|
106
|
+
- For HTTP probe: 'port' must be provided alongside 'health_path'.
|
|
107
|
+
|
|
108
|
+
PRESENTATION GUIDANCE:
|
|
109
|
+
- Match the user's language.
|
|
110
|
+
- Never mention internal tool names.
|
|
111
|
+
- Summarise the state in one sentence: running, dead_pid (stale), http_unreachable (process up but endpoint dead), or not_running.
|
|
112
|
+
- For 'dead_pid': suggest the user run the stop launcher (or 'pm2 delete' for PM2 mode) to clean up before starting again.
|
|
113
|
+
- For 'http_unreachable': process is alive but the HTTP probe failed — usually a config mismatch (wrong port or path) or server still booting. Suggest re-trying after a moment or checking server logs.
|
|
114
|
+
- For 'not_running': the server isn't currently running. Suggest generating a launcher (if not already done) and asking the user to execute it.
|
|
115
|
+
- Do not echo the JSON envelope unless explicitly asked.
|
|
116
|
+
- The HTTP probe targets the local machine by default (127.0.0.1). If the server binds to a different address (SERVER_ADDRESS in .env), the user can pass host_address to override.`,
|
|
117
|
+
inputSchema: {
|
|
118
|
+
cwd: z
|
|
119
|
+
.string()
|
|
120
|
+
.min(1)
|
|
121
|
+
.describe('Absolute path of the project folder root (must contain .restforge/ if host mode was used)'),
|
|
122
|
+
mode: z
|
|
123
|
+
.enum(['auto', 'host', 'pm2'])
|
|
124
|
+
.default('auto')
|
|
125
|
+
.describe('Detection mode. auto = try host first, fallback to pm2. host = only check PID file. pm2 = only check pm2 list.'),
|
|
126
|
+
project: z
|
|
127
|
+
.string()
|
|
128
|
+
.min(1)
|
|
129
|
+
.optional()
|
|
130
|
+
.describe('Project name (e.g. mini-inventory). Required for pm2 mode to find the specific app in pm2 list. Optional for host mode (only used in summary text).'),
|
|
131
|
+
port: z
|
|
132
|
+
.number()
|
|
133
|
+
.int()
|
|
134
|
+
.min(1)
|
|
135
|
+
.max(65535)
|
|
136
|
+
.optional()
|
|
137
|
+
.describe('Port the server should be listening on. Required if health_path is set.'),
|
|
138
|
+
host_address: z
|
|
139
|
+
.string()
|
|
140
|
+
.min(1)
|
|
141
|
+
.default('127.0.0.1')
|
|
142
|
+
.describe('Host or IP for the HTTP health check (default 127.0.0.1).'),
|
|
143
|
+
health_path: z
|
|
144
|
+
.string()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe('If set, perform an HTTP GET against http://<host_address>:<port><health_path> and report the result. If omitted, no HTTP probe is performed.'),
|
|
147
|
+
timeout_ms: z
|
|
148
|
+
.number()
|
|
149
|
+
.int()
|
|
150
|
+
.min(100)
|
|
151
|
+
.max(30000)
|
|
152
|
+
.default(3000)
|
|
153
|
+
.describe('HTTP probe timeout in milliseconds (default 3000).'),
|
|
154
|
+
},
|
|
155
|
+
annotations: {
|
|
156
|
+
title: 'Check Server Status',
|
|
157
|
+
readOnlyHint: true,
|
|
158
|
+
idempotentHint: false,
|
|
159
|
+
},
|
|
160
|
+
}, async ({ cwd, mode, project, port, host_address, health_path, timeout_ms }) => {
|
|
161
|
+
const projectCwd = resolve(cwd);
|
|
162
|
+
// === Branch A: Precondition check ===
|
|
163
|
+
try {
|
|
164
|
+
await access(projectCwd);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return {
|
|
168
|
+
content: [
|
|
169
|
+
{
|
|
170
|
+
type: 'text',
|
|
171
|
+
text: `Precondition not met: cwd does not exist.
|
|
172
|
+
|
|
173
|
+
Project path: ${projectCwd}
|
|
174
|
+
|
|
175
|
+
For the assistant:
|
|
176
|
+
- The user pointed to a folder that does not exist on this machine.
|
|
177
|
+
- Suggest verifying the path or asking the user to navigate to the correct project root first.
|
|
178
|
+
- Match the user's language. Do not mention internal tool names.`,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
isError: false,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// === Validate health_path requires port ===
|
|
185
|
+
if (health_path !== undefined && port === undefined) {
|
|
186
|
+
return {
|
|
187
|
+
content: [
|
|
188
|
+
{
|
|
189
|
+
type: 'text',
|
|
190
|
+
text: `Invalid input: health_path is set but port is not provided. HTTP health check requires a port.
|
|
191
|
+
|
|
192
|
+
For the assistant:
|
|
193
|
+
- Ask the user which port the server is listening on, then retry with both health_path and port.`,
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
isError: false,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const result = {
|
|
200
|
+
cwd: projectCwd,
|
|
201
|
+
requested_mode: mode,
|
|
202
|
+
detected_mode: 'none',
|
|
203
|
+
pid_file: {
|
|
204
|
+
path: join(projectCwd, '.restforge', 'server.pid'),
|
|
205
|
+
exists: false,
|
|
206
|
+
pid: null,
|
|
207
|
+
process_alive: null,
|
|
208
|
+
},
|
|
209
|
+
port_check: {
|
|
210
|
+
checked: false,
|
|
211
|
+
port: port ?? null,
|
|
212
|
+
in_use: null,
|
|
213
|
+
},
|
|
214
|
+
pm2: {
|
|
215
|
+
checked: false,
|
|
216
|
+
cli_available: false,
|
|
217
|
+
apps_total: 0,
|
|
218
|
+
project_app: null,
|
|
219
|
+
},
|
|
220
|
+
http_check: {
|
|
221
|
+
requested: health_path !== undefined,
|
|
222
|
+
url: null,
|
|
223
|
+
status_code: null,
|
|
224
|
+
response_time_ms: null,
|
|
225
|
+
error: null,
|
|
226
|
+
},
|
|
227
|
+
summary: {
|
|
228
|
+
is_running: false,
|
|
229
|
+
state: 'unknown',
|
|
230
|
+
description: '',
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
// === Step 1: Host mode check ===
|
|
234
|
+
// Primary: PID file (legacy launchers from older runtime_generate_launcher).
|
|
235
|
+
// Fallback: port-based detection (current launchers no longer write a PID file).
|
|
236
|
+
if (mode === 'auto' || mode === 'host') {
|
|
237
|
+
try {
|
|
238
|
+
const raw = await readFile(result.pid_file.path, 'utf8');
|
|
239
|
+
const pid = parseInt(raw.trim(), 10);
|
|
240
|
+
result.pid_file.exists = true;
|
|
241
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
242
|
+
result.pid_file.pid = pid;
|
|
243
|
+
result.pid_file.process_alive = isProcessAlive(pid);
|
|
244
|
+
if (result.pid_file.process_alive === true) {
|
|
245
|
+
result.detected_mode = 'host';
|
|
246
|
+
}
|
|
247
|
+
else if (result.pid_file.process_alive === false) {
|
|
248
|
+
// stale PID file
|
|
249
|
+
result.summary.state = 'dead_pid';
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// file missing — leave defaults
|
|
255
|
+
}
|
|
256
|
+
// Port-based fallback: only if host not yet detected and port was provided.
|
|
257
|
+
if (result.detected_mode === 'none' && port !== undefined) {
|
|
258
|
+
result.port_check.checked = true;
|
|
259
|
+
const inUse = await isPortInUse(port, host_address);
|
|
260
|
+
result.port_check.in_use = inUse;
|
|
261
|
+
if (inUse) {
|
|
262
|
+
result.detected_mode = 'host';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// === Step 2: PM2 fallback (if not detected as host) ===
|
|
267
|
+
const tryPm2 = mode === 'pm2' || (mode === 'auto' && result.detected_mode === 'none');
|
|
268
|
+
if (tryPm2) {
|
|
269
|
+
result.pm2.checked = true;
|
|
270
|
+
const pm2Result = await execProcess('pm2', ['jlist'], { timeout: 10_000 });
|
|
271
|
+
if (pm2Result.success) {
|
|
272
|
+
result.pm2.cli_available = true;
|
|
273
|
+
const apps = parsePm2Apps(pm2Result.stdout);
|
|
274
|
+
result.pm2.apps_total = apps.length;
|
|
275
|
+
if (project !== undefined) {
|
|
276
|
+
const matched = findProjectApp(apps, project);
|
|
277
|
+
if (matched && matched.pid !== undefined && matched.pm2_env !== undefined) {
|
|
278
|
+
const status = matched.pm2_env.status ?? 'unknown';
|
|
279
|
+
const uptime = matched.pm2_env.pm_uptime;
|
|
280
|
+
const uptimeMs = status === 'online' && typeof uptime === 'number' ? Date.now() - uptime : null;
|
|
281
|
+
result.pm2.project_app = {
|
|
282
|
+
name: matched.name ?? project,
|
|
283
|
+
pid: matched.pid,
|
|
284
|
+
status,
|
|
285
|
+
uptime_ms: uptimeMs,
|
|
286
|
+
restart_count: matched.pm2_env.restart_time ?? 0,
|
|
287
|
+
};
|
|
288
|
+
if (status === 'online') {
|
|
289
|
+
result.detected_mode = 'pm2';
|
|
290
|
+
}
|
|
291
|
+
else if (result.detected_mode === 'none') {
|
|
292
|
+
result.summary.state = 'dead_pid';
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
result.pm2.cli_available = false;
|
|
299
|
+
if (mode === 'pm2') {
|
|
300
|
+
// Mode pm2 explicit: real error
|
|
301
|
+
return {
|
|
302
|
+
content: [
|
|
303
|
+
{
|
|
304
|
+
type: 'text',
|
|
305
|
+
text: `Failed to query PM2: pm2 command not available or returned an error.
|
|
306
|
+
|
|
307
|
+
Project path: ${projectCwd}
|
|
308
|
+
Command: ${pm2Result.command}
|
|
309
|
+
Exit code: ${pm2Result.exitCode}
|
|
310
|
+
stderr: ${pm2Result.stderr || '(empty)'}
|
|
311
|
+
|
|
312
|
+
For the assistant:
|
|
313
|
+
- The user requested PM2 mode but PM2 is not installed (or pm2 jlist failed).
|
|
314
|
+
- Suggest installing PM2 globally first: npm install -g pm2.
|
|
315
|
+
- Or, if the user is using host mode instead, retry with mode='host' or mode='auto'.
|
|
316
|
+
- Match the user's language. Do not mention internal tool names.`,
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
isError: true,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
// mode='auto': silently continue
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// === Step 3: HTTP health check (optional) ===
|
|
326
|
+
if (result.http_check.requested &&
|
|
327
|
+
port !== undefined &&
|
|
328
|
+
health_path !== undefined &&
|
|
329
|
+
result.detected_mode !== 'none') {
|
|
330
|
+
const url = `http://${host_address}:${port}${health_path.startsWith('/') ? health_path : '/' + health_path}`;
|
|
331
|
+
result.http_check.url = url;
|
|
332
|
+
const probe = await probeHttp(url, timeout_ms);
|
|
333
|
+
result.http_check.status_code = probe.status_code;
|
|
334
|
+
result.http_check.response_time_ms = probe.response_time_ms;
|
|
335
|
+
result.http_check.error = probe.error;
|
|
336
|
+
}
|
|
337
|
+
// === Step 4: Determine final summary state ===
|
|
338
|
+
if (result.detected_mode === 'host' || result.detected_mode === 'pm2') {
|
|
339
|
+
if (result.http_check.requested) {
|
|
340
|
+
if (result.http_check.status_code !== null &&
|
|
341
|
+
result.http_check.status_code >= 200 &&
|
|
342
|
+
result.http_check.status_code < 300) {
|
|
343
|
+
result.summary.state = 'running';
|
|
344
|
+
result.summary.is_running = true;
|
|
345
|
+
result.summary.description = `Server running in ${result.detected_mode} mode and HTTP health check returned ${result.http_check.status_code}.`;
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
result.summary.state = 'http_unreachable';
|
|
349
|
+
result.summary.is_running = false;
|
|
350
|
+
result.summary.description = `Server process exists in ${result.detected_mode} mode but HTTP health check failed (${result.http_check.error ?? `status ${result.http_check.status_code}`}).`;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
result.summary.state = 'running';
|
|
355
|
+
result.summary.is_running = true;
|
|
356
|
+
const detectionSource = result.detected_mode === 'host'
|
|
357
|
+
? result.pid_file.process_alive === true
|
|
358
|
+
? 'PID file points to a live process'
|
|
359
|
+
: `port ${result.port_check.port ?? '?'} is in use`
|
|
360
|
+
: 'PM2 app is online';
|
|
361
|
+
result.summary.description = `Server process detected in ${result.detected_mode} mode (${detectionSource}; HTTP probe not requested).`;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else if (result.summary.state === 'dead_pid') {
|
|
365
|
+
result.summary.is_running = false;
|
|
366
|
+
result.summary.description = result.pid_file.exists
|
|
367
|
+
? `PID file exists at ${result.pid_file.path} but the process is no longer alive (stale).`
|
|
368
|
+
: result.pm2.project_app
|
|
369
|
+
? `PM2 app '${result.pm2.project_app.name}' is not online (status=${result.pm2.project_app.status}).`
|
|
370
|
+
: 'A previous server instance is no longer alive.';
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
result.summary.state = 'not_running';
|
|
374
|
+
result.summary.is_running = false;
|
|
375
|
+
result.summary.description = 'No running server detected (no PID file and no matching PM2 app).';
|
|
376
|
+
}
|
|
377
|
+
const prettyJson = JSON.stringify(result, null, 2);
|
|
378
|
+
const labeledFacts = [
|
|
379
|
+
`Project path: ${result.cwd}`,
|
|
380
|
+
`Requested mode: ${result.requested_mode}`,
|
|
381
|
+
`Detected mode: ${result.detected_mode}`,
|
|
382
|
+
`PID file: ${result.pid_file.exists ? `exists (pid=${result.pid_file.pid}, alive=${result.pid_file.process_alive})` : 'absent'}`,
|
|
383
|
+
`Port check: ${result.port_check.checked ? `port ${result.port_check.port} ${result.port_check.in_use ? 'in use' : 'free'}` : 'not checked'}`,
|
|
384
|
+
`PM2 checked: ${result.pm2.checked ? `yes (cli_available=${result.pm2.cli_available}, apps_total=${result.pm2.apps_total})` : 'no'}`,
|
|
385
|
+
`HTTP check: ${result.http_check.requested
|
|
386
|
+
? `requested → ${result.http_check.url ?? '(no url)'} → ${result.http_check.status_code ?? `error: ${result.http_check.error ?? 'n/a'}`}`
|
|
387
|
+
: 'not requested'}`,
|
|
388
|
+
`State: ${result.summary.state}`,
|
|
389
|
+
`Is running: ${result.summary.is_running}`,
|
|
390
|
+
].join('\n');
|
|
391
|
+
return {
|
|
392
|
+
content: [
|
|
393
|
+
{
|
|
394
|
+
type: 'text',
|
|
395
|
+
text: `${result.summary.description}
|
|
396
|
+
|
|
397
|
+
${labeledFacts}
|
|
398
|
+
|
|
399
|
+
--- Status Result (JSON) ---
|
|
400
|
+
${prettyJson}
|
|
401
|
+
--- end Status Result (JSON) ---
|
|
402
|
+
|
|
403
|
+
For the assistant:
|
|
404
|
+
- Summarise the running state in plain language.
|
|
405
|
+
- If state='running': confirm to the user that the server is up. If HTTP check was performed, mention it returned 2xx and the response time.
|
|
406
|
+
- If state='http_unreachable': tell the user the process exists but the HTTP endpoint did not respond — suggest checking the server logs or that the health_path/port matches the actual config.
|
|
407
|
+
- If state='dead_pid': tell the user there's a stale PID file (or PM2 app in non-online state). Suggest cleaning up by running the stop launcher (or 'pm2 delete' for PM2 mode), then starting the server again.
|
|
408
|
+
- If state='not_running': tell the user no server is currently running. Suggest generating a launcher (if not already done) and running it.
|
|
409
|
+
- Do not paste the JSON unless the user explicitly asks. Do not mention internal tool names.
|
|
410
|
+
- Match the user's language.`,
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
isError: false,
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
//# sourceMappingURL=check-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-status.js","sourceRoot":"","sources":["../../../src/tools/runtime/check-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import { resolve, join } from 'node:path';
|
|
4
|
+
export function registerRuntimeDetectConfig(server) {
|
|
5
|
+
server.registerTool('runtime_detect_config', {
|
|
6
|
+
title: 'Detect RESTForge Config Files',
|
|
7
|
+
description: `Detect RESTForge configuration files by scanning the 'config/' folder for .env files. Each .env file is a candidate config to pass via --config=<filename> when invoking the RESTForge runtime.
|
|
8
|
+
|
|
9
|
+
USE WHEN:
|
|
10
|
+
- The user asks "which config files are available?", "config apa saja yang ada", "list .env"
|
|
11
|
+
- Before generating a launcher script — to confirm which config file to pass
|
|
12
|
+
- Before invoking 'runtime_generate_launcher' — to know if a config exists at all
|
|
13
|
+
- The user mentions running the server but the config file is unknown or ambiguous
|
|
14
|
+
|
|
15
|
+
DO NOT USE FOR:
|
|
16
|
+
- Listing project names -> use 'runtime_detect_project'
|
|
17
|
+
- Reading content of a specific .env file -> use 'setup_read_env'
|
|
18
|
+
- Validating connection credentials in a config -> use 'setup_validate_config' or 'runtime_validate_preflight'
|
|
19
|
+
|
|
20
|
+
Preconditions:
|
|
21
|
+
- The 'config/' folder must exist at <cwd>/config/. If missing, the precondition response will say so.
|
|
22
|
+
|
|
23
|
+
PRESENTATION GUIDANCE:
|
|
24
|
+
- Match the user's language.
|
|
25
|
+
- Never mention internal tool names.
|
|
26
|
+
- When exactly one config file is found, proceed without asking. When multiple are found, ask the user which environment they want.
|
|
27
|
+
- The filename is what gets passed as --config=<filename>; the runtime will resolve it relative to the config/ folder automatically.
|
|
28
|
+
- When a precondition is not met (folder missing or empty), frame it as a question or next-step suggestion rather than an error.`,
|
|
29
|
+
inputSchema: {
|
|
30
|
+
cwd: z
|
|
31
|
+
.string()
|
|
32
|
+
.min(1)
|
|
33
|
+
.describe('Absolute path of the project folder root (must contain config/)'),
|
|
34
|
+
},
|
|
35
|
+
annotations: {
|
|
36
|
+
title: 'Detect Config Files',
|
|
37
|
+
readOnlyHint: true,
|
|
38
|
+
idempotentHint: true,
|
|
39
|
+
},
|
|
40
|
+
}, async ({ cwd }) => {
|
|
41
|
+
const projectCwd = resolve(cwd);
|
|
42
|
+
const configDir = join(projectCwd, 'config');
|
|
43
|
+
let entries;
|
|
44
|
+
try {
|
|
45
|
+
entries = await readdir(configDir);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return {
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: 'text',
|
|
52
|
+
text: `Precondition not met: the config folder does not exist.
|
|
53
|
+
|
|
54
|
+
Project path: ${projectCwd}
|
|
55
|
+
Expected folder: ${configDir}
|
|
56
|
+
|
|
57
|
+
For the assistant:
|
|
58
|
+
- No config/ folder was found in this project.
|
|
59
|
+
- Before launching the runtime, the user needs at least one .env config file in 'config/'.
|
|
60
|
+
- Suggest setting up the initial config first (e.g. via the setup tools).
|
|
61
|
+
- Match the user's language. Do not mention internal tool names.`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
isError: false,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const configs = entries.filter((name) => name.endsWith('.env'));
|
|
68
|
+
const envelope = {
|
|
69
|
+
cwd: projectCwd,
|
|
70
|
+
config_dir: configDir,
|
|
71
|
+
configs,
|
|
72
|
+
count: configs.length,
|
|
73
|
+
};
|
|
74
|
+
const prettyJson = JSON.stringify(envelope, null, 2);
|
|
75
|
+
if (configs.length === 0) {
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: `No .env config files were found in config/.
|
|
81
|
+
|
|
82
|
+
Project path: ${projectCwd}
|
|
83
|
+
Config folder: ${configDir}
|
|
84
|
+
Count: 0
|
|
85
|
+
|
|
86
|
+
--- Detection Result (JSON) ---
|
|
87
|
+
${prettyJson}
|
|
88
|
+
--- end Detection Result (JSON) ---
|
|
89
|
+
|
|
90
|
+
For the assistant:
|
|
91
|
+
- The config/ folder exists but contains no .env files.
|
|
92
|
+
- The user needs to create one before launching the runtime (e.g. db-connection.env).
|
|
93
|
+
- Suggest setting up the initial config.
|
|
94
|
+
- Match the user's language. Do not mention internal tool names.`,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
isError: false,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const isSingle = configs.length === 1;
|
|
101
|
+
const summary = isSingle
|
|
102
|
+
? `One config file detected: '${configs[0]}'.`
|
|
103
|
+
: `${configs.length} config files detected: ${configs.map((c) => `'${c}'`).join(', ')}.`;
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: 'text',
|
|
108
|
+
text: `${summary}
|
|
109
|
+
|
|
110
|
+
Project path: ${projectCwd}
|
|
111
|
+
Config folder: ${configDir}
|
|
112
|
+
Count: ${configs.length}
|
|
113
|
+
|
|
114
|
+
--- Detection Result (JSON) ---
|
|
115
|
+
${prettyJson}
|
|
116
|
+
--- end Detection Result (JSON) ---
|
|
117
|
+
|
|
118
|
+
For the assistant:
|
|
119
|
+
- ${isSingle
|
|
120
|
+
? `Only one config file is present, so it can be used without asking.`
|
|
121
|
+
: `Multiple config files are present. Ask the user which environment to use (e.g. db-connection.env vs production.env).`}
|
|
122
|
+
- The config filename is passed to the runtime as --config=<filename>.
|
|
123
|
+
- Match the user's language. Do not mention internal tool names.`,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
isError: false,
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=detect-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-config.js","sourceRoot":"","sources":["../../../src/tools/runtime/detect-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAG1C,MAAM,UAAU,2BAA2B,CAAC,MAAiB;IAC3D,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,KAAK,EAAE,+BAA+B;QACtC,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;iIAqB8G;QAC3H,WAAW,EAAE;YACX,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,iEAAiE,CAAC;SAC/E;QACD,WAAW,EAAE;YACX,KAAK,EAAE,qBAAqB;YAC5B,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;SACrB;KACF,EACD,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;QAChB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAE7C,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;mBACP,SAAS;;;;;;iEAMqC;qBACpD;iBACF;gBACD,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAChE,MAAM,QAAQ,GAAG;YACf,GAAG,EAAE,UAAU;YACf,UAAU,EAAE,SAAS;YACrB,OAAO;YACP,KAAK,EAAE,OAAO,CAAC,MAAM;SACtB,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAErD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;iBACT,SAAS;;;;EAIxB,UAAU;;;;;;;iEAOqD;qBACpD;iBACF;gBACD,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,QAAQ;YACtB,CAAC,CAAC,8BAA8B,OAAO,CAAC,CAAC,CAAC,IAAI;YAC9C,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,2BAA2B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAE3F,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,GAAG,OAAO;;gBAEZ,UAAU;iBACT,SAAS;SACjB,OAAO,CAAC,MAAM;;;EAGrB,UAAU;;;;IAKE,QAAQ;wBACN,CAAC,CAAC,oEAAoE;wBACtE,CAAC,CAAC,sHACN;;iEAEqD;iBACtD;aACF;YACD,OAAO,EAAE,KAAK;SACf,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|