@mgsoftwarebv/mg-dashboard-mcp 7.0.7 → 7.0.9
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 +187 -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));
|
|
@@ -6717,6 +6759,7 @@ var apiKey = getArg2("api-key") || process.env.MG_DASHBOARD_API_KEY;
|
|
|
6717
6759
|
var sshKeyPath = getArg2("ssh-key") || process.env.MG_DASHBOARD_SSH_KEY;
|
|
6718
6760
|
var databaseUrl = getArg2("database-url") || process.env.DATABASE_PRIMARY_POOLER_URL || process.env.DATABASE_PRIMARY_URL;
|
|
6719
6761
|
var encryptionKey = getArg2("encryption-key") || process.env.ENCRYPTION_KEY;
|
|
6762
|
+
var dashboardBaseUrl = (getArg2("dashboard-url") || process.env.MG_DASHBOARD_BASE_URL || "https://dashboard.mgsoftware.nl").replace(/\/$/, "");
|
|
6720
6763
|
var mijnhostApiKey = getArg2("mijnhost-api-key") || process.env.MIJNHOST_API_KEY;
|
|
6721
6764
|
function isMijnhostViaSshProxyEnabled() {
|
|
6722
6765
|
const arg = getArg2("mijnhost-via-ssh-proxy");
|
|
@@ -7435,7 +7478,7 @@ function getEncryptionKey() {
|
|
|
7435
7478
|
throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
|
|
7436
7479
|
return buf;
|
|
7437
7480
|
}
|
|
7438
|
-
function encrypt(
|
|
7481
|
+
function encrypt(text7) {
|
|
7439
7482
|
const key = getEncryptionKey();
|
|
7440
7483
|
const iv = randomBytes(ENC_IV_LENGTH);
|
|
7441
7484
|
const cipher = createCipheriv(
|
|
@@ -7443,7 +7486,7 @@ function encrypt(text6) {
|
|
|
7443
7486
|
new Uint8Array(key),
|
|
7444
7487
|
new Uint8Array(iv)
|
|
7445
7488
|
);
|
|
7446
|
-
let encrypted = cipher.update(
|
|
7489
|
+
let encrypted = cipher.update(text7, "utf8", "hex");
|
|
7447
7490
|
encrypted += cipher.final("hex");
|
|
7448
7491
|
const authTag = cipher.getAuthTag();
|
|
7449
7492
|
return Buffer.concat([
|
|
@@ -8163,10 +8206,10 @@ async function r2GetObjectRange(bucket, key, range) {
|
|
|
8163
8206
|
const body = result.Body;
|
|
8164
8207
|
if (!body?.transformToString)
|
|
8165
8208
|
throw new Error("R2 returned no readable body");
|
|
8166
|
-
const
|
|
8209
|
+
const text7 = await body.transformToString();
|
|
8167
8210
|
const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
|
|
8168
8211
|
return `${header}
|
|
8169
|
-
${
|
|
8212
|
+
${text7}`;
|
|
8170
8213
|
} catch (e) {
|
|
8171
8214
|
throw r2WrapError(bucket, key, e);
|
|
8172
8215
|
}
|
|
@@ -8529,15 +8572,15 @@ async function sftpRead(opts, filePath, proxy, options) {
|
|
|
8529
8572
|
clearTimeout(timer);
|
|
8530
8573
|
cleanup?.();
|
|
8531
8574
|
cleanup = void 0;
|
|
8532
|
-
const
|
|
8575
|
+
const text7 = Buffer.concat(
|
|
8533
8576
|
chunks.map((ch) => new Uint8Array(ch))
|
|
8534
8577
|
).toString("utf-8");
|
|
8535
8578
|
if (!isWholeFileRequest) {
|
|
8536
8579
|
const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
|
|
8537
8580
|
resolve(`${header}
|
|
8538
|
-
${
|
|
8581
|
+
${text7}`);
|
|
8539
8582
|
} else {
|
|
8540
|
-
resolve(
|
|
8583
|
+
resolve(text7);
|
|
8541
8584
|
}
|
|
8542
8585
|
});
|
|
8543
8586
|
rs.on("error", (e) => {
|
|
@@ -8667,11 +8710,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
|
|
|
8667
8710
|
if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
|
|
8668
8711
|
return e.names;
|
|
8669
8712
|
}
|
|
8670
|
-
function truncateForLLM(
|
|
8671
|
-
const totalBytes = Buffer.byteLength(
|
|
8713
|
+
function truncateForLLM(text7, maxBytes) {
|
|
8714
|
+
const totalBytes = Buffer.byteLength(text7, "utf8");
|
|
8672
8715
|
if (totalBytes <= maxBytes)
|
|
8673
|
-
return { text:
|
|
8674
|
-
const buf = Buffer.from(
|
|
8716
|
+
return { text: text7, truncated: false, totalBytes, shownBytes: totalBytes };
|
|
8717
|
+
const buf = Buffer.from(text7, "utf8");
|
|
8675
8718
|
let cut = maxBytes;
|
|
8676
8719
|
while (cut > 0 && (buf[cut] & 192) === 128) cut--;
|
|
8677
8720
|
const head = buf.subarray(0, cut).toString("utf8");
|
|
@@ -8701,10 +8744,10 @@ function isTransientSshError(stderr, exitCode) {
|
|
|
8701
8744
|
function postprocessResult(result, meta) {
|
|
8702
8745
|
if (!result.content?.length) return result;
|
|
8703
8746
|
const block = result.content[0];
|
|
8704
|
-
let
|
|
8705
|
-
const trunc = truncateForLLM(
|
|
8747
|
+
let text7 = String(block.text ?? "");
|
|
8748
|
+
const trunc = truncateForLLM(text7, RESPONSE_MAX_BYTES);
|
|
8706
8749
|
if (trunc.truncated) {
|
|
8707
|
-
|
|
8750
|
+
text7 = trunc.text + "\n\n... " + buildTruncationHint(
|
|
8708
8751
|
meta.toolName,
|
|
8709
8752
|
meta.args,
|
|
8710
8753
|
trunc.totalBytes,
|
|
@@ -8718,11 +8761,11 @@ function postprocessResult(result, meta) {
|
|
|
8718
8761
|
const parts = [`took ${tookStr}`, sizeStr];
|
|
8719
8762
|
if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
|
|
8720
8763
|
if (meta.cached) parts.push("cached");
|
|
8721
|
-
|
|
8764
|
+
text7 = `${text7}
|
|
8722
8765
|
|
|
8723
8766
|
[${parts.join(", ")}]`;
|
|
8724
8767
|
}
|
|
8725
|
-
return { ...result, content: [{ ...block, text:
|
|
8768
|
+
return { ...result, content: [{ ...block, text: text7 }] };
|
|
8726
8769
|
}
|
|
8727
8770
|
function buildPipelineScript(commands, shell, marker, stopOnError) {
|
|
8728
8771
|
if (shell === "powershell") {
|
|
@@ -9508,11 +9551,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
|
|
|
9508
9551
|
applied_by TEXT
|
|
9509
9552
|
);
|
|
9510
9553
|
`.trim();
|
|
9511
|
-
function normaliseMigrationSql(
|
|
9512
|
-
return
|
|
9554
|
+
function normaliseMigrationSql(sql29) {
|
|
9555
|
+
return sql29.replace(/\r\n/g, "\n").trim() + "\n";
|
|
9513
9556
|
}
|
|
9514
|
-
function migrationSha256(
|
|
9515
|
-
return createHash("sha256").update(normaliseMigrationSql(
|
|
9557
|
+
function migrationSha256(sql29) {
|
|
9558
|
+
return createHash("sha256").update(normaliseMigrationSql(sql29), "utf8").digest("hex");
|
|
9516
9559
|
}
|
|
9517
9560
|
function dollarQuoteTag(value) {
|
|
9518
9561
|
let tag = "_mcp";
|
|
@@ -10748,6 +10791,34 @@ var TOOLS = [
|
|
|
10748
10791
|
required: ["action", "domain", "type", "name"]
|
|
10749
10792
|
}
|
|
10750
10793
|
},
|
|
10794
|
+
// ----- Team memory -----
|
|
10795
|
+
{
|
|
10796
|
+
name: "search-team-memory",
|
|
10797
|
+
description: "Search the team's ENTIRE past Cursor history (every developer, every project) for how something was handled before. Use this FIRST, before investigating from scratch, whenever you: hit a non-trivial bug or error, are about to build something that may have been done before, need a project-specific convention/gotcha, or the user asks 'have we done X', 'how did we fix Y', or 'did we solve this already'. Hybrid semantic + keyword search over mirrored conversations. Returns ranked past chats with repo, date, a solution snippet and a similarity score. Phrase the query as the problem in natural language (e.g. 'release pipeline PM2 deploy fails with module not found').",
|
|
10798
|
+
inputSchema: {
|
|
10799
|
+
type: "object",
|
|
10800
|
+
properties: {
|
|
10801
|
+
query: {
|
|
10802
|
+
type: "string",
|
|
10803
|
+
description: "The problem/topic in natural language. Include error text, tool/module names, or symptoms for best recall."
|
|
10804
|
+
},
|
|
10805
|
+
repo: {
|
|
10806
|
+
type: "string",
|
|
10807
|
+
description: "Optional repo filter, e.g. 'github.com/MGSoftwareBV/mg-dashboard'. Omit to search across all projects."
|
|
10808
|
+
},
|
|
10809
|
+
scope: {
|
|
10810
|
+
type: "string",
|
|
10811
|
+
enum: ["team", "mine"],
|
|
10812
|
+
description: "'team' (default) searches everyone's history; 'mine' restricts to the calling user's own conversations."
|
|
10813
|
+
},
|
|
10814
|
+
limit: {
|
|
10815
|
+
type: "number",
|
|
10816
|
+
description: "Max results to return (1-25, default 8)."
|
|
10817
|
+
}
|
|
10818
|
+
},
|
|
10819
|
+
required: ["query"]
|
|
10820
|
+
}
|
|
10821
|
+
},
|
|
10751
10822
|
// ----- Trigger.dev -----
|
|
10752
10823
|
...TRIGGER_TOOLS
|
|
10753
10824
|
];
|
|
@@ -10819,6 +10890,65 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
10819
10890
|
const ctx = authContext;
|
|
10820
10891
|
try {
|
|
10821
10892
|
switch (name) {
|
|
10893
|
+
// ----- Team memory -----
|
|
10894
|
+
case "search-team-memory": {
|
|
10895
|
+
const query = typeof a.query === "string" ? a.query.trim() : "";
|
|
10896
|
+
if (!query) {
|
|
10897
|
+
return { content: [{ type: "text", text: "Error: query is required" }] };
|
|
10898
|
+
}
|
|
10899
|
+
const res = await fetch(`${dashboardBaseUrl}/api/team-memory/search`, {
|
|
10900
|
+
method: "POST",
|
|
10901
|
+
headers: {
|
|
10902
|
+
"content-type": "application/json",
|
|
10903
|
+
authorization: `Bearer ${apiKey}`
|
|
10904
|
+
},
|
|
10905
|
+
body: JSON.stringify({
|
|
10906
|
+
query,
|
|
10907
|
+
repo: typeof a.repo === "string" ? a.repo : void 0,
|
|
10908
|
+
scope: a.scope === "mine" ? "mine" : "team",
|
|
10909
|
+
limit: typeof a.limit === "number" ? a.limit : void 0
|
|
10910
|
+
})
|
|
10911
|
+
});
|
|
10912
|
+
if (!res.ok) {
|
|
10913
|
+
const detail = await res.text().catch(() => "");
|
|
10914
|
+
return {
|
|
10915
|
+
content: [
|
|
10916
|
+
{
|
|
10917
|
+
type: "text",
|
|
10918
|
+
text: `Error: team-memory search failed (${res.status}). ${detail.slice(0, 300)}`
|
|
10919
|
+
}
|
|
10920
|
+
]
|
|
10921
|
+
};
|
|
10922
|
+
}
|
|
10923
|
+
const data = await res.json();
|
|
10924
|
+
if (data.count === 0) {
|
|
10925
|
+
return {
|
|
10926
|
+
content: [
|
|
10927
|
+
{
|
|
10928
|
+
type: "text",
|
|
10929
|
+
text: `No prior team conversations matched "${query}". Proceed from scratch.`
|
|
10930
|
+
}
|
|
10931
|
+
]
|
|
10932
|
+
};
|
|
10933
|
+
}
|
|
10934
|
+
const lines = data.hits.map((hit, index5) => {
|
|
10935
|
+
const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
|
|
10936
|
+
const repo = hit.repo ?? "unknown repo";
|
|
10937
|
+
const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
|
|
10938
|
+
return `${index5 + 1}. [${repo} \xB7 ${when}${sim}] ${hit.title}
|
|
10939
|
+
${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
|
|
10940
|
+
});
|
|
10941
|
+
return {
|
|
10942
|
+
content: [
|
|
10943
|
+
{
|
|
10944
|
+
type: "text",
|
|
10945
|
+
text: `Found ${data.count} prior conversation(s) (${data.mode} search) \u2014 reuse this before solving from scratch:
|
|
10946
|
+
|
|
10947
|
+
` + lines.join("\n\n")
|
|
10948
|
+
}
|
|
10949
|
+
]
|
|
10950
|
+
};
|
|
10951
|
+
}
|
|
10822
10952
|
// ----- Servers -----
|
|
10823
10953
|
case "list-servers": {
|
|
10824
10954
|
const data = ctx.allowedServerIds !== null ? await db.execute(sql`
|
|
@@ -11777,8 +11907,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11777
11907
|
};
|
|
11778
11908
|
const filtered = sortRows(applyFilter(only.rows));
|
|
11779
11909
|
if (format === "json") {
|
|
11780
|
-
const
|
|
11781
|
-
return { content: [{ type: "text", text:
|
|
11910
|
+
const text8 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
|
|
11911
|
+
return { content: [{ type: "text", text: text8 }] };
|
|
11782
11912
|
}
|
|
11783
11913
|
if (groupByProject) {
|
|
11784
11914
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -11805,8 +11935,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11805
11935
|
}
|
|
11806
11936
|
const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
|
|
11807
11937
|
const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
|
|
11808
|
-
const
|
|
11809
|
-
return { content: [{ type: "text", text:
|
|
11938
|
+
const text7 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
|
|
11939
|
+
return { content: [{ type: "text", text: text7 }] };
|
|
11810
11940
|
}
|
|
11811
11941
|
if (format === "json") {
|
|
11812
11942
|
const lines = [];
|