@mgsoftwarebv/mg-dashboard-mcp 7.4.22 → 7.4.24

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.
Files changed (2) hide show
  1. package/dist/index.js +399 -103
  2. 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 sql33 = `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`;
727
+ const sql35 = `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 "${sql33}" 2>/dev/null | tr -d '[:space:]')`,
730
+ `ROW=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql35}" 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 sql33 = `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 "${sql33}" 2>/dev/null`;
752
+ const sql35 = `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 "${sql35}" 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 sql33 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
836
- const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql33}" 2>/dev/null`;
835
+ const sql35 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
836
+ const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql35}" 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 text22 = `Env vars for ${project}/${env} (${vars.length}) \u2014 values hidden, use action="get" with a key:
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: text22 }] };
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 text22 = formatRunDetail(run);
1104
- if (logs) text22 += "\n\n--- Logs ---\n" + logs;
1105
- return { content: [{ type: "text", text: text22 }] };
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 text22 = formatRunDetail(run);
1127
+ let text23 = formatRunDetail(run);
1128
1128
  const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
1129
- if (logs) text22 += "\n\n--- Logs ---\n" + logs;
1130
- return { content: [{ type: "text", text: text22 }] };
1129
+ if (logs) text23 += "\n\n--- Logs ---\n" + logs;
1130
+ return { content: [{ type: "text", text: text23 }] };
1131
1131
  }
1132
1132
  }
1133
1133
  return {
@@ -1391,11 +1391,11 @@ function tokenFromDotenv(content) {
1391
1391
  for (const line of content.split(/\r?\n/)) {
1392
1392
  const trimmed = line.trim();
1393
1393
  if (!trimmed || trimmed.startsWith("#")) continue;
1394
- const eq5 = trimmed.indexOf("=");
1395
- if (eq5 < 1) continue;
1396
- const key = trimmed.slice(0, eq5).trim();
1394
+ const eq6 = trimmed.indexOf("=");
1395
+ if (eq6 < 1) continue;
1396
+ const key = trimmed.slice(0, eq6).trim();
1397
1397
  if (key !== "MAILSERVER_MCP_TOKEN") continue;
1398
- let value = trimmed.slice(eq5 + 1).trim();
1398
+ let value = trimmed.slice(eq6 + 1).trim();
1399
1399
  if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
1400
1400
  value = value.slice(1, -1);
1401
1401
  }
@@ -1500,12 +1500,12 @@ async function mailserverFetch(deps, plan) {
1500
1500
  },
1501
1501
  body: plan.body ? JSON.stringify(plan.body) : void 0
1502
1502
  });
1503
- const text22 = await res.text();
1504
- let json = text22;
1503
+ const text23 = await res.text();
1504
+ let json = text23;
1505
1505
  try {
1506
- json = text22 ? JSON.parse(text22) : null;
1506
+ json = text23 ? JSON.parse(text23) : null;
1507
1507
  } catch {
1508
- json = { error: text22.slice(0, 500) };
1508
+ json = { error: text23.slice(0, 500) };
1509
1509
  }
1510
1510
  return { status: res.status, json };
1511
1511
  }
@@ -4789,10 +4789,10 @@ var ZodObject = class _ZodObject extends ZodType {
4789
4789
  // }) as any;
4790
4790
  // return merged;
4791
4791
  // }
4792
- catchall(index19) {
4792
+ catchall(index20) {
4793
4793
  return new _ZodObject({
4794
4794
  ...this._def,
4795
- catchall: index19
4795
+ catchall: index20
4796
4796
  });
4797
4797
  }
4798
4798
  pick(mask) {
@@ -5110,9 +5110,9 @@ function mergeValues(a, b) {
5110
5110
  return { valid: false };
5111
5111
  }
5112
5112
  const newArray = [];
5113
- for (let index19 = 0; index19 < a.length; index19++) {
5114
- const itemA = a[index19];
5115
- const itemB = b[index19];
5113
+ for (let index20 = 0; index20 < a.length; index20++) {
5114
+ const itemA = a[index20];
5115
+ const itemB = b[index20];
5116
5116
  const sharedValue = mergeValues(itemA, itemB);
5117
5117
  if (!sharedValue.valid) {
5118
5118
  return { valid: false };
@@ -5318,10 +5318,10 @@ var ZodMap = class extends ZodType {
5318
5318
  }
5319
5319
  const keyType = this._def.keyType;
5320
5320
  const valueType = this._def.valueType;
5321
- const pairs = [...ctx.data.entries()].map(([key, value], index19) => {
5321
+ const pairs = [...ctx.data.entries()].map(([key, value], index20) => {
5322
5322
  return {
5323
- key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index19, "key"])),
5324
- value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index19, "value"]))
5323
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index20, "key"])),
5324
+ value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index20, "value"]))
5325
5325
  };
5326
5326
  });
