@mgsoftwarebv/mg-dashboard-mcp 7.1.0 → 7.3.0
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 +910 -38
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -956,9 +956,9 @@ async function fetchAndFormatRun(conn, proxy, sshExec2, instance, runId) {
|
|
|
956
956
|
return { content: [{ type: "text", text: `Invalid API response:
|
|
957
957
|
${rawJson.substring(0, 500)}` }] };
|
|
958
958
|
}
|
|
959
|
-
let
|
|
960
|
-
if (logs)
|
|
961
|
-
return { content: [{ type: "text", text:
|
|
959
|
+
let text9 = formatRunDetail(run);
|
|
960
|
+
if (logs) text9 += "\n\n--- Logs ---\n" + logs;
|
|
961
|
+
return { content: [{ type: "text", text: text9 }] };
|
|
962
962
|
}
|
|
963
963
|
async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSeconds) {
|
|
964
964
|
const pollInterval = 3e3;
|
|
@@ -980,10 +980,10 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
|
|
|
980
980
|
continue;
|
|
981
981
|
}
|
|
982
982
|
if (TERMINAL_STATUSES.has(run.status)) {
|
|
983
|
-
let
|
|
983
|
+
let text9 = formatRunDetail(run);
|
|
984
984
|
const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
|
|
985
|
-
if (logs)
|
|
986
|
-
return { content: [{ type: "text", text:
|
|
985
|
+
if (logs) text9 += "\n\n--- Logs ---\n" + logs;
|
|
986
|
+
return { content: [{ type: "text", text: text9 }] };
|
|
987
987
|
}
|
|
988
988
|
}
|
|
989
989
|
return {
|
|
@@ -3670,10 +3670,10 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
3670
3670
|
// }) as any;
|
|
3671
3671
|
// return merged;
|
|
3672
3672
|
// }
|
|
3673
|
-
catchall(
|
|
3673
|
+
catchall(index7) {
|
|
3674
3674
|
return new _ZodObject({
|
|
3675
3675
|
...this._def,
|
|
3676
|
-
catchall:
|
|
3676
|
+
catchall: index7
|
|
3677
3677
|
});
|
|
3678
3678
|
}
|
|
3679
3679
|
pick(mask) {
|
|
@@ -3991,9 +3991,9 @@ function mergeValues(a, b) {
|
|
|
3991
3991
|
return { valid: false };
|
|
3992
3992
|
}
|
|
3993
3993
|
const newArray = [];
|
|
3994
|
-
for (let
|
|
3995
|
-
const itemA = a[
|
|
3996
|
-
const itemB = b[
|
|
3994
|
+
for (let index7 = 0; index7 < a.length; index7++) {
|
|
3995
|
+
const itemA = a[index7];
|
|
3996
|
+
const itemB = b[index7];
|
|
3997
3997
|
const sharedValue = mergeValues(itemA, itemB);
|
|
3998
3998
|
if (!sharedValue.valid) {
|
|
3999
3999
|
return { valid: false };
|
|
@@ -4199,10 +4199,10 @@ var ZodMap = class extends ZodType {
|
|
|
4199
4199
|
}
|
|
4200
4200
|
const keyType = this._def.keyType;
|
|
4201
4201
|
const valueType = this._def.valueType;
|
|
4202
|
-
const pairs = [...ctx.data.entries()].map(([key, value],
|
|
4202
|
+
const pairs = [...ctx.data.entries()].map(([key, value], index7) => {
|
|
4203
4203
|
return {
|
|
4204
|
-
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [
|
|
4205
|
-
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [
|
|
4204
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index7, "key"])),
|
|
4205
|
+
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index7, "value"]))
|
|
4206
4206
|
};
|
|
4207
4207
|
});
|
|
4208
4208
|
if (ctx.common.async) {
|
|
@@ -5170,6 +5170,231 @@ pgTable("two_factor", {
|
|
|
5170
5170
|
secret: text("secret").notNull(),
|
|
5171
5171
|
backupCodes: text("backup_codes")
|
|
5172
5172
|
});
|
|
5173
|
+
var contentSourceType = pgEnum("content_source_type", [
|
|
5174
|
+
"own_site",
|
|
5175
|
+
"client_site",
|
|
5176
|
+
"external_source",
|
|
5177
|
+
"competitor",
|
|
5178
|
+
"news",
|
|
5179
|
+
"government",
|
|
5180
|
+
"supplier",
|
|
5181
|
+
"unknown"
|
|
5182
|
+
]);
|
|
5183
|
+
var contentSnapshotKind = pgEnum("content_snapshot_kind", [
|
|
5184
|
+
"corpus",
|
|
5185
|
+
"source"
|
|
5186
|
+
]);
|
|
5187
|
+
var contentSources = pgTable(
|
|
5188
|
+
"content_sources",
|
|
5189
|
+
{
|
|
5190
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5191
|
+
url: text("url").notNull(),
|
|
5192
|
+
canonicalUrl: text("canonical_url"),
|
|
5193
|
+
domain: text("domain").notNull(),
|
|
5194
|
+
sourceType: contentSourceType("source_type").notNull().default("unknown"),
|
|
5195
|
+
title: text("title"),
|
|
5196
|
+
author: text("author"),
|
|
5197
|
+
publishedAt: timestamp("published_at", { withTimezone: true }),
|
|
5198
|
+
extractionMethod: text("extraction_method"),
|
|
5199
|
+
summary: text("summary"),
|
|
5200
|
+
facts: jsonb("facts").$type().notNull().default([]),
|
|
5201
|
+
limitations: jsonb("limitations").$type().notNull().default([]),
|
|
5202
|
+
fullText: text("full_text"),
|
|
5203
|
+
textHash: text("text_hash"),
|
|
5204
|
+
wordCount: integer("word_count"),
|
|
5205
|
+
language: text("language"),
|
|
5206
|
+
projectKey: text("project_key"),
|
|
5207
|
+
siteKey: text("site_key"),
|
|
5208
|
+
ticketNumber: text("ticket_number"),
|
|
5209
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5210
|
+
fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5211
|
+
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5212
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5213
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5214
|
+
},
|
|
5215
|
+
(table) => [
|
|
5216
|
+
uniqueIndex("content_sources_canonical_url_uidx").on(table.canonicalUrl),
|
|
5217
|
+
index("content_sources_domain_idx").on(table.domain),
|
|
5218
|
+
index("content_sources_type_idx").on(table.sourceType),
|
|
5219
|
+
index("content_sources_text_hash_idx").on(table.textHash),
|
|
5220
|
+
index("content_sources_project_idx").on(table.projectKey),
|
|
5221
|
+
index("content_sources_ticket_idx").on(table.ticketNumber),
|
|
5222
|
+
index("content_sources_last_seen_idx").on(table.lastSeenAt.desc())
|
|
5223
|
+
]
|
|
5224
|
+
);
|
|
5225
|
+
var contentEvidencePacks = pgTable(
|
|
5226
|
+
"content_evidence_packs",
|
|
5227
|
+
{
|
|
5228
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5229
|
+
topic: text("topic"),
|
|
5230
|
+
locale: text("locale"),
|
|
5231
|
+
projectKey: text("project_key"),
|
|
5232
|
+
siteKey: text("site_key"),
|
|
5233
|
+
ticketNumber: text("ticket_number"),
|
|
5234
|
+
generatedAt: timestamp("generated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5235
|
+
sources: jsonb("sources").$type().notNull().default([]),
|
|
5236
|
+
verifiedFacts: jsonb("verified_facts").$type().notNull().default([]),
|
|
5237
|
+
conflicts: jsonb("conflicts").$type().notNull().default([]),
|
|
5238
|
+
uniqueClaims: jsonb("unique_claims").$type().notNull().default([]),
|
|
5239
|
+
openQuestions: jsonb("open_questions").$type().notNull().default([]),
|
|
5240
|
+
verbatimWarnings: jsonb("verbatim_warnings").$type().notNull().default([]),
|
|
5241
|
+
limitations: jsonb("limitations").$type().notNull().default([]),
|
|
5242
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5243
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5244
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5245
|
+
},
|
|
5246
|
+
(table) => [
|
|
5247
|
+
index("content_evidence_packs_project_idx").on(table.projectKey),
|
|
5248
|
+
index("content_evidence_packs_ticket_idx").on(table.ticketNumber),
|
|
5249
|
+
index("content_evidence_packs_generated_idx").on(table.generatedAt.desc())
|
|
5250
|
+
]
|
|
5251
|
+
);
|
|
5252
|
+
pgTable(
|
|
5253
|
+
"content_evidence_pack_source",
|
|
5254
|
+
{
|
|
5255
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5256
|
+
packId: uuid("pack_id").notNull().references(() => contentEvidencePacks.id, { onDelete: "cascade" }),
|
|
5257
|
+
contentSourceId: uuid("content_source_id").notNull().references(() => contentSources.id, { onDelete: "cascade" }),
|
|
5258
|
+
packSourceRef: text("pack_source_ref"),
|
|
5259
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5260
|
+
},
|
|
5261
|
+
(table) => [
|
|
5262
|
+
uniqueIndex("content_evidence_pack_source_uidx").on(
|
|
5263
|
+
table.packId,
|
|
5264
|
+
table.contentSourceId
|
|
5265
|
+
),
|
|
5266
|
+
index("content_evidence_pack_source_source_idx").on(table.contentSourceId)
|
|
5267
|
+
]
|
|
5268
|
+
);
|
|
5269
|
+
var contentCorpusItems = pgTable(
|
|
5270
|
+
"content_corpus_items",
|
|
5271
|
+
{
|
|
5272
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5273
|
+
projectKey: text("project_key").notNull(),
|
|
5274
|
+
siteKey: text("site_key"),
|
|
5275
|
+
url: text("url").notNull(),
|
|
5276
|
+
canonicalUrl: text("canonical_url"),
|
|
5277
|
+
domain: text("domain").notNull(),
|
|
5278
|
+
pagePath: text("page_path"),
|
|
5279
|
+
sourceType: contentSourceType("source_type").notNull().default("own_site"),
|
|
5280
|
+
title: text("title"),
|
|
5281
|
+
h1: text("h1"),
|
|
5282
|
+
headings: jsonb("headings").$type().notNull().default([]),
|
|
5283
|
+
wordCount: integer("word_count"),
|
|
5284
|
+
fullText: text("full_text"),
|
|
5285
|
+
textHash: text("text_hash"),
|
|
5286
|
+
internalLinks: jsonb("internal_links").$type().notNull().default([]),
|
|
5287
|
+
externalLinks: jsonb("external_links").$type().notNull().default([]),
|
|
5288
|
+
language: text("language"),
|
|
5289
|
+
ticketNumber: text("ticket_number"),
|
|
5290
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5291
|
+
extractedAt: timestamp("extracted_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5292
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5293
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5294
|
+
},
|
|
5295
|
+
(table) => [
|
|
5296
|
+
uniqueIndex("content_corpus_items_project_canonical_uidx").on(
|
|
5297
|
+
table.projectKey,
|
|
5298
|
+
table.canonicalUrl
|
|
5299
|
+
),
|
|
5300
|
+
index("content_corpus_items_domain_idx").on(table.domain),
|
|
5301
|
+
index("content_corpus_items_project_idx").on(table.projectKey),
|
|
5302
|
+
index("content_corpus_items_type_idx").on(table.sourceType),
|
|
5303
|
+
index("content_corpus_items_text_hash_idx").on(table.textHash),
|
|
5304
|
+
index("content_corpus_items_extracted_idx").on(table.extractedAt.desc())
|
|
5305
|
+
]
|
|
5306
|
+
);
|
|
5307
|
+
pgTable(
|
|
5308
|
+
"content_quality_runs",
|
|
5309
|
+
{
|
|
5310
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5311
|
+
corpusItemId: uuid("corpus_item_id").references(
|
|
5312
|
+
() => contentCorpusItems.id,
|
|
5313
|
+
{ onDelete: "set null" }
|
|
5314
|
+
),
|
|
5315
|
+
contentSourceId: uuid("content_source_id").references(
|
|
5316
|
+
() => contentSources.id,
|
|
5317
|
+
{ onDelete: "set null" }
|
|
5318
|
+
),
|
|
5319
|
+
projectKey: text("project_key"),
|
|
5320
|
+
siteKey: text("site_key"),
|
|
5321
|
+
url: text("url"),
|
|
5322
|
+
canonicalUrl: text("canonical_url"),
|
|
5323
|
+
tool: text("tool").notNull(),
|
|
5324
|
+
toolVersion: text("tool_version"),
|
|
5325
|
+
score: integer("score"),
|
|
5326
|
+
flags: jsonb("flags").$type().notNull().default([]),
|
|
5327
|
+
report: jsonb("report").$type().notNull().default({}),
|
|
5328
|
+
reportUrl: text("report_url"),
|
|
5329
|
+
ticketNumber: text("ticket_number"),
|
|
5330
|
+
runAt: timestamp("run_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5331
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5332
|
+
},
|
|
5333
|
+
(table) => [
|
|
5334
|
+
index("content_quality_runs_corpus_idx").on(table.corpusItemId),
|
|
5335
|
+
index("content_quality_runs_source_idx").on(table.contentSourceId),
|
|
5336
|
+
index("content_quality_runs_project_run_idx").on(
|
|
5337
|
+
table.projectKey,
|
|
5338
|
+
table.runAt.desc()
|
|
5339
|
+
),
|
|
5340
|
+
index("content_quality_runs_url_idx").on(table.url)
|
|
5341
|
+
]
|
|
5342
|
+
);
|
|
5343
|
+
pgTable(
|
|
5344
|
+
"content_snapshots",
|
|
5345
|
+
{
|
|
5346
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5347
|
+
corpusItemId: uuid("corpus_item_id").references(
|
|
5348
|
+
() => contentCorpusItems.id,
|
|
5349
|
+
{ onDelete: "cascade" }
|
|
5350
|
+
),
|
|
5351
|
+
contentSourceId: uuid("content_source_id").references(
|
|
5352
|
+
() => contentSources.id,
|
|
5353
|
+
{ onDelete: "cascade" }
|
|
5354
|
+
),
|
|
5355
|
+
kind: contentSnapshotKind("kind").notNull(),
|
|
5356
|
+
url: text("url"),
|
|
5357
|
+
canonicalUrl: text("canonical_url"),
|
|
5358
|
+
domain: text("domain"),
|
|
5359
|
+
title: text("title"),
|
|
5360
|
+
fullText: text("full_text"),
|
|
5361
|
+
textHash: text("text_hash"),
|
|
5362
|
+
wordCount: integer("word_count"),
|
|
5363
|
+
headings: jsonb("headings").$type().notNull().default([]),
|
|
5364
|
+
internalLinks: jsonb("internal_links").$type().notNull().default([]),
|
|
5365
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5366
|
+
/**
|
|
5367
|
+
* Run that captured this snapshot (managed site content sync, 2026-DASMG-042).
|
|
5368
|
+
* Soft pointer (no Drizzle .references() to avoid a schema import cycle); the
|
|
5369
|
+
* DB foreign key is declared in the migration.
|
|
5370
|
+
*/
|
|
5371
|
+
ingestionRunId: uuid("ingestion_run_id"),
|
|
5372
|
+
/** Source git commit / deploy id this snapshot was captured for, when known. */
|
|
5373
|
+
sourceCommit: text("source_commit"),
|
|
5374
|
+
capturedAt: timestamp("captured_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5375
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5376
|
+
},
|
|
5377
|
+
(table) => [
|
|
5378
|
+
index("content_snapshots_corpus_idx").on(
|
|
5379
|
+
table.corpusItemId,
|
|
5380
|
+
table.capturedAt.desc()
|
|
5381
|
+
),
|
|
5382
|
+
index("content_snapshots_source_idx").on(
|
|
5383
|
+
table.contentSourceId,
|
|
5384
|
+
table.capturedAt.desc()
|
|
5385
|
+
),
|
|
5386
|
+
index("content_snapshots_text_hash_idx").on(table.textHash),
|
|
5387
|
+
index("content_snapshots_ingestion_run_idx").on(table.ingestionRunId),
|
|
5388
|
+
uniqueIndex("content_snapshots_corpus_hash_uidx").on(
|
|
5389
|
+
table.corpusItemId,
|
|
5390
|
+
table.textHash
|
|
5391
|
+
),
|
|
5392
|
+
uniqueIndex("content_snapshots_source_hash_uidx").on(
|
|
5393
|
+
table.contentSourceId,
|
|
5394
|
+
table.textHash
|
|
5395
|
+
)
|
|
5396
|
+
]
|
|
5397
|
+
);
|
|
5173
5398
|
var managedServerOs = pgEnum("managed_server_os", [
|
|
5174
5399
|
"linux",
|
|
5175
5400
|
"windows",
|
|
@@ -5745,6 +5970,103 @@ pgTable(
|
|
|
5745
5970
|
index("github_webhook_secret_owner_idx").on(table.owner)
|
|
5746
5971
|
]
|
|
5747
5972
|
);
|
|
5973
|
+
var managedSiteSyncMode = pgEnum("managed_site_sync_mode", [
|
|
5974
|
+
"sitemap",
|
|
5975
|
+
"git",
|
|
5976
|
+
"hybrid",
|
|
5977
|
+
"manual"
|
|
5978
|
+
]);
|
|
5979
|
+
var contentIngestionTrigger = pgEnum("content_ingestion_trigger", [
|
|
5980
|
+
"manual",
|
|
5981
|
+
"git_push",
|
|
5982
|
+
"deploy",
|
|
5983
|
+
"cron",
|
|
5984
|
+
"sitemap"
|
|
5985
|
+
]);
|
|
5986
|
+
var contentIngestionStatus = pgEnum("content_ingestion_status", [
|
|
5987
|
+
"queued",
|
|
5988
|
+
"running",
|
|
5989
|
+
"completed",
|
|
5990
|
+
"partial",
|
|
5991
|
+
"failed"
|
|
5992
|
+
]);
|
|
5993
|
+
var managedSites = pgTable(
|
|
5994
|
+
"managed_sites",
|
|
5995
|
+
{
|
|
5996
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5997
|
+
projectKey: text("project_key").notNull(),
|
|
5998
|
+
siteKey: text("site_key"),
|
|
5999
|
+
name: text("name"),
|
|
6000
|
+
domain: text("domain").notNull(),
|
|
6001
|
+
/** GitHub repo (owner/repo) that publishes this site, when git-driven. */
|
|
6002
|
+
repo: text("repo"),
|
|
6003
|
+
/** Branch whose pushes trigger a sync; null = any branch. */
|
|
6004
|
+
branch: text("branch"),
|
|
6005
|
+
sitemapUrl: text("sitemap_url"),
|
|
6006
|
+
sourceType: contentSourceType("source_type").notNull().default("own_site"),
|
|
6007
|
+
enabled: boolean("enabled").notNull().default(true),
|
|
6008
|
+
syncMode: managedSiteSyncMode("sync_mode").notNull().default("sitemap"),
|
|
6009
|
+
/** Git path -> URL mapping rules used to turn changed files into URLs. */
|
|
6010
|
+
pathUrlMap: jsonb("path_url_map").$type().notNull().default([]),
|
|
6011
|
+
/** Per-run URL cap; null = use the engine default. */
|
|
6012
|
+
maxUrlsPerRun: integer("max_urls_per_run"),
|
|
6013
|
+
/** When true, changed pages are flagged for an MG SEO CLI follow-up (soft handoff). */
|
|
6014
|
+
exportToSeoCli: boolean("export_to_seo_cli").notNull().default(false),
|
|
6015
|
+
lastSyncAt: timestamp("last_sync_at", { withTimezone: true }),
|
|
6016
|
+
/** Soft pointer to the most recent run (no FK to avoid a cycle). */
|
|
6017
|
+
lastRunId: uuid("last_run_id"),
|
|
6018
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
6019
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
6020
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
6021
|
+
},
|
|
6022
|
+
(table) => [
|
|
6023
|
+
uniqueIndex("managed_sites_project_domain_uidx").on(
|
|
6024
|
+
table.projectKey,
|
|
6025
|
+
table.domain
|
|
6026
|
+
),
|
|
6027
|
+
index("managed_sites_repo_idx").on(table.repo),
|
|
6028
|
+
index("managed_sites_enabled_idx").on(table.enabled),
|
|
6029
|
+
index("managed_sites_project_idx").on(table.projectKey)
|
|
6030
|
+
]
|
|
6031
|
+
);
|
|
6032
|
+
pgTable(
|
|
6033
|
+
"content_ingestion_runs",
|
|
6034
|
+
{
|
|
6035
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
6036
|
+
siteId: uuid("site_id").notNull().references(() => managedSites.id, { onDelete: "cascade" }),
|
|
6037
|
+
projectKey: text("project_key"),
|
|
6038
|
+
triggerType: contentIngestionTrigger("trigger_type").notNull(),
|
|
6039
|
+
status: contentIngestionStatus("status").notNull().default("queued"),
|
|
6040
|
+
/** Trigger.dev run id handling this ingestion, when dispatched. */
|
|
6041
|
+
triggerRunId: text("trigger_run_id"),
|
|
6042
|
+
sourceCommit: text("source_commit"),
|
|
6043
|
+
sourceRef: text("source_ref"),
|
|
6044
|
+
changedUrls: jsonb("changed_urls").$type().notNull().default([]),
|
|
6045
|
+
skippedUrls: jsonb("skipped_urls").$type().notNull().default([]),
|
|
6046
|
+
failedUrls: jsonb("failed_urls").$type().notNull().default([]),
|
|
6047
|
+
limitations: jsonb("limitations").$type().notNull().default([]),
|
|
6048
|
+
/** Corpus item ids touched by this run (for QC / MG SEO CLI handoff). */
|
|
6049
|
+
corpusItemIds: jsonb("corpus_item_ids").$type().notNull().default([]),
|
|
6050
|
+
changedCount: integer("changed_count").notNull().default(0),
|
|
6051
|
+
skippedCount: integer("skipped_count").notNull().default(0),
|
|
6052
|
+
failedCount: integer("failed_count").notNull().default(0),
|
|
6053
|
+
/** Set when changed pages were flagged for an MG SEO CLI follow-up. */
|
|
6054
|
+
qcMarked: boolean("qc_marked").notNull().default(false),
|
|
6055
|
+
error: text("error"),
|
|
6056
|
+
startedAt: timestamp("started_at", { withTimezone: true }),
|
|
6057
|
+
finishedAt: timestamp("finished_at", { withTimezone: true }),
|
|
6058
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
6059
|
+
},
|
|
6060
|
+
(table) => [
|
|
6061
|
+
index("content_ingestion_runs_site_idx").on(
|
|
6062
|
+
table.siteId,
|
|
6063
|
+
table.createdAt.desc()
|
|
6064
|
+
),
|
|
6065
|
+
index("content_ingestion_runs_status_idx").on(table.status),
|
|
6066
|
+
index("content_ingestion_runs_trigger_run_idx").on(table.triggerRunId),
|
|
6067
|
+
index("content_ingestion_runs_project_idx").on(table.projectKey)
|
|
6068
|
+
]
|
|
6069
|
+
);
|
|
5748
6070
|
var GITHUB_API = "https://api.github.com";
|
|
5749
6071
|
var ReleaseProfileQuickCreateError = class extends Error {
|
|
5750
6072
|
constructor(message, code = "INTERNAL_SERVER_ERROR") {
|
|
@@ -7075,7 +7397,7 @@ function getEncryptionKey() {
|
|
|
7075
7397
|
throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
|
|
7076
7398
|
return buf;
|
|
7077
7399
|
}
|
|
7078
|
-
function encrypt(
|
|
7400
|
+
function encrypt(text9) {
|
|
7079
7401
|
const key = getEncryptionKey();
|
|
7080
7402
|
const iv = randomBytes(ENC_IV_LENGTH);
|
|
7081
7403
|
const cipher = createCipheriv(
|
|
@@ -7083,7 +7405,7 @@ function encrypt(text7) {
|
|
|
7083
7405
|
new Uint8Array(key),
|
|
7084
7406
|
new Uint8Array(iv)
|
|
7085
7407
|
);
|
|
7086
|
-
let encrypted = cipher.update(
|
|
7408
|
+
let encrypted = cipher.update(text9, "utf8", "hex");
|
|
7087
7409
|
encrypted += cipher.final("hex");
|
|
7088
7410
|
const authTag = cipher.getAuthTag();
|
|
7089
7411
|
return Buffer.concat([
|
|
@@ -7803,10 +8125,10 @@ async function r2GetObjectRange(bucket, key, range) {
|
|
|
7803
8125
|
const body = result.Body;
|
|
7804
8126
|
if (!body?.transformToString)
|
|
7805
8127
|
throw new Error("R2 returned no readable body");
|
|
7806
|
-
const
|
|
8128
|
+
const text9 = await body.transformToString();
|
|
7807
8129
|
const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
|
|
7808
8130
|
return `${header}
|
|
7809
|
-
${
|
|
8131
|
+
${text9}`;
|
|
7810
8132
|
} catch (e) {
|
|
7811
8133
|
throw r2WrapError(bucket, key, e);
|
|
7812
8134
|
}
|
|
@@ -8169,15 +8491,15 @@ async function sftpRead(opts, filePath, proxy, options) {
|
|
|
8169
8491
|
clearTimeout(timer);
|
|
8170
8492
|
cleanup?.();
|
|
8171
8493
|
cleanup = void 0;
|
|
8172
|
-
const
|
|
8494
|
+
const text9 = Buffer.concat(
|
|
8173
8495
|
chunks.map((ch) => new Uint8Array(ch))
|
|
8174
8496
|
).toString("utf-8");
|
|
8175
8497
|
if (!isWholeFileRequest) {
|
|
8176
8498
|
const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
|
|
8177
8499
|
resolve(`${header}
|
|
8178
|
-
${
|
|
8500
|
+
${text9}`);
|
|
8179
8501
|
} else {
|
|
8180
|
-
resolve(
|
|
8502
|
+
resolve(text9);
|
|
8181
8503
|
}
|
|
8182
8504
|
});
|
|
8183
8505
|
rs.on("error", (e) => {
|
|
@@ -8211,7 +8533,27 @@ var NO_FOOTER_TOOLS = /* @__PURE__ */ new Set();
|
|
|
8211
8533
|
var RAW_JSON_TOOLS = /* @__PURE__ */ new Set([
|
|
8212
8534
|
"get_mg_dashboard_commits",
|
|
8213
8535
|
"extract_article",
|
|
8214
|
-
"research_topic"
|
|
8536
|
+
"research_topic",
|
|
8537
|
+
// Content registry / corpus (2026-DASMG-041) — compact JSON consumed verbatim.
|
|
8538
|
+
"save_content_source",
|
|
8539
|
+
"get_content_source_by_url",
|
|
8540
|
+
"get_content_source_by_id",
|
|
8541
|
+
"search_content_sources",
|
|
8542
|
+
"save_research_pack",
|
|
8543
|
+
"get_research_pack",
|
|
8544
|
+
"save_content_corpus_item",
|
|
8545
|
+
"list_project_content_corpus",
|
|
8546
|
+
"get_content_context_for_project",
|
|
8547
|
+
"save_content_quality_run",
|
|
8548
|
+
"list_content_snapshots",
|
|
8549
|
+
"link_content_pack_to_ticket",
|
|
8550
|
+
"prune_content_snapshots",
|
|
8551
|
+
// Managed site content sync (2026-DASMG-042) — compact JSON consumed verbatim.
|
|
8552
|
+
"register_managed_site",
|
|
8553
|
+
"update_managed_site",
|
|
8554
|
+
"list_managed_sites",
|
|
8555
|
+
"trigger_content_sync",
|
|
8556
|
+
"get_content_sync_status"
|
|
8215
8557
|
]);
|
|
8216
8558
|
var TOOL_CACHE_TTL_MS = {
|
|
8217
8559
|
"list-servers": 6e4,
|
|
@@ -8312,11 +8654,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
|
|
|
8312
8654
|
if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
|
|
8313
8655
|
return e.names;
|
|
8314
8656
|
}
|
|
8315
|
-
function truncateForLLM(
|
|
8316
|
-
const totalBytes = Buffer.byteLength(
|
|
8657
|
+
function truncateForLLM(text9, maxBytes) {
|
|
8658
|
+
const totalBytes = Buffer.byteLength(text9, "utf8");
|
|
8317
8659
|
if (totalBytes <= maxBytes)
|
|
8318
|
-
return { text:
|
|
8319
|
-
const buf = Buffer.from(
|
|
8660
|
+
return { text: text9, truncated: false, totalBytes, shownBytes: totalBytes };
|
|
8661
|
+
const buf = Buffer.from(text9, "utf8");
|
|
8320
8662
|
let cut = maxBytes;
|
|
8321
8663
|
while (cut > 0 && (buf[cut] & 192) === 128) cut--;
|
|
8322
8664
|
const head = buf.subarray(0, cut).toString("utf8");
|
|
@@ -8347,10 +8689,10 @@ function postprocessResult(result, meta) {
|
|
|
8347
8689
|
if (!result.content?.length) return result;
|
|
8348
8690
|
if (RAW_JSON_TOOLS.has(meta.toolName)) return result;
|
|
8349
8691
|
const block = result.content[0];
|
|
8350
|
-
let
|
|
8351
|
-
const trunc = truncateForLLM(
|
|
8692
|
+
let text9 = String(block.text ?? "");
|
|
8693
|
+
const trunc = truncateForLLM(text9, RESPONSE_MAX_BYTES);
|
|
8352
8694
|
if (trunc.truncated) {
|
|
8353
|
-
|
|
8695
|
+
text9 = trunc.text + "\n\n... " + buildTruncationHint(
|
|
8354
8696
|
meta.toolName,
|
|
8355
8697
|
meta.args,
|
|
8356
8698
|
trunc.totalBytes,
|
|
@@ -8364,11 +8706,11 @@ function postprocessResult(result, meta) {
|
|
|
8364
8706
|
const parts = [`took ${tookStr}`, sizeStr];
|
|
8365
8707
|
if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
|
|
8366
8708
|
if (meta.cached) parts.push("cached");
|
|
8367
|
-
|
|
8709
|
+
text9 = `${text9}
|
|
8368
8710
|
|
|
8369
8711
|
[${parts.join(", ")}]`;
|
|
8370
8712
|
}
|
|
8371
|
-
return { ...result, content: [{ ...block, text:
|
|
8713
|
+
return { ...result, content: [{ ...block, text: text9 }] };
|
|
8372
8714
|
}
|
|
8373
8715
|
function buildPipelineScript(commands, shell, marker, stopOnError) {
|
|
8374
8716
|
if (shell === "powershell") {
|
|
@@ -10420,6 +10762,462 @@ var TOOLS = [
|
|
|
10420
10762
|
}
|
|
10421
10763
|
}
|
|
10422
10764
|
},
|
|
10765
|
+
// ----- Content registry / corpus persistence (2026-DASMG-041) -----
|
|
10766
|
+
{
|
|
10767
|
+
name: "save_content_source",
|
|
10768
|
+
description: "Persist (dedupe + upsert) ONE fetched source \u2014 the durable companion to extract_article (2026-DASMG-041). Pass a `url` (re-extracts internally) and/or already-extracted fields (title/summary/facts/fullText). Dedupes on canonical URL then text hash, and snapshots the previous version into content_snapshots when the text changed. External sources keep their `source_type` so they stay labelled as evidence, never own content. Returns the stored id + a compact summary + whether it was created/updated/snapshotted.",
|
|
10769
|
+
inputSchema: {
|
|
10770
|
+
type: "object",
|
|
10771
|
+
properties: {
|
|
10772
|
+
url: { type: "string", description: "Source URL (required)." },
|
|
10773
|
+
canonicalUrl: { type: "string", description: "Canonical URL used for dedupe." },
|
|
10774
|
+
sourceType: {
|
|
10775
|
+
type: "string",
|
|
10776
|
+
enum: [
|
|
10777
|
+
"own_site",
|
|
10778
|
+
"client_site",
|
|
10779
|
+
"external_source",
|
|
10780
|
+
"competitor",
|
|
10781
|
+
"news",
|
|
10782
|
+
"government",
|
|
10783
|
+
"supplier",
|
|
10784
|
+
"unknown"
|
|
10785
|
+
],
|
|
10786
|
+
description: "Provenance label. own_site/client_site = managed content; the rest are evidence only."
|
|
10787
|
+
},
|
|
10788
|
+
title: { type: "string", description: "Title." },
|
|
10789
|
+
author: { type: "string", description: "Author." },
|
|
10790
|
+
publishedAt: { type: "string", description: "Publish date (any parseable format)." },
|
|
10791
|
+
summary: { type: "string", description: "Short factual summary." },
|
|
10792
|
+
facts: { type: "array", items: { type: "string" }, description: "Key facts." },
|
|
10793
|
+
limitations: { type: "array", items: { type: "string" } },
|
|
10794
|
+
fullText: { type: "string", description: "Extracted body text." },
|
|
10795
|
+
language: { type: "string", description: "Language code." },
|
|
10796
|
+
projectKey: { type: "string", description: "Project/site identifier." },
|
|
10797
|
+
siteKey: { type: "string", description: "Site identifier." },
|
|
10798
|
+
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
10799
|
+
metadata: { type: "object" },
|
|
10800
|
+
reExtract: {
|
|
10801
|
+
type: "boolean",
|
|
10802
|
+
description: "Force a re-fetch even when fields are supplied."
|
|
10803
|
+
}
|
|
10804
|
+
},
|
|
10805
|
+
required: ["url"]
|
|
10806
|
+
}
|
|
10807
|
+
},
|
|
10808
|
+
{
|
|
10809
|
+
name: "get_content_source_by_url",
|
|
10810
|
+
description: "Look up a stored source by URL or canonical URL (2026-DASMG-041). Compact by default (id + summary + counts); set `includeFullText` for the body. Use to answer 'was this link already scanned?' before re-extracting.",
|
|
10811
|
+
inputSchema: {
|
|
10812
|
+
type: "object",
|
|
10813
|
+
properties: {
|
|
10814
|
+
url: { type: "string", description: "URL or canonical URL." },
|
|
10815
|
+
includeFullText: { type: "boolean", description: "Return full_text." }
|
|
10816
|
+
},
|
|
10817
|
+
required: ["url"]
|
|
10818
|
+
}
|
|
10819
|
+
},
|
|
10820
|
+
{
|
|
10821
|
+
name: "get_content_source_by_id",
|
|
10822
|
+
description: "Fetch a stored source by id (2026-DASMG-041). Compact by default; set `includeFullText` for the body.",
|
|
10823
|
+
inputSchema: {
|
|
10824
|
+
type: "object",
|
|
10825
|
+
properties: {
|
|
10826
|
+
id: { type: "string", description: "content_sources.id (uuid)." },
|
|
10827
|
+
includeFullText: { type: "boolean", description: "Return full_text." }
|
|
10828
|
+
},
|
|
10829
|
+
required: ["id"]
|
|
10830
|
+
}
|
|
10831
|
+
},
|
|
10832
|
+
{
|
|
10833
|
+
name: "search_content_sources",
|
|
10834
|
+
description: "Full-text + filter search over stored sources (2026-DASMG-041): query (title/summary/body), domain, sourceType, projectKey, ticketNumber, dateFrom/dateTo. Compact, paginated results. Use to reuse evidence already in the registry.",
|
|
10835
|
+
inputSchema: {
|
|
10836
|
+
type: "object",
|
|
10837
|
+
properties: {
|
|
10838
|
+
query: { type: "string", description: "Free-text search (title/summary/body)." },
|
|
10839
|
+
domain: { type: "string", description: "Exact domain filter." },
|
|
10840
|
+
sourceType: {
|
|
10841
|
+
type: "string",
|
|
10842
|
+
enum: [
|
|
10843
|
+
"own_site",
|
|
10844
|
+
"client_site",
|
|
10845
|
+
"external_source",
|
|
10846
|
+
"competitor",
|
|
10847
|
+
"news",
|
|
10848
|
+
"government",
|
|
10849
|
+
"supplier",
|
|
10850
|
+
"unknown"
|
|
10851
|
+
]
|
|
10852
|
+
},
|
|
10853
|
+
projectKey: { type: "string", description: "Project filter." },
|
|
10854
|
+
ticketNumber: { type: "string", description: "Ticket filter." },
|
|
10855
|
+
dateFrom: { type: "string", description: "Last-seen lower bound (ISO/date)." },
|
|
10856
|
+
dateTo: { type: "string", description: "Last-seen upper bound (ISO/date)." },
|
|
10857
|
+
limit: { type: "integer", description: "Max rows (default 20, max 100)." },
|
|
10858
|
+
offset: { type: "integer", description: "Pagination offset." },
|
|
10859
|
+
includeFullText: { type: "boolean", description: "Return full_text per row." }
|
|
10860
|
+
}
|
|
10861
|
+
}
|
|
10862
|
+
},
|
|
10863
|
+
{
|
|
10864
|
+
name: "save_research_pack",
|
|
10865
|
+
description: "Persist a research_topic evidence pack (2026-DASMG-041): cross-checked verified facts, conflicts, unique claims, open questions. Pass an already-built `pack`, or `urls`/`topic` to research then save. Each source is deduped into content_sources and linked via the join table, so packs can be reused without re-crawling. Returns the pack id + compact counts + linked source ids.",
|
|
10866
|
+
inputSchema: {
|
|
10867
|
+
type: "object",
|
|
10868
|
+
properties: {
|
|
10869
|
+
pack: { type: "object", description: "A research_topic result payload." },
|
|
10870
|
+
urls: {
|
|
10871
|
+
type: "array",
|
|
10872
|
+
items: { type: "string" },
|
|
10873
|
+
description: "URLs to research if no pack is given."
|
|
10874
|
+
},
|
|
10875
|
+
topic: { type: "string", description: "Topic to research if no pack is given." },
|
|
10876
|
+
locale: { type: "string", description: "Locale of the pack." },
|
|
10877
|
+
projectKey: { type: "string", description: "Project identifier." },
|
|
10878
|
+
siteKey: { type: "string", description: "Site identifier." },
|
|
10879
|
+
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
10880
|
+
sourceType: {
|
|
10881
|
+
type: "string",
|
|
10882
|
+
enum: [
|
|
10883
|
+
"own_site",
|
|
10884
|
+
"client_site",
|
|
10885
|
+
"external_source",
|
|
10886
|
+
"competitor",
|
|
10887
|
+
"news",
|
|
10888
|
+
"government",
|
|
10889
|
+
"supplier",
|
|
10890
|
+
"unknown"
|
|
10891
|
+
]
|
|
10892
|
+
},
|
|
10893
|
+
metadata: { type: "object" }
|
|
10894
|
+
}
|
|
10895
|
+
}
|
|
10896
|
+
},
|
|
10897
|
+
{
|
|
10898
|
+
name: "get_research_pack",
|
|
10899
|
+
description: "Fetch a stored evidence pack by id (2026-DASMG-041). Compact by default (counts + provenance); set `includeSources` / `includeClaims` for the full arrays.",
|
|
10900
|
+
inputSchema: {
|
|
10901
|
+
type: "object",
|
|
10902
|
+
properties: {
|
|
10903
|
+
id: { type: "string", description: "content_evidence_packs.id (uuid)." },
|
|
10904
|
+
includeSources: { type: "boolean", description: "Return the source list." },
|
|
10905
|
+
includeClaims: {
|
|
10906
|
+
type: "boolean",
|
|
10907
|
+
description: "Return verified facts / conflicts / unique claims."
|
|
10908
|
+
}
|
|
10909
|
+
},
|
|
10910
|
+
required: ["id"]
|
|
10911
|
+
}
|
|
10912
|
+
},
|
|
10913
|
+
{
|
|
10914
|
+
name: "save_content_corpus_item",
|
|
10915
|
+
description: "Upsert ONE own/managed page into the corpus (2026-DASMG-041), keyed by (projectKey, canonical url) \u2014 the current state of content you own. Pass a `url` (re-extracts) and/or title/h1/headings/fullText/internalLinks. Snapshots the previous version when the text changed. Use this for your/your client's pages, not external evidence.",
|
|
10916
|
+
inputSchema: {
|
|
10917
|
+
type: "object",
|
|
10918
|
+
properties: {
|
|
10919
|
+
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
10920
|
+
url: { type: "string", description: "Page URL (required)." },
|
|
10921
|
+
siteKey: { type: "string", description: "Site identifier." },
|
|
10922
|
+
canonicalUrl: { type: "string", description: "Canonical URL." },
|
|
10923
|
+
sourceType: {
|
|
10924
|
+
type: "string",
|
|
10925
|
+
enum: [
|
|
10926
|
+
"own_site",
|
|
10927
|
+
"client_site",
|
|
10928
|
+
"external_source",
|
|
10929
|
+
"competitor",
|
|
10930
|
+
"news",
|
|
10931
|
+
"government",
|
|
10932
|
+
"supplier",
|
|
10933
|
+
"unknown"
|
|
10934
|
+
],
|
|
10935
|
+
description: "Defaults to own_site."
|
|
10936
|
+
},
|
|
10937
|
+
pagePath: { type: "string", description: "Path within the site." },
|
|
10938
|
+
title: { type: "string", description: "Title." },
|
|
10939
|
+
h1: { type: "string", description: "Main heading." },
|
|
10940
|
+
headings: {
|
|
10941
|
+
type: "array",
|
|
10942
|
+
items: { type: "object" },
|
|
10943
|
+
description: "Headings ({level,text})."
|
|
10944
|
+
},
|
|
10945
|
+
wordCount: { type: "integer", description: "Word count." },
|
|
10946
|
+
fullText: { type: "string", description: "Full page text." },
|
|
10947
|
+
internalLinks: {
|
|
10948
|
+
type: "array",
|
|
10949
|
+
items: { type: "object" },
|
|
10950
|
+
description: "Internal links ({url,text})."
|
|
10951
|
+
},
|
|
10952
|
+
externalLinks: { type: "array", items: { type: "object" } },
|
|
10953
|
+
language: { type: "string", description: "Language code." },
|
|
10954
|
+
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
10955
|
+
metadata: { type: "object" },
|
|
10956
|
+
reExtract: {
|
|
10957
|
+
type: "boolean",
|
|
10958
|
+
description: "Force a re-fetch even when fields are supplied."
|
|
10959
|
+
}
|
|
10960
|
+
},
|
|
10961
|
+
required: ["projectKey", "url"]
|
|
10962
|
+
}
|
|
10963
|
+
},
|
|
10964
|
+
{
|
|
10965
|
+
name: "list_project_content_corpus",
|
|
10966
|
+
description: "List corpus items for a project (2026-DASMG-041), filter by domain/sourceType/query. Compact, paginated. Use to see content coverage and reuse own pages.",
|
|
10967
|
+
inputSchema: {
|
|
10968
|
+
type: "object",
|
|
10969
|
+
properties: {
|
|
10970
|
+
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
10971
|
+
domain: { type: "string", description: "Domain filter." },
|
|
10972
|
+
sourceType: {
|
|
10973
|
+
type: "string",
|
|
10974
|
+
enum: [
|
|
10975
|
+
"own_site",
|
|
10976
|
+
"client_site",
|
|
10977
|
+
"external_source",
|
|
10978
|
+
"competitor",
|
|
10979
|
+
"news",
|
|
10980
|
+
"government",
|
|
10981
|
+
"supplier",
|
|
10982
|
+
"unknown"
|
|
10983
|
+
]
|
|
10984
|
+
},
|
|
10985
|
+
query: { type: "string", description: "Free-text search." },
|
|
10986
|
+
limit: { type: "integer", description: "Max rows (default 20)." },
|
|
10987
|
+
offset: { type: "integer", description: "Pagination offset." },
|
|
10988
|
+
includeFullText: { type: "boolean", description: "Return full_text per row." }
|
|
10989
|
+
},
|
|
10990
|
+
required: ["projectKey"]
|
|
10991
|
+
}
|
|
10992
|
+
},
|
|
10993
|
+
{
|
|
10994
|
+
name: "get_content_context_for_project",
|
|
10995
|
+
description: "Aggregate context for a content agent (2026-DASMG-041): recent corpus items, recent evidence packs and related sources for a project (optionally focused by topic or url), plus totals. One call to brief an agent on what already exists before it writes.",
|
|
10996
|
+
inputSchema: {
|
|
10997
|
+
type: "object",
|
|
10998
|
+
properties: {
|
|
10999
|
+
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
11000
|
+
topic: { type: "string", description: "Optional topic focus." },
|
|
11001
|
+
url: { type: "string", description: "Optional url focus." },
|
|
11002
|
+
limit: { type: "integer", description: "Max items per section (default 10)." }
|
|
11003
|
+
},
|
|
11004
|
+
required: ["projectKey"]
|
|
11005
|
+
}
|
|
11006
|
+
},
|
|
11007
|
+
{
|
|
11008
|
+
name: "save_content_quality_run",
|
|
11009
|
+
description: "Store a reference to an MG SEO CLI / audit run (2026-DASMG-041): score, flags and report, attached to a corpus item, source or url. MG Dashboard stores the run; the CLI does the scoring (the CLI stays the QC layer).",
|
|
11010
|
+
inputSchema: {
|
|
11011
|
+
type: "object",
|
|
11012
|
+
properties: {
|
|
11013
|
+
tool: { type: "string", description: "Tool name, e.g. mg-seo (required)." },
|
|
11014
|
+
corpusItemId: { type: "string", description: "content_corpus_items.id." },
|
|
11015
|
+
contentSourceId: { type: "string", description: "content_sources.id." },
|
|
11016
|
+
projectKey: { type: "string", description: "Project identifier." },
|
|
11017
|
+
siteKey: { type: "string", description: "Site identifier." },
|
|
11018
|
+
url: { type: "string", description: "Audited URL." },
|
|
11019
|
+
canonicalUrl: { type: "string", description: "Canonical URL." },
|
|
11020
|
+
toolVersion: { type: "string", description: "Tool version." },
|
|
11021
|
+
score: { type: "integer", description: "Overall score 0-100." },
|
|
11022
|
+
flags: {
|
|
11023
|
+
type: "array",
|
|
11024
|
+
items: { type: "object" },
|
|
11025
|
+
description: "Flags ({code,severity,message})."
|
|
11026
|
+
},
|
|
11027
|
+
report: { type: "object", description: "Full report payload." },
|
|
11028
|
+
reportUrl: { type: "string", description: "Link to the full report." },
|
|
11029
|
+
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
11030
|
+
runAt: { type: "string", description: "Run timestamp (ISO)." }
|
|
11031
|
+
},
|
|
11032
|
+
required: ["tool"]
|
|
11033
|
+
}
|
|
11034
|
+
},
|
|
11035
|
+
{
|
|
11036
|
+
name: "list_content_snapshots",
|
|
11037
|
+
description: "List historical snapshots for a corpus item or source (2026-DASMG-041) by id, url or canonical url, newest first. Compact; set `includeFullText` for the captured body. Use to see how content changed over time.",
|
|
11038
|
+
inputSchema: {
|
|
11039
|
+
type: "object",
|
|
11040
|
+
properties: {
|
|
11041
|
+
corpusItemId: { type: "string", description: "content_corpus_items.id." },
|
|
11042
|
+
contentSourceId: { type: "string", description: "content_sources.id." },
|
|
11043
|
+
url: { type: "string", description: "URL to match." },
|
|
11044
|
+
canonicalUrl: { type: "string", description: "Canonical URL to match." },
|
|
11045
|
+
limit: { type: "integer", description: "Max rows (default 20)." },
|
|
11046
|
+
includeFullText: { type: "boolean", description: "Return captured full_text." }
|
|
11047
|
+
}
|
|
11048
|
+
}
|
|
11049
|
+
},
|
|
11050
|
+
{
|
|
11051
|
+
name: "link_content_pack_to_ticket",
|
|
11052
|
+
description: "Attach a stored evidence pack to a ticket number (2026-DASMG-041).",
|
|
11053
|
+
inputSchema: {
|
|
11054
|
+
type: "object",
|
|
11055
|
+
properties: {
|
|
11056
|
+
packId: { type: "string", description: "content_evidence_packs.id (uuid)." },
|
|
11057
|
+
ticketNumber: { type: "string", description: "Ticket number." }
|
|
11058
|
+
},
|
|
11059
|
+
required: ["packId", "ticketNumber"]
|
|
11060
|
+
}
|
|
11061
|
+
},
|
|
11062
|
+
{
|
|
11063
|
+
name: "prune_content_snapshots",
|
|
11064
|
+
description: "Retention cleanup (2026-DASMG-041): keep the newest N snapshots per item and drop the rest older than the retention window. Defaults: keepPerItem 20, olderThanDays 365. Returns how many were pruned.",
|
|
11065
|
+
inputSchema: {
|
|
11066
|
+
type: "object",
|
|
11067
|
+
properties: {
|
|
11068
|
+
keepPerItem: { type: "integer", description: "Snapshots to keep per item (default 20)." },
|
|
11069
|
+
olderThanDays: {
|
|
11070
|
+
type: "integer",
|
|
11071
|
+
description: "Only prune snapshots older than this many days (default 365)."
|
|
11072
|
+
}
|
|
11073
|
+
}
|
|
11074
|
+
}
|
|
11075
|
+
},
|
|
11076
|
+
{
|
|
11077
|
+
name: "register_managed_site",
|
|
11078
|
+
description: "Register (or upsert by projectKey+domain) a site for content sync (2026-DASMG-042): domain, optional repo/branch + sitemapUrl, syncMode and a git pathUrlMap. Optional + additive \u2014 external/unmanaged sites still work via extract_article without this. Enables git-push/cron/manual ingestion that re-indexes only CHANGED pages into the corpus (hash-skips unchanged ones).",
|
|
11079
|
+
inputSchema: {
|
|
11080
|
+
type: "object",
|
|
11081
|
+
properties: {
|
|
11082
|
+
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
11083
|
+
domain: { type: "string", description: "Site domain, e.g. example.nl (required)." },
|
|
11084
|
+
siteKey: { type: "string", description: "Optional site identifier within the project." },
|
|
11085
|
+
name: { type: "string", description: "Human-friendly site name." },
|
|
11086
|
+
repo: { type: "string", description: "GitHub repo owner/repo that publishes the site (for git pushes)." },
|
|
11087
|
+
branch: { type: "string", description: "Branch whose pushes trigger a sync; omit for any branch." },
|
|
11088
|
+
sitemapUrl: { type: "string", description: "Sitemap URL for sitemap/hybrid detection." },
|
|
11089
|
+
sourceType: {
|
|
11090
|
+
type: "string",
|
|
11091
|
+
enum: [
|
|
11092
|
+
"own_site",
|
|
11093
|
+
"client_site",
|
|
11094
|
+
"external_source",
|
|
11095
|
+
"competitor",
|
|
11096
|
+
"news",
|
|
11097
|
+
"government",
|
|
11098
|
+
"supplier",
|
|
11099
|
+
"unknown"
|
|
11100
|
+
],
|
|
11101
|
+
description: "Provenance label for produced corpus items (default own_site)."
|
|
11102
|
+
},
|
|
11103
|
+
enabled: { type: "boolean", description: "Whether syncs run (default true)." },
|
|
11104
|
+
syncMode: {
|
|
11105
|
+
type: "string",
|
|
11106
|
+
enum: ["sitemap", "git", "hybrid", "manual"],
|
|
11107
|
+
description: "Change detection: sitemap (lastmod/hash), git (changed paths via pathUrlMap), hybrid (both), manual (only on trigger_content_sync)."
|
|
11108
|
+
},
|
|
11109
|
+
pathUrlMap: {
|
|
11110
|
+
type: "array",
|
|
11111
|
+
items: { type: "object" },
|
|
11112
|
+
description: "Git path -> URL rules ([{pathPrefix,urlPrefix}])."
|
|
11113
|
+
},
|
|
11114
|
+
maxUrlsPerRun: { type: "integer", description: "Per-run URL cap (default 200)." },
|
|
11115
|
+
exportToSeoCli: {
|
|
11116
|
+
type: "boolean",
|
|
11117
|
+
description: "Flag changed pages for an MG SEO CLI follow-up (soft handoff)."
|
|
11118
|
+
},
|
|
11119
|
+
metadata: { type: "object" }
|
|
11120
|
+
},
|
|
11121
|
+
required: ["projectKey", "domain"]
|
|
11122
|
+
}
|
|
11123
|
+
},
|
|
11124
|
+
{
|
|
11125
|
+
name: "update_managed_site",
|
|
11126
|
+
description: "Update an existing managed site by id (2026-DASMG-042). Only the provided fields change \u2014 enable/disable, switch syncMode, set sitemapUrl/pathUrlMap, etc.",
|
|
11127
|
+
inputSchema: {
|
|
11128
|
+
type: "object",
|
|
11129
|
+
properties: {
|
|
11130
|
+
id: { type: "string", description: "managed_sites.id (uuid, required)." },
|
|
11131
|
+
name: { type: "string", description: "Human-friendly site name." },
|
|
11132
|
+
domain: { type: "string", description: "Site domain." },
|
|
11133
|
+
siteKey: { type: "string", description: "Site identifier within the project." },
|
|
11134
|
+
repo: { type: "string", description: "GitHub repo owner/repo." },
|
|
11135
|
+
branch: { type: "string", description: "Branch whose pushes trigger a sync." },
|
|
11136
|
+
sitemapUrl: { type: "string", description: "Sitemap URL." },
|
|
11137
|
+
sourceType: {
|
|
11138
|
+
type: "string",
|
|
11139
|
+
enum: [
|
|
11140
|
+
"own_site",
|
|
11141
|
+
"client_site",
|
|
11142
|
+
"external_source",
|
|
11143
|
+
"competitor",
|
|
11144
|
+
"news",
|
|
11145
|
+
"government",
|
|
11146
|
+
"supplier",
|
|
11147
|
+
"unknown"
|
|
11148
|
+
]
|
|
11149
|
+
},
|
|
11150
|
+
enabled: { type: "boolean", description: "Enable/disable syncs." },
|
|
11151
|
+
syncMode: {
|
|
11152
|
+
type: "string",
|
|
11153
|
+
enum: ["sitemap", "git", "hybrid", "manual"]
|
|
11154
|
+
},
|
|
11155
|
+
pathUrlMap: { type: "array", items: { type: "object" } },
|
|
11156
|
+
maxUrlsPerRun: { type: "integer", description: "Per-run URL cap." },
|
|
11157
|
+
exportToSeoCli: { type: "boolean" },
|
|
11158
|
+
metadata: { type: "object" }
|
|
11159
|
+
},
|
|
11160
|
+
required: ["id"]
|
|
11161
|
+
}
|
|
11162
|
+
},
|
|
11163
|
+
{
|
|
11164
|
+
name: "list_managed_sites",
|
|
11165
|
+
description: "List managed sites (2026-DASMG-042), filter by projectKey/domain/repo/enabledOnly. Compact, paginated.",
|
|
11166
|
+
inputSchema: {
|
|
11167
|
+
type: "object",
|
|
11168
|
+
properties: {
|
|
11169
|
+
projectKey: { type: "string", description: "Project filter." },
|
|
11170
|
+
domain: { type: "string", description: "Domain filter." },
|
|
11171
|
+
repo: { type: "string", description: "Repo filter (owner/repo)." },
|
|
11172
|
+
enabledOnly: { type: "boolean", description: "Only enabled sites." },
|
|
11173
|
+
limit: { type: "integer", description: "Max rows (default 50, max 200)." },
|
|
11174
|
+
offset: { type: "integer", description: "Pagination offset." }
|
|
11175
|
+
}
|
|
11176
|
+
}
|
|
11177
|
+
},
|
|
11178
|
+
{
|
|
11179
|
+
name: "trigger_content_sync",
|
|
11180
|
+
description: "Start a content ingestion run for a managed site (2026-DASMG-042) by siteId, or projectKey+domain. Detects changed pages (sitemap/git/hash), re-indexes only changes into the corpus and snapshots the previous version. Returns the runId; poll get_content_sync_status for results.",
|
|
11181
|
+
inputSchema: {
|
|
11182
|
+
type: "object",
|
|
11183
|
+
properties: {
|
|
11184
|
+
siteId: { type: "string", description: "managed_sites.id (uuid)." },
|
|
11185
|
+
projectKey: { type: "string", description: "Project identifier (with domain)." },
|
|
11186
|
+
domain: { type: "string", description: "Site domain (with projectKey)." },
|
|
11187
|
+
triggerType: {
|
|
11188
|
+
type: "string",
|
|
11189
|
+
enum: ["manual", "git_push", "deploy", "cron", "sitemap"],
|
|
11190
|
+
description: "Trigger label recorded on the run (default manual)."
|
|
11191
|
+
},
|
|
11192
|
+
sourceCommit: { type: "string", description: "Source git commit/deploy id, when known." },
|
|
11193
|
+
sourceRef: { type: "string", description: "Source git ref, when known." },
|
|
11194
|
+
urls: {
|
|
11195
|
+
type: "array",
|
|
11196
|
+
items: { type: "string" },
|
|
11197
|
+
description: "Restrict the run to these URLs (skips detection)."
|
|
11198
|
+
},
|
|
11199
|
+
changedPaths: {
|
|
11200
|
+
type: "array",
|
|
11201
|
+
items: { type: "string" },
|
|
11202
|
+
description: "Changed repo paths to map to URLs via pathUrlMap."
|
|
11203
|
+
}
|
|
11204
|
+
}
|
|
11205
|
+
}
|
|
11206
|
+
},
|
|
11207
|
+
{
|
|
11208
|
+
name: "get_content_sync_status",
|
|
11209
|
+
description: "Read ingestion run status (2026-DASMG-042): by runId (that run), or by siteId / projectKey+domain (recent runs). Returns changed/skipped/failed counts, the changed URLs, limitations and the site summary.",
|
|
11210
|
+
inputSchema: {
|
|
11211
|
+
type: "object",
|
|
11212
|
+
properties: {
|
|
11213
|
+
runId: { type: "string", description: "content_ingestion_runs.id (uuid)." },
|
|
11214
|
+
siteId: { type: "string", description: "managed_sites.id (uuid)." },
|
|
11215
|
+
projectKey: { type: "string", description: "Project identifier (with domain)." },
|
|
11216
|
+
domain: { type: "string", description: "Site domain (with projectKey)." },
|
|
11217
|
+
limit: { type: "integer", description: "Max recent runs to return (default 10)." }
|
|
11218
|
+
}
|
|
11219
|
+
}
|
|
11220
|
+
},
|
|
10423
11221
|
{
|
|
10424
11222
|
name: "search-team-memory",
|
|
10425
11223
|
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').",
|
|
@@ -10520,7 +11318,7 @@ var TOOLS = [
|
|
|
10520
11318
|
// ----- Trigger.dev -----
|
|
10521
11319
|
...TRIGGER_TOOLS
|
|
10522
11320
|
];
|
|
10523
|
-
var MCP_VERSION = "7.0
|
|
11321
|
+
var MCP_VERSION = "7.3.0";
|
|
10524
11322
|
async function handleListTools() {
|
|
10525
11323
|
if (!authContext) return { tools: TOOLS };
|
|
10526
11324
|
const allowedTools = authContext.allowedTools;
|
|
@@ -10723,6 +11521,80 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
10723
11521
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
10724
11522
|
};
|
|
10725
11523
|
}
|
|
11524
|
+
// ----- Content registry / corpus persistence (2026-DASMG-041) -----
|
|
11525
|
+
// All content-registry tools proxy to one dispatcher route; the tool name
|
|
11526
|
+
// is the dispatcher `action` and the args are forwarded verbatim as
|
|
11527
|
+
// `params` (the route validates them with the matching Zod schema).
|
|
11528
|
+
case "save_content_source":
|
|
11529
|
+
case "get_content_source_by_url":
|
|
11530
|
+
case "get_content_source_by_id":
|
|
11531
|
+
case "search_content_sources":
|
|
11532
|
+
case "save_research_pack":
|
|
11533
|
+
case "get_research_pack":
|
|
11534
|
+
case "save_content_corpus_item":
|
|
11535
|
+
case "list_project_content_corpus":
|
|
11536
|
+
case "get_content_context_for_project":
|
|
11537
|
+
case "save_content_quality_run":
|
|
11538
|
+
case "list_content_snapshots":
|
|
11539
|
+
case "link_content_pack_to_ticket":
|
|
11540
|
+
case "prune_content_snapshots": {
|
|
11541
|
+
const res = await fetch(`${dashboardBaseUrl}/api/tools/content-registry`, {
|
|
11542
|
+
method: "POST",
|
|
11543
|
+
headers: {
|
|
11544
|
+
"content-type": "application/json",
|
|
11545
|
+
authorization: `Bearer ${apiKey}`
|
|
11546
|
+
},
|
|
11547
|
+
body: JSON.stringify({ action: name, params: a })
|
|
11548
|
+
});
|
|
11549
|
+
if (!res.ok) {
|
|
11550
|
+
const detail = await res.text().catch(() => "");
|
|
11551
|
+
return {
|
|
11552
|
+
content: [
|
|
11553
|
+
{
|
|
11554
|
+
type: "text",
|
|
11555
|
+
text: `Error: ${name} failed (${res.status}). ${detail.slice(0, 300)}`
|
|
11556
|
+
}
|
|
11557
|
+
]
|
|
11558
|
+
};
|
|
11559
|
+
}
|
|
11560
|
+
const data = await res.json();
|
|
11561
|
+
return {
|
|
11562
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
11563
|
+
};
|
|
11564
|
+
}
|
|
11565
|
+
// ----- Managed site content sync (2026-DASMG-042) -----
|
|
11566
|
+
// The 5 PUBLIC managed-site tools proxy to one dispatcher route; the tool
|
|
11567
|
+
// name is the dispatcher `action`, args forwarded verbatim as `params`
|
|
11568
|
+
// (the route validates them and only allows the public actions).
|
|
11569
|
+
case "register_managed_site":
|
|
11570
|
+
case "update_managed_site":
|
|
11571
|
+
case "list_managed_sites":
|
|
11572
|
+
case "trigger_content_sync":
|
|
11573
|
+
case "get_content_sync_status": {
|
|
11574
|
+
const res = await fetch(`${dashboardBaseUrl}/api/tools/managed-site`, {
|
|
11575
|
+
method: "POST",
|
|
11576
|
+
headers: {
|
|
11577
|
+
"content-type": "application/json",
|
|
11578
|
+
authorization: `Bearer ${apiKey}`
|
|
11579
|
+
},
|
|
11580
|
+
body: JSON.stringify({ action: name, params: a })
|
|
11581
|
+
});
|
|
11582
|
+
if (!res.ok) {
|
|
11583
|
+
const detail = await res.text().catch(() => "");
|
|
11584
|
+
return {
|
|
11585
|
+
content: [
|
|
11586
|
+
{
|
|
11587
|
+
type: "text",
|
|
11588
|
+
text: `Error: ${name} failed (${res.status}). ${detail.slice(0, 300)}`
|
|
11589
|
+
}
|
|
11590
|
+
]
|
|
11591
|
+
};
|
|
11592
|
+
}
|
|
11593
|
+
const data = await res.json();
|
|
11594
|
+
return {
|
|
11595
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
11596
|
+
};
|
|
11597
|
+
}
|
|
10726
11598
|
// ----- Team memory -----
|
|
10727
11599
|
case "search-team-memory": {
|
|
10728
11600
|
const query = typeof a.query === "string" ? a.query.trim() : "";
|
|
@@ -10764,7 +11636,7 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
10764
11636
|
]
|
|
10765
11637
|
};
|
|
10766
11638
|
}
|
|
10767
|
-
const lines = data.hits.map((hit,
|
|
11639
|
+
const lines = data.hits.map((hit, index7) => {
|
|
10768
11640
|
const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
|
|
10769
11641
|
const repo = hit.repo ?? "unknown repo";
|
|
10770
11642
|
const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
|
|
@@ -10774,7 +11646,7 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
10774
11646
|
...Array.isArray(hit.tech) ? hit.tech.slice(0, 4) : []
|
|
10775
11647
|
].filter(Boolean);
|
|
10776
11648
|
const tags = facets.length > 0 ? ` \xB7 ${facets.join(", ")}` : "";
|
|
10777
|
-
return `${
|
|
11649
|
+
return `${index7 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
|
|
10778
11650
|
id: ${hit.id}
|
|
10779
11651
|
${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
|
|
10780
11652
|
});
|
|
@@ -11959,8 +12831,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11959
12831
|
};
|
|
11960
12832
|
const filtered = sortRows(applyFilter(only.rows));
|
|
11961
12833
|
if (format === "json") {
|
|
11962
|
-
const
|
|
11963
|
-
return { content: [{ type: "text", text:
|
|
12834
|
+
const text10 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
|
|
12835
|
+
return { content: [{ type: "text", text: text10 }] };
|
|
11964
12836
|
}
|
|
11965
12837
|
if (groupByProject) {
|
|
11966
12838
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -11987,8 +12859,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11987
12859
|
}
|
|
11988
12860
|
const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
|
|
11989
12861
|
const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
|
|
11990
|
-
const
|
|
11991
|
-
return { content: [{ type: "text", text:
|
|
12862
|
+
const text9 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
|
|
12863
|
+
return { content: [{ type: "text", text: text9 }] };
|
|
11992
12864
|
}
|
|
11993
12865
|
if (format === "json") {
|
|
11994
12866
|
const lines = [];
|