@mgsoftwarebv/mg-dashboard-mcp 7.4.23 → 7.4.25
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 +489 -103
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import { sql, or, eq, and, inArray, like, desc } from 'drizzle-orm';
|
|
|
22
22
|
import { once } from 'events';
|
|
23
23
|
import { lookup } from 'dns/promises';
|
|
24
24
|
import { connect } from 'tls';
|
|
25
|
-
import { pgEnum, pgTable, timestamp, text, jsonb, integer, uuid, uniqueIndex, index, bigint, boolean, customType, date, real, check, doublePrecision, primaryKey, foreignKey } from 'drizzle-orm/pg-core';
|
|
25
|
+
import { pgEnum, pgTable, timestamp, text, jsonb, integer, uuid, uniqueIndex, index, bigint, boolean, customType, date, real, check, numeric, bigserial, doublePrecision, primaryKey, foreignKey } from 'drizzle-orm/pg-core';
|
|
26
26
|
import { HeadObjectCommand, ListObjectsV2Command, DeleteObjectsCommand, S3Client, DeleteObjectCommand, CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand, AbortMultipartUploadCommand, PutObjectCommand, GetObjectCommand, CopyObjectCommand } from '@aws-sdk/client-s3';
|
|
27
27
|
|
|
28
28
|
var __defProp = Object.defineProperty;
|
|
@@ -724,10 +724,10 @@ var TRIGGER_TOOL_MODULE_MAP = {
|
|
|
724
724
|
"trigger-env": "settings"
|
|
725
725
|
};
|
|
726
726
|
async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
|
|
727
|
-
const
|
|
727
|
+
const sql36 = `SELECT re.\\"apiKey\\" || '~~' || p.\\"externalRef\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
|
|
728
728
|
const cmd = [
|
|
729
729
|
`PORT=$(docker port "${WA_CONTAINER}" 3000/tcp 2>/dev/null | head -1 | sed 's/.*://')`,
|
|
730
|
-
`ROW=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
730
|
+
`ROW=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql36}" 2>/dev/null | tr -d '[:space:]')`,
|
|
731
731
|
'echo "$PORT|$ROW"'
|
|
732
732
|
].join(" && ");
|
|
733
733
|
const result = await sshExec2(conn, cmd, proxy);
|
|
@@ -749,8 +749,8 @@ async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
|
|
|
749
749
|
return { port, apiKey: apiKey2, projectRef: projectRef || "" };
|
|
750
750
|
}
|
|
751
751
|
async function fetchRunLogs(runId, conn, proxy, sshExec2) {
|
|
752
|
-
const
|
|
753
|
-
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
752
|
+
const sql36 = `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`;
|
|
753
|
+
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql36}" 2>/dev/null`;
|
|
754
754
|
const result = await sshExec2(conn, cmd, proxy);
|
|
755
755
|
const output = result.stdout.trim();
|
|
756
756
|
if (!output) return "";
|
|
@@ -832,8 +832,8 @@ async function handleTriggerTool(name, args2, deps) {
|
|
|
832
832
|
switch (name) {
|
|
833
833
|
// -----------------------------------------------------------------
|
|
834
834
|
case "trigger-list": {
|
|
835
|
-
const
|
|
836
|
-
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
835
|
+
const sql36 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
|
|
836
|
+
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql36}" 2>/dev/null`;
|
|
837
837
|
const result = await sshExec2(conn, cmd, proxy);
|
|
838
838
|
const output = result.stdout.trim();
|
|
839
839
|
if (!output) {
|
|
@@ -1011,9 +1011,9 @@ ${raw2.substring(0, 500)}` }] };
|
|
|
1011
1011
|
return { content: [{ type: "text", text: `No environment variables for ${project}/${env}.` }] };
|
|
1012
1012
|
}
|
|
1013
1013
|
const lines = vars.map((v) => `${v.isSecret ? "[secret]" : " "} ${v.name}`).sort();
|
|
1014
|
-
const
|
|
1014
|
+
const text23 = `Env vars for ${project}/${env} (${vars.length}) \u2014 values hidden, use action="get" with a key:
|
|
1015
1015
|
` + "-".repeat(50) + "\n" + lines.join("\n");
|
|
1016
|
-
return { content: [{ type: "text", text:
|
|
1016
|
+
return { content: [{ type: "text", text: text23 }] };
|
|
1017
1017
|
}
|
|
1018
1018
|
if (action === "get") {
|
|
1019
1019
|
const key2 = String(args2.key ?? "");
|
|
@@ -1100,9 +1100,9 @@ async function fetchAndFormatRun(conn, proxy, sshExec2, instance, runId) {
|
|
|
1100
1100
|
return { content: [{ type: "text", text: `Invalid API response:
|
|
1101
1101
|
${rawJson.substring(0, 500)}` }] };
|
|
1102
1102
|
}
|
|
1103
|
-
let
|
|
1104
|
-
if (logs)
|
|
1105
|
-
return { content: [{ type: "text", text:
|
|
1103
|
+
let text23 = formatRunDetail(run);
|
|
1104
|
+
if (logs) text23 += "\n\n--- Logs ---\n" + logs;
|
|
1105
|
+
return { content: [{ type: "text", text: text23 }] };
|
|
1106
1106
|
}
|
|
1107
1107
|
async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSeconds) {
|
|
1108
1108
|
const pollInterval = 3e3;
|
|
@@ -1124,10 +1124,10 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
|
|
|
1124
1124
|
continue;
|
|
1125
1125
|
}
|
|
1126
1126
|
if (TERMINAL_STATUSES.has(run.status)) {
|
|
1127
|
-
let
|
|
1127
|
+
let text23 = formatRunDetail(run);
|
|
1128
1128
|
const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
|
|
1129
|
-
if (logs)
|
|
1130
|
-
return { content: [{ type: "text", text:
|
|
1129
|
+
if (logs) text23 += "\n\n--- Logs ---\n" + logs;
|
|
1130
|
+
return { content: [{ type: "text", text: text23 }] };
|
|
1131
1131
|
}
|
|
1132
1132
|
}
|
|
1133
1133
|
return {
|
|
@@ -1317,6 +1317,56 @@ async function proxyJson(ctx, route, body) {
|
|
|
1317
1317
|
};
|
|
1318
1318
|
}
|
|
1319
1319
|
|
|
1320
|
+
// src/git-credential-tools.ts
|
|
1321
|
+
var GIT_CREDENTIAL_TOOL_NAME = "git-credential";
|
|
1322
|
+
var GIT_CREDENTIAL_TOOLS = [
|
|
1323
|
+
{
|
|
1324
|
+
name: GIT_CREDENTIAL_TOOL_NAME,
|
|
1325
|
+
description: "Issue a 15-minute mggit access token for git.mgsoftware.nl (username=token, Basic password). Bound to owner/name + write bit. Intended for git credential helpers and agents that need to clone/fetch/push. Not a git runner \u2014 do not use this to execute git commands. Requires module git_hosting.",
|
|
1326
|
+
inputSchema: {
|
|
1327
|
+
type: "object",
|
|
1328
|
+
properties: {
|
|
1329
|
+
repo: {
|
|
1330
|
+
type: "string",
|
|
1331
|
+
description: "Repository as owner/name (e.g. MGSoftwareBV/mg-dashboard)."
|
|
1332
|
+
},
|
|
1333
|
+
write: {
|
|
1334
|
+
type: "boolean",
|
|
1335
|
+
description: "Request push rights (default true). Fetch-only: false."
|
|
1336
|
+
}
|
|
1337
|
+
},
|
|
1338
|
+
required: ["repo"]
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
];
|
|
1342
|
+
async function handleGitCredentialTool(args2, ctx) {
|
|
1343
|
+
const repo = typeof args2.repo === "string" ? args2.repo.trim() : "";
|
|
1344
|
+
if (!repo) {
|
|
1345
|
+
return { content: [{ type: "text", text: "Error: repo is required (owner/name)" }] };
|
|
1346
|
+
}
|
|
1347
|
+
const write = args2.write !== false;
|
|
1348
|
+
const res = await fetch(`${ctx.dashboardBaseUrl.replace(/\/$/, "")}/api/git/credential`, {
|
|
1349
|
+
method: "POST",
|
|
1350
|
+
headers: {
|
|
1351
|
+
"content-type": "application/json",
|
|
1352
|
+
authorization: `Bearer ${ctx.apiKey}`
|
|
1353
|
+
},
|
|
1354
|
+
body: JSON.stringify({ repo, write })
|
|
1355
|
+
});
|
|
1356
|
+
const text23 = await res.text().catch(() => "");
|
|
1357
|
+
if (!res.ok) {
|
|
1358
|
+
return {
|
|
1359
|
+
content: [
|
|
1360
|
+
{
|
|
1361
|
+
type: "text",
|
|
1362
|
+
text: `Error: git-credential failed (${res.status}). ${text23.slice(0, 300)}`
|
|
1363
|
+
}
|
|
1364
|
+
]
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
return { content: [{ type: "text", text: text23 || "{}" }] };
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1320
1370
|
// src/mailserver-tools.ts
|
|
1321
1371
|
var MAILSERVER_TOOL_NAME = "mailserver";
|
|
1322
1372
|
var MAILSERVER_ACTIONS = [
|
|
@@ -1391,11 +1441,11 @@ function tokenFromDotenv(content) {
|
|
|
1391
1441
|
for (const line of content.split(/\r?\n/)) {
|
|
1392
1442
|
const trimmed = line.trim();
|
|
1393
1443
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1394
|
-
const
|
|
1395
|
-
if (
|
|
1396
|
-
const key = trimmed.slice(0,
|
|
1444
|
+
const eq6 = trimmed.indexOf("=");
|
|
1445
|
+
if (eq6 < 1) continue;
|
|
1446
|
+
const key = trimmed.slice(0, eq6).trim();
|
|
1397
1447
|
if (key !== "MAILSERVER_MCP_TOKEN") continue;
|
|
1398
|
-
let value = trimmed.slice(
|
|
1448
|
+
let value = trimmed.slice(eq6 + 1).trim();
|
|
1399
1449
|
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1400
1450
|
value = value.slice(1, -1);
|
|
1401
1451
|
}
|
|
@@ -1500,12 +1550,12 @@ async function mailserverFetch(deps, plan) {
|
|
|
1500
1550
|
},
|
|
1501
1551
|
body: plan.body ? JSON.stringify(plan.body) : void 0
|
|
1502
1552
|
});
|
|
1503
|
-
const
|
|
1504
|
-
let json =
|
|
1553
|
+
const text23 = await res.text();
|
|
1554
|
+
let json = text23;
|
|
1505
1555
|
try {
|
|
1506
|
-
json =
|
|
1556
|
+
json = text23 ? JSON.parse(text23) : null;
|
|
1507
1557
|
} catch {
|
|
1508
|
-
json = { error:
|
|
1558
|
+
json = { error: text23.slice(0, 500) };
|
|
1509
1559
|
}
|
|
1510
1560
|
return { status: res.status, json };
|
|
1511
1561
|
}
|
|
@@ -4789,10 +4839,10 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
4789
4839
|
// }) as any;
|
|
4790
4840
|
// return merged;
|
|
4791
4841
|
// }
|
|
4792
|
-
catchall(
|
|
4842
|
+
catchall(index20) {
|
|
4793
4843
|
return new _ZodObject({
|
|
4794
4844
|
...this._def,
|
|
4795
|
-
catchall:
|
|
4845
|
+
catchall: index20
|
|
4796
4846
|
});
|
|
4797
4847
|
}
|
|
4798
4848
|
pick(mask) {
|
|
@@ -5110,9 +5160,9 @@ function mergeValues(a, b) {
|
|
|
5110
5160
|
return { valid: false };
|
|
5111
5161
|
}
|
|
5112
5162
|
const newArray = [];
|
|
5113
|
-
for (let
|
|
5114
|
-
const itemA = a[
|
|
5115
|
-
const itemB = b[
|
|
5163
|
+
for (let index20 = 0; index20 < a.length; index20++) {
|
|
5164
|
+
const itemA = a[index20];
|
|
5165
|
+
const itemB = b[index20];
|
|
5116
5166
|
const sharedValue = mergeValues(itemA, itemB);
|
|
5117
5167
|
if (!sharedValue.valid) {
|
|
5118
5168
|
return { valid: false };
|
|
@@ -5318,10 +5368,10 @@ var ZodMap = class extends ZodType {
|
|
|
5318
5368
|
}
|
|
5319
5369
|
const keyType = this._def.keyType;
|
|
5320
5370
|
const valueType = this._def.valueType;
|
|
5321
|
-
const pairs = [...ctx.data.entries()].map(([key, value],
|
|
5371
|
+
const pairs = [...ctx.data.entries()].map(([key, value], index20) => {
|
|
5322
5372
|
return {
|
|
5323
|
-
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [
|
|
5324
|
-
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [
|
|
5373
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index20, "key"])),
|
|
5374
|
+
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index20, "value"]))
|
|
5325
5375
|
};
|
|
5326
5376
|
});
|
|
5327
5377
|
if (ctx.common.async) {
|
|
@@ -6336,6 +6386,8 @@ pgTable(
|
|
|
6336
6386
|
verifyNote: text("verify_note"),
|
|
6337
6387
|
dirty: boolean("dirty").notNull().default(false),
|
|
6338
6388
|
vaultSyncedAt: timestamp("vault_synced_at", { withTimezone: true }),
|
|
6389
|
+
/** Flattened frontmatter aliases for tsv weight A. */
|
|
6390
|
+
aliasesText: text("aliases_text").notNull().default(""),
|
|
6339
6391
|
/** Reviewer / itemized-phase lease holder. */
|
|
6340
6392
|
claimedBy: text("claimed_by"),
|
|
6341
6393
|
claimedAt: timestamp("claimed_at", { withTimezone: true }),
|
|
@@ -6532,6 +6584,7 @@ pgTable(
|
|
|
6532
6584
|
topId: text("top_id"),
|
|
6533
6585
|
caller: text("caller"),
|
|
6534
6586
|
repo: text("repo"),
|
|
6587
|
+
confident: boolean("confident"),
|
|
6535
6588
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
6536
6589
|
},
|
|
6537
6590
|
(table) => [index("idx_team_memory_search_log_created").on(table.createdAt)]
|
|
@@ -6558,6 +6611,9 @@ pgTable(
|
|
|
6558
6611
|
reviewed: boolean("reviewed").notNull().default(false),
|
|
6559
6612
|
reviewNotes: text("review_notes"),
|
|
6560
6613
|
reviewedAt: timestamp("reviewed_at", { withTimezone: true }),
|
|
6614
|
+
inputBytes: integer("input_bytes"),
|
|
6615
|
+
inputTokens: integer("input_tokens"),
|
|
6616
|
+
outputTokens: integer("output_tokens"),
|
|
6561
6617
|
startedAt: timestamp("started_at", { withTimezone: true }),
|
|
6562
6618
|
finishedAt: timestamp("finished_at", { withTimezone: true }).notNull().defaultNow(),
|
|
6563
6619
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
@@ -6588,6 +6644,7 @@ pgTable(
|
|
|
6588
6644
|
evidenceRefs: text("evidence_refs").array().notNull().default([]),
|
|
6589
6645
|
dedupeKey: text("dedupe_key").notNull(),
|
|
6590
6646
|
source: text("source").notNull().default("auto"),
|
|
6647
|
+
ledgerId: uuid("ledger_id"),
|
|
6591
6648
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
6592
6649
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
6593
6650
|
},
|
|
@@ -6599,6 +6656,138 @@ pgTable(
|
|
|
6599
6656
|
)
|
|
6600
6657
|
]
|
|
6601
6658
|
);
|
|
6659
|
+
pgTable(
|
|
6660
|
+
"wiki_page_chunk",
|
|
6661
|
+
{
|
|
6662
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6663
|
+
pageId: uuid("page_id").notNull(),
|
|
6664
|
+
slug: text("slug").notNull(),
|
|
6665
|
+
headingPath: text("heading_path").notNull().default(""),
|
|
6666
|
+
chunkIndex: integer("chunk_index").notNull().default(0),
|
|
6667
|
+
content: text("content").notNull().default(""),
|
|
6668
|
+
embeddingVec: vector("embedding_vec", {
|
|
6669
|
+
dimensions: WIKI_EMBEDDING_DIMENSIONS
|
|
6670
|
+
}),
|
|
6671
|
+
embeddingModel: text("embedding_model"),
|
|
6672
|
+
embeddedAt: timestamp("embedded_at", { withTimezone: true }),
|
|
6673
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
6674
|
+
},
|
|
6675
|
+
(table) => [
|
|
6676
|
+
uniqueIndex("wiki_page_chunk_page_id_chunk_index_key").on(
|
|
6677
|
+
table.pageId,
|
|
6678
|
+
table.chunkIndex
|
|
6679
|
+
),
|
|
6680
|
+
index("idx_wiki_page_chunk_slug").on(table.slug)
|
|
6681
|
+
]
|
|
6682
|
+
);
|
|
6683
|
+
pgTable("brain_config", {
|
|
6684
|
+
key: text("key").primaryKey(),
|
|
6685
|
+
value: jsonb("value").$type().notNull(),
|
|
6686
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
6687
|
+
updatedBy: text("updated_by")
|
|
6688
|
+
});
|
|
6689
|
+
pgTable(
|
|
6690
|
+
"brain_metrics_daily",
|
|
6691
|
+
{
|
|
6692
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6693
|
+
metricDate: date("metric_date").notNull(),
|
|
6694
|
+
callerClass: text("caller_class").notNull(),
|
|
6695
|
+
wikiSearches: integer("wiki_searches").notNull().default(0),
|
|
6696
|
+
confidentHits: integer("confident_hits").notNull().default(0),
|
|
6697
|
+
misses: integer("misses").notNull().default(0),
|
|
6698
|
+
hitAt1: real("hit_at_1"),
|
|
6699
|
+
hitAt3: real("hit_at_3"),
|
|
6700
|
+
searchToRead: real("search_to_read"),
|
|
6701
|
+
tmFallbacks: integer("tm_fallbacks").notNull().default(0),
|
|
6702
|
+
reviewerRuns: integer("reviewer_runs").notNull().default(0),
|
|
6703
|
+
reviewerAvgInputBytes: integer("reviewer_avg_input_bytes"),
|
|
6704
|
+
tokensPerPhase: jsonb("tokens_per_phase").$type().notNull().default({}),
|
|
6705
|
+
pagesPromoted: integer("pages_promoted").notNull().default(0),
|
|
6706
|
+
pagesDemoted: integer("pages_demoted").notNull().default(0),
|
|
6707
|
+
thrashCount: integer("thrash_count").notNull().default(0),
|
|
6708
|
+
flagsPer100Reads: real("flags_per_100_reads"),
|
|
6709
|
+
verifierDisagreeRate: real("verifier_disagree_rate"),
|
|
6710
|
+
verifyGateIdle: boolean("verify_gate_idle").notNull().default(false),
|
|
6711
|
+
verifyGateActing: boolean("verify_gate_acting"),
|
|
6712
|
+
draftsOlder14d: integer("drafts_older_14d").notNull().default(0),
|
|
6713
|
+
developersHarvested24h: integer("developers_harvested_24h").notNull().default(0),
|
|
6714
|
+
teamMemoryCited: integer("team_memory_cited").notNull().default(0),
|
|
6715
|
+
extras: jsonb("extras").$type().notNull().default({}),
|
|
6716
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
6717
|
+
},
|
|
6718
|
+
(table) => [
|
|
6719
|
+
uniqueIndex("brain_metrics_daily_metric_date_caller_class_key").on(
|
|
6720
|
+
table.metricDate,
|
|
6721
|
+
table.callerClass
|
|
6722
|
+
)
|
|
6723
|
+
]
|
|
6724
|
+
);
|
|
6725
|
+
pgTable(
|
|
6726
|
+
"brain_change_ledger",
|
|
6727
|
+
{
|
|
6728
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6729
|
+
scope: text("scope").notNull(),
|
|
6730
|
+
tier: text("tier").notNull(),
|
|
6731
|
+
kind: text("kind").notNull(),
|
|
6732
|
+
hypothesis: text("hypothesis").notNull().default(""),
|
|
6733
|
+
target: text("target"),
|
|
6734
|
+
beforeValue: jsonb("before_value"),
|
|
6735
|
+
afterValue: jsonb("after_value"),
|
|
6736
|
+
baselineMetrics: jsonb("baseline_metrics"),
|
|
6737
|
+
outcomeMetrics: jsonb("outcome_metrics"),
|
|
6738
|
+
verdict: text("verdict").notNull().default("pending"),
|
|
6739
|
+
evidenceRefs: text("evidence_refs").array().notNull().default([]),
|
|
6740
|
+
createdBy: text("created_by"),
|
|
6741
|
+
suggestionId: uuid("suggestion_id"),
|
|
6742
|
+
ticketNumber: text("ticket_number"),
|
|
6743
|
+
ticketId: text("ticket_id"),
|
|
6744
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
6745
|
+
verdictAt: timestamp("verdict_at", { withTimezone: true }),
|
|
6746
|
+
revertedAt: timestamp("reverted_at", { withTimezone: true })
|
|
6747
|
+
},
|
|
6748
|
+
(table) => [
|
|
6749
|
+
index("idx_brain_change_ledger_verdict").on(table.verdict, table.createdAt),
|
|
6750
|
+
index("idx_brain_change_ledger_scope").on(table.scope, table.createdAt)
|
|
6751
|
+
]
|
|
6752
|
+
);
|
|
6753
|
+
pgTable(
|
|
6754
|
+
"wiki_search_shadow",
|
|
6755
|
+
{
|
|
6756
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6757
|
+
query: text("query").notNull(),
|
|
6758
|
+
caller: text("caller"),
|
|
6759
|
+
servedSlug: text("served_slug"),
|
|
6760
|
+
servedScore: real("served_score"),
|
|
6761
|
+
shadowSlug: text("shadow_slug"),
|
|
6762
|
+
shadowScore: real("shadow_score"),
|
|
6763
|
+
ledgerId: uuid("ledger_id"),
|
|
6764
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
6765
|
+
},
|
|
6766
|
+
(table) => [index("idx_wiki_search_shadow_created").on(table.createdAt)]
|
|
6767
|
+
);
|
|
6768
|
+
pgTable("wiki_eval_snapshot", {
|
|
6769
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6770
|
+
name: text("name").notNull(),
|
|
6771
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
6772
|
+
});
|
|
6773
|
+
pgTable(
|
|
6774
|
+
"wiki_eval_label",
|
|
6775
|
+
{
|
|
6776
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6777
|
+
snapshotId: uuid("snapshot_id").notNull(),
|
|
6778
|
+
query: text("query").notNull(),
|
|
6779
|
+
caller: text("caller"),
|
|
6780
|
+
positiveSlug: text("positive_slug"),
|
|
6781
|
+
isNegative: boolean("is_negative").notNull().default(false),
|
|
6782
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
6783
|
+
},
|
|
6784
|
+
(table) => [index("idx_wiki_eval_label_snapshot").on(table.snapshotId)]
|
|
6785
|
+
);
|
|
6786
|
+
pgTable("wiki_status_dirty", {
|
|
6787
|
+
slug: text("slug").primaryKey(),
|
|
6788
|
+
markedAt: timestamp("marked_at", { withTimezone: true }).notNull().defaultNow(),
|
|
6789
|
+
reason: text("reason")
|
|
6790
|
+
});
|
|
6602
6791
|
|
|
6603
6792
|
// ../db/src/schema/agent-memory.ts
|
|
6604
6793
|
pgTable(
|
|
@@ -6639,7 +6828,8 @@ pgTable(
|
|
|
6639
6828
|
),
|
|
6640
6829
|
index("idx_agent_memory_repo").on(table.gitRepository),
|
|
6641
6830
|
index("idx_agent_memory_user").on(table.userId),
|
|
6642
|
-
index("idx_agent_memory_kind").on(table.kind)
|
|
6831
|
+
index("idx_agent_memory_kind").on(table.kind),
|
|
6832
|
+
index("idx_agent_memory_automated_created").on(table.createdAt).where(sql`kind = 'automated'`)
|
|
6643
6833
|
]
|
|
6644
6834
|
);
|
|
6645
6835
|
var contentSourceType = pgEnum("content_source_type", [
|
|
@@ -7084,6 +7274,133 @@ pgTable(
|
|
|
7084
7274
|
index("dns_migration_mailbox_migration_idx").on(table.migrationId)
|
|
7085
7275
|
]
|
|
7086
7276
|
);
|
|
7277
|
+
pgTable(
|
|
7278
|
+
"git_repo",
|
|
7279
|
+
{
|
|
7280
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
7281
|
+
/** `owner/name`, the canonical id on mggit and GitHub. */
|
|
7282
|
+
fullName: text("full_name").notNull(),
|
|
7283
|
+
owner: text("owner").notNull(),
|
|
7284
|
+
name: text("name").notNull(),
|
|
7285
|
+
source: text("source").$type().notNull().default("mirror"),
|
|
7286
|
+
/** GitHub clone URL the repo follows while it is a mirror. */
|
|
7287
|
+
upstreamUrl: text("upstream_url"),
|
|
7288
|
+
/** Full ref names mggit follows (`refs/heads/main`, `refs/tags/v1`). */
|
|
7289
|
+
followRefs: jsonb("follow_refs").$type().notNull().default([]),
|
|
7290
|
+
/** Sync job keeps the follow list current and the repo present on mggit. */
|
|
7291
|
+
mirrorEnabled: boolean("mirror_enabled").notNull().default(true),
|
|
7292
|
+
/** Present on mggit (repo created). */
|
|
7293
|
+
existsOnMggit: boolean("exists_on_mggit").notNull().default(false),
|
|
7294
|
+
githubArchived: boolean("github_archived").notNull().default(false),
|
|
7295
|
+
githubPrivate: boolean("github_private").notNull().default(true),
|
|
7296
|
+
githubDefaultBranch: text("github_default_branch"),
|
|
7297
|
+
githubPushedAt: timestamp("github_pushed_at", { withTimezone: true }),
|
|
7298
|
+
branchCount: integer("branch_count").notNull().default(0),
|
|
7299
|
+
tagCount: integer("tag_count").notNull().default(0),
|
|
7300
|
+
/** Latest WAL seq seen in an event for this repo. */
|
|
7301
|
+
lastSeq: bigint("last_seq", { mode: "number" }).notNull().default(0),
|
|
7302
|
+
lastEventAt: timestamp("last_event_at", { withTimezone: true }),
|
|
7303
|
+
followState: jsonb("follow_state").$type().notNull().default({}),
|
|
7304
|
+
/** Refs where GitHub and mggit disagree after the last sync (name → {github, mggit}). */
|
|
7305
|
+
behindRefs: jsonb("behind_refs").$type().notNull().default({}),
|
|
7306
|
+
lastSyncAt: timestamp("last_sync_at", { withTimezone: true }),
|
|
7307
|
+
lastSyncError: text("last_sync_error"),
|
|
7308
|
+
notes: text("notes"),
|
|
7309
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
7310
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
7311
|
+
},
|
|
7312
|
+
(table) => [
|
|
7313
|
+
uniqueIndex("git_repo_full_name_uidx").on(table.fullName),
|
|
7314
|
+
index("git_repo_source_idx").on(table.source, table.mirrorEnabled),
|
|
7315
|
+
index("git_repo_last_event_idx").on(table.lastEventAt)
|
|
7316
|
+
]
|
|
7317
|
+
);
|
|
7318
|
+
pgTable(
|
|
7319
|
+
"git_ref_event",
|
|
7320
|
+
{
|
|
7321
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
7322
|
+
repoFullName: text("repo_full_name").notNull(),
|
|
7323
|
+
/** `_walgit.seq` (uint64 as string on the wire). */
|
|
7324
|
+
seq: bigint("seq", { mode: "number" }).notNull(),
|
|
7325
|
+
refName: text("ref_name").notNull(),
|
|
7326
|
+
/** `branch` | `tag` | `` */
|
|
7327
|
+
refType: text("ref_type").notNull().default(""),
|
|
7328
|
+
/** `create` | `update` | `delete` */
|
|
7329
|
+
action: text("action").notNull(),
|
|
7330
|
+
oldOid: text("old_oid").notNull(),
|
|
7331
|
+
newOid: text("new_oid").notNull(),
|
|
7332
|
+
/** Authenticated principal (`upstream` for mirror follows). */
|
|
7333
|
+
pusher: text("pusher").notNull().default(""),
|
|
7334
|
+
correlationId: text("correlation_id"),
|
|
7335
|
+
entryKind: text("entry_kind"),
|
|
7336
|
+
/** `X-Walgit-Delivery` of the batch that carried it. */
|
|
7337
|
+
deliveryId: text("delivery_id"),
|
|
7338
|
+
receivedAt: timestamp("received_at", { withTimezone: true }).notNull().defaultNow()
|
|
7339
|
+
},
|
|
7340
|
+
(table) => [
|
|
7341
|
+
uniqueIndex("git_ref_event_dedup_uidx").on(
|
|
7342
|
+
table.repoFullName,
|
|
7343
|
+
table.seq,
|
|
7344
|
+
table.refName
|
|
7345
|
+
),
|
|
7346
|
+
index("git_ref_event_repo_received_idx").on(
|
|
7347
|
+
table.repoFullName,
|
|
7348
|
+
table.receivedAt
|
|
7349
|
+
),
|
|
7350
|
+
index("git_ref_event_received_idx").on(table.receivedAt),
|
|
7351
|
+
index("git_ref_event_pusher_idx").on(table.pusher)
|
|
7352
|
+
]
|
|
7353
|
+
);
|
|
7354
|
+
pgTable(
|
|
7355
|
+
"git_access_log",
|
|
7356
|
+
{
|
|
7357
|
+
id: bigserial("id", { mode: "number" }).primaryKey(),
|
|
7358
|
+
ts: timestamp("ts", { withTimezone: true }).notNull(),
|
|
7359
|
+
/** nginx `$request_id`; dedup key for re-shipped batches. */
|
|
7360
|
+
requestId: text("request_id").notNull(),
|
|
7361
|
+
ip: text("ip").notNull().default(""),
|
|
7362
|
+
principal: text("principal").notNull().default(""),
|
|
7363
|
+
repo: text("repo").notNull().default(""),
|
|
7364
|
+
/** `advertise` | `fetch` | `push` | `api` | `` */
|
|
7365
|
+
op: text("op").notNull().default(""),
|
|
7366
|
+
method: text("method").notNull().default(""),
|
|
7367
|
+
uri: text("uri").notNull().default(""),
|
|
7368
|
+
status: integer("status").notNull().default(0),
|
|
7369
|
+
bytesIn: bigint("bytes_in", { mode: "number" }).notNull().default(0),
|
|
7370
|
+
bytesOut: bigint("bytes_out", { mode: "number" }).notNull().default(0),
|
|
7371
|
+
durationMs: numeric("duration_ms", { precision: 12, scale: 3 }).notNull().default("0"),
|
|
7372
|
+
userAgent: text("user_agent").notNull().default(""),
|
|
7373
|
+
upstreamStatus: text("upstream_status").notNull().default(""),
|
|
7374
|
+
receivedAt: timestamp("received_at", { withTimezone: true }).notNull().defaultNow()
|
|
7375
|
+
},
|
|
7376
|
+
(table) => [
|
|
7377
|
+
uniqueIndex("git_access_log_request_id_uidx").on(table.requestId),
|
|
7378
|
+
index("git_access_log_ts_idx").on(table.ts),
|
|
7379
|
+
index("git_access_log_repo_ts_idx").on(table.repo, table.ts),
|
|
7380
|
+
index("git_access_log_principal_ts_idx").on(table.principal, table.ts),
|
|
7381
|
+
index("git_access_log_op_status_idx").on(table.op, table.status)
|
|
7382
|
+
]
|
|
7383
|
+
);
|
|
7384
|
+
pgTable(
|
|
7385
|
+
"git_credential_grant",
|
|
7386
|
+
{
|
|
7387
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
7388
|
+
tokenHash: text("token_hash").notNull(),
|
|
7389
|
+
userId: uuid("user_id").notNull(),
|
|
7390
|
+
apiKeyId: uuid("api_key_id").notNull(),
|
|
7391
|
+
repo: text("repo").notNull(),
|
|
7392
|
+
write: boolean("write").notNull().default(true),
|
|
7393
|
+
principal: text("principal").notNull(),
|
|
7394
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
7395
|
+
issuedIp: text("issued_ip").notNull().default(""),
|
|
7396
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
7397
|
+
},
|
|
7398
|
+
(table) => [
|
|
7399
|
+
uniqueIndex("git_credential_grant_token_hash_uidx").on(table.tokenHash),
|
|
7400
|
+
index("git_credential_grant_expires_idx").on(table.expiresAt),
|
|
7401
|
+
index("git_credential_grant_user_idx").on(table.userId, table.expiresAt)
|
|
7402
|
+
]
|
|
7403
|
+
);
|
|
7087
7404
|
var directoryLinkType = pgEnum("directory_link_type", [
|
|
7088
7405
|
"dofollow",
|
|
7089
7406
|
"nofollow",
|
|
@@ -7888,6 +8205,22 @@ pgTable(
|
|
|
7888
8205
|
)
|
|
7889
8206
|
]
|
|
7890
8207
|
);
|
|
8208
|
+
|
|
8209
|
+
// ../platform/dist/utils/permissions.js
|
|
8210
|
+
var MODULE_KEYS = [
|
|
8211
|
+
"users",
|
|
8212
|
+
"ssh_servers",
|
|
8213
|
+
"wiki",
|
|
8214
|
+
"ci_cd",
|
|
8215
|
+
"settings",
|
|
8216
|
+
"cursor_remote",
|
|
8217
|
+
"code_battle",
|
|
8218
|
+
"git_hosting"
|
|
8219
|
+
];
|
|
8220
|
+
({
|
|
8221
|
+
modules: Object.fromEntries(MODULE_KEYS.map((k) => [k, false]))});
|
|
8222
|
+
({
|
|
8223
|
+
modules: Object.fromEntries(MODULE_KEYS.map((k) => [k, true]))});
|
|
7891
8224
|
var SSH_POOL_IDLE_MS = 6e4;
|
|
7892
8225
|
var sshPool = /* @__PURE__ */ new Map();
|
|
7893
8226
|
function poolKey(options) {
|
|
@@ -9140,6 +9473,24 @@ function hourBucket(now = /* @__PURE__ */ new Date()) {
|
|
|
9140
9473
|
)
|
|
9141
9474
|
);
|
|
9142
9475
|
}
|
|
9476
|
+
var GITHUB_API_STAT_FLUSH_EVERY = 50;
|
|
9477
|
+
var pendingGithubApiStats = /* @__PURE__ */ new Map();
|
|
9478
|
+
var pendingGithubApiStatIncrements = 0;
|
|
9479
|
+
function githubApiStatMapKey(tokenKey, jobId, bucket) {
|
|
9480
|
+
return `${tokenKey} ${jobId} ${bucket}`;
|
|
9481
|
+
}
|
|
9482
|
+
function mergeGithubApiStatDelta(current, add) {
|
|
9483
|
+
if (!current) return { ...add };
|
|
9484
|
+
return {
|
|
9485
|
+
tokenKey: add.tokenKey,
|
|
9486
|
+
jobId: add.jobId,
|
|
9487
|
+
hourBucket: add.hourBucket,
|
|
9488
|
+
requestCount: current.requestCount + add.requestCount,
|
|
9489
|
+
errorCount: current.errorCount + add.errorCount,
|
|
9490
|
+
cacheHitCount: current.cacheHitCount + add.cacheHitCount,
|
|
9491
|
+
breakerHitCount: current.breakerHitCount + add.breakerHitCount
|
|
9492
|
+
};
|
|
9493
|
+
}
|
|
9143
9494
|
function cacheKeyFor(url, tokenKey) {
|
|
9144
9495
|
return createHash("sha256").update(`${tokenKey}:${url}`).digest("hex");
|
|
9145
9496
|
}
|
|
@@ -9336,28 +9687,54 @@ async function forceConsumeBudget(tokenKey) {
|
|
|
9336
9687
|
}
|
|
9337
9688
|
async function recordStat(input) {
|
|
9338
9689
|
const bucket = hourBucket().toISOString();
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9690
|
+
const key = githubApiStatMapKey(input.tokenKey, input.jobId, bucket);
|
|
9691
|
+
const add = {
|
|
9692
|
+
tokenKey: input.tokenKey,
|
|
9693
|
+
jobId: input.jobId,
|
|
9694
|
+
hourBucket: bucket,
|
|
9695
|
+
requestCount: input.breakerHit ? 0 : 1,
|
|
9696
|
+
errorCount: input.error ? 1 : 0,
|
|
9697
|
+
cacheHitCount: input.cacheHit ? 1 : 0,
|
|
9698
|
+
breakerHitCount: input.breakerHit ? 1 : 0
|
|
9699
|
+
};
|
|
9700
|
+
pendingGithubApiStats.set(
|
|
9701
|
+
key,
|
|
9702
|
+
mergeGithubApiStatDelta(pendingGithubApiStats.get(key), add)
|
|
9703
|
+
);
|
|
9704
|
+
pendingGithubApiStatIncrements += 1;
|
|
9705
|
+
if (pendingGithubApiStatIncrements >= GITHUB_API_STAT_FLUSH_EVERY) {
|
|
9706
|
+
await flushPendingGithubApiStats();
|
|
9707
|
+
}
|
|
9708
|
+
}
|
|
9709
|
+
async function flushPendingGithubApiStats() {
|
|
9710
|
+
const rows = [...pendingGithubApiStats.values()];
|
|
9711
|
+
pendingGithubApiStats.clear();
|
|
9712
|
+
pendingGithubApiStatIncrements = 0;
|
|
9713
|
+
for (const row of rows) {
|
|
9714
|
+
await getDb().execute(sql`
|
|
9715
|
+
INSERT INTO github_api_stat (
|
|
9716
|
+
token_key, job_id, hour_bucket, request_count, error_count,
|
|
9717
|
+
cache_hit_count, breaker_hit_count, updated_at
|
|
9718
|
+
)
|
|
9719
|
+
VALUES (
|
|
9720
|
+
${row.tokenKey},
|
|
9721
|
+
${row.jobId},
|
|
9722
|
+
${row.hourBucket}::timestamptz,
|
|
9723
|
+
${row.requestCount},
|
|
9724
|
+
${row.errorCount},
|
|
9725
|
+
${row.cacheHitCount},
|
|
9726
|
+
${row.breakerHitCount},
|
|
9727
|
+
now()
|
|
9728
|
+
)
|
|
9729
|
+
ON CONFLICT (token_key, job_id, hour_bucket) DO UPDATE SET
|
|
9730
|
+
request_count = github_api_stat.request_count + ${row.requestCount},
|
|
9731
|
+
error_count = github_api_stat.error_count + ${row.errorCount},
|
|
9732
|
+
cache_hit_count = github_api_stat.cache_hit_count + ${row.cacheHitCount},
|
|
9733
|
+
breaker_hit_count = github_api_stat.breaker_hit_count + ${row.breakerHitCount},
|
|
9734
|
+
updated_at = now()
|
|
9735
|
+
`);
|
|
9736
|
+
}
|
|
9737
|
+
return rows.length;
|
|
9361
9738
|
}
|
|
9362
9739
|
async function recordBreakerHit(input) {
|
|
9363
9740
|
await recordStat({
|
|
@@ -10397,7 +10774,7 @@ async function writeAuditLog(entry) {
|
|
|
10397
10774
|
);
|
|
10398
10775
|
}
|
|
10399
10776
|
}
|
|
10400
|
-
var
|
|
10777
|
+
var MODULE_KEYS2 = [
|
|
10401
10778
|
"users",
|
|
10402
10779
|
"ssh_servers",
|
|
10403
10780
|
"wiki",
|
|
@@ -10405,11 +10782,12 @@ var MODULE_KEYS = [
|
|
|
10405
10782
|
"domains",
|
|
10406
10783
|
"settings",
|
|
10407
10784
|
"cursor_remote",
|
|
10408
|
-
"code_battle"
|
|
10785
|
+
"code_battle",
|
|
10786
|
+
"git_hosting"
|
|
10409
10787
|
];
|
|
10410
|
-
var
|
|
10788
|
+
var FULL_PERMISSIONS2 = {
|
|
10411
10789
|
modules: Object.fromEntries(
|
|
10412
|
-
|
|
10790
|
+
MODULE_KEYS2.map((k) => [k, true])
|
|
10413
10791
|
),
|
|
10414
10792
|
resources: { ssh_servers: ["*"] }
|
|
10415
10793
|
};
|
|
@@ -10417,12 +10795,12 @@ function parsePermissions(raw) {
|
|
|
10417
10795
|
if (!raw || typeof raw !== "object") return null;
|
|
10418
10796
|
return raw;
|
|
10419
10797
|
}
|
|
10420
|
-
function
|
|
10421
|
-
if (roleName === "superadmin") return
|
|
10798
|
+
function resolvePermissions2(roleName, roleDefaults, userOverrides) {
|
|
10799
|
+
if (roleName === "superadmin") return FULL_PERMISSIONS2;
|
|
10422
10800
|
const base = parsePermissions(roleDefaults);
|
|
10423
10801
|
const overrides = parsePermissions(userOverrides);
|
|
10424
10802
|
const modules = {};
|
|
10425
|
-
for (const key of
|
|
10803
|
+
for (const key of MODULE_KEYS2) {
|
|
10426
10804
|
const userVal = overrides?.modules?.[key];
|
|
10427
10805
|
const roleVal = base?.modules?.[key];
|
|
10428
10806
|
modules[key] = userVal !== void 0 ? userVal : roleVal !== void 0 ? roleVal : false;
|
|
@@ -10471,6 +10849,7 @@ var TOOL_MODULE_MAP = {
|
|
|
10471
10849
|
"cursor-remote-list": "cursor_remote",
|
|
10472
10850
|
"cursor-remote-run": "cursor_remote",
|
|
10473
10851
|
"ai-company": "ssh_servers",
|
|
10852
|
+
"git-credential": "git_hosting",
|
|
10474
10853
|
// cursor-skill intentionally unmapped — any valid API key may pull shared skills
|
|
10475
10854
|
...TRIGGER_TOOL_MODULE_MAP
|
|
10476
10855
|
};
|
|
@@ -10614,7 +10993,7 @@ async function validateApiKey(key) {
|
|
|
10614
10993
|
const roleName = userData.role_name || "user";
|
|
10615
10994
|
const roleDefaults = userData.role_default_permissions ?? {};
|
|
10616
10995
|
const userOverrides = userData.permissions ?? null;
|
|
10617
|
-
const permissions =
|
|
10996
|
+
const permissions = resolvePermissions2(roleName, roleDefaults, userOverrides);
|
|
10618
10997
|
const allowedServerIds = intersectServerAccess(
|
|
10619
10998
|
data.allowed_server_ids,
|
|
10620
10999
|
permissions.resources.ssh_servers
|
|
@@ -10625,9 +11004,9 @@ async function validateApiKey(key) {
|
|
|
10625
11004
|
WHERE id = ${data.id}
|
|
10626
11005
|
AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')
|
|
10627
11006
|
`);
|
|
10628
|
-
const moduleCount =
|
|
11007
|
+
const moduleCount = MODULE_KEYS2.filter((k) => permissions.modules[k]).length;
|
|
10629
11008
|
console.error(
|
|
10630
|
-
`Authenticated as user ${data.created_by} (role: ${roleName}, modules: ${moduleCount}/${
|
|
11009
|
+
`Authenticated as user ${data.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS2.length})`
|
|
10631
11010
|
);
|
|
10632
11011
|
return {
|
|
10633
11012
|
apiKeyId: data.id,
|
|
@@ -10861,7 +11240,7 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
|
|
|
10861
11240
|
const roleName = userData.role_name || "user";
|
|
10862
11241
|
const roleDefaults = userData.role_default_permissions ?? {};
|
|
10863
11242
|
const userOverrides = userData.permissions ?? null;
|
|
10864
|
-
const permissions =
|
|
11243
|
+
const permissions = resolvePermissions2(roleName, roleDefaults, userOverrides);
|
|
10865
11244
|
const allowedServerIds = intersectServerAccess(
|
|
10866
11245
|
apiRow.allowed_server_ids,
|
|
10867
11246
|
permissions.resources.ssh_servers
|
|
@@ -10878,9 +11257,9 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
|
|
|
10878
11257
|
AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')`
|
|
10879
11258
|
)
|
|
10880
11259
|
]);
|
|
10881
|
-
const moduleCount =
|
|
11260
|
+
const moduleCount = MODULE_KEYS2.filter((k) => permissions.modules[k]).length;
|
|
10882
11261
|
console.error(
|
|
10883
|
-
`Authenticated via SSH key "${keyRow.name}" (fp ${fingerprint.slice(0, 24)}...) as user ${apiRow.created_by} (role: ${roleName}, modules: ${moduleCount}/${
|
|
11262
|
+
`Authenticated via SSH key "${keyRow.name}" (fp ${fingerprint.slice(0, 24)}...) as user ${apiRow.created_by} (role: ${roleName}, modules: ${moduleCount}/${MODULE_KEYS2.length})`
|
|
10884
11263
|
);
|
|
10885
11264
|
return {
|
|
10886
11265
|
apiKeyId: apiRow.id,
|
|
@@ -11044,7 +11423,7 @@ function getEncryptionKey() {
|
|
|
11044
11423
|
throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
|
|
11045
11424
|
return buf;
|
|
11046
11425
|
}
|
|
11047
|
-
function encrypt(
|
|
11426
|
+
function encrypt(text23) {
|
|
11048
11427
|
const key = getEncryptionKey();
|
|
11049
11428
|
const iv = randomBytes(ENC_IV_LENGTH);
|
|
11050
11429
|
const cipher = createCipheriv(
|
|
@@ -11052,7 +11431,7 @@ function encrypt(text22) {
|
|
|
11052
11431
|
new Uint8Array(key),
|
|
11053
11432
|
new Uint8Array(iv)
|
|
11054
11433
|
);
|
|
11055
|
-
let encrypted = cipher.update(
|
|
11434
|
+
let encrypted = cipher.update(text23, "utf8", "hex");
|
|
11056
11435
|
encrypted += cipher.final("hex");
|
|
11057
11436
|
const authTag = cipher.getAuthTag();
|
|
11058
11437
|
return Buffer.concat([
|
|
@@ -11789,10 +12168,10 @@ async function r2GetObjectRange(bucket, key, range) {
|
|
|
11789
12168
|
const body = result.Body;
|
|
11790
12169
|
if (!body?.transformToString)
|
|
11791
12170
|
throw new Error("R2 returned no readable body");
|
|
11792
|
-
const
|
|
12171
|
+
const text23 = await body.transformToString();
|
|
11793
12172
|
const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
|
|
11794
12173
|
return `${header}
|
|
11795
|
-
${
|
|
12174
|
+
${text23}`;
|
|
11796
12175
|
} catch (e) {
|
|
11797
12176
|
throw r2WrapError(bucket, key, e);
|
|
11798
12177
|
}
|
|
@@ -12155,15 +12534,15 @@ async function sftpRead(opts, filePath, proxy, options) {
|
|
|
12155
12534
|
clearTimeout(timer);
|
|
12156
12535
|
cleanup?.();
|
|
12157
12536
|
cleanup = void 0;
|
|
12158
|
-
const
|
|
12537
|
+
const text23 = Buffer.concat(
|
|
12159
12538
|
chunks.map((ch) => new Uint8Array(ch))
|
|
12160
12539
|
).toString("utf-8");
|
|
12161
12540
|
if (!isWholeFileRequest) {
|
|
12162
12541
|
const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
|
|
12163
12542
|
resolve(`${header}
|
|
12164
|
-
${
|
|
12543
|
+
${text23}`);
|
|
12165
12544
|
} else {
|
|
12166
|
-
resolve(
|
|
12545
|
+
resolve(text23);
|
|
12167
12546
|
}
|
|
12168
12547
|
});
|
|
12169
12548
|
rs.on("error", (e) => {
|
|
@@ -12319,11 +12698,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
|
|
|
12319
12698
|
if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
|
|
12320
12699
|
return e.names;
|
|
12321
12700
|
}
|
|
12322
|
-
function truncateForLLM(
|
|
12323
|
-
const totalBytes = Buffer.byteLength(
|
|
12701
|
+
function truncateForLLM(text23, maxBytes) {
|
|
12702
|
+
const totalBytes = Buffer.byteLength(text23, "utf8");
|
|
12324
12703
|
if (totalBytes <= maxBytes)
|
|
12325
|
-
return { text:
|
|
12326
|
-
const buf = Buffer.from(
|
|
12704
|
+
return { text: text23, truncated: false, totalBytes, shownBytes: totalBytes };
|
|
12705
|
+
const buf = Buffer.from(text23, "utf8");
|
|
12327
12706
|
let cut = maxBytes;
|
|
12328
12707
|
while (cut > 0 && (buf[cut] & 192) === 128) cut--;
|
|
12329
12708
|
const head = buf.subarray(0, cut).toString("utf8");
|
|
@@ -12354,10 +12733,10 @@ function postprocessResult(result, meta) {
|
|
|
12354
12733
|
if (!result.content?.length) return result;
|
|
12355
12734
|
if (RAW_JSON_TOOLS.has(meta.toolName)) return result;
|
|
12356
12735
|
const block = result.content[0];
|
|
12357
|
-
let
|
|
12358
|
-
const trunc = truncateForLLM(
|
|
12736
|
+
let text23 = String(block.text ?? "");
|
|
12737
|
+
const trunc = truncateForLLM(text23, RESPONSE_MAX_BYTES);
|
|
12359
12738
|
if (trunc.truncated) {
|
|
12360
|
-
|
|
12739
|
+
text23 = trunc.text + "\n\n... " + buildTruncationHint(
|
|
12361
12740
|
meta.toolName,
|
|
12362
12741
|
meta.args,
|
|
12363
12742
|
trunc.totalBytes,
|
|
@@ -12371,11 +12750,11 @@ function postprocessResult(result, meta) {
|
|
|
12371
12750
|
const parts = [`took ${tookStr}`, sizeStr];
|
|
12372
12751
|
if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
|
|
12373
12752
|
if (meta.cached) parts.push("cached");
|
|
12374
|
-
|
|
12753
|
+
text23 = `${text23}
|
|
12375
12754
|
|
|
12376
12755
|
[${parts.join(", ")}]`;
|
|
12377
12756
|
}
|
|
12378
|
-
return { ...result, content: [{ ...block, text:
|
|
12757
|
+
return { ...result, content: [{ ...block, text: text23 }] };
|
|
12379
12758
|
}
|
|
12380
12759
|
function buildPipelineScript(commands, shell, marker, stopOnError) {
|
|
12381
12760
|
if (shell === "powershell") {
|
|
@@ -13161,11 +13540,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
|
|
|
13161
13540
|
applied_by TEXT
|
|
13162
13541
|
);
|
|
13163
13542
|
`.trim();
|
|
13164
|
-
function normaliseMigrationSql(
|
|
13165
|
-
return
|
|
13543
|
+
function normaliseMigrationSql(sql36) {
|
|
13544
|
+
return sql36.replace(/\r\n/g, "\n").trim() + "\n";
|
|
13166
13545
|
}
|
|
13167
|
-
function migrationSha256(
|
|
13168
|
-
return createHash("sha256").update(
|
|
13546
|
+
function migrationSha256(sql36) {
|
|
13547
|
+
return createHash("sha256").update(sql36.replace(/\r\n/g, "\n"), "utf8").digest("hex");
|
|
13169
13548
|
}
|
|
13170
13549
|
function dollarQuoteTag(value) {
|
|
13171
13550
|
let tag = "_mcp";
|
|
@@ -15177,6 +15556,7 @@ var TOOLS = [
|
|
|
15177
15556
|
}
|
|
15178
15557
|
},
|
|
15179
15558
|
...MAILSERVER_TOOLS,
|
|
15559
|
+
...GIT_CREDENTIAL_TOOLS,
|
|
15180
15560
|
...HERMES_COMPANY_TOOLS,
|
|
15181
15561
|
// ----- Trigger.dev -----
|
|
15182
15562
|
...TRIGGER_TOOLS
|
|
@@ -15498,7 +15878,7 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
15498
15878
|
]
|
|
15499
15879
|
};
|
|
15500
15880
|
}
|
|
15501
|
-
const lines = data.hits.map((hit,
|
|
15881
|
+
const lines = data.hits.map((hit, index20) => {
|
|
15502
15882
|
const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
|
|
15503
15883
|
const repo = hit.repo ?? "unknown repo";
|
|
15504
15884
|
const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
|
|
@@ -15508,7 +15888,7 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
15508
15888
|
...Array.isArray(hit.tech) ? hit.tech.slice(0, 4) : []
|
|
15509
15889
|
].filter(Boolean);
|
|
15510
15890
|
const tags = facets.length > 0 ? ` \xB7 ${facets.join(", ")}` : "";
|
|
15511
|
-
return `${
|
|
15891
|
+
return `${index20 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
|
|
15512
15892
|
id: ${hit.id}
|
|
15513
15893
|
${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
|
|
15514
15894
|
});
|
|
@@ -15715,12 +16095,12 @@ Searchable via search-team-memory once indexing completes (usually immediate).`
|
|
|
15715
16095
|
]
|
|
15716
16096
|
};
|
|
15717
16097
|
}
|
|
15718
|
-
const lines = data.hits.map((hit,
|
|
16098
|
+
const lines = data.hits.map((hit, index20) => {
|
|
15719
16099
|
const sim = hit.similarity !== null ? ` \xB7 ${Math.round(hit.similarity * 100)}% match` : "";
|
|
15720
16100
|
const tags = hit.tags.length ? ` \xB7 ${hit.tags.slice(0, 5).join(", ")}` : "";
|
|
15721
16101
|
const sources = hit.sources.length ? `
|
|
15722
16102
|
sources: ${hit.sources.slice(0, 6).map((s) => `${s.type}:${s.ref}`).join(", ")}` : "";
|
|
15723
|
-
return `${
|
|
16103
|
+
return `${index20 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
|
|
15724
16104
|
slug: ${hit.slug}${sources}
|
|
15725
16105
|
${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 400)}`;
|
|
15726
16106
|
});
|
|
@@ -15898,7 +16278,7 @@ Archived sources (${archiveOnly.length}):
|
|
|
15898
16278
|
let formatItems2 = function(items) {
|
|
15899
16279
|
if (items.length === 0) return "(none)";
|
|
15900
16280
|
return items.map(
|
|
15901
|
-
(item,
|
|
16281
|
+
(item, index20) => `${index20 + 1}. [${item.status} \xB7 ${item.folder} \xB7 updated ${item.updated ?? "unknown"}] ${item.title}
|
|
15902
16282
|
slug: ${item.slug}`
|
|
15903
16283
|
).join("\n");
|
|
15904
16284
|
};
|
|
@@ -15924,10 +16304,10 @@ Archived sources (${archiveOnly.length}):
|
|
|
15924
16304
|
};
|
|
15925
16305
|
}
|
|
15926
16306
|
const data = await res.json();
|
|
15927
|
-
const countLine = Object.entries(data.counts).sort(([left], [right]) => left.localeCompare(right)).map(([status,
|
|
15928
|
-
const flagLines = data.flags.length === 0 ? "(none)" : data.flags.map((flag,
|
|
16307
|
+
const countLine = Object.entries(data.counts).sort(([left], [right]) => left.localeCompare(right)).map(([status, count2]) => `${status} ${count2}`).join(" \xB7 ");
|
|
16308
|
+
const flagLines = data.flags.length === 0 ? "(none)" : data.flags.map((flag, index20) => {
|
|
15929
16309
|
const detail = flag.detail.replace(/\s+/g, " ").slice(0, 240);
|
|
15930
|
-
return `${
|
|
16310
|
+
return `${index20 + 1}. [${flag.kind} \xB7 ${flag.folder}${flag.status ? ` \xB7 page ${flag.status}` : ""}] ${flag.title}
|
|
15931
16311
|
slug: ${flag.slug}
|
|
15932
16312
|
${detail}`;
|
|
15933
16313
|
}).join("\n");
|
|
@@ -16834,14 +17214,14 @@ ${renderOne(r)}`);
|
|
|
16834
17214
|
const dryRun = a.dryRun === true;
|
|
16835
17215
|
const confirmAbove = a.confirmAbove !== void 0 ? Math.max(0, Number(a.confirmAbove)) : void 0;
|
|
16836
17216
|
const confirmCount = a.confirmCount !== void 0 ? Number(a.confirmCount) : void 0;
|
|
16837
|
-
const guard = (
|
|
16838
|
-
if (confirmAbove !== void 0 &&
|
|
16839
|
-
if (confirmCount ===
|
|
17217
|
+
const guard = (count2, sizeBytes, where) => {
|
|
17218
|
+
if (confirmAbove !== void 0 && count2 > confirmAbove) {
|
|
17219
|
+
if (confirmCount === count2) return null;
|
|
16840
17220
|
return {
|
|
16841
17221
|
content: [
|
|
16842
17222
|
{
|
|
16843
17223
|
type: "text",
|
|
16844
|
-
text: `Refusing to delete ${
|
|
17224
|
+
text: `Refusing to delete ${count2} item(s) (${formatBytes(sizeBytes)}) under ${where}: exceeds confirmAbove=${confirmAbove}. To proceed, re-issue the call with confirmCount: ${count2}.`
|
|
16845
17225
|
}
|
|
16846
17226
|
]
|
|
16847
17227
|
};
|
|
@@ -17093,8 +17473,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
17093
17473
|
};
|
|
17094
17474
|
const filtered = sortRows(applyFilter(only.rows));
|
|
17095
17475
|
if (format === "json") {
|
|
17096
|
-
const
|
|
17097
|
-
return { content: [{ type: "text", text:
|
|
17476
|
+
const text24 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
|
|
17477
|
+
return { content: [{ type: "text", text: text24 }] };
|
|
17098
17478
|
}
|
|
17099
17479
|
if (groupByProject) {
|
|
17100
17480
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -17121,8 +17501,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
17121
17501
|
}
|
|
17122
17502
|
const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
|
|
17123
17503
|
const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
|
|
17124
|
-
const
|
|
17125
|
-
return { content: [{ type: "text", text:
|
|
17504
|
+
const text23 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
|
|
17505
|
+
return { content: [{ type: "text", text: text23 }] };
|
|
17126
17506
|
}
|
|
17127
17507
|
if (format === "json") {
|
|
17128
17508
|
const lines = [];
|
|
@@ -18945,6 +19325,12 @@ Install: GET https://dashboard.mgsoftware.nl/api/downloads/versions.txt then \u2
|
|
|
18945
19325
|
token
|
|
18946
19326
|
});
|
|
18947
19327
|
}
|
|
19328
|
+
case GIT_CREDENTIAL_TOOL_NAME: {
|
|
19329
|
+
return handleGitCredentialTool(a, {
|
|
19330
|
+
dashboardBaseUrl,
|
|
19331
|
+
apiKey: apiKey ?? ""
|
|
19332
|
+
});
|
|
19333
|
+
}
|
|
18948
19334
|
case HERMES_COMPANY_TOOL_NAME: {
|
|
18949
19335
|
return handleHermesCompanyTool(a, {
|
|
18950
19336
|
userId: ctx.userId,
|
package/package.json
CHANGED