@yinuo-ngm/mcp-server 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -208
- package/lib/audit/audit-event.d.ts +14 -0
- package/lib/audit/audit-event.js +2 -0
- package/lib/audit/audit-log.service.d.ts +7 -0
- package/lib/audit/audit-log.service.js +187 -0
- package/lib/audit/redact.d.ts +3 -0
- package/lib/audit/redact.js +28 -0
- package/lib/catalog/capabilities/blocked-local-actions.d.ts +1 -0
- package/lib/catalog/capabilities/blocked-local-actions.js +18 -0
- package/lib/catalog/capabilities/frontend-standard.d.ts +2 -0
- package/lib/catalog/capabilities/frontend-standard.js +36 -0
- package/lib/catalog/capabilities/hub-v2.d.ts +2 -0
- package/lib/catalog/capabilities/hub-v2.js +34 -0
- package/lib/catalog/capabilities/nginx.d.ts +2 -0
- package/lib/catalog/capabilities/nginx.js +23 -0
- package/lib/catalog/capabilities/project.d.ts +2 -0
- package/lib/catalog/capabilities/project.js +23 -0
- package/lib/catalog/capabilities/router.d.ts +2 -0
- package/lib/catalog/capabilities/router.js +11 -0
- package/lib/catalog/capabilities/runtime.d.ts +2 -0
- package/lib/catalog/capabilities/runtime.js +17 -0
- package/lib/catalog/capabilities/workspace.d.ts +2 -0
- package/lib/catalog/capabilities/workspace.js +23 -0
- package/lib/catalog/helpers.d.ts +3 -0
- package/lib/catalog/helpers.js +42 -0
- package/lib/catalog/index.d.ts +4 -0
- package/lib/catalog/index.js +23 -0
- package/lib/catalog/tools/frontend-standard.d.ts +2 -0
- package/lib/catalog/tools/frontend-standard.js +166 -0
- package/lib/catalog/tools/hub-v2-api.d.ts +2 -0
- package/lib/catalog/tools/hub-v2-api.js +124 -0
- package/lib/catalog/tools/hub-v2-docs.d.ts +2 -0
- package/lib/catalog/tools/hub-v2-docs.js +40 -0
- package/lib/catalog/tools/nginx.d.ts +2 -0
- package/lib/catalog/tools/nginx.js +82 -0
- package/lib/catalog/tools/project.d.ts +2 -0
- package/lib/catalog/tools/project.js +124 -0
- package/lib/catalog/tools/router.d.ts +2 -0
- package/lib/catalog/tools/router.js +26 -0
- package/lib/catalog/tools/runtime.d.ts +2 -0
- package/lib/catalog/tools/runtime.js +40 -0
- package/lib/catalog/tools/workspace.d.ts +2 -0
- package/lib/catalog/tools/workspace.js +75 -0
- package/lib/catalog/types.d.ts +15 -0
- package/lib/catalog/types.js +2 -0
- package/lib/context/create-tool-context.js +11 -10
- package/lib/context/local-server-client.d.ts +2 -0
- package/lib/context/local-server-client.js +174 -0
- package/lib/context/tool-context.d.ts +36 -0
- package/lib/doctor.d.ts +8 -0
- package/lib/doctor.js +194 -0
- package/lib/errors/error-codes.d.ts +12 -0
- package/lib/errors/error-codes.js +14 -0
- package/lib/errors/mcp-tool-error.d.ts +8 -0
- package/lib/errors/mcp-tool-error.js +14 -0
- package/lib/filesystem/project-files.d.ts +18 -0
- package/lib/filesystem/project-files.js +112 -0
- package/lib/git/local-git-read-service.d.ts +2 -0
- package/lib/git/local-git-read-service.js +96 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +4 -0
- package/lib/policy/assert-tool-policy.js +10 -1
- package/lib/register-tools.js +67 -10
- package/lib/registry/tool-names.d.ts +95 -0
- package/lib/registry/tool-names.js +97 -0
- package/lib/services/path-guard.service.d.ts +4 -0
- package/lib/services/path-guard.service.js +75 -0
- package/lib/services/permission.service.d.ts +5 -0
- package/lib/services/permission.service.js +38 -0
- package/lib/services/project-resolver.service.d.ts +32 -0
- package/lib/services/project-resolver.service.js +95 -0
- package/lib/standard/frontend-standard.default.d.ts +2 -0
- package/lib/standard/frontend-standard.default.js +51 -0
- package/lib/standard/frontend-standard.schema.d.ts +196 -0
- package/lib/standard/frontend-standard.schema.js +61 -0
- package/lib/standard/frontend-standard.service.d.ts +79 -0
- package/lib/standard/frontend-standard.service.js +115 -0
- package/lib/standard/project-scan.d.ts +9 -0
- package/lib/standard/project-scan.js +91 -0
- package/lib/standard/validators/angular-structure.validator.d.ts +4 -0
- package/lib/standard/validators/angular-structure.validator.js +75 -0
- package/lib/standard/validators/component.validator.d.ts +4 -0
- package/lib/standard/validators/component.validator.js +94 -0
- package/lib/standard/validators/git.validator.d.ts +8 -0
- package/lib/standard/validators/git.validator.js +32 -0
- package/lib/standard/validators/review.validator.d.ts +15 -0
- package/lib/standard/validators/review.validator.js +67 -0
- package/lib/standard/validators/test.validator.d.ts +19 -0
- package/lib/standard/validators/test.validator.js +89 -0
- package/lib/tool-catalog.d.ts +2 -0
- package/lib/tool-catalog.js +6 -0
- package/lib/tools/angular/angular-standard.tools.d.ts +2 -0
- package/lib/tools/angular/angular-standard.tools.js +53 -0
- package/lib/tools/angular/index.d.ts +1 -0
- package/lib/tools/angular/index.js +5 -0
- package/lib/tools/capability.tools.d.ts +2 -0
- package/lib/tools/capability.tools.js +205 -0
- package/lib/tools/controlled/index.d.ts +2 -0
- package/lib/tools/controlled/index.js +13 -0
- package/lib/tools/controlled/local-server.d.ts +6 -0
- package/lib/tools/controlled/local-server.js +17 -0
- package/lib/tools/controlled/operation-policy.d.ts +22 -0
- package/lib/tools/controlled/operation-policy.js +50 -0
- package/lib/tools/controlled/operation-result.d.ts +30 -0
- package/lib/tools/controlled/operation-result.js +33 -0
- package/lib/tools/controlled/schemas.d.ts +159 -0
- package/lib/tools/controlled/schemas.js +49 -0
- package/lib/tools/controlled.tools.d.ts +1 -0
- package/lib/tools/controlled.tools.js +5 -0
- package/lib/tools/file-write.tools.d.ts +2 -0
- package/lib/tools/file-write.tools.js +70 -0
- package/lib/tools/git.tools.js +109 -8
- package/lib/tools/hub-v2/client.d.ts +6 -1
- package/lib/tools/hub-v2/client.js +15 -0
- package/lib/tools/hub-v2/config/config-paths.d.ts +2 -0
- package/lib/tools/hub-v2/config/config-paths.js +17 -0
- package/lib/tools/hub-v2/config/env.d.ts +1 -0
- package/lib/tools/hub-v2/config/env.js +12 -0
- package/lib/tools/hub-v2/config/index.d.ts +8 -0
- package/lib/tools/hub-v2/config/index.js +18 -0
- package/lib/tools/hub-v2/config/jsonc.d.ts +5 -0
- package/lib/tools/hub-v2/config/jsonc.js +86 -0
- package/lib/tools/hub-v2/config/load-config.d.ts +18 -0
- package/lib/tools/hub-v2/config/load-config.js +93 -0
- package/lib/tools/hub-v2/config/project-selector.d.ts +5 -0
- package/lib/tools/hub-v2/config/project-selector.js +92 -0
- package/lib/tools/hub-v2/config/resolve-context.d.ts +13 -0
- package/lib/tools/hub-v2/config/resolve-context.js +33 -0
- package/lib/tools/hub-v2/docs.tools.js +138 -4
- package/lib/tools/hub-v2/index.js +2 -0
- package/lib/tools/hub-v2/issues-workflow.tools.d.ts +2 -0
- package/lib/tools/hub-v2/issues-workflow.tools.js +199 -0
- package/lib/tools/hub-v2/issues.tools.js +96 -6
- package/lib/tools/hub-v2/projects.tools.js +16 -3
- package/lib/tools/hub-v2/raw.d.ts +8 -0
- package/lib/tools/hub-v2/raw.js +33 -0
- package/lib/tools/hub-v2/rd.tools.js +167 -8
- package/lib/tools/hub-v2/schemas.d.ts +668 -71
- package/lib/tools/hub-v2/schemas.js +152 -1
- package/lib/tools/hub-v2/upload.tools.js +53 -5
- package/lib/tools/index.d.ts +1 -0
- package/lib/tools/index.js +22 -0
- package/lib/tools/log.tools.js +33 -6
- package/lib/tools/nginx/index.d.ts +1 -0
- package/lib/tools/nginx/index.js +5 -0
- package/lib/tools/nginx/nginx-control.tools.d.ts +2 -0
- package/lib/tools/nginx/nginx-control.tools.js +133 -0
- package/lib/tools/nginx/nginx-proxy.d.ts +24 -0
- package/lib/tools/nginx/nginx-proxy.js +154 -0
- package/lib/tools/nginx.tools.d.ts +2 -0
- package/lib/tools/nginx.tools.js +111 -0
- package/lib/tools/project/index.d.ts +2 -0
- package/lib/tools/project/index.js +7 -0
- package/lib/tools/project/launch-status.d.ts +10 -0
- package/lib/tools/project/launch-status.js +78 -0
- package/lib/tools/project/local-diagnostics.d.ts +19 -0
- package/lib/tools/project/local-diagnostics.js +97 -0
- package/lib/tools/project/observe-redaction.d.ts +3 -0
- package/lib/tools/project/observe-redaction.js +25 -0
- package/lib/tools/project/observe-runtime.d.ts +72 -0
- package/lib/tools/project/observe-runtime.js +147 -0
- package/lib/tools/project/project-control.tools.d.ts +2 -0
- package/lib/tools/project/project-control.tools.js +216 -0
- package/lib/tools/project/project-observe.tools.d.ts +2 -0
- package/lib/tools/project/project-observe.tools.js +191 -0
- package/lib/tools/project/runtime-config.d.ts +7 -0
- package/lib/tools/project/runtime-config.js +50 -0
- package/lib/tools/project-observe.tools.d.ts +1 -0
- package/lib/tools/project-observe.tools.js +5 -0
- package/lib/tools/project.tools.d.ts +8 -0
- package/lib/tools/project.tools.js +97 -6
- package/lib/tools/proxy.tools.js +4 -4
- package/lib/tools/review/index.d.ts +1 -0
- package/lib/tools/review/index.js +5 -0
- package/lib/tools/review/review.tools.d.ts +2 -0
- package/lib/tools/review/review.tools.js +152 -0
- package/lib/tools/runtime/index.d.ts +1 -0
- package/lib/tools/runtime/index.js +5 -0
- package/lib/tools/runtime/runtime-control.tools.d.ts +2 -0
- package/lib/tools/runtime/runtime-control.tools.js +89 -0
- package/lib/tools/runtime.tools.js +41 -4
- package/lib/tools/standard/index.d.ts +1 -0
- package/lib/tools/standard/index.js +5 -0
- package/lib/tools/standard/standard.tools.d.ts +2 -0
- package/lib/tools/standard/standard.tools.js +91 -0
- package/lib/tools/task.tools.js +44 -9
- package/lib/tools/test/index.d.ts +1 -0
- package/lib/tools/test/index.js +5 -0
- package/lib/tools/test/test-standard.tools.d.ts +2 -0
- package/lib/tools/test/test-standard.tools.js +51 -0
- package/lib/tools/tool-catalog.d.ts +2 -0
- package/lib/tools/tool-catalog.js +7 -0
- package/lib/tools/workflow/frontend-workflow.tools.d.ts +2 -0
- package/lib/tools/workflow/frontend-workflow.tools.js +364 -0
- package/lib/tools/workflow/index.d.ts +1 -0
- package/lib/tools/workflow/index.js +5 -0
- package/lib/tools/workspace-package.d.ts +22 -0
- package/lib/tools/workspace-package.js +130 -0
- package/lib/tools/workspace.tools.d.ts +7 -0
- package/lib/tools/workspace.tools.js +336 -0
- package/lib/utils/errors.js +6 -1
- package/lib/utils/result.d.ts +9 -0
- package/lib/utils/result.js +9 -0
- package/lib/workflow/frontend-task.schema.d.ts +83 -0
- package/lib/workflow/frontend-task.schema.js +25 -0
- package/lib/workflow/frontend-task.service.d.ts +57 -0
- package/lib/workflow/frontend-task.service.js +195 -0
- package/lib/workflow/workflow-status.d.ts +2 -0
- package/lib/workflow/workflow-status.js +14 -0
- package/lib/workflow/workflow-transition.d.ts +9 -0
- package/lib/workflow/workflow-transition.js +38 -0
- package/package.json +5 -3
- package/lib/tools/hub-v2/config.d.ts +0 -34
- package/lib/tools/hub-v2/config.js +0 -297
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.localServerAvailability = localServerAvailability;
|
|
4
|
+
exports.runtimeSummary = runtimeSummary;
|
|
5
|
+
exports.taskSummary = taskSummary;
|
|
6
|
+
exports.latestTimestamp = latestTimestamp;
|
|
7
|
+
exports.taskStatus = taskStatus;
|
|
8
|
+
exports.listProjectRows = listProjectRows;
|
|
9
|
+
exports.activeTasks = activeTasks;
|
|
10
|
+
exports.fitLogLines = fitLogLines;
|
|
11
|
+
const observe_redaction_1 = require("./observe-redaction");
|
|
12
|
+
function defaultLocalServer() {
|
|
13
|
+
return { available: false, reason: "local server client is not configured" };
|
|
14
|
+
}
|
|
15
|
+
async function localServerAvailability(context) {
|
|
16
|
+
return context.services.localServer ? context.services.localServer.availability() : defaultLocalServer();
|
|
17
|
+
}
|
|
18
|
+
function pidExists(pid) {
|
|
19
|
+
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
process.kill(pid, 0);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error?.code === "ESRCH")
|
|
27
|
+
return false;
|
|
28
|
+
if (error?.code === "EPERM")
|
|
29
|
+
return true;
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function parsePortFromUrl(value) {
|
|
34
|
+
if (typeof value !== "string")
|
|
35
|
+
return null;
|
|
36
|
+
try {
|
|
37
|
+
const url = new URL(value);
|
|
38
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
39
|
+
return null;
|
|
40
|
+
const port = Number(url.port || (url.protocol === "https:" ? 443 : 80));
|
|
41
|
+
return { url: (0, observe_redaction_1.redactText)(value), host: url.hostname, port, protocol: url.protocol.replace(":", "") };
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function portSummary(runtime) {
|
|
48
|
+
const urls = Array.isArray(runtime?.urls) ? runtime?.urls : [];
|
|
49
|
+
return urls.map(parsePortFromUrl).filter(Boolean);
|
|
50
|
+
}
|
|
51
|
+
function runtimeSummary(runtime) {
|
|
52
|
+
if (!runtime)
|
|
53
|
+
return undefined;
|
|
54
|
+
return {
|
|
55
|
+
taskId: runtime.taskId,
|
|
56
|
+
runId: runtime.runId,
|
|
57
|
+
status: runtime.status,
|
|
58
|
+
pid: runtime.pid,
|
|
59
|
+
pidExists: pidExists(runtime.pid),
|
|
60
|
+
startedAt: runtime.startedAt,
|
|
61
|
+
stoppedAt: runtime.stoppedAt,
|
|
62
|
+
lastOutputAt: runtime.lastOutputAt,
|
|
63
|
+
readyAt: runtime.readyAt,
|
|
64
|
+
exitCode: runtime.exitCode,
|
|
65
|
+
signal: runtime.signal,
|
|
66
|
+
lastError: typeof runtime.lastError === "string" ? (0, observe_redaction_1.redactText)(runtime.lastError) : runtime.lastError,
|
|
67
|
+
urls: Array.isArray(runtime.urls) ? runtime.urls.map((url) => typeof url === "string" ? (0, observe_redaction_1.redactText)(url) : url) : [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function taskSummary(rowOrRuntime) {
|
|
71
|
+
const row = rowOrRuntime;
|
|
72
|
+
const spec = row.spec;
|
|
73
|
+
const runtime = (row.runtime ?? (!row.spec ? rowOrRuntime : undefined));
|
|
74
|
+
return {
|
|
75
|
+
taskId: spec?.id ?? runtime?.taskId,
|
|
76
|
+
projectId: spec?.projectId ?? runtime?.projectId,
|
|
77
|
+
projectPath: spec?.projectRoot,
|
|
78
|
+
scriptName: spec?.name ?? runtime?.name,
|
|
79
|
+
status: runtime?.status ?? "idle",
|
|
80
|
+
pid: runtime?.pid,
|
|
81
|
+
startTime: runtime?.startedAt,
|
|
82
|
+
runtime: runtimeSummary(runtime),
|
|
83
|
+
ports: portSummary(runtime),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function latestTimestamp(runtime) {
|
|
87
|
+
const values = [runtime?.lastOutputAt, runtime?.stoppedAt, runtime?.startedAt].filter((item) => typeof item === "number");
|
|
88
|
+
return values.length ? Math.max(...values) : undefined;
|
|
89
|
+
}
|
|
90
|
+
async function taskStatus(context, taskId) {
|
|
91
|
+
const localServer = context.services.localServer;
|
|
92
|
+
const availability = await localServerAvailability(context);
|
|
93
|
+
if (availability.available && localServer) {
|
|
94
|
+
return { runtime: await localServer.getTaskStatus(taskId), controlPlane: "local-server", localServer: availability };
|
|
95
|
+
}
|
|
96
|
+
return { runtime: null, controlPlane: "unavailable", localServer: availability };
|
|
97
|
+
}
|
|
98
|
+
async function listProjectRows(context, projectId, availability) {
|
|
99
|
+
const localServer = context.services.localServer;
|
|
100
|
+
if (availability.available && localServer) {
|
|
101
|
+
return { rows: await localServer.listTaskViews(projectId), controlPlane: "local-server" };
|
|
102
|
+
}
|
|
103
|
+
return { rows: [], controlPlane: "unavailable" };
|
|
104
|
+
}
|
|
105
|
+
async function activeTasks(context, availability) {
|
|
106
|
+
const localServer = context.services.localServer;
|
|
107
|
+
if (availability.available && localServer) {
|
|
108
|
+
return { tasks: await localServer.listActiveTasks(), controlPlane: "local-server" };
|
|
109
|
+
}
|
|
110
|
+
return { tasks: [], controlPlane: "unavailable" };
|
|
111
|
+
}
|
|
112
|
+
function fitLogLines(lines, maxChars) {
|
|
113
|
+
const redacted = (0, observe_redaction_1.redactValue)(lines);
|
|
114
|
+
const selected = [];
|
|
115
|
+
let used = 0;
|
|
116
|
+
for (let i = redacted.length - 1; i >= 0; i--) {
|
|
117
|
+
let item = redacted[i];
|
|
118
|
+
const remaining = maxChars - used;
|
|
119
|
+
if (remaining <= 0)
|
|
120
|
+
break;
|
|
121
|
+
const length = JSON.stringify(item).length;
|
|
122
|
+
if (length > remaining) {
|
|
123
|
+
if (selected.length === 0) {
|
|
124
|
+
item = truncateLogItem(item, remaining);
|
|
125
|
+
selected.unshift(item);
|
|
126
|
+
used += JSON.stringify(item).length;
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
if (selected.length > 0 && used + length > maxChars)
|
|
131
|
+
break;
|
|
132
|
+
selected.unshift(item);
|
|
133
|
+
used += length;
|
|
134
|
+
}
|
|
135
|
+
return { lines: selected, returned: selected.length, omitted: Math.max(0, redacted.length - selected.length), maxChars };
|
|
136
|
+
}
|
|
137
|
+
function truncateLogItem(item, maxChars) {
|
|
138
|
+
const budget = Math.max(0, maxChars - 80);
|
|
139
|
+
if (typeof item === "string")
|
|
140
|
+
return `${item.slice(0, budget)}...[truncated]`;
|
|
141
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
142
|
+
const record = item;
|
|
143
|
+
if (typeof record.text === "string")
|
|
144
|
+
return { ...record, text: `${record.text.slice(0, budget)}...[truncated]`, truncated: true };
|
|
145
|
+
}
|
|
146
|
+
return { text: `${JSON.stringify(item).slice(0, budget)}...[truncated]`, truncated: true };
|
|
147
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.projectControlTools = projectControlTools;
|
|
4
|
+
const operation_result_1 = require("../controlled/operation-result");
|
|
5
|
+
const workspace_tools_1 = require("../workspace.tools");
|
|
6
|
+
const operation_policy_1 = require("../controlled/operation-policy");
|
|
7
|
+
const local_server_1 = require("../controlled/local-server");
|
|
8
|
+
const result_1 = require("../../utils/result");
|
|
9
|
+
const schemas_1 = require("../controlled/schemas");
|
|
10
|
+
const runtime_config_1 = require("./runtime-config");
|
|
11
|
+
const launch_status_1 = require("./launch-status");
|
|
12
|
+
const project_resolver_service_1 = require("../../services/project-resolver.service");
|
|
13
|
+
const error_codes_1 = require("../../errors/error-codes");
|
|
14
|
+
async function findScriptTaskViaServer(context, projectId, script) {
|
|
15
|
+
const { server, availability } = await (0, local_server_1.requireLocalServer)(context);
|
|
16
|
+
if (!server) {
|
|
17
|
+
return { availability, row: undefined };
|
|
18
|
+
}
|
|
19
|
+
const rows = await server.refreshTaskProject(projectId);
|
|
20
|
+
return {
|
|
21
|
+
availability,
|
|
22
|
+
row: rows.find((row) => row?.spec?.name === script),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async function findStopCandidates(context, args) {
|
|
26
|
+
const serverCheck = await (0, local_server_1.requireLocalServer)(context);
|
|
27
|
+
if (serverCheck.server) {
|
|
28
|
+
if (args.taskId) {
|
|
29
|
+
try {
|
|
30
|
+
const runtime = await serverCheck.server.getTaskStatus(args.taskId);
|
|
31
|
+
return {
|
|
32
|
+
candidates: (0, launch_status_1.runtimeStatus)(runtime) === "running" || (0, launch_status_1.runtimeStatus)(runtime) === "stopping"
|
|
33
|
+
? [{ taskId: args.taskId, runtime }]
|
|
34
|
+
: [],
|
|
35
|
+
controlPlane: "local-server",
|
|
36
|
+
localServer: serverCheck.availability,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return { candidates: [], controlPlane: "local-server", localServer: serverCheck.availability };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (args.projectId) {
|
|
44
|
+
const project = await projectResolver(context).resolveProject(args.projectId);
|
|
45
|
+
const rows = await serverCheck.server.listTaskViews(project.id);
|
|
46
|
+
return {
|
|
47
|
+
candidates: rows
|
|
48
|
+
.filter((row) => (0, launch_status_1.runtimeStatus)(row?.runtime) === "running" || (0, launch_status_1.runtimeStatus)(row?.runtime) === "stopping")
|
|
49
|
+
.filter((row) => !args.script || row?.spec?.name === args.script)
|
|
50
|
+
.map((row) => ({ taskId: row.spec.id, spec: row.spec, runtime: row.runtime })),
|
|
51
|
+
controlPlane: "local-server",
|
|
52
|
+
localServer: serverCheck.availability,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
candidates: (await serverCheck.server.listActiveTasks()).map((runtime) => ({ taskId: runtime.taskId, runtime })),
|
|
57
|
+
controlPlane: "local-server",
|
|
58
|
+
localServer: serverCheck.availability,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
candidates: [],
|
|
63
|
+
controlPlane: "unavailable",
|
|
64
|
+
localServer: serverCheck.availability,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function hasStopLocator(args) {
|
|
68
|
+
return Boolean(args.taskId || args.projectId);
|
|
69
|
+
}
|
|
70
|
+
function projectResolver(context) {
|
|
71
|
+
return context.services.projectResolver ?? new project_resolver_service_1.ProjectResolverService(context.services.core.project);
|
|
72
|
+
}
|
|
73
|
+
function runScriptTool() {
|
|
74
|
+
return {
|
|
75
|
+
name: "ngm_project_run_script",
|
|
76
|
+
description: "Skill ngm-project. Controlled execute tool for running an existing package.json script for a registered ng-manager projectId through the local ng-manager server task runtime. Prefer this over direct shell npm/pnpm/yarn commands so UI, CLI, desktop, WebSocket logs, and audit stay in sync. Previews by default; real execution requires confirm=true and NGM_MCP_ALLOW_EXECUTE=true. It never accepts arbitrary cwd, workspaceRoot, absolute paths, or shell commands.",
|
|
77
|
+
riskLevel: "execute",
|
|
78
|
+
allowPreviewWhenBlocked: true,
|
|
79
|
+
deferPolicyToHandler: true,
|
|
80
|
+
isConfirmed: operation_result_1.isConfirmed,
|
|
81
|
+
inputSchema: schemas_1.runScriptSchema,
|
|
82
|
+
async handler(args, context) {
|
|
83
|
+
const confirmed = (0, operation_result_1.isConfirmed)(args);
|
|
84
|
+
if (confirmed) {
|
|
85
|
+
const policyBlock = (0, operation_policy_1.requireExecutePolicy)("execute", "medium", `Run package.json script "${args.script}" for registered project "${args.projectId}".`);
|
|
86
|
+
if (policyBlock)
|
|
87
|
+
return (0, result_1.ok)("ngm_project_run_script", { ...(0, operation_result_1.controlledFields)("execute", true, (0, operation_policy_1.requiredEnv)("execute")), ...policyBlock });
|
|
88
|
+
}
|
|
89
|
+
const project = await projectResolver(context).resolveProject(args.projectId);
|
|
90
|
+
const safetyMessage = `Run package.json script "${args.script}" for managed project "${project.name}".`;
|
|
91
|
+
const packageJson = await (0, workspace_tools_1.readWorkspacePackageJson)(project.root, project.root);
|
|
92
|
+
const scriptCommand = packageJson.scripts[args.script];
|
|
93
|
+
if (!scriptCommand) {
|
|
94
|
+
return (0, result_1.ok)("ngm_project_run_script", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "script not found in package.json", {
|
|
95
|
+
project: { id: project.id, name: project.name, path: project.root },
|
|
96
|
+
availableScripts: Object.keys(packageJson.scripts),
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
const packageManager = (0, runtime_config_1.normalizePackageManager)(project.packageManager);
|
|
100
|
+
const requestedRuntime = (0, runtime_config_1.runtimeConfigForProject)(project);
|
|
101
|
+
const resolvedRuntime = await context.services.core.nodeRuntime.resolveRuntime(requestedRuntime);
|
|
102
|
+
const preview = {
|
|
103
|
+
...(0, operation_result_1.controlledFields)("execute", confirmed, (0, operation_policy_1.requiredEnv)("execute")),
|
|
104
|
+
operation: (0, operation_result_1.operation)("preview", "execute", "medium", safetyMessage),
|
|
105
|
+
project: { id: project.id, name: project.name, path: project.root },
|
|
106
|
+
script: { name: args.script, command: scriptCommand },
|
|
107
|
+
packageManager,
|
|
108
|
+
requestedRuntime,
|
|
109
|
+
resolvedRuntime,
|
|
110
|
+
logHint: "Use ngm_log_tail with taskId or runId after execution.",
|
|
111
|
+
};
|
|
112
|
+
if (!confirmed)
|
|
113
|
+
return (0, result_1.ok)("ngm_project_run_script", preview);
|
|
114
|
+
const serverCheck = await (0, local_server_1.requireLocalServer)(context);
|
|
115
|
+
if (!serverCheck.server) {
|
|
116
|
+
return (0, result_1.ok)("ngm_project_run_script", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "local ng-manager server is unavailable; start ng-manager UI/server before executing managed project scripts", {
|
|
117
|
+
...(0, operation_result_1.controlledFields)("execute", true, (0, operation_policy_1.requiredEnv)("execute")),
|
|
118
|
+
localServer: serverCheck.availability,
|
|
119
|
+
project: { id: project.id, name: project.name, path: project.root },
|
|
120
|
+
script: args.script,
|
|
121
|
+
}, error_codes_1.McpErrorCodes.LOCAL_SERVER_UNAVAILABLE));
|
|
122
|
+
}
|
|
123
|
+
await serverCheck.server.refreshProjectScripts(project.id);
|
|
124
|
+
const { row } = await findScriptTaskViaServer(context, project.id, args.script);
|
|
125
|
+
if (!row?.spec?.runnable) {
|
|
126
|
+
return (0, result_1.ok)("ngm_project_run_script", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "managed task spec was not runnable", {
|
|
127
|
+
project: { id: project.id, name: project.name, path: project.root },
|
|
128
|
+
script: args.script,
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
let runtime;
|
|
132
|
+
try {
|
|
133
|
+
runtime = await serverCheck.server.startTask(row.spec.id);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
return (0, result_1.ok)("ngm_project_run_script", {
|
|
137
|
+
...preview,
|
|
138
|
+
...(0, operation_result_1.controlledFields)("execute", true, (0, operation_policy_1.requiredEnv)("execute")),
|
|
139
|
+
operation: (0, operation_result_1.operation)("failed", "execute", "medium", safetyMessage),
|
|
140
|
+
task: row.spec,
|
|
141
|
+
taskId: row.spec.id,
|
|
142
|
+
controlPlane: "local-server",
|
|
143
|
+
localServer: serverCheck.availability,
|
|
144
|
+
result: { status: "failed", reason: error instanceof Error ? error.message : String(error) },
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const launch = await (0, launch_status_1.observeLaunch)(context, row.spec.id, runtime, args.waitMs ?? 3000);
|
|
148
|
+
const runId = (0, launch_status_1.runtimeRunId)(launch.runtime) ?? (0, launch_status_1.runtimeRunId)(runtime);
|
|
149
|
+
return (0, result_1.ok)("ngm_project_run_script", {
|
|
150
|
+
...preview,
|
|
151
|
+
...(0, operation_result_1.controlledFields)("execute", true, (0, operation_policy_1.requiredEnv)("execute")),
|
|
152
|
+
operation: (0, operation_result_1.operation)("executed", "execute", "medium", safetyMessage),
|
|
153
|
+
controlPlane: "local-server",
|
|
154
|
+
localServer: serverCheck.availability,
|
|
155
|
+
task: row.spec,
|
|
156
|
+
taskId: row.spec.id,
|
|
157
|
+
runtime,
|
|
158
|
+
launch,
|
|
159
|
+
logHint: `Use ngm_log_tail with taskId=${row.spec.id}${runId ? ` or runId=${runId}` : ""}.`,
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function stopProjectTool() {
|
|
165
|
+
return {
|
|
166
|
+
name: "ngm_project_stop",
|
|
167
|
+
description: "Skill ngm-project. Controlled execute tool for stopping ng-manager managed tasks by taskId or registered projectId via the local ng-manager server runtime. Prefer this over kill/taskkill so shared task state, WebSocket logs, and audit stay consistent. Previews by default; real stop requires confirm=true and NGM_MCP_ALLOW_EXECUTE=true. It never kills arbitrary PIDs or unmanaged processes.",
|
|
168
|
+
riskLevel: "execute",
|
|
169
|
+
allowPreviewWhenBlocked: true,
|
|
170
|
+
deferPolicyToHandler: true,
|
|
171
|
+
isConfirmed: operation_result_1.isConfirmed,
|
|
172
|
+
inputSchema: schemas_1.stopProjectSchema,
|
|
173
|
+
async handler(args, context) {
|
|
174
|
+
const confirmed = (0, operation_result_1.isConfirmed)(args);
|
|
175
|
+
const safetyMessage = "Stop one ng-manager managed task process.";
|
|
176
|
+
if (args.script && !args.projectId) {
|
|
177
|
+
return (0, result_1.ok)("ngm_project_stop", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "script stop requires projectId so MCP can resolve a registered ng-manager project", {
|
|
178
|
+
...(0, operation_result_1.controlledFields)("execute", confirmed, (0, operation_policy_1.requiredEnv)("execute")),
|
|
179
|
+
}, error_codes_1.McpErrorCodes.TOOL_INPUT_INVALID));
|
|
180
|
+
}
|
|
181
|
+
if (confirmed && !hasStopLocator(args)) {
|
|
182
|
+
return (0, result_1.ok)("ngm_project_stop", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "confirmed stop requires taskId or projectId; MCP will not stop the only active task by inference", {
|
|
183
|
+
...(0, operation_result_1.controlledFields)("execute", true, (0, operation_policy_1.requiredEnv)("execute")),
|
|
184
|
+
}, error_codes_1.McpErrorCodes.CONFIRM_REQUIRED));
|
|
185
|
+
}
|
|
186
|
+
if (confirmed) {
|
|
187
|
+
const policyBlock = (0, operation_policy_1.requireExecutePolicy)("execute", "medium", safetyMessage);
|
|
188
|
+
if (policyBlock)
|
|
189
|
+
return (0, result_1.ok)("ngm_project_stop", { ...(0, operation_result_1.controlledFields)("execute", true, (0, operation_policy_1.requiredEnv)("execute")), ...policyBlock });
|
|
190
|
+
}
|
|
191
|
+
const { candidates, controlPlane, localServer } = await findStopCandidates(context, args);
|
|
192
|
+
if (controlPlane === "unavailable") {
|
|
193
|
+
return (0, result_1.ok)("ngm_project_stop", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "ng-manager local server is unavailable; start ngm server or ngm ui before stopping managed tasks", {
|
|
194
|
+
...(0, operation_result_1.controlledFields)("execute", confirmed, (0, operation_policy_1.requiredEnv)("execute")),
|
|
195
|
+
localServer,
|
|
196
|
+
}, error_codes_1.McpErrorCodes.LOCAL_SERVER_UNAVAILABLE));
|
|
197
|
+
}
|
|
198
|
+
if (candidates.length === 0)
|
|
199
|
+
return (0, result_1.ok)("ngm_project_stop", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "no managed running task matched the request"));
|
|
200
|
+
if (candidates.length > 1)
|
|
201
|
+
return (0, result_1.ok)("ngm_project_stop", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "multiple managed tasks matched; pass taskId or script", { candidates }));
|
|
202
|
+
const candidate = candidates[0];
|
|
203
|
+
const preview = { ...(0, operation_result_1.controlledFields)("execute", confirmed, (0, operation_policy_1.requiredEnv)("execute")), operation: (0, operation_result_1.operation)("preview", "execute", "medium", safetyMessage), controlPlane, localServer, target: candidate, taskId: candidate.taskId };
|
|
204
|
+
if (!confirmed)
|
|
205
|
+
return (0, result_1.ok)("ngm_project_stop", preview);
|
|
206
|
+
const server = context.services.localServer;
|
|
207
|
+
if (!server)
|
|
208
|
+
return (0, result_1.ok)("ngm_project_stop", (0, operation_result_1.blocked)("execute", "medium", safetyMessage, "local server client is not configured"));
|
|
209
|
+
const runtime = await server.stopTask(candidate.taskId);
|
|
210
|
+
return (0, result_1.ok)("ngm_project_stop", { ...preview, ...(0, operation_result_1.controlledFields)("execute", true, (0, operation_policy_1.requiredEnv)("execute")), operation: (0, operation_result_1.operation)("executed", "execute", "medium", safetyMessage), runtime, launch: { status: (0, launch_status_1.runtimeStatus)(runtime) } });
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function projectControlTools() {
|
|
215
|
+
return [runScriptTool(), stopProjectTool()];
|
|
216
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.projectObserveTools = projectObserveTools;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const project_tools_1 = require("../project.tools");
|
|
6
|
+
const result_1 = require("../../utils/result");
|
|
7
|
+
const local_diagnostics_1 = require("./local-diagnostics");
|
|
8
|
+
const observe_redaction_1 = require("./observe-redaction");
|
|
9
|
+
const observe_runtime_1 = require("./observe-runtime");
|
|
10
|
+
const taskListSchema = zod_1.z.object({
|
|
11
|
+
projectId: zod_1.z.string().trim().min(1).optional(),
|
|
12
|
+
activeOnly: zod_1.z.boolean().optional(),
|
|
13
|
+
}).strict();
|
|
14
|
+
const taskStatusSchema = zod_1.z.object({
|
|
15
|
+
taskId: zod_1.z.string().trim().min(1),
|
|
16
|
+
}).strict();
|
|
17
|
+
const taskLogsSchema = zod_1.z.object({
|
|
18
|
+
taskId: zod_1.z.string().trim().min(1).optional(),
|
|
19
|
+
runId: zod_1.z.string().trim().min(1).optional(),
|
|
20
|
+
tail: zod_1.z.number().int().min(1).max(200).optional(),
|
|
21
|
+
maxChars: zod_1.z.number().int().min(200).max(20000).optional(),
|
|
22
|
+
}).strict();
|
|
23
|
+
const portCheckSchema = zod_1.z.object({
|
|
24
|
+
host: zod_1.z.string().trim().min(1).max(253).optional(),
|
|
25
|
+
port: zod_1.z.number().int().min(1).max(65535),
|
|
26
|
+
timeoutMs: zod_1.z.number().int().min(50).max(3000).optional(),
|
|
27
|
+
}).strict();
|
|
28
|
+
const healthCheckSchema = zod_1.z.object({
|
|
29
|
+
projectId: zod_1.z.string().trim().min(1).optional(),
|
|
30
|
+
taskId: zod_1.z.string().trim().min(1).optional(),
|
|
31
|
+
url: zod_1.z.string().trim().min(1).optional(),
|
|
32
|
+
method: zod_1.z.enum(["GET", "HEAD"]).optional(),
|
|
33
|
+
timeoutMs: zod_1.z.number().int().min(100).max(5000).optional(),
|
|
34
|
+
headers: zod_1.z.record(zod_1.z.string()).optional(),
|
|
35
|
+
includeBodyPreview: zod_1.z.boolean().optional(),
|
|
36
|
+
}).strict();
|
|
37
|
+
async function deriveHealthUrl(context, args) {
|
|
38
|
+
if (args.url)
|
|
39
|
+
return { url: args.url };
|
|
40
|
+
if (args.taskId) {
|
|
41
|
+
const status = await (0, observe_runtime_1.taskStatus)(context, args.taskId);
|
|
42
|
+
const urls = Array.isArray(status.runtime?.urls) ? status.runtime?.urls.filter((item) => typeof item === "string") : [];
|
|
43
|
+
return urls[0] ? { url: urls[0], runtime: status.runtime } : { reason: "task runtime has no detected URL", runtime: status.runtime };
|
|
44
|
+
}
|
|
45
|
+
if (args.projectId) {
|
|
46
|
+
const project = await (0, project_tools_1.resolveProject)(context, args);
|
|
47
|
+
const availability = await (0, observe_runtime_1.localServerAvailability)(context);
|
|
48
|
+
const { rows } = await (0, observe_runtime_1.listProjectRows)(context, project.id, availability);
|
|
49
|
+
const row = rows.find((item) => item.runtime?.status === "running" && Array.isArray(item.runtime?.urls) && item.runtime.urls.length > 0);
|
|
50
|
+
const url = row?.runtime?.urls?.find((item) => typeof item === "string");
|
|
51
|
+
return url ? { url, runtime: row?.runtime } : { reason: "project has no running task with detected URL" };
|
|
52
|
+
}
|
|
53
|
+
return { reason: "url, taskId, or projectId is required" };
|
|
54
|
+
}
|
|
55
|
+
function listTasksTool() {
|
|
56
|
+
return {
|
|
57
|
+
name: "ngm_project_list_tasks",
|
|
58
|
+
description: "Skill ngm-project. Read-only diagnostic tool that lists ng-manager managed project tasks from the local server control plane when available. It does not execute scripts, stop processes, or inspect unmanaged system processes.",
|
|
59
|
+
riskLevel: "read",
|
|
60
|
+
inputSchema: taskListSchema,
|
|
61
|
+
async handler(args, context) {
|
|
62
|
+
const availability = await (0, observe_runtime_1.localServerAvailability)(context);
|
|
63
|
+
if (!availability.available) {
|
|
64
|
+
return (0, result_1.ok)("ngm_project_list_tasks", { controlPlane: "unavailable", localServer: availability, status: "unavailable", reason: "ng-manager local server is not running; start it with ngm server or ngm ui to inspect managed task state", taskGroups: [] });
|
|
65
|
+
}
|
|
66
|
+
if (args.activeOnly && !args.projectId) {
|
|
67
|
+
const { tasks, controlPlane } = await (0, observe_runtime_1.activeTasks)(context, availability);
|
|
68
|
+
return (0, result_1.ok)("ngm_project_list_tasks", { controlPlane, localServer: availability, activeOnly: true, tasks: tasks.map(observe_runtime_1.taskSummary) });
|
|
69
|
+
}
|
|
70
|
+
const projects = args.projectId ? [await (0, project_tools_1.resolveProject)(context, args)] : await context.services.core.project.list();
|
|
71
|
+
const taskGroups = [];
|
|
72
|
+
for (const project of projects) {
|
|
73
|
+
const { rows, controlPlane } = await (0, observe_runtime_1.listProjectRows)(context, project.id, availability);
|
|
74
|
+
const tasks = rows.map(observe_runtime_1.taskSummary).filter((task) => !args.activeOnly || task.status === "running" || task.status === "stopping");
|
|
75
|
+
taskGroups.push({ project: { id: project.id, name: project.name, root: project.root }, controlPlane, tasks });
|
|
76
|
+
}
|
|
77
|
+
return (0, result_1.ok)("ngm_project_list_tasks", { localServer: availability, activeOnly: args.activeOnly === true, taskGroups });
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function taskStatusTool() {
|
|
82
|
+
return {
|
|
83
|
+
name: "ngm_project_task_status",
|
|
84
|
+
description: "Skill ngm-project. Read-only diagnostic tool that returns structured status for one ng-manager managed task, including runtime status, pid existence, exit code, recent update time, and error summary.",
|
|
85
|
+
riskLevel: "read",
|
|
86
|
+
inputSchema: taskStatusSchema,
|
|
87
|
+
async handler(args, context) {
|
|
88
|
+
const status = await (0, observe_runtime_1.taskStatus)(context, args.taskId);
|
|
89
|
+
const runtime = status.runtime;
|
|
90
|
+
if (!runtime) {
|
|
91
|
+
return (0, result_1.ok)("ngm_project_task_status", { controlPlane: status.controlPlane, localServer: status.localServer, taskId: args.taskId, status: "unavailable", running: false, pidExists: null, reason: "ng-manager local server is not running or the task is not available from the shared server runtime" });
|
|
92
|
+
}
|
|
93
|
+
return (0, result_1.ok)("ngm_project_task_status", {
|
|
94
|
+
controlPlane: status.controlPlane,
|
|
95
|
+
localServer: status.localServer,
|
|
96
|
+
taskId: args.taskId,
|
|
97
|
+
running: runtime?.status === "running",
|
|
98
|
+
pidExists: (0, observe_runtime_1.runtimeSummary)(runtime)?.pidExists ?? null,
|
|
99
|
+
status: runtime?.status ?? "unknown",
|
|
100
|
+
exitCode: runtime?.exitCode,
|
|
101
|
+
signal: runtime?.signal,
|
|
102
|
+
updatedAt: (0, observe_runtime_1.latestTimestamp)(runtime ?? undefined),
|
|
103
|
+
errorSummary: typeof runtime?.lastError === "string" ? (0, observe_redaction_1.redactText)(runtime.lastError) : undefined,
|
|
104
|
+
runtime: (0, observe_runtime_1.runtimeSummary)(runtime ?? undefined),
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function taskLogsTool() {
|
|
110
|
+
return {
|
|
111
|
+
name: "ngm_project_task_logs",
|
|
112
|
+
description: "Skill ngm-project. Read-only diagnostic tool that reads a limited tail of logs for one ng-manager managed task/run. It enforces line and character limits and redacts token/password/secret/authorization-like values.",
|
|
113
|
+
riskLevel: "read",
|
|
114
|
+
inputSchema: taskLogsSchema,
|
|
115
|
+
async handler(args, context) {
|
|
116
|
+
const tail = args.tail ?? 50;
|
|
117
|
+
const maxChars = args.maxChars ?? 8000;
|
|
118
|
+
const availability = await (0, observe_runtime_1.localServerAvailability)(context);
|
|
119
|
+
let runId = args.runId;
|
|
120
|
+
if (!runId && args.taskId) {
|
|
121
|
+
const status = await (0, observe_runtime_1.taskStatus)(context, args.taskId);
|
|
122
|
+
if (!status.runtime)
|
|
123
|
+
return (0, result_1.ok)("ngm_project_task_logs", { controlPlane: status.controlPlane, localServer: status.localServer, status: "unavailable", reason: "ng-manager local server is not running or the task has no shared runtime state", taskId: args.taskId, tail, maxChars, lines: [] });
|
|
124
|
+
runId = status.runtime?.runId;
|
|
125
|
+
}
|
|
126
|
+
if (!runId)
|
|
127
|
+
return (0, result_1.ok)("ngm_project_task_logs", { controlPlane: availability.available ? "local-server" : "unavailable", localServer: availability, status: "unavailable", reason: "runId could not be resolved from the provided taskId", taskId: args.taskId, tail, maxChars, lines: [] });
|
|
128
|
+
const localServer = context.services.localServer;
|
|
129
|
+
if (!availability.available || !localServer)
|
|
130
|
+
return (0, result_1.ok)("ngm_project_task_logs", { controlPlane: "unavailable", localServer: availability, status: "unavailable", reason: "ng-manager local server is not running; start it with ngm server or ngm ui to read shared task logs", taskId: args.taskId, runId, tail, maxChars, lines: [] });
|
|
131
|
+
const fitted = (0, observe_runtime_1.fitLogLines)(await localServer.getTaskLogTail(runId, tail), maxChars);
|
|
132
|
+
return (0, result_1.ok)("ngm_project_task_logs", { controlPlane: "local-server", localServer: availability, status: fitted.lines.length > 0 ? "ok" : "empty", reason: fitted.lines.length > 0 ? undefined : "no task log lines were found for this run", taskId: args.taskId, runId, tail, ...fitted });
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function portCheckTool() {
|
|
137
|
+
return {
|
|
138
|
+
name: "ngm_project_port_check",
|
|
139
|
+
description: "Skill ngm-project. Read-only diagnostic tool that checks one local host/port for TCP listening state without running shell commands, killing processes, occupying the port, or scanning ranges.",
|
|
140
|
+
riskLevel: "read",
|
|
141
|
+
inputSchema: portCheckSchema,
|
|
142
|
+
async handler(args) {
|
|
143
|
+
const host = args.host ?? "127.0.0.1";
|
|
144
|
+
const timeoutMs = args.timeoutMs ?? 800;
|
|
145
|
+
const normalized = (0, local_diagnostics_1.normalizeLocalHost)(host);
|
|
146
|
+
if (!normalized.allowed)
|
|
147
|
+
return (0, result_1.ok)("ngm_project_port_check", { host, port: args.port, timeoutMs, status: "blocked", reason: normalized.reason });
|
|
148
|
+
return (0, result_1.ok)("ngm_project_port_check", { host, checkedHost: normalized.connectHost, port: args.port, timeoutMs, ...(await (0, local_diagnostics_1.portCheck)(normalized.connectHost, args.port, timeoutMs)) });
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function healthCheckTool() {
|
|
153
|
+
return {
|
|
154
|
+
name: "ngm_project_health_check",
|
|
155
|
+
description: "Skill ngm-project. Read-only diagnostic tool that performs a short local HTTP health check by URL or by URL detected from a managed project/task runtime. It uses GET by default, does not send request bodies, redacts sensitive headers, and returns only a small optional body preview.",
|
|
156
|
+
riskLevel: "read",
|
|
157
|
+
inputSchema: healthCheckSchema,
|
|
158
|
+
async handler(args, context) {
|
|
159
|
+
const timeoutMs = args.timeoutMs ?? 1500;
|
|
160
|
+
const method = args.method ?? "GET";
|
|
161
|
+
const derived = await deriveHealthUrl(context, args);
|
|
162
|
+
if (!derived.url)
|
|
163
|
+
return (0, result_1.ok)("ngm_project_health_check", { status: "unavailable", reachable: false, reason: derived.reason, runtime: (0, observe_runtime_1.runtimeSummary)(derived.runtime ?? undefined) });
|
|
164
|
+
let url;
|
|
165
|
+
try {
|
|
166
|
+
url = new URL(derived.url);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return (0, result_1.ok)("ngm_project_health_check", { status: "unavailable", reachable: false, reason: "invalid URL", url: (0, observe_redaction_1.redactText)(derived.url) });
|
|
170
|
+
}
|
|
171
|
+
const normalizedUrl = (0, local_diagnostics_1.normalizeLocalUrl)(url);
|
|
172
|
+
if ((url.protocol !== "http:" && url.protocol !== "https:") || !normalizedUrl.allowed) {
|
|
173
|
+
return (0, result_1.ok)("ngm_project_health_check", { status: "blocked", reachable: false, reason: "health_check only supports local http/https URLs", url: (0, observe_redaction_1.redactText)(derived.url) });
|
|
174
|
+
}
|
|
175
|
+
const startedAt = Date.now();
|
|
176
|
+
const requestHeaders = (0, local_diagnostics_1.redactHeaders)(args.headers);
|
|
177
|
+
try {
|
|
178
|
+
const response = await (0, local_diagnostics_1.fetchWithTimeout)(normalizedUrl.url.toString(), { method, headers: args.headers }, timeoutMs);
|
|
179
|
+
const responseTimeMs = Date.now() - startedAt;
|
|
180
|
+
const bodyPreview = args.includeBodyPreview ? await (0, local_diagnostics_1.readBodyPreview)(response, 500) : undefined;
|
|
181
|
+
return (0, result_1.ok)("ngm_project_health_check", { status: "ok", reachable: true, url: (0, observe_redaction_1.redactText)(url.toString()), checkedUrl: (0, observe_redaction_1.redactText)(normalizedUrl.url.toString()), method, timeoutMs, statusCode: response.status, responseTimeMs, requestHeaders, responseHeaders: (0, local_diagnostics_1.headersObject)(response.headers), bodyPreview, runtime: (0, observe_runtime_1.runtimeSummary)(derived.runtime ?? undefined) });
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
return (0, result_1.ok)("ngm_project_health_check", { status: "unavailable", reachable: false, url: (0, observe_redaction_1.redactText)(url.toString()), checkedUrl: (0, observe_redaction_1.redactText)(normalizedUrl.url.toString()), method, timeoutMs, responseTimeMs: Date.now() - startedAt, requestHeaders, error: error?.name === "AbortError" ? "timeout" : (0, observe_redaction_1.redactText)(error?.message ?? String(error)), runtime: (0, observe_runtime_1.runtimeSummary)(derived.runtime ?? undefined) });
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function projectObserveTools() {
|
|
190
|
+
return [listTasksTool(), taskStatusTool(), taskLogsTool(), portCheckTool(), healthCheckTool()];
|
|
191
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProjectNodeRuntimeConfig } from "@yinuo-ngm/project";
|
|
2
|
+
import type { RuntimeConfigArgs } from "../controlled/schemas";
|
|
3
|
+
import type { resolveProject } from "../project.tools";
|
|
4
|
+
export declare function normalizePackageManager(value: unknown): "npm" | "pnpm" | "yarn";
|
|
5
|
+
export declare function toRuntimeConfig(input: RuntimeConfigArgs): ProjectNodeRuntimeConfig;
|
|
6
|
+
export declare function runtimeConfigForProject(project: Awaited<ReturnType<typeof resolveProject>>): ProjectNodeRuntimeConfig;
|
|
7
|
+
export declare function validateTargetRuntime(runtime: ProjectNodeRuntimeConfig): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizePackageManager = normalizePackageManager;
|
|
4
|
+
exports.toRuntimeConfig = toRuntimeConfig;
|
|
5
|
+
exports.runtimeConfigForProject = runtimeConfigForProject;
|
|
6
|
+
exports.validateTargetRuntime = validateTargetRuntime;
|
|
7
|
+
function normalizePackageManager(value) {
|
|
8
|
+
return value === "pnpm" || value === "yarn" ? value : "npm";
|
|
9
|
+
}
|
|
10
|
+
function toRuntimeConfig(input) {
|
|
11
|
+
const config = {
|
|
12
|
+
type: input.type,
|
|
13
|
+
};
|
|
14
|
+
if (input.name !== undefined)
|
|
15
|
+
config.name = input.name;
|
|
16
|
+
if (input.version !== undefined)
|
|
17
|
+
config.version = input.version;
|
|
18
|
+
if (input.nodePath !== undefined)
|
|
19
|
+
config.nodePath = input.nodePath;
|
|
20
|
+
if (input.packageManager !== undefined)
|
|
21
|
+
config.packageManager = input.packageManager;
|
|
22
|
+
return config;
|
|
23
|
+
}
|
|
24
|
+
function runtimeConfigForProject(project) {
|
|
25
|
+
if (project.runtime) {
|
|
26
|
+
return {
|
|
27
|
+
...project.runtime,
|
|
28
|
+
packageManager: normalizePackageManager(project.runtime.packageManager ?? project.packageManager),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (project.nodeVersion) {
|
|
32
|
+
return {
|
|
33
|
+
type: "managed",
|
|
34
|
+
version: project.nodeVersion,
|
|
35
|
+
packageManager: normalizePackageManager(project.packageManager),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
type: "system",
|
|
40
|
+
packageManager: normalizePackageManager(project.packageManager),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function validateTargetRuntime(runtime) {
|
|
44
|
+
if (runtime.type === "managed" && !runtime.name && !runtime.version) {
|
|
45
|
+
throw new Error("managed runtime requires name or version");
|
|
46
|
+
}
|
|
47
|
+
if (runtime.type === "custom" && !runtime.nodePath) {
|
|
48
|
+
throw new Error("custom runtime requires nodePath");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { projectObserveTools } from "./project/project-observe.tools";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.projectObserveTools = void 0;
|
|
4
|
+
var project_observe_tools_1 = require("./project/project-observe.tools");
|
|
5
|
+
Object.defineProperty(exports, "projectObserveTools", { enumerable: true, get: function () { return project_observe_tools_1.projectObserveTools; } });
|
|
@@ -13,6 +13,14 @@ export declare const projectLocatorSchema: z.ZodObject<{
|
|
|
13
13
|
projectPath?: string | undefined;
|
|
14
14
|
}>;
|
|
15
15
|
type ProjectLocator = z.infer<typeof projectLocatorSchema>;
|
|
16
|
+
export interface McpProjectItem {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
path: string;
|
|
20
|
+
framework?: string;
|
|
21
|
+
favorite?: boolean;
|
|
22
|
+
scripts: string[];
|
|
23
|
+
}
|
|
16
24
|
export declare function resolveProject(context: ToolContext, locator: ProjectLocator): Promise<Project>;
|
|
17
25
|
export declare function projectTools(): McpToolDefinition[];
|
|
18
26
|
export {};
|