@mgsoftwarebv/mg-dashboard-mcp 7.0.7 → 7.0.8
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/dist/index.js +99 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -619,6 +619,12 @@ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
|
619
619
|
"INTERRUPTED",
|
|
620
620
|
"EXPIRED"
|
|
621
621
|
]);
|
|
622
|
+
var EXECUTE_PIPELINE_TASK = "execute-pipeline";
|
|
623
|
+
function validateExecutePipelineTestPayload(payload) {
|
|
624
|
+
const stepIds = payload.stepIds;
|
|
625
|
+
if (Array.isArray(stepIds) && stepIds.length > 0) return null;
|
|
626
|
+
return 'execute-pipeline requires real stepIds from a release row. Use a lightweight task (e.g. "hello-world") for connectivity checks, or pass a payload with releaseId (UUID) and non-empty stepIds.';
|
|
627
|
+
}
|
|
622
628
|
var TRIGGER_SERVER_ID = "03659d55-e194-400d-b82a-bf6457371ded";
|
|
623
629
|
var COMPOSE_PROJECT = "mg-dashboard-supabase-trigger";
|
|
624
630
|
var PG_CONTAINER = `${COMPOSE_PROJECT}-postgres-1`;
|
|
@@ -678,10 +684,10 @@ var TRIGGER_TOOL_MODULE_MAP = {
|
|
|
678
684
|
"trigger-run": "ci_cd"
|
|
679
685
|
};
|
|
680
686
|
async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
|
|
681
|
-
const
|
|
687
|
+
const sql29 = `SELECT re.\\"apiKey\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
|
|
682
688
|
const cmd = [
|
|
683
689
|
`PORT=$(docker port "${WA_CONTAINER}" 3000/tcp 2>/dev/null | head -1 | sed 's/.*://')`,
|
|
684
|
-
`KEY=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
690
|
+
`KEY=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null | tr -d '[:space:]')`,
|
|
685
691
|
'echo "$PORT|$KEY"'
|
|
686
692
|
].join(" && ");
|
|
687
693
|
const result = await sshExec2(conn, cmd, proxy);
|
|
@@ -702,8 +708,8 @@ async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
|
|
|
702
708
|
return { port, apiKey: apiKey2 };
|
|
703
709
|
}
|
|
704
710
|
async function fetchRunLogs(runId, conn, proxy, sshExec2) {
|
|
705
|
-
const
|
|
706
|
-
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
711
|
+
const sql29 = `SELECT level, message, \\"isError\\", \\"createdAt\\" FROM \\"TaskEvent\\" WHERE \\"runId\\" = '${runId}' AND level IN ('INFO','WARN','ERROR','DEBUG','LOG','TRACE') ORDER BY \\"startTime\\" ASC LIMIT 200`;
|
|
712
|
+
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null`;
|
|
707
713
|
const result = await sshExec2(conn, cmd, proxy);
|
|
708
714
|
const output = result.stdout.trim();
|
|
709
715
|
if (!output) return "";
|
|
@@ -785,8 +791,8 @@ async function handleTriggerTool(name, args2, deps) {
|
|
|
785
791
|
switch (name) {
|
|
786
792
|
// -----------------------------------------------------------------
|
|
787
793
|
case "trigger-list": {
|
|
788
|
-
const
|
|
789
|
-
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
794
|
+
const sql29 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
|
|
795
|
+
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null`;
|
|
790
796
|
const result = await sshExec2(conn, cmd, proxy);
|
|
791
797
|
const output = result.stdout.trim();
|
|
792
798
|
if (!output) {
|
|
@@ -867,8 +873,15 @@ ${rawJson.substring(0, 500)}` }] };
|
|
|
867
873
|
throw new Error(`Invalid JSON payload: ${String(args2.payload).substring(0, 200)}`);
|
|
868
874
|
}
|
|
869
875
|
}
|
|
876
|
+
const parsedPayload = JSON.parse(payload);
|
|
877
|
+
if (taskId === EXECUTE_PIPELINE_TASK) {
|
|
878
|
+
const validationError = validateExecutePipelineTestPayload(parsedPayload);
|
|
879
|
+
if (validationError) {
|
|
880
|
+
return { content: [{ type: "text", text: `Error: ${validationError}` }] };
|
|
881
|
+
}
|
|
882
|
+
}
|
|
870
883
|
const triggerBody = JSON.stringify({
|
|
871
|
-
payload:
|
|
884
|
+
payload: parsedPayload,
|
|
872
885
|
options: { tags: ["mcp-test"], test: true }
|
|
873
886
|
});
|
|
874
887
|
const triggerJson = await triggerApi(
|
|
@@ -944,9 +957,9 @@ async function fetchAndFormatRun(conn, proxy, sshExec2, instance, runId) {
|
|
|
944
957
|
return { content: [{ type: "text", text: `Invalid API response:
|
|
945
958
|
${rawJson.substring(0, 500)}` }] };
|
|
946
959
|
}
|
|
947
|
-
let
|
|
948
|
-
if (logs)
|
|
949
|
-
return { content: [{ type: "text", text:
|
|
960
|
+
let text7 = formatRunDetail(run);
|
|
961
|
+
if (logs) text7 += "\n\n--- Logs ---\n" + logs;
|
|
962
|
+
return { content: [{ type: "text", text: text7 }] };
|
|
950
963
|
}
|
|
951
964
|
async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSeconds) {
|
|
952
965
|
const pollInterval = 3e3;
|
|
@@ -968,10 +981,10 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
|
|
|
968
981
|
continue;
|
|
969
982
|
}
|
|
970
983
|
if (TERMINAL_STATUSES.has(run.status)) {
|
|
971
|
-
let
|
|
984
|
+
let text7 = formatRunDetail(run);
|
|
972
985
|
const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
|
|
973
|
-
if (logs)
|
|
974
|
-
return { content: [{ type: "text", text:
|
|
986
|
+
if (logs) text7 += "\n\n--- Logs ---\n" + logs;
|
|
987
|
+
return { content: [{ type: "text", text: text7 }] };
|
|
975
988
|
}
|
|
976
989
|
}
|
|
977
990
|
return {
|
|
@@ -3658,10 +3671,10 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
3658
3671
|
// }) as any;
|
|
3659
3672
|
// return merged;
|
|
3660
3673
|
// }
|
|
3661
|
-
catchall(
|
|
3674
|
+
catchall(index5) {
|
|
3662
3675
|
return new _ZodObject({
|
|
3663
3676
|
...this._def,
|
|
3664
|
-
catchall:
|
|
3677
|
+
catchall: index5
|
|
3665
3678
|
});
|
|
3666
3679
|
}
|
|
3667
3680
|
pick(mask) {
|
|
@@ -3979,9 +3992,9 @@ function mergeValues(a, b) {
|
|
|
3979
3992
|
return { valid: false };
|
|
3980
3993
|
}
|
|
3981
3994
|
const newArray = [];
|
|
3982
|
-
for (let
|
|
3983
|
-
const itemA = a[
|
|
3984
|
-
const itemB = b[
|
|
3995
|
+
for (let index5 = 0; index5 < a.length; index5++) {
|
|
3996
|
+
const itemA = a[index5];
|
|
3997
|
+
const itemB = b[index5];
|
|
3985
3998
|
const sharedValue = mergeValues(itemA, itemB);
|
|
3986
3999
|
if (!sharedValue.valid) {
|
|
3987
4000
|
return { valid: false };
|
|
@@ -4187,10 +4200,10 @@ var ZodMap = class extends ZodType {
|
|
|
4187
4200
|
}
|
|
4188
4201
|
const keyType = this._def.keyType;
|
|
4189
4202
|
const valueType = this._def.valueType;
|
|
4190
|
-
const pairs = [...ctx.data.entries()].map(([key, value],
|
|
4203
|
+
const pairs = [...ctx.data.entries()].map(([key, value], index5) => {
|
|
4191
4204
|
return {
|
|
4192
|
-
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [
|
|
4193
|
-
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [
|
|
4205
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index5, "key"])),
|
|
4206
|
+
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index5, "value"]))
|
|
4194
4207
|
};
|
|
4195
4208
|
});
|
|
4196
4209
|
if (ctx.common.async) {
|
|
@@ -5716,7 +5729,30 @@ pgTable(
|
|
|
5716
5729
|
index("github_token_owner_idx").on(table.owner)
|
|
5717
5730
|
]
|
|
5718
5731
|
);
|
|
5732
|
+
pgTable(
|
|
5733
|
+
"github_webhook_secret",
|
|
5734
|
+
{
|
|
5735
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5736
|
+
label: text("label").notNull(),
|
|
5737
|
+
owner: text("owner"),
|
|
5738
|
+
notes: text("notes"),
|
|
5739
|
+
secretEncrypted: text("secret_encrypted").notNull(),
|
|
5740
|
+
enabled: boolean("enabled").notNull().default(true),
|
|
5741
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5742
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5743
|
+
},
|
|
5744
|
+
(table) => [
|
|
5745
|
+
index("github_webhook_secret_label_idx").on(table.label),
|
|
5746
|
+
index("github_webhook_secret_owner_idx").on(table.owner)
|
|
5747
|
+
]
|
|
5748
|
+
);
|
|
5749
|
+
function assertPipelinePayload(payload) {
|
|
5750
|
+
if (!payload.stepIds?.length) {
|
|
5751
|
+
throw new Error("Cannot trigger execute-pipeline: stepIds is empty");
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5719
5754
|
async function triggerPipeline(payload) {
|
|
5755
|
+
assertPipelinePayload(payload);
|
|
5720
5756
|
return tasks.trigger(
|
|
5721
5757
|
"execute-pipeline",
|
|
5722
5758
|
payload
|
|
@@ -6168,6 +6204,24 @@ async function createReleaseForStage(userId, params) {
|
|
|
6168
6204
|
if (!triggerAuthorName && userId) {
|
|
6169
6205
|
triggerAuthorName = await resolveUserDisplayName(userId);
|
|
6170
6206
|
}
|
|
6207
|
+
const enabledSteps = buildStepList(
|
|
6208
|
+
stage,
|
|
6209
|
+
profile,
|
|
6210
|
+
await resolveBuildStepListOptions({
|
|
6211
|
+
profile,
|
|
6212
|
+
triggerSha,
|
|
6213
|
+
githubToken: token,
|
|
6214
|
+
isManual: triggerType === "manual" || triggerType === "mcp",
|
|
6215
|
+
stageId: params.stageId,
|
|
6216
|
+
deployRetryScripts: stage.deploy_retry_scripts
|
|
6217
|
+
})
|
|
6218
|
+
);
|
|
6219
|
+
if (enabledSteps.length === 0) {
|
|
6220
|
+
throw new CreateReleaseError(
|
|
6221
|
+
"No pipeline steps configured for this stage (enable a PM2/Docker stage_app or deployment_project_server)",
|
|
6222
|
+
"PRECONDITION_FAILED"
|
|
6223
|
+
);
|
|
6224
|
+
}
|
|
6171
6225
|
const initialStatus = "running";
|
|
6172
6226
|
let release;
|
|
6173
6227
|
try {
|
|
@@ -6201,18 +6255,6 @@ async function createReleaseForStage(userId, params) {
|
|
|
6201
6255
|
"INTERNAL_SERVER_ERROR"
|
|
6202
6256
|
);
|
|
6203
6257
|
}
|
|
6204
|
-
const enabledSteps = buildStepList(
|
|
6205
|
-
stage,
|
|
6206
|
-
profile,
|
|
6207
|
-
await resolveBuildStepListOptions({
|
|
6208
|
-
profile,
|
|
6209
|
-
triggerSha,
|
|
6210
|
-
githubToken: token,
|
|
6211
|
-
isManual: triggerType === "manual" || triggerType === "mcp",
|
|
6212
|
-
stageId: params.stageId,
|
|
6213
|
-
deployRetryScripts: stage.deploy_retry_scripts
|
|
6214
|
-
})
|
|
6215
|
-
);
|
|
6216
6258
|
const stepIds = [];
|
|
6217
6259
|
try {
|
|
6218
6260
|
stepIds.push(...await insertReleaseSteps(release.id, enabledSteps));
|
|
@@ -7435,7 +7477,7 @@ function getEncryptionKey() {
|
|
|
7435
7477
|
throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
|
|
7436
7478
|
return buf;
|
|
7437
7479
|
}
|
|
7438
|
-
function encrypt(
|
|
7480
|
+
function encrypt(text7) {
|
|
7439
7481
|
const key = getEncryptionKey();
|
|
7440
7482
|
const iv = randomBytes(ENC_IV_LENGTH);
|
|
7441
7483
|
const cipher = createCipheriv(
|
|
@@ -7443,7 +7485,7 @@ function encrypt(text6) {
|
|
|
7443
7485
|
new Uint8Array(key),
|
|
7444
7486
|
new Uint8Array(iv)
|
|
7445
7487
|
);
|
|
7446
|
-
let encrypted = cipher.update(
|
|
7488
|
+
let encrypted = cipher.update(text7, "utf8", "hex");
|
|
7447
7489
|
encrypted += cipher.final("hex");
|
|
7448
7490
|
const authTag = cipher.getAuthTag();
|
|
7449
7491
|
return Buffer.concat([
|
|
@@ -8163,10 +8205,10 @@ async function r2GetObjectRange(bucket, key, range) {
|
|
|
8163
8205
|
const body = result.Body;
|
|
8164
8206
|
if (!body?.transformToString)
|
|
8165
8207
|
throw new Error("R2 returned no readable body");
|
|
8166
|
-
const
|
|
8208
|
+
const text7 = await body.transformToString();
|
|
8167
8209
|
const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
|
|
8168
8210
|
return `${header}
|
|
8169
|
-
${
|
|
8211
|
+
${text7}`;
|
|
8170
8212
|
} catch (e) {
|
|
8171
8213
|
throw r2WrapError(bucket, key, e);
|
|
8172
8214
|
}
|
|
@@ -8529,15 +8571,15 @@ async function sftpRead(opts, filePath, proxy, options) {
|
|
|
8529
8571
|
clearTimeout(timer);
|
|
8530
8572
|
cleanup?.();
|
|
8531
8573
|
cleanup = void 0;
|
|
8532
|
-
const
|
|
8574
|
+
const text7 = Buffer.concat(
|
|
8533
8575
|
chunks.map((ch) => new Uint8Array(ch))
|
|
8534
8576
|
).toString("utf-8");
|
|
8535
8577
|
if (!isWholeFileRequest) {
|
|
8536
8578
|
const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
|
|
8537
8579
|
resolve(`${header}
|
|
8538
|
-
${
|
|
8580
|
+
${text7}`);
|
|
8539
8581
|
} else {
|
|
8540
|
-
resolve(
|
|
8582
|
+
resolve(text7);
|
|
8541
8583
|
}
|
|
8542
8584
|
});
|
|
8543
8585
|
rs.on("error", (e) => {
|
|
@@ -8667,11 +8709,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
|
|
|
8667
8709
|
if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
|
|
8668
8710
|
return e.names;
|
|
8669
8711
|
}
|
|
8670
|
-
function truncateForLLM(
|
|
8671
|
-
const totalBytes = Buffer.byteLength(
|
|
8712
|
+
function truncateForLLM(text7, maxBytes) {
|
|
8713
|
+
const totalBytes = Buffer.byteLength(text7, "utf8");
|
|
8672
8714
|
if (totalBytes <= maxBytes)
|
|
8673
|
-
return { text:
|
|
8674
|
-
const buf = Buffer.from(
|
|
8715
|
+
return { text: text7, truncated: false, totalBytes, shownBytes: totalBytes };
|
|
8716
|
+
const buf = Buffer.from(text7, "utf8");
|
|
8675
8717
|
let cut = maxBytes;
|
|
8676
8718
|
while (cut > 0 && (buf[cut] & 192) === 128) cut--;
|
|
8677
8719
|
const head = buf.subarray(0, cut).toString("utf8");
|
|
@@ -8701,10 +8743,10 @@ function isTransientSshError(stderr, exitCode) {
|
|
|
8701
8743
|
function postprocessResult(result, meta) {
|
|
8702
8744
|
if (!result.content?.length) return result;
|
|
8703
8745
|
const block = result.content[0];
|
|
8704
|
-
let
|
|
8705
|
-
const trunc = truncateForLLM(
|
|
8746
|
+
let text7 = String(block.text ?? "");
|
|
8747
|
+
const trunc = truncateForLLM(text7, RESPONSE_MAX_BYTES);
|
|
8706
8748
|
if (trunc.truncated) {
|
|
8707
|
-
|
|
8749
|
+
text7 = trunc.text + "\n\n... " + buildTruncationHint(
|
|
8708
8750
|
meta.toolName,
|
|
8709
8751
|
meta.args,
|
|
8710
8752
|
trunc.totalBytes,
|
|
@@ -8718,11 +8760,11 @@ function postprocessResult(result, meta) {
|
|
|
8718
8760
|
const parts = [`took ${tookStr}`, sizeStr];
|
|
8719
8761
|
if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
|
|
8720
8762
|
if (meta.cached) parts.push("cached");
|
|
8721
|
-
|
|
8763
|
+
text7 = `${text7}
|
|
8722
8764
|
|
|
8723
8765
|
[${parts.join(", ")}]`;
|
|
8724
8766
|
}
|
|
8725
|
-
return { ...result, content: [{ ...block, text:
|
|
8767
|
+
return { ...result, content: [{ ...block, text: text7 }] };
|
|
8726
8768
|
}
|
|
8727
8769
|
function buildPipelineScript(commands, shell, marker, stopOnError) {
|
|
8728
8770
|
if (shell === "powershell") {
|
|
@@ -9508,11 +9550,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
|
|
|
9508
9550
|
applied_by TEXT
|
|
9509
9551
|
);
|
|
9510
9552
|
`.trim();
|
|
9511
|
-
function normaliseMigrationSql(
|
|
9512
|
-
return
|
|
9553
|
+
function normaliseMigrationSql(sql29) {
|
|
9554
|
+
return sql29.replace(/\r\n/g, "\n").trim() + "\n";
|
|
9513
9555
|
}
|
|
9514
|
-
function migrationSha256(
|
|
9515
|
-
return createHash("sha256").update(normaliseMigrationSql(
|
|
9556
|
+
function migrationSha256(sql29) {
|
|
9557
|
+
return createHash("sha256").update(normaliseMigrationSql(sql29), "utf8").digest("hex");
|
|
9516
9558
|
}
|
|
9517
9559
|
function dollarQuoteTag(value) {
|
|
9518
9560
|
let tag = "_mcp";
|
|
@@ -11777,8 +11819,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11777
11819
|
};
|
|
11778
11820
|
const filtered = sortRows(applyFilter(only.rows));
|
|
11779
11821
|
if (format === "json") {
|
|
11780
|
-
const
|
|
11781
|
-
return { content: [{ type: "text", text:
|
|
11822
|
+
const text8 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
|
|
11823
|
+
return { content: [{ type: "text", text: text8 }] };
|
|
11782
11824
|
}
|
|
11783
11825
|
if (groupByProject) {
|
|
11784
11826
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -11805,8 +11847,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11805
11847
|
}
|
|
11806
11848
|
const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
|
|
11807
11849
|
const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
|
|
11808
|
-
const
|
|
11809
|
-
return { content: [{ type: "text", text:
|
|
11850
|
+
const text7 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
|
|
11851
|
+
return { content: [{ type: "text", text: text7 }] };
|
|
11810
11852
|
}
|
|
11811
11853
|
if (format === "json") {
|
|
11812
11854
|
const lines = [];
|