5327
5327
  if (ctx.common.async) {
@@ -6336,6 +6336,8 @@ pgTable(
6336
6336
  verifyNote: text("verify_note"),
6337
6337
  dirty: boolean("dirty").notNull().default(false),
6338
6338
  vaultSyncedAt: timestamp("vault_synced_at", { withTimezone: true }),
6339
+ /** Flattened frontmatter aliases for tsv weight A. */
6340
+ aliasesText: text("aliases_text").notNull().default(""),
6339
6341
  /** Reviewer / itemized-phase lease holder. */
6340
6342
  claimedBy: text("claimed_by"),
6341
6343
  claimedAt: timestamp("claimed_at", { withTimezone: true }),
@@ -6532,6 +6534,7 @@ pgTable(
6532
6534
  topId: text("top_id"),
6533
6535
  caller: text("caller"),
6534
6536
  repo: text("repo"),
6537
+ confident: boolean("confident"),
6535
6538
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6536
6539
  },
6537
6540
  (table) => [index("idx_team_memory_search_log_created").on(table.createdAt)]
@@ -6558,6 +6561,9 @@ pgTable(
6558
6561
  reviewed: boolean("reviewed").notNull().default(false),
6559
6562
  reviewNotes: text("review_notes"),
6560
6563
  reviewedAt: timestamp("reviewed_at", { withTimezone: true }),
6564
+ inputBytes: integer("input_bytes"),
6565
+ inputTokens: integer("input_tokens"),
6566
+ outputTokens: integer("output_tokens"),
6561
6567
  startedAt: timestamp("started_at", { withTimezone: true }),
6562
6568
  finishedAt: timestamp("finished_at", { withTimezone: true }).notNull().defaultNow(),
6563
6569
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
@@ -6588,6 +6594,7 @@ pgTable(
6588
6594
  evidenceRefs: text("evidence_refs").array().notNull().default([]),
6589
6595
  dedupeKey: text("dedupe_key").notNull(),
6590
6596
  source: text("source").notNull().default("auto"),
6597
+ ledgerId: uuid("ledger_id"),
6591
6598
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6592
6599
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6593
6600
  },
@@ -6599,6 +6606,138 @@ pgTable(
6599
6606
  )
6600
6607
  ]
6601
6608
  );
6609
+ pgTable(
6610
+ "wiki_page_chunk",
6611
+ {
6612
+ id: uuid("id").primaryKey().defaultRandom(),
6613
+ pageId: uuid("page_id").notNull(),
6614
+ slug: text("slug").notNull(),
6615
+ headingPath: text("heading_path").notNull().default(""),
6616
+ chunkIndex: integer("chunk_index").notNull().default(0),
6617
+ content: text("content").notNull().default(""),
6618
+ embeddingVec: vector("embedding_vec", {
6619
+ dimensions: WIKI_EMBEDDING_DIMENSIONS
6620
+ }),
6621
+ embeddingModel: text("embedding_model"),
6622
+ embeddedAt: timestamp("embedded_at", { withTimezone: true }),
6623
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6624
+ },
6625
+ (table) => [
6626
+ uniqueIndex("wiki_page_chunk_page_id_chunk_index_key").on(
6627
+ table.pageId,
6628
+ table.chunkIndex
6629
+ ),
6630
+ index("idx_wiki_page_chunk_slug").on(table.slug)
6631
+ ]
6632
+ );
6633
+ pgTable("brain_config", {
6634
+ key: text("key").primaryKey(),
6635
+ value: jsonb("value").$type().notNull(),
6636
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
6637
+ updatedBy: text("updated_by")
6638
+ });
6639
+ pgTable(
6640
+ "brain_metrics_daily",
6641
+ {
6642
+ id: uuid("id").primaryKey().defaultRandom(),
6643
+ metricDate: date("metric_date").notNull(),
6644
+ callerClass: text("caller_class").notNull(),
6645
+ wikiSearches: integer("wiki_searches").notNull().default(0),
6646
+ confidentHits: integer("confident_hits").notNull().default(0),
6647
+ misses: integer("misses").notNull().default(0),
6648
+ hitAt1: real("hit_at_1"),
6649
+ hitAt3: real("hit_at_3"),
6650
+ searchToRead: real("search_to_read"),
6651
+ tmFallbacks: integer("tm_fallbacks").notNull().default(0),
6652
+ reviewerRuns: integer("reviewer_runs").notNull().default(0),
6653
+ reviewerAvgInputBytes: integer("reviewer_avg_input_bytes"),
6654
+ tokensPerPhase: jsonb("tokens_per_phase").$type().notNull().default({}),
6655
+ pagesPromoted: integer("pages_promoted").notNull().default(0),
6656
+ pagesDemoted: integer("pages_demoted").notNull().default(0),
6657
+ thrashCount: integer("thrash_count").notNull().default(0),
6658
+ flagsPer100Reads: real("flags_per_100_reads"),
6659
+ verifierDisagreeRate: real("verifier_disagree_rate"),
6660
+ verifyGateIdle: boolean("verify_gate_idle").notNull().default(false),
6661
+ verifyGateActing: boolean("verify_gate_acting"),
6662
+ draftsOlder14d: integer("drafts_older_14d").notNull().default(0),
6663
+ developersHarvested24h: integer("developers_harvested_24h").notNull().default(0),
6664
+ teamMemoryCited: integer("team_memory_cited").notNull().default(0),
6665
+ extras: jsonb("extras").$type().notNull().default({}),
6666
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6667
+ },
6668
+ (table) => [
6669
+ uniqueIndex("brain_metrics_daily_metric_date_caller_class_key").on(
6670
+ table.metricDate,
6671
+ table.callerClass
6672
+ )
6673
+ ]
6674
+ );
6675
+ pgTable(
6676
+ "brain_change_ledger",
6677
+ {
6678
+ id: uuid("id").primaryKey().defaultRandom(),
6679
+ scope: text("scope").notNull(),
6680
+ tier: text("tier").notNull(),
6681
+ kind: text("kind").notNull(),
6682
+ hypothesis: text("hypothesis").notNull().default(""),
6683
+ target: text("target"),
6684
+ beforeValue: jsonb("before_value"),
6685
+ afterValue: jsonb("after_value"),
6686
+ baselineMetrics: jsonb("baseline_metrics"),
6687
+ outcomeMetrics: jsonb("outcome_metrics"),
6688
+ verdict: text("verdict").notNull().default("pending"),
6689
+ evidenceRefs: text("evidence_refs").array().notNull().default([]),
6690
+ createdBy: text("created_by"),
6691
+ suggestionId: uuid("suggestion_id"),
6692
+ ticketNumber: text("ticket_number"),
6693
+ ticketId: text("ticket_id"),
6694
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6695
+ verdictAt: timestamp("verdict_at", { withTimezone: true }),
6696
+ revertedAt: timestamp("reverted_at", { withTimezone: true })
6697
+ },
6698
+ (table) => [
6699
+ index("idx_brain_change_ledger_verdict").on(table.verdict, table.createdAt),
6700
+ index("idx_brain_change_ledger_scope").on(table.scope, table.createdAt)
6701
+ ]
6702
+ );
6703
+ pgTable(
6704
+ "wiki_search_shadow",
6705
+ {
6706
+ id: uuid("id").primaryKey().defaultRandom(),
6707
+ query: text("query").notNull(),
6708
+ caller: text("caller"),
6709
+ servedSlug: text("served_slug"),
6710
+ servedScore: real("served_score"),
6711
+ shadowSlug: text("shadow_slug"),
6712
+ shadowScore: real("shadow_score"),
6713
+ ledgerId: uuid("ledger_id"),
6714
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6715
+ },
6716
+ (table) => [index("idx_wiki_search_shadow_created").on(table.createdAt)]
6717
+ );
6718
+ pgTable("wiki_eval_snapshot", {
6719
+ id: uuid("id").primaryKey().defaultRandom(),
6720
+ name: text("name").notNull(),
6721
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6722
+ });
6723
+ pgTable(
6724
+ "wiki_eval_label",
6725
+ {
6726
+ id: uuid("id").primaryKey().defaultRandom(),
6727
+ snapshotId: uuid("snapshot_id").notNull(),
6728
+ query: text("query").notNull(),
6729
+ caller: text("caller"),
6730
+ positiveSlug: text("positive_slug"),
6731
+ isNegative: boolean("is_negative").notNull().default(false),
6732
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6733
+ },
6734
+ (table) => [index("idx_wiki_eval_label_snapshot").on(table.snapshotId)]
6735
+ );
6736
+ pgTable("wiki_status_dirty", {
6737
+ slug: text("slug").primaryKey(),
6738
+ markedAt: timestamp("marked_at", { withTimezone: true }).notNull().defaultNow(),
6739
+ reason: text("reason")
6740
+ });
6602
6741
 
6603
6742
  // ../db/src/schema/agent-memory.ts
6604
6743
  pgTable(
@@ -6639,7 +6778,8 @@ pgTable(
6639
6778
  ),
6640
6779
  index("idx_agent_memory_repo").on(table.gitRepository),
6641
6780
  index("idx_agent_memory_user").on(table.userId),
6642
- index("idx_agent_memory_kind").on(table.kind)
6781
+ index("idx_agent_memory_kind").on(table.kind),
6782
+ index("idx_agent_memory_automated_created").on(table.createdAt).where(sql`kind = 'automated'`)
6643
6783
  ]
6644
6784
  );
6645
6785
  var contentSourceType = pgEnum("content_source_type", [
@@ -7084,6 +7224,113 @@ pgTable(
7084
7224
  index("dns_migration_mailbox_migration_idx").on(table.migrationId)
7085
7225
  ]
7086
7226
  );
7227
+ pgTable(
7228
+ "git_repo",
7229
+ {
7230
+ id: uuid("id").primaryKey().defaultRandom(),
7231
+ /** `owner/name`, the canonical id on mggit and GitHub. */
7232
+ fullName: text("full_name").notNull(),
7233
+ owner: text("owner").notNull(),
7234
+ name: text("name").notNull(),
7235
+ source: text("source").$type().notNull().default("mirror"),
7236
+ /** GitHub clone URL the repo follows while it is a mirror. */
7237
+ upstreamUrl: text("upstream_url"),
7238
+ /** Full ref names mggit follows (`refs/heads/main`, `refs/tags/v1`). */
7239
+ followRefs: jsonb("follow_refs").$type().notNull().default([]),
7240
+ /** Sync job keeps the follow list current and the repo present on mggit. */
7241
+ mirrorEnabled: boolean("mirror_enabled").notNull().default(true),
7242
+ /** Present on mggit (repo created). */
7243
+ existsOnMggit: boolean("exists_on_mggit").notNull().default(false),
7244
+ githubArchived: boolean("github_archived").notNull().default(false),
7245
+ githubPrivate: boolean("github_private").notNull().default(true),
7246
+ githubDefaultBranch: text("github_default_branch"),
7247
+ githubPushedAt: timestamp("github_pushed_at", { withTimezone: true }),
7248
+ branchCount: integer("branch_count").notNull().default(0),
7249
+ tagCount: integer("tag_count").notNull().default(0),
7250
+ /** Latest WAL seq seen in an event for this repo. */
7251
+ lastSeq: bigint("last_seq", { mode: "number" }).notNull().default(0),
7252
+ lastEventAt: timestamp("last_event_at", { withTimezone: true }),
7253
+ followState: jsonb("follow_state").$type().notNull().default({}),
7254
+ /** Refs where GitHub and mggit disagree after the last sync (name → {github, mggit}). */
7255
+ behindRefs: jsonb("behind_refs").$type().notNull().default({}),
7256
+ lastSyncAt: timestamp("last_sync_at", { withTimezone: true }),
7257
+ lastSyncError: text("last_sync_error"),
7258
+ notes: text("notes"),
7259
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
7260
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
7261
+ },
7262
+ (table) => [
7263
+ uniqueIndex("git_repo_full_name_uidx").on(table.fullName),
7264
+ index("git_repo_source_idx").on(table.source, table.mirrorEnabled),
7265
+ index("git_repo_last_event_idx").on(table.lastEventAt)
7266
+ ]
7267
+ );
7268
+ pgTable(
7269
+ "git_ref_event",
7270
+ {
7271
+ id: uuid("id").primaryKey().defaultRandom(),
7272
+ repoFullName: text("repo_full_name").notNull(),
7273
+ /** `_walgit.seq` (uint64 as string on the wire). */
7274
+ seq: bigint("seq", { mode: "number" }).notNull(),
7275
+ refName: text("ref_name").notNull(),
7276
+ /** `branch` | `tag` | `` */
7277
+ refType: text("ref_type").notNull().default(""),
7278
+ /** `create` | `update` | `delete` */
7279
+ action: text("action").notNull(),
7280
+ oldOid: text("old_oid").notNull(),
7281
+ newOid: text("new_oid").notNull(),
7282
+ /** Authenticated principal (`upstream` for mirror follows). */
7283
+ pusher: text("pusher").notNull().default(""),
7284
+ correlationId: text("correlation_id"),
7285
+ entryKind: text("entry_kind"),
7286
+ /** `X-Walgit-Delivery` of the batch that carried it. */
7287
+ deliveryId: text("delivery_id"),
7288
+ receivedAt: timestamp("received_at", { withTimezone: true }).notNull().defaultNow()
7289
+ },
7290
+ (table) => [
7291
+ uniqueIndex("git_ref_event_dedup_uidx").on(
7292
+ table.repoFullName,
7293
+ table.seq,
7294
+ table.refName
7295
+ ),
7296
+ index("git_ref_event_repo_received_idx").on(
7297
+ table.repoFullName,
7298
+ table.receivedAt
7299
+ ),
7300
+ index("git_ref_event_received_idx").on(table.receivedAt),
7301
+ index("git_ref_event_pusher_idx").on(table.pusher)
7302
+ ]
7303
+ );
7304
+ pgTable(
7305
+ "git_access_log",
7306
+ {
7307
+ id: bigserial("id", { mode: "number" }).primaryKey(),
7308
+ ts: timestamp("ts", { withTimezone: true }).notNull(),
7309
+ /** nginx `$request_id`; dedup key for re-shipped batches. */
7310
+ requestId: text("request_id").notNull(),
7311
+ ip: text("ip").notNull().default(""),
7312
+ principal: text("principal").notNull().default(""),
7313
+ repo: text("repo").notNull().default(""),
7314
+ /** `advertise` | `fetch` | `push` | `api` | `` */
7315
+ op: text("op").notNull().default(""),
7316
+ method: text("method").notNull().default(""),
7317
+ uri: text("uri").notNull().default(""),
7318
+ status: integer("status").notNull().default(0),
7319
+ bytesIn: bigint("bytes_in", { mode: "number" }).notNull().default(0),
7320
+ bytesOut: bigint("bytes_out", { mode: "number" }).notNull().default(0),
7321
+ durationMs: numeric("duration_ms", { precision: 12, scale: 3 }).notNull().default("0"),
7322
+ userAgent: text("user_agent").notNull().default(""),
7323
+ upstreamStatus: text("upstream_status").notNull().default(""),
7324
+ receivedAt: timestamp("received_at", { withTimezone: true }).notNull().defaultNow()
7325
+ },
7326
+ (table) => [
7327
+ uniqueIndex("git_access_log_request_id_uidx").on(table.requestId),
7328
+ index("git_access_log_ts_idx").on(table.ts),
7329
+ index("git_access_log_repo_ts_idx").on(table.repo, table.ts),
7330
+ index("git_access_log_principal_ts_idx").on(table.principal, table.ts),
7331
+ index("git_access_log_op_status_idx").on(table.op, table.status)
7332
+ ]
7333
+ );
7087
7334
  var directoryLinkType = pgEnum("directory_link_type", [
7088
7335
  "dofollow",
7089
7336
  "nofollow",
@@ -8474,16 +8721,16 @@ var HermesCompanyError = class extends Error {
8474
8721
  }
8475
8722
  };
8476
8723
  function containerNameFor(brandId) {
8477
- return `hermes-${brandId}`;
8724
+ return `hermes-${brandId}-company`;
8478
8725
  }
8479
8726
  function modulesFlags(modules) {
8480
- const flags = [];
8481
- if (modules.seo) flags.push("--seo");
8482
- if (modules.ads) flags.push("--ads");
8483
- if (modules.catalogus) flags.push("--catalogus");
8484
- if (modules.customerFacing) flags.push("--customer-facing");
8485
- if (modules.exact) flags.push("--exact");
8486
- return flags.join(" ");
8727
+ return [
8728
+ modules.seo ? "--seo" : "--no-seo",
8729
+ modules.ads ? "--ads" : "--no-ads",
8730
+ modules.catalogus ? "--catalogus" : "--no-catalogus",
8731
+ modules.customerFacing ? "--customer-facing" : "--no-customer-facing",
8732
+ modules.exact ? "--exact" : "--no-exact"
8733
+ ].join(" ");
8487
8734
  }
8488
8735
  function parseBrandId(raw) {
8489
8736
  const brandId = raw.trim().toLowerCase();
@@ -9140,6 +9387,24 @@ function hourBucket(now = /* @__PURE__ */ new Date()) {
9140
9387
  )
9141
9388
  );
9142
9389
  }
9390
+ var GITHUB_API_STAT_FLUSH_EVERY = 50;
9391
+ var pendingGithubApiStats = /* @__PURE__ */ new Map();
9392
+ var pendingGithubApiStatIncrements = 0;
9393
+ function githubApiStatMapKey(tokenKey, jobId, bucket) {
9394
+ return `${tokenKey} ${jobId} ${bucket}`;
9395
+ }
9396
+ function mergeGithubApiStatDelta(current, add) {
9397
+ if (!current) return { ...add };
9398
+ return {
9399
+ tokenKey: add.tokenKey,
9400
+ jobId: add.jobId,
9401
+ hourBucket: add.hourBucket,
9402
+ requestCount: current.requestCount + add.requestCount,
9403
+ errorCount: current.errorCount + add.errorCount,
9404
+ cacheHitCount: current.cacheHitCount + add.cacheHitCount,
9405
+ breakerHitCount: current.breakerHitCount + add.breakerHitCount
9406
+ };
9407
+ }
9143
9408
  function cacheKeyFor(url, tokenKey) {
9144
9409
  return createHash("sha256").update(`${tokenKey}:${url}`).digest("hex");
9145
9410
  }
@@ -9336,28 +9601,54 @@ async function forceConsumeBudget(tokenKey) {
9336
9601
  }
9337
9602
  async function recordStat(input) {
9338
9603
  const bucket = hourBucket().toISOString();
9339
- await getDb().execute(sql`
9340
- INSERT INTO github_api_stat (
9341
- token_key, job_id, hour_bucket, request_count, error_count,
9342
- cache_hit_count, breaker_hit_count, updated_at
9343
- )
9344
- VALUES (
9345
- ${input.tokenKey},
9346
- ${input.jobId},
9347
- ${bucket}::timestamptz,
9348
- ${input.breakerHit ? 0 : 1},
9349
- ${input.error ? 1 : 0},
9350
- ${input.cacheHit ? 1 : 0},
9351
- ${input.breakerHit ? 1 : 0},
9352
- now()
9353
- )
9354
- ON CONFLICT (token_key, job_id, hour_bucket) DO UPDATE SET
9355
- request_count = github_api_stat.request_count + ${input.breakerHit ? 0 : 1},
9356
- error_count = github_api_stat.error_count + ${input.error ? 1 : 0},
9357
- cache_hit_count = github_api_stat.cache_hit_count + ${input.cacheHit ? 1 : 0},
9358
- breaker_hit_count = github_api_stat.breaker_hit_count + ${input.breakerHit ? 1 : 0},
9359
- updated_at = now()
9360
- `);
9604
+ const key = githubApiStatMapKey(input.tokenKey, input.jobId, bucket);
9605
+ const add = {
9606
+ tokenKey: input.tokenKey,
9607
+ jobId: input.jobId,
9608
+ hourBucket: bucket,
9609
+ requestCount: input.breakerHit ? 0 : 1,
9610
+ errorCount: input.error ? 1 : 0,
9611
+ cacheHitCount: input.cacheHit ? 1 : 0,
9612
+ breakerHitCount: input.breakerHit ? 1 : 0
9613
+ };
9614
+ pendingGithubApiStats.set(
9615
+ key,
9616
+ mergeGithubApiStatDelta(pendingGithubApiStats.get(key), add)
9617
+ );
9618
+ pendingGithubApiStatIncrements += 1;
9619
+ if (pendingGithubApiStatIncrements >= GITHUB_API_STAT_FLUSH_EVERY) {
9620
+ await flushPendingGithubApiStats();
9621
+ }
9622
+ }
9623
+ async function flushPendingGithubApiStats() {
9624
+ const rows = [...pendingGithubApiStats.values()];
9625
+ pendingGithubApiStats.clear();
9626
+ pendingGithubApiStatIncrements = 0;
9627
+ for (const row of rows) {
9628
+ await getDb().execute(sql`
9629
+ INSERT INTO github_api_stat (
9630
+ token_key, job_id, hour_bucket, request_count, error_count,
9631
+ cache_hit_count, breaker_hit_count, updated_at
9632
+ )
9633
+ VALUES (
9634
+ ${row.tokenKey},
9635
+ ${row.jobId},
9636
+ ${row.hourBucket}::timestamptz,
9637
+ ${row.requestCount},
9638
+ ${row.errorCount},
9639
+ ${row.cacheHitCount},
9640
+ ${row.breakerHitCount},
9641
+ now()
9642
+ )
9643
+ ON CONFLICT (token_key, job_id, hour_bucket) DO UPDATE SET
9644
+ request_count = github_api_stat.request_count + ${row.requestCount},
9645
+ error_count = github_api_stat.error_count + ${row.errorCount},
9646
+ cache_hit_count = github_api_stat.cache_hit_count + ${row.cacheHitCount},
9647
+ breaker_hit_count = github_api_stat.breaker_hit_count + ${row.breakerHitCount},
9648
+ updated_at = now()
9649
+ `);
9650
+ }
9651
+ return rows.length;
9361
9652
  }
9362
9653
  async function recordBreakerHit(input) {
9363
9654
  await recordStat({
@@ -10405,7 +10696,8 @@ var MODULE_KEYS = [
10405
10696
  "domains",
10406
10697
  "settings",
10407
10698
  "cursor_remote",
10408
- "code_battle"
10699
+ "code_battle",
10700
+ "git_hosting"
10409
10701
  ];
10410
10702
  var FULL_PERMISSIONS = {
10411
10703
  modules: Object.fromEntries(
@@ -10621,8 +10913,9 @@ async function validateApiKey(key) {
10621
10913
  );
10622
10914
  await db.execute(sql`
10623
10915
  UPDATE dashboard_mcp_api_key
10624
- SET last_used_at = ${(/* @__PURE__ */ new Date()).toISOString()}
10916
+ SET last_used_at = now()
10625
10917
  WHERE id = ${data.id}
10918
+ AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')
10626
10919
  `);
10627
10920
  const moduleCount = MODULE_KEYS.filter((k) => permissions.modules[k]).length;
10628
10921
  console.error(
@@ -10865,13 +11158,16 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
10865
11158
  apiRow.allowed_server_ids,
10866
11159
  permissions.resources.ssh_servers
10867
11160
  );
10868
- const nowIso = (/* @__PURE__ */ new Date()).toISOString();
10869
11161
  await Promise.all([
10870
11162
  db.execute(
10871
- sql`UPDATE dashboard_mcp_ssh_key SET last_used_at = ${nowIso} WHERE id = ${keyRow.id}`
11163
+ sql`UPDATE dashboard_mcp_ssh_key SET last_used_at = now()
11164
+ WHERE id = ${keyRow.id}
11165
+ AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')`
10872
11166
  ),
10873
11167
  db.execute(
10874
- sql`UPDATE dashboard_mcp_api_key SET last_used_at = ${nowIso} WHERE id = ${apiRow.id}`
11168
+ sql`UPDATE dashboard_mcp_api_key SET last_used_at = now()
11169
+ WHERE id = ${apiRow.id}
11170
+ AND (last_used_at IS NULL OR last_used_at < now() - interval '60 seconds')`
10875
11171
  )
10876
11172
  ]);
10877
11173
  const moduleCount = MODULE_KEYS.filter((k) => permissions.modules[k]).length;
@@ -11040,7 +11336,7 @@ function getEncryptionKey() {
11040
11336
  throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
11041
11337
  return buf;
11042
11338
  }
11043
- function encrypt(text22) {
11339
+ function encrypt(text23) {
11044
11340
  const key = getEncryptionKey();
11045
11341
  const iv = randomBytes(ENC_IV_LENGTH);
11046
11342
  const cipher = createCipheriv(
@@ -11048,7 +11344,7 @@ function encrypt(text22) {
11048
11344
  new Uint8Array(key),
11049
11345
  new Uint8Array(iv)
11050
11346
  );
11051
- let encrypted = cipher.update(text22, "utf8", "hex");
11347
+ let encrypted = cipher.update(text23, "utf8", "hex");
11052
11348
  encrypted += cipher.final("hex");
11053
11349
  const authTag = cipher.getAuthTag();
11054
11350
  return Buffer.concat([
@@ -11785,10 +12081,10 @@ async function r2GetObjectRange(bucket, key, range) {
11785
12081
  const body = result.Body;
11786
12082
  if (!body?.transformToString)
11787
12083
  throw new Error("R2 returned no readable body");
11788
- const text22 = await body.transformToString();
12084
+ const text23 = await body.transformToString();
11789
12085
  const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
11790
12086
  return `${header}
11791
- ${text22}`;
12087
+ ${text23}`;
11792
12088
  } catch (e) {
11793
12089
  throw r2WrapError(bucket, key, e);
11794
12090
  }
@@ -12151,15 +12447,15 @@ async function sftpRead(opts, filePath, proxy, options) {
12151
12447
  clearTimeout(timer);
12152
12448
  cleanup?.();
12153
12449
  cleanup = void 0;
12154
- const text22 = Buffer.concat(
12450
+ const text23 = Buffer.concat(
12155
12451
  chunks.map((ch) => new Uint8Array(ch))
12156
12452
  ).toString("utf-8");
12157
12453
  if (!isWholeFileRequest) {
12158
12454
  const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
12159
12455
  resolve(`${header}
12160
- ${text22}`);
12456
+ ${text23}`);
12161
12457
  } else {
12162
- resolve(text22);
12458
+ resolve(text23);
12163
12459
  }
12164
12460
  });
12165
12461
  rs.on("error", (e) => {
@@ -12315,11 +12611,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
12315
12611
  if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
12316
12612
  return e.names;
12317
12613
  }
12318
- function truncateForLLM(text22, maxBytes) {
12319
- const totalBytes = Buffer.byteLength(text22, "utf8");
12614
+ function truncateForLLM(text23, maxBytes) {
12615
+ const totalBytes = Buffer.byteLength(text23, "utf8");
12320
12616
  if (totalBytes <= maxBytes)
12321
- return { text: text22, truncated: false, totalBytes, shownBytes: totalBytes };
12322
- const buf = Buffer.from(text22, "utf8");
12617
+ return { text: text23, truncated: false, totalBytes, shownBytes: totalBytes };
12618
+ const buf = Buffer.from(text23, "utf8");
12323
12619
  let cut = maxBytes;
12324
12620
  while (cut > 0 && (buf[cut] & 192) === 128) cut--;
12325
12621
  const head = buf.subarray(0, cut).toString("utf8");
@@ -12350,10 +12646,10 @@ function postprocessResult(result, meta) {
12350
12646
  if (!result.content?.length) return result;
12351
12647
  if (RAW_JSON_TOOLS.has(meta.toolName)) return result;
12352
12648
  const block = result.content[0];
12353
- let text22 = String(block.text ?? "");
12354
- const trunc = truncateForLLM(text22, RESPONSE_MAX_BYTES);
12649
+ let text23 = String(block.text ?? "");
12650
+ const trunc = truncateForLLM(text23, RESPONSE_MAX_BYTES);
12355
12651
  if (trunc.truncated) {
12356
- text22 = trunc.text + "\n\n... " + buildTruncationHint(
12652
+ text23 = trunc.text + "\n\n... " + buildTruncationHint(
12357
12653
  meta.toolName,
12358
12654
  meta.args,
12359
12655
  trunc.totalBytes,
@@ -12367,11 +12663,11 @@ function postprocessResult(result, meta) {
12367
12663
  const parts = [`took ${tookStr}`, sizeStr];
12368
12664
  if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
12369
12665
  if (meta.cached) parts.push("cached");
12370
- text22 = `${text22}
12666
+ text23 = `${text23}
12371
12667
 
12372
12668
  [${parts.join(", ")}]`;
12373
12669
  }
12374
- return { ...result, content: [{ ...block, text: text22 }] };
12670
+ return { ...result, content: [{ ...block, text: text23 }] };
12375
12671
  }
12376
12672
  function buildPipelineScript(commands, shell, marker, stopOnError) {
12377
12673
  if (shell === "powershell") {
@@ -13157,11 +13453,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
13157
13453
  applied_by TEXT
13158
13454
  );
13159
13455
  `.trim();
13160
- function normaliseMigrationSql(sql33) {
13161
- return sql33.replace(/\r\n/g, "\n").trim() + "\n";
13456
+ function normaliseMigrationSql(sql35) {
13457
+ return sql35.replace(/\r\n/g, "\n").trim() + "\n";
13162
13458
  }
13163
- function migrationSha256(sql33) {
13164
- return createHash("sha256").update(sql33.replace(/\r\n/g, "\n"), "utf8").digest("hex");
13459
+ function migrationSha256(sql35) {
13460
+ return createHash("sha256").update(sql35.replace(/\r\n/g, "\n"), "utf8").digest("hex");
13165
13461
  }
13166
13462
  function dollarQuoteTag(value) {
13167
13463
  let tag = "_mcp";
@@ -15494,7 +15790,7 @@ async function executeToolCall(name, a, _serverId) {
15494
15790
  ]
15495
15791
  };
15496
15792
  }
15497
- const lines = data.hits.map((hit, index19) => {
15793
+ const lines = data.hits.map((hit, index20) => {
15498
15794
  const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
15499
15795
  const repo = hit.repo ?? "unknown repo";
15500
15796
  const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
@@ -15504,7 +15800,7 @@ async function executeToolCall(name, a, _serverId) {
15504
15800
  ...Array.isArray(hit.tech) ? hit.tech.slice(0, 4) : []
15505
15801
  ].filter(Boolean);
15506
15802
  const tags = facets.length > 0 ? ` \xB7 ${facets.join(", ")}` : "";
15507
- return `${index19 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
15803
+ return `${index20 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
15508
15804
  id: ${hit.id}
15509
15805
  ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
15510
15806
  });
@@ -15711,12 +16007,12 @@ Searchable via search-team-memory once indexing completes (usually immediate).`
15711
16007
  ]
15712
16008
  };
15713
16009
  }
15714
- const lines = data.hits.map((hit, index19) => {
16010
+ const lines = data.hits.map((hit, index20) => {
15715
16011
  const sim = hit.similarity !== null ? ` \xB7 ${Math.round(hit.similarity * 100)}% match` : "";
15716
16012
  const tags = hit.tags.length ? ` \xB7 ${hit.tags.slice(0, 5).join(", ")}` : "";
15717
16013
  const sources = hit.sources.length ? `
15718
16014
  sources: ${hit.sources.slice(0, 6).map((s) => `${s.type}:${s.ref}`).join(", ")}` : "";
15719
- return `${index19 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
16015
+ return `${index20 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
15720
16016
  slug: ${hit.slug}${sources}
15721
16017
  ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 400)}`;
15722
16018
  });
@@ -15894,7 +16190,7 @@ Archived sources (${archiveOnly.length}):
15894
16190
  let formatItems2 = function(items) {
15895
16191
  if (items.length === 0) return "(none)";
15896
16192
  return items.map(
15897
- (item, index19) => `${index19 + 1}. [${item.status} \xB7 ${item.folder} \xB7 updated ${item.updated ?? "unknown"}] ${item.title}
16193
+ (item, index20) => `${index20 + 1}. [${item.status} \xB7 ${item.folder} \xB7 updated ${item.updated ?? "unknown"}] ${item.title}
15898
16194
  slug: ${item.slug}`
15899
16195
  ).join("\n");
15900
16196
  };
@@ -15920,10 +16216,10 @@ Archived sources (${archiveOnly.length}):
15920
16216
  };
15921
16217
  }
15922
16218
  const data = await res.json();
15923
- const countLine = Object.entries(data.counts).sort(([left], [right]) => left.localeCompare(right)).map(([status, count]) => `${status} ${count}`).join(" \xB7 ");
15924
- const flagLines = data.flags.length === 0 ? "(none)" : data.flags.map((flag, index19) => {
16219
+ const countLine = Object.entries(data.counts).sort(([left], [right]) => left.localeCompare(right)).map(([status, count2]) => `${status} ${count2}`).join(" \xB7 ");
16220
+ const flagLines = data.flags.length === 0 ? "(none)" : data.flags.map((flag, index20) => {
15925
16221
  const detail = flag.detail.replace(/\s+/g, " ").slice(0, 240);
15926
- return `${index19 + 1}. [${flag.kind} \xB7 ${flag.folder}${flag.status ? ` \xB7 page ${flag.status}` : ""}] ${flag.title}
16222
+ return `${index20 + 1}. [${flag.kind} \xB7 ${flag.folder}${flag.status ? ` \xB7 page ${flag.status}` : ""}] ${flag.title}
15927
16223
  slug: ${flag.slug}
15928
16224
  ${detail}`;
15929
16225
  }).join("\n");
@@ -16830,14 +17126,14 @@ ${renderOne(r)}`);
16830
17126
  const dryRun = a.dryRun === true;
16831
17127
  const confirmAbove = a.confirmAbove !== void 0 ? Math.max(0, Number(a.confirmAbove)) : void 0;
16832
17128
  const confirmCount = a.confirmCount !== void 0 ? Number(a.confirmCount) : void 0;
16833
- const guard = (count, sizeBytes, where) => {
16834
- if (confirmAbove !== void 0 && count > confirmAbove) {
16835
- if (confirmCount === count) return null;
17129
+ const guard = (count2, sizeBytes, where) => {
17130
+ if (confirmAbove !== void 0 && count2 > confirmAbove) {
17131
+ if (confirmCount === count2) return null;
16836
17132
  return {
16837
17133
  content: [
16838
17134
  {
16839
17135
  type: "text",
16840
- text: `Refusing to delete ${count} item(s) (${formatBytes(sizeBytes)}) under ${where}: exceeds confirmAbove=${confirmAbove}. To proceed, re-issue the call with confirmCount: ${count}.`
17136
+ text: `Refusing to delete ${count2} item(s) (${formatBytes(sizeBytes)}) under ${where}: exceeds confirmAbove=${confirmAbove}. To proceed, re-issue the call with confirmCount: ${count2}.`
16841
17137
  }
16842
17138
  ]
16843
17139
  };
@@ -17089,8 +17385,8 @@ ${sample.join("\n")}${files.length > 5 ? `
17089
17385
  };
17090
17386
  const filtered = sortRows(applyFilter(only.rows));
17091
17387
  if (format === "json") {
17092
- const text23 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
17093
- return { content: [{ type: "text", text: text23 }] };
17388
+ const text24 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
17389
+ return { content: [{ type: "text", text: text24 }] };
17094
17390
  }
17095
17391
  if (groupByProject) {
17096
17392
  const groups = /* @__PURE__ */ new Map();
@@ -17117,8 +17413,8 @@ ${sample.join("\n")}${files.length > 5 ? `
17117
17413
  }
17118
17414
  const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
17119
17415
  const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
17120
- const text22 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
17121
- return { content: [{ type: "text", text: text22 }] };
17416
+ const text23 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
17417
+ return { content: [{ type: "text", text: text23 }] };
17122
17418
  }
17123
17419
  if (format === "json") {
17124
17420
  const lines = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mgsoftwarebv/mg-dashboard-mcp",
3
- "version": "7.4.22",
3
+ "version": "7.4.24",
4
4
  "description": "MCP Server for MG Dashboard - SSH, SFTP, Docker, domains, DNS, and environment config tools for Cursor",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",