@mgsoftwarebv/mg-dashboard-mcp 7.4.21 → 7.4.22

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 +2606 -1330
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -18,11 +18,11 @@ import { AsyncLocalStorage } from 'async_hooks';
18
18
  import { drizzle } from 'drizzle-orm/postgres-js';
19
19
  import postgres from 'postgres';
20
20
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
21
- import { sql } from 'drizzle-orm';
21
+ 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 { pgTable, timestamp, uuid, jsonb, boolean, text, customType, integer, date, real, index, uniqueIndex, pgEnum, check, bigint, 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, 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;
@@ -553,7 +553,7 @@ async function fetchViaSshProxy(options) {
553
553
  headers["Content-Length"] = String(Buffer.byteLength(body));
554
554
  }
555
555
  headers.Host = parsedUrl.host;
556
- const ssh = await connectSsh(options.proxy);
556
+ const ssh2 = await connectSsh(options.proxy);
557
557
  const timeoutMs = options.timeoutMs ?? 6e4;
558
558
  let timer;
559
559
  try {
@@ -563,7 +563,7 @@ async function fetchViaSshProxy(options) {
563
563
  connectHost = resolved.address;
564
564
  } catch {
565
565
  }
566
- const stream = await forwardOut(ssh, connectHost, targetPort);
566
+ const stream = await forwardOut(ssh2, connectHost, targetPort);
567
567
  const tlsSocket = connect({
568
568
  socket: stream,
569
569
  servername: targetHost,
@@ -625,7 +625,7 @@ async function fetchViaSshProxy(options) {
625
625
  });
626
626
  } finally {
627
627
  if (timer) clearTimeout(timer);
628
- ssh.end();
628
+ ssh2.end();
629
629
  }
630
630
  }
631
631
 
@@ -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 sql31 = `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 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`;
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 "${sql31}" 2>/dev/null | tr -d '[:space:]')`,
730
+ `ROW=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql33}" 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 sql31 = `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 "${sql31}" 2>/dev/null`;
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`;
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 sql31 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
836
- const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql31}" 2>/dev/null`;
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`;
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 text21 = `Env vars for ${project}/${env} (${vars.length}) \u2014 values hidden, use action="get" with a key:
1014
+ const text22 = `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: text21 }] };
1016
+ return { content: [{ type: "text", text: text22 }] };
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 text21 = formatRunDetail(run);
1104
- if (logs) text21 += "\n\n--- Logs ---\n" + logs;
1105
- return { content: [{ type: "text", text: text21 }] };
1103
+ let text22 = formatRunDetail(run);
1104
+ if (logs) text22 += "\n\n--- Logs ---\n" + logs;
1105
+ return { content: [{ type: "text", text: text22 }] };
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 text21 = formatRunDetail(run);
1127
+ let text22 = formatRunDetail(run);
1128
1128
  const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
1129
- if (logs) text21 += "\n\n--- Logs ---\n" + logs;
1130
- return { content: [{ type: "text", text: text21 }] };
1129
+ if (logs) text22 += "\n\n--- Logs ---\n" + logs;
1130
+ return { content: [{ type: "text", text: text22 }] };
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 eq4 = trimmed.indexOf("=");
1395
- if (eq4 < 1) continue;
1396
- const key = trimmed.slice(0, eq4).trim();
1394
+ const eq5 = trimmed.indexOf("=");
1395
+ if (eq5 < 1) continue;
1396
+ const key = trimmed.slice(0, eq5).trim();
1397
1397
  if (key !== "MAILSERVER_MCP_TOKEN") continue;
1398
- let value = trimmed.slice(eq4 + 1).trim();
1398
+ let value = trimmed.slice(eq5 + 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 text21 = await res.text();
1504
- let json = text21;
1503
+ const text22 = await res.text();
1504
+ let json = text22;
1505
1505
  try {
1506
- json = text21 ? JSON.parse(text21) : null;
1506
+ json = text22 ? JSON.parse(text22) : null;
1507
1507
  } catch {
1508
- json = { error: text21.slice(0, 500) };
1508
+ json = { error: text22.slice(0, 500) };
1509
1509
  }
1510
1510
  return { status: res.status, json };
1511
1511
  }
@@ -1567,6 +1567,470 @@ async function handleMailserverTool(args2, deps) {
1567
1567
  ]
1568
1568
  };
1569
1569
  }
1570
+ var managedServerOs = pgEnum("managed_server_os", [
1571
+ "linux",
1572
+ "windows",
1573
+ "unknown"
1574
+ ]);
1575
+ var serverAgentStatus = pgEnum("server_agent_status", [
1576
+ "not_installed",
1577
+ "installing",
1578
+ "online",
1579
+ "degraded",
1580
+ "offline",
1581
+ "error"
1582
+ ]);
1583
+ var resourceKind = pgEnum("resource_kind", [
1584
+ "wordpress",
1585
+ "prestashop",
1586
+ "postgres_cluster",
1587
+ "postgres_database",
1588
+ "mysql_database",
1589
+ "mariadb_database",
1590
+ "boiler_project",
1591
+ "boiler_web_project",
1592
+ "static_site",
1593
+ "docker_app",
1594
+ "domain",
1595
+ "backup_policy",
1596
+ "server_service"
1597
+ ]);
1598
+ var resourceStatus = pgEnum("resource_status", [
1599
+ "unknown",
1600
+ "healthy",
1601
+ "degraded",
1602
+ "failed",
1603
+ "missing",
1604
+ "provisioning",
1605
+ "disabled"
1606
+ ]);
1607
+ var resourceOwnership = pgEnum("resource_ownership", [
1608
+ "managed",
1609
+ "discovered",
1610
+ "external"
1611
+ ]);
1612
+ var operationStatus = pgEnum("operation_status", [
1613
+ "queued",
1614
+ "running",
1615
+ "succeeded",
1616
+ "failed",
1617
+ "cancelled"
1618
+ ]);
1619
+ var alertSeverity = pgEnum("alert_severity", [
1620
+ "info",
1621
+ "warning",
1622
+ "critical"
1623
+ ]);
1624
+ var managedServer = pgTable(
1625
+ "managed_server",
1626
+ {
1627
+ id: uuid("id").primaryKey().defaultRandom(),
1628
+ name: text("name").notNull(),
1629
+ hostname: text("hostname").notNull(),
1630
+ port: integer("port").notNull().default(22),
1631
+ username: text("username").notNull(),
1632
+ authMethod: text("auth_method").notNull().default("ssh_key"),
1633
+ os: managedServerOs("os").notNull().default("unknown"),
1634
+ provider: text("provider"),
1635
+ region: text("region"),
1636
+ tags: jsonb("tags").$type().notNull().default([]),
1637
+ agentStatus: serverAgentStatus("agent_status").notNull().default("not_installed"),
1638
+ agentVersion: text("agent_version"),
1639
+ lastSeenAt: timestamp("last_seen_at", { withTimezone: true }),
1640
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1641
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
1642
+ },
1643
+ (table) => [
1644
+ uniqueIndex("managed_server_hostname_port_uidx").on(
1645
+ table.hostname,
1646
+ table.port
1647
+ ),
1648
+ index("managed_server_agent_status_idx").on(table.agentStatus)
1649
+ ]
1650
+ );
1651
+ pgTable("server_credential", {
1652
+ serverId: uuid("server_id").primaryKey().references(() => managedServer.id, { onDelete: "cascade" }),
1653
+ passwordEncrypted: text("password_encrypted"),
1654
+ sshKeyEncrypted: text("ssh_key_encrypted"),
1655
+ sshKeyPassphraseEncrypted: text("ssh_key_passphrase_encrypted"),
1656
+ dbRootPasswordEncrypted: text("db_root_password_encrypted"),
1657
+ monitoringApiKeyEncrypted: text("monitoring_api_key_encrypted"),
1658
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1659
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
1660
+ });
1661
+ pgTable(
1662
+ "server_connection_log",
1663
+ {
1664
+ id: uuid("id").primaryKey().defaultRandom(),
1665
+ serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
1666
+ userId: uuid("user_id").notNull(),
1667
+ connectedAt: timestamp("connected_at", { withTimezone: true }).notNull().defaultNow(),
1668
+ disconnectedAt: timestamp("disconnected_at", { withTimezone: true }),
1669
+ durationSeconds: integer("duration_seconds"),
1670
+ status: text("status").notNull(),
1671
+ errorMessage: text("error_message"),
1672
+ clientIp: text("client_ip"),
1673
+ terminalCols: integer("terminal_cols"),
1674
+ terminalRows: integer("terminal_rows"),
1675
+ outputSizeBytes: bigint("output_size_bytes", { mode: "number" }).notNull().default(0),
1676
+ inputSizeBytes: bigint("input_size_bytes", { mode: "number" }).notNull().default(0),
1677
+ commandCount: integer("command_count").notNull().default(0),
1678
+ connectionType: text("connection_type").notNull().default("terminal"),
1679
+ action: text("action"),
1680
+ rawOutput: text("raw_output"),
1681
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
1682
+ },
1683
+ (table) => [
1684
+ index("server_connection_log_server_idx").on(table.serverId),
1685
+ index("server_connection_log_user_idx").on(table.userId),
1686
+ index("server_connection_log_connected_idx").on(table.connectedAt),
1687
+ index("server_connection_log_status_idx").on(table.status)
1688
+ ]
1689
+ );
1690
+ pgTable(
1691
+ "mcp_audit_log",
1692
+ {
1693
+ id: uuid("id").primaryKey().defaultRandom(),
1694
+ apiKeyId: uuid("api_key_id"),
1695
+ userId: uuid("user_id"),
1696
+ toolName: text("tool_name"),
1697
+ arguments: jsonb("arguments"),
1698
+ ipAddress: text("ip_address"),
1699
+ serverId: uuid("server_id"),
1700
+ resultStatus: text("result_status").notNull().default("success"),
1701
+ errorMessage: text("error_message"),
1702
+ durationMs: integer("duration_ms"),
1703
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
1704
+ },
1705
+ (table) => [
1706
+ index("mcp_audit_log_created_at_idx").on(table.createdAt),
1707
+ index("mcp_audit_log_user_idx").on(table.userId),
1708
+ index("mcp_audit_log_tool_name_idx").on(table.toolName),
1709
+ index("mcp_audit_log_server_idx").on(table.serverId),
1710
+ index("mcp_audit_log_api_key_idx").on(table.apiKeyId),
1711
+ index("mcp_audit_log_result_status_idx").on(table.resultStatus)
1712
+ ]
1713
+ );
1714
+ var resource = pgTable(
1715
+ "resource",
1716
+ {
1717
+ id: uuid("id").primaryKey().defaultRandom(),
1718
+ serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
1719
+ parentResourceId: uuid("parent_resource_id"),
1720
+ kind: resourceKind("kind").notNull(),
1721
+ slug: text("slug").notNull(),
1722
+ displayName: text("display_name").notNull(),
1723
+ spec: jsonb("spec").$type().notNull().default({}),
1724
+ state: jsonb("state").$type().notNull().default({}),
1725
+ status: resourceStatus("status").notNull().default("unknown"),
1726
+ ownership: resourceOwnership("ownership").notNull().default("managed"),
1727
+ lastDetectedAt: timestamp("last_detected_at", { withTimezone: true }),
1728
+ lastEventId: uuid("last_event_id"),
1729
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1730
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
1731
+ },
1732
+ (table) => [
1733
+ uniqueIndex("resource_server_kind_slug_uidx").on(
1734
+ table.serverId,
1735
+ table.kind,
1736
+ table.slug
1737
+ ),
1738
+ index("resource_server_idx").on(table.serverId),
1739
+ index("resource_parent_idx").on(table.parentResourceId),
1740
+ index("resource_kind_status_idx").on(table.kind, table.status)
1741
+ ]
1742
+ );
1743
+ pgTable(
1744
+ "resource_event",
1745
+ {
1746
+ id: uuid("id").primaryKey().defaultRandom(),
1747
+ resourceId: uuid("resource_id").notNull().references(() => resource.id, { onDelete: "cascade" }),
1748
+ kind: text("kind").notNull(),
1749
+ before: jsonb("before").$type(),
1750
+ after: jsonb("after").$type(),
1751
+ actorUserId: text("actor_user_id"),
1752
+ operationId: uuid("operation_id"),
1753
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
1754
+ },
1755
+ (table) => [
1756
+ index("resource_event_resource_created_idx").on(
1757
+ table.resourceId,
1758
+ table.createdAt
1759
+ ),
1760
+ index("resource_event_operation_idx").on(table.operationId)
1761
+ ]
1762
+ );
1763
+ var operation = pgTable(
1764
+ "operation",
1765
+ {
1766
+ id: uuid("id").primaryKey().defaultRandom(),
1767
+ kind: text("kind").notNull(),
1768
+ resourceId: uuid("resource_id").references(() => resource.id, {
1769
+ onDelete: "set null"
1770
+ }),
1771
+ serverId: uuid("server_id").references(() => managedServer.id, {
1772
+ onDelete: "set null"
1773
+ }),
1774
+ status: operationStatus("status").notNull().default("queued"),
1775
+ steps: jsonb("steps").$type().notNull().default([]),
1776
+ idempotencyKey: text("idempotency_key").notNull(),
1777
+ triggerRunId: text("trigger_run_id"),
1778
+ logsR2Key: text("logs_r2_key"),
1779
+ logs: text("logs"),
1780
+ startedBy: text("started_by"),
1781
+ startedAt: timestamp("started_at", { withTimezone: true }),
1782
+ endedAt: timestamp("ended_at", { withTimezone: true }),
1783
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1784
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
1785
+ },
1786
+ (table) => [
1787
+ uniqueIndex("operation_idempotency_uidx").on(table.idempotencyKey),
1788
+ index("operation_resource_idx").on(table.resourceId),
1789
+ index("operation_server_idx").on(table.serverId),
1790
+ index("operation_status_created_idx").on(table.status, table.createdAt)
1791
+ ]
1792
+ );
1793
+ pgTable(
1794
+ "agent_installation",
1795
+ {
1796
+ id: uuid("id").primaryKey().defaultRandom(),
1797
+ serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
1798
+ agentId: text("agent_id"),
1799
+ version: text("version").notNull(),
1800
+ installPath: text("install_path").notNull().default("/usr/local/bin/mg-agent"),
1801
+ configHash: text("config_hash").notNull(),
1802
+ status: serverAgentStatus("status").notNull().default("installing"),
1803
+ lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }),
1804
+ lastError: text("last_error"),
1805
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1806
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
1807
+ },
1808
+ (table) => [
1809
+ uniqueIndex("agent_installation_server_uidx").on(table.serverId),
1810
+ index("agent_installation_status_idx").on(table.status)
1811
+ ]
1812
+ );
1813
+ pgTable(
1814
+ "monitoring_sample",
1815
+ {
1816
+ id: uuid("id").primaryKey().defaultRandom(),
1817
+ serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
1818
+ resourceId: uuid("resource_id").references(() => resource.id, {
1819
+ onDelete: "cascade"
1820
+ }),
1821
+ metric: text("metric").notNull(),
1822
+ value: integer("value").notNull(),
1823
+ unit: text("unit").notNull(),
1824
+ tags: jsonb("tags").$type().notNull().default({}),
1825
+ sampledAt: timestamp("sampled_at", { withTimezone: true }).notNull().defaultNow()
1826
+ },
1827
+ (table) => [
1828
+ index("monitoring_sample_server_metric_time_idx").on(
1829
+ table.serverId,
1830
+ table.metric,
1831
+ table.sampledAt
1832
+ ),
1833
+ // Descending on sampled_at so "latest metric per (server, metric)" lookups
1834
+ // (DISTINCT ON ... ORDER BY sampled_at DESC) are served by an index-only
1835
+ // scan instead of a full-table sort. The migration also adds
1836
+ // INCLUDE (value, unit) to make it covering for the overview snapshot query
1837
+ // (Drizzle 0.44 can't express INCLUDE here). See migration 20260529210000.
1838
+ index("monitoring_sample_server_metric_time_desc_idx").on(
1839
+ table.serverId,
1840
+ table.metric,
1841
+ table.sampledAt.desc()
1842
+ ),
1843
+ index("monitoring_sample_resource_metric_time_idx").on(
1844
+ table.resourceId,
1845
+ table.metric,
1846
+ table.sampledAt
1847
+ ),
1848
+ // Retention cleanup (monitoring-sample-retention-cleanup) filters only on
1849
+ // sampled_at. Without this index Postgres seq-scans the full firehose.
1850
+ // Live DB name uses idx_ prefix (see migration 20260731070000).
1851
+ index("idx_monitoring_sample_sampled_at").on(table.sampledAt)
1852
+ ]
1853
+ );
1854
+ var appLogSource = pgTable(
1855
+ "app_log_source",
1856
+ {
1857
+ id: uuid("id").primaryKey().defaultRandom(),
1858
+ serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
1859
+ resourceId: uuid("resource_id").references(() => resource.id, {
1860
+ onDelete: "set null"
1861
+ }),
1862
+ sourceType: text("source_type").notNull(),
1863
+ sourceKey: text("source_key").notNull(),
1864
+ displayName: text("display_name").notNull(),
1865
+ path: text("path").notNull(),
1866
+ enabled: boolean("enabled").notNull().default(true),
1867
+ mutedUntil: timestamp("muted_until", { withTimezone: true }),
1868
+ metadata: jsonb("metadata").$type().notNull().default({}),
1869
+ lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
1870
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1871
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
1872
+ },
1873
+ (table) => [
1874
+ uniqueIndex("app_log_source_server_type_key_uidx").on(
1875
+ table.serverId,
1876
+ table.sourceType,
1877
+ table.sourceKey
1878
+ ),
1879
+ index("app_log_source_server_idx").on(table.serverId),
1880
+ index("app_log_source_resource_idx").on(table.resourceId),
1881
+ index("app_log_source_type_seen_idx").on(
1882
+ table.sourceType,
1883
+ table.lastSeenAt
1884
+ )
1885
+ ]
1886
+ );
1887
+ pgTable(
1888
+ "app_error_event",
1889
+ {
1890
+ id: uuid("id").primaryKey().defaultRandom(),
1891
+ serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
1892
+ resourceId: uuid("resource_id").references(() => resource.id, {
1893
+ onDelete: "set null"
1894
+ }),
1895
+ logSourceId: uuid("log_source_id").references(() => appLogSource.id, {
1896
+ onDelete: "set null"
1897
+ }),
1898
+ fingerprint: text("fingerprint").notNull(),
1899
+ severity: text("severity").notNull().default("critical"),
1900
+ category: text("category").notNull(),
1901
+ sourceType: text("source_type").notNull(),
1902
+ sourcePath: text("source_path").notNull(),
1903
+ sampleMessage: text("sample_message").notNull(),
1904
+ sampleLines: jsonb("sample_lines").$type().notNull().default([]),
1905
+ signals: jsonb("signals").$type().notNull().default({}),
1906
+ occurrences: integer("occurrences").notNull().default(1),
1907
+ firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull().defaultNow(),
1908
+ lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
1909
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
1910
+ },
1911
+ (table) => [
1912
+ index("app_error_event_fingerprint_created_idx").on(
1913
+ table.fingerprint,
1914
+ table.createdAt
1915
+ ),
1916
+ index("app_error_event_server_created_idx").on(
1917
+ table.serverId,
1918
+ table.createdAt
1919
+ ),
1920
+ index("app_error_event_resource_created_idx").on(
1921
+ table.resourceId,
1922
+ table.createdAt
1923
+ ),
1924
+ index("app_error_event_severity_created_idx").on(
1925
+ table.severity,
1926
+ table.createdAt
1927
+ )
1928
+ ]
1929
+ );
1930
+ pgTable(
1931
+ "app_error_alert",
1932
+ {
1933
+ id: uuid("id").primaryKey().defaultRandom(),
1934
+ fingerprint: text("fingerprint").notNull(),
1935
+ serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
1936
+ resourceId: uuid("resource_id").references(() => resource.id, {
1937
+ onDelete: "set null"
1938
+ }),
1939
+ logSourceId: uuid("log_source_id").references(() => appLogSource.id, {
1940
+ onDelete: "set null"
1941
+ }),
1942
+ severity: text("severity").notNull().default("critical"),
1943
+ status: text("status").notNull().default("open"),
1944
+ category: text("category").notNull(),
1945
+ title: text("title").notNull(),
1946
+ lastError: text("last_error").notNull(),
1947
+ sampleLines: jsonb("sample_lines").$type().notNull().default([]),
1948
+ signals: jsonb("signals").$type().notNull().default({}),
1949
+ failureCount: integer("failure_count").notNull().default(1),
1950
+ firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull().defaultNow(),
1951
+ lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
1952
+ lastAlertAt: timestamp("last_alert_at", { withTimezone: true }),
1953
+ resolvedAt: timestamp("resolved_at", { withTimezone: true }),
1954
+ mutedUntil: timestamp("muted_until", { withTimezone: true }),
1955
+ cursorPrompt: text("cursor_prompt"),
1956
+ telegramChatId: text("telegram_chat_id"),
1957
+ telegramMessageId: bigint("telegram_message_id", { mode: "number" }),
1958
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1959
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
1960
+ },
1961
+ (table) => [
1962
+ uniqueIndex("app_error_alert_fingerprint_uidx").on(table.fingerprint),
1963
+ index("app_error_alert_status_seen_idx").on(
1964
+ table.status,
1965
+ table.lastSeenAt
1966
+ ),
1967
+ index("app_error_alert_server_seen_idx").on(
1968
+ table.serverId,
1969
+ table.lastSeenAt
1970
+ ),
1971
+ index("app_error_alert_resource_seen_idx").on(
1972
+ table.resourceId,
1973
+ table.lastSeenAt
1974
+ ),
1975
+ index("app_error_alert_alerted_idx").on(table.lastAlertAt)
1976
+ ]
1977
+ );
1978
+ pgTable(
1979
+ "alert_rule",
1980
+ {
1981
+ id: uuid("id").primaryKey().defaultRandom(),
1982
+ serverId: uuid("server_id").references(() => managedServer.id, {
1983
+ onDelete: "cascade"
1984
+ }),
1985
+ resourceId: uuid("resource_id").references(() => resource.id, {
1986
+ onDelete: "cascade"
1987
+ }),
1988
+ metric: text("metric").notNull(),
1989
+ expression: text("expression").notNull(),
1990
+ severity: alertSeverity("severity").notNull().default("warning"),
1991
+ cooldownSeconds: integer("cooldown_seconds").notNull().default(900),
1992
+ enabled: boolean("enabled").notNull().default(true),
1993
+ routes: jsonb("routes").$type().notNull().default({}),
1994
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1995
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
1996
+ },
1997
+ (table) => [
1998
+ index("alert_rule_server_idx").on(table.serverId),
1999
+ index("alert_rule_resource_idx").on(table.resourceId),
2000
+ index("alert_rule_enabled_severity_idx").on(table.enabled, table.severity)
2001
+ ]
2002
+ );
2003
+ var defaultHermesCompanyModules = {
2004
+ seo: false,
2005
+ ads: false,
2006
+ catalogus: false,
2007
+ customerFacing: false,
2008
+ exact: false
2009
+ };
2010
+ var hermesCompany = pgTable(
2011
+ "hermes_company",
2012
+ {
2013
+ id: uuid("id").primaryKey().defaultRandom(),
2014
+ brandId: text("brand_id").notNull(),
2015
+ displayName: text("display_name").notNull(),
2016
+ serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "restrict" }),
2017
+ refrontCustomerId: uuid("refront_customer_id"),
2018
+ refrontCustomerName: text("refront_customer_name"),
2019
+ refrontTeamLabel: text("refront_team_label"),
2020
+ kitPath: text("kit_path").notNull().default("/opt/hermes-company-kit"),
2021
+ modules: jsonb("modules").$type().notNull().default(defaultHermesCompanyModules),
2022
+ lastDeployAt: timestamp("last_deploy_at", { withTimezone: true }),
2023
+ lastDeployStatus: text("last_deploy_status"),
2024
+ lastDeployLog: text("last_deploy_log"),
2025
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
2026
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
2027
+ },
2028
+ (table) => [
2029
+ uniqueIndex("hermes_company_brand_id_uidx").on(table.brandId),
2030
+ index("hermes_company_server_id_idx").on(table.serverId),
2031
+ index("hermes_company_customer_id_idx").on(table.refrontCustomerId)
2032
+ ]
2033
+ );
1570
2034
  var ALGORITHM = "aes-256-gcm";
1571
2035
  var IV_LENGTH = 16;
1572
2036
  var AUTH_TAG_LENGTH = 16;
@@ -1644,6 +2108,87 @@ async function resolveReleaseGitHubToken(githubTokenId) {
1644
2108
  }
1645
2109
  return getMgBoilerGitHubToken();
1646
2110
  }
2111
+ function decryptNullable(value) {
2112
+ return value ? decrypt(value) : null;
2113
+ }
2114
+ function normalizeManagedServerConnectionRow(row) {
2115
+ return {
2116
+ id: row.id,
2117
+ name: row.name,
2118
+ hostname: row.hostname,
2119
+ port: row.port,
2120
+ username: row.username,
2121
+ password_encrypted: decryptNullable(row.password_encrypted),
2122
+ ssh_key_encrypted: decryptNullable(row.ssh_key_encrypted),
2123
+ ssh_key_passphrase_encrypted: decryptNullable(row.ssh_key_passphrase_encrypted),
2124
+ description: null,
2125
+ tags: row.tags ?? [],
2126
+ last_connected_at: null,
2127
+ last_software_scan_at: null,
2128
+ monitoring_api_key: null,
2129
+ monitoring_enabled: false,
2130
+ hosted_by: row.hosted_by,
2131
+ php_version: null,
2132
+ php_version_number: null,
2133
+ php_distro: null,
2134
+ php_ini_path: null,
2135
+ php_binary_path: null,
2136
+ lsphp_install_path: null,
2137
+ installed_lsphp_versions: null,
2138
+ last_php_scan_at: null,
2139
+ docker_prune_enabled: row.tags?.includes("docker-prune") ?? false,
2140
+ disk_hygiene_enabled: row.tags?.includes("disk-hygiene") ?? false,
2141
+ disk_hygiene_auto_clean: row.tags?.includes("disk-hygiene-auto-clean") ?? false,
2142
+ os_type: row.os_type === "windows" ? "windows" : "linux",
2143
+ created_at: row.created_at,
2144
+ updated_at: row.updated_at,
2145
+ created_by: row.created_by
2146
+ };
2147
+ }
2148
+ async function getManagedServerConnectionByIdForUse(id) {
2149
+ try {
2150
+ const rows = await getDb().execute(sql`
2151
+ SELECT
2152
+ ms.id,
2153
+ ms.name,
2154
+ ms.hostname,
2155
+ ms.port,
2156
+ ms.username,
2157
+ ms.auth_method,
2158
+ ms.os AS os_type,
2159
+ ms.provider AS hosted_by,
2160
+ ms.tags,
2161
+ sc.password_encrypted,
2162
+ sc.ssh_key_encrypted,
2163
+ sc.ssh_key_passphrase_encrypted,
2164
+ ms.created_at,
2165
+ ms.updated_at,
2166
+ NULL::text AS created_by
2167
+ FROM managed_server ms
2168
+ LEFT JOIN server_credential sc ON sc.server_id = ms.id
2169
+ WHERE ms.id = ${id}
2170
+ LIMIT 1
2171
+ `);
2172
+ const row = rows[0];
2173
+ if (row) {
2174
+ return {
2175
+ server: normalizeManagedServerConnectionRow(row),
2176
+ error: null
2177
+ };
2178
+ }
2179
+ return { server: null, error: `Server with id ${id} not found` };
2180
+ } catch (error) {
2181
+ return {
2182
+ server: null,
2183
+ error: error instanceof Error ? error.message : "Failed to resolve server connection"
2184
+ };
2185
+ }
2186
+ }
2187
+
2188
+ // ../platform/dist/queries/ssh-server.js
2189
+ async function getSshServerByIdForUse(id) {
2190
+ return getManagedServerConnectionByIdForUse(id);
2191
+ }
1647
2192
 
1648
2193
  // ../../node_modules/zod/dist/esm/v3/external.js
1649
2194
  var external_exports = {};
@@ -4244,10 +4789,10 @@ var ZodObject = class _ZodObject extends ZodType {
4244
4789
  // }) as any;
4245
4790
  // return merged;
4246
4791
  // }
4247
- catchall(index18) {
4792
+ catchall(index19) {
4248
4793
  return new _ZodObject({
4249
4794
  ...this._def,
4250
- catchall: index18
4795
+ catchall: index19
4251
4796
  });
4252
4797
  }
4253
4798
  pick(mask) {
@@ -4565,9 +5110,9 @@ function mergeValues(a, b) {
4565
5110
  return { valid: false };
4566
5111
  }
4567
5112
  const newArray = [];
4568
- for (let index18 = 0; index18 < a.length; index18++) {
4569
- const itemA = a[index18];
4570
- const itemB = b[index18];
5113
+ for (let index19 = 0; index19 < a.length; index19++) {
5114
+ const itemA = a[index19];
5115
+ const itemB = b[index19];
4571
5116
  const sharedValue = mergeValues(itemA, itemB);
4572
5117
  if (!sharedValue.valid) {
4573
5118
  return { valid: false };
@@ -4773,10 +5318,10 @@ var ZodMap = class extends ZodType {
4773
5318
  }
4774
5319
  const keyType = this._def.keyType;
4775
5320
  const valueType = this._def.valueType;
4776
- const pairs = [...ctx.data.entries()].map(([key, value], index18) => {
5321
+ const pairs = [...ctx.data.entries()].map(([key, value], index19) => {
4777
5322
  return {
4778
- key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index18, "key"])),
4779
- value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index18, "value"]))
5323
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index19, "key"])),
5324
+ value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index19, "value"]))
4780
5325
  };
4781
5326
  });
4782
5327
  if (ctx.common.async) {
@@ -5417,1405 +5962,972 @@ var ZodCatch = class extends ZodType {
5417
5962
  value: result2.status === "valid" ? result2.value : this._def.catchValue({
5418
5963
  get error() {
5419
5964
  return new ZodError(newCtx.common.issues);
5420
- },
5421
- input: newCtx.data
5422
- })
5423
- };
5424
- });
5425
- } else {
5426
- return {
5427
- status: "valid",
5428
- value: result.status === "valid" ? result.value : this._def.catchValue({
5429
- get error() {
5430
- return new ZodError(newCtx.common.issues);
5431
- },
5432
- input: newCtx.data
5433
- })
5434
- };
5435
- }
5436
- }
5437
- removeCatch() {
5438
- return this._def.innerType;
5439
- }
5440
- };
5441
- ZodCatch.create = (type, params) => {
5442
- return new ZodCatch({
5443
- innerType: type,
5444
- typeName: ZodFirstPartyTypeKind.ZodCatch,
5445
- catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
5446
- ...processCreateParams(params)
5447
- });
5448
- };
5449
- var ZodNaN = class extends ZodType {
5450
- _parse(input) {
5451
- const parsedType = this._getType(input);
5452
- if (parsedType !== ZodParsedType.nan) {
5453
- const ctx = this._getOrReturnCtx(input);
5454
- addIssueToContext(ctx, {
5455
- code: ZodIssueCode.invalid_type,
5456
- expected: ZodParsedType.nan,
5457
- received: ctx.parsedType
5458
- });
5459
- return INVALID;
5460
- }
5461
- return { status: "valid", value: input.data };
5462
- }
5463
- };
5464
- ZodNaN.create = (params) => {
5465
- return new ZodNaN({
5466
- typeName: ZodFirstPartyTypeKind.ZodNaN,
5467
- ...processCreateParams(params)
5468
- });
5469
- };
5470
- var BRAND = /* @__PURE__ */ Symbol("zod_brand");
5471
- var ZodBranded = class extends ZodType {
5472
- _parse(input) {
5473
- const { ctx } = this._processInputParams(input);
5474
- const data = ctx.data;
5475
- return this._def.type._parse({
5476
- data,
5477
- path: ctx.path,
5478
- parent: ctx
5479
- });
5480
- }
5481
- unwrap() {
5482
- return this._def.type;
5483
- }
5484
- };
5485
- var ZodPipeline = class _ZodPipeline extends ZodType {
5486
- _parse(input) {
5487
- const { status, ctx } = this._processInputParams(input);
5488
- if (ctx.common.async) {
5489
- const handleAsync = async () => {
5490
- const inResult = await this._def.in._parseAsync({
5491
- data: ctx.data,
5492
- path: ctx.path,
5493
- parent: ctx
5494
- });
5495
- if (inResult.status === "aborted")
5496
- return INVALID;
5497
- if (inResult.status === "dirty") {
5498
- status.dirty();
5499
- return DIRTY(inResult.value);
5500
- } else {
5501
- return this._def.out._parseAsync({
5502
- data: inResult.value,
5503
- path: ctx.path,
5504
- parent: ctx
5505
- });
5506
- }
5507
- };
5508
- return handleAsync();
5509
- } else {
5510
- const inResult = this._def.in._parseSync({
5511
- data: ctx.data,
5512
- path: ctx.path,
5513
- parent: ctx
5514
- });
5515
- if (inResult.status === "aborted")
5516
- return INVALID;
5517
- if (inResult.status === "dirty") {
5518
- status.dirty();
5519
- return {
5520
- status: "dirty",
5521
- value: inResult.value
5522
- };
5523
- } else {
5524
- return this._def.out._parseSync({
5525
- data: inResult.value,
5526
- path: ctx.path,
5527
- parent: ctx
5528
- });
5529
- }
5530
- }
5531
- }
5532
- static create(a, b) {
5533
- return new _ZodPipeline({
5534
- in: a,
5535
- out: b,
5536
- typeName: ZodFirstPartyTypeKind.ZodPipeline
5537
- });
5538
- }
5539
- };
5540
- var ZodReadonly = class extends ZodType {
5541
- _parse(input) {
5542
- const result = this._def.innerType._parse(input);
5543
- const freeze = (data) => {
5544
- if (isValid(data)) {
5545
- data.value = Object.freeze(data.value);
5546
- }
5547
- return data;
5548
- };
5549
- return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result);
5965
+ },
5966
+ input: newCtx.data
5967
+ })
5968
+ };
5969
+ });
5970
+ } else {
5971
+ return {
5972
+ status: "valid",
5973
+ value: result.status === "valid" ? result.value : this._def.catchValue({
5974
+ get error() {
5975
+ return new ZodError(newCtx.common.issues);
5976
+ },
5977
+ input: newCtx.data
5978
+ })
5979
+ };
5980
+ }
5550
5981
  }
5551
- unwrap() {
5982
+ removeCatch() {
5552
5983
  return this._def.innerType;
5553
5984
  }
5554
5985
  };
5555
- ZodReadonly.create = (type, params) => {
5556
- return new ZodReadonly({
5986
+ ZodCatch.create = (type, params) => {
5987
+ return new ZodCatch({
5557
5988
  innerType: type,
5558
- typeName: ZodFirstPartyTypeKind.ZodReadonly,
5989
+ typeName: ZodFirstPartyTypeKind.ZodCatch,
5990
+ catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
5559
5991
  ...processCreateParams(params)
5560
5992
  });
5561
5993
  };
5562
- function cleanParams(params, data) {
5563
- const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
5564
- const p2 = typeof p === "string" ? { message: p } : p;
5565
- return p2;
5566
- }
5567
- function custom(check3, _params = {}, fatal) {
5568
- if (check3)
5569
- return ZodAny.create().superRefine((data, ctx) => {
5570
- const r = check3(data);
5571
- if (r instanceof Promise) {
5572
- return r.then((r2) => {
5573
- if (!r2) {
5574
- const params = cleanParams(_params, data);
5575
- const _fatal = params.fatal ?? fatal ?? true;
5576
- ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
5577
- }
5578
- });
5579
- }
5580
- if (!r) {
5581
- const params = cleanParams(_params, data);
5582
- const _fatal = params.fatal ?? fatal ?? true;
5583
- ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
5584
- }
5585
- return;
5586
- });
5587
- return ZodAny.create();
5588
- }
5589
- var late = {
5590
- object: ZodObject.lazycreate
5994
+ var ZodNaN = class extends ZodType {
5995
+ _parse(input) {
5996
+ const parsedType = this._getType(input);
5997
+ if (parsedType !== ZodParsedType.nan) {
5998
+ const ctx = this._getOrReturnCtx(input);
5999
+ addIssueToContext(ctx, {
6000
+ code: ZodIssueCode.invalid_type,
6001
+ expected: ZodParsedType.nan,
6002
+ received: ctx.parsedType
6003
+ });
6004
+ return INVALID;
6005
+ }
6006
+ return { status: "valid", value: input.data };
6007
+ }
5591
6008
  };
5592
- var ZodFirstPartyTypeKind;
5593
- (function(ZodFirstPartyTypeKind2) {
5594
- ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
5595
- ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber";
5596
- ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN";
5597
- ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt";
5598
- ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean";
5599
- ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate";
5600
- ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol";
5601
- ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined";
5602
- ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull";
5603
- ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny";
5604
- ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown";
5605
- ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever";
5606
- ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid";
5607
- ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray";
5608
- ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject";
5609
- ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion";
5610
- ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
5611
- ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection";
5612
- ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple";
5613
- ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord";
5614
- ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap";
5615
- ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet";
5616
- ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction";
5617
- ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy";
5618
- ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral";
5619
- ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum";
5620
- ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects";
5621
- ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum";
5622
- ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional";
5623
- ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable";
5624
- ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault";
5625
- ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch";
5626
- ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise";
5627
- ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded";
5628
- ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
5629
- ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
5630
- })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
5631
- var instanceOfType = (cls, params = {
5632
- message: `Input not instance of ${cls.name}`
5633
- }) => custom((data) => data instanceof cls, params);
5634
- var stringType = ZodString.create;
5635
- var numberType = ZodNumber.create;
5636
- var nanType = ZodNaN.create;
5637
- var bigIntType = ZodBigInt.create;
5638
- var booleanType = ZodBoolean.create;
5639
- var dateType = ZodDate.create;
5640
- var symbolType = ZodSymbol.create;
5641
- var undefinedType = ZodUndefined.create;
5642
- var nullType = ZodNull.create;
5643
- var anyType = ZodAny.create;
5644
- var unknownType = ZodUnknown.create;
5645
- var neverType = ZodNever.create;
5646
- var voidType = ZodVoid.create;
5647
- var arrayType = ZodArray.create;
5648
- var objectType = ZodObject.create;
5649
- var strictObjectType = ZodObject.strictCreate;
5650
- var unionType = ZodUnion.create;
5651
- var discriminatedUnionType = ZodDiscriminatedUnion.create;
5652
- var intersectionType = ZodIntersection.create;
5653
- var tupleType = ZodTuple.create;
5654
- var recordType = ZodRecord.create;
5655
- var mapType = ZodMap.create;
5656
- var setType = ZodSet.create;
5657
- var functionType = ZodFunction.create;
5658
- var lazyType = ZodLazy.create;
5659
- var literalType = ZodLiteral.create;
5660
- var enumType = ZodEnum.create;
5661
- var nativeEnumType = ZodNativeEnum.create;
5662
- var promiseType = ZodPromise.create;
5663
- var effectsType = ZodEffects.create;
5664
- var optionalType = ZodOptional.create;
5665
- var nullableType = ZodNullable.create;
5666
- var preprocessType = ZodEffects.createWithPreprocess;
5667
- var pipelineType = ZodPipeline.create;
5668
- var ostring = () => stringType().optional();
5669
- var onumber = () => numberType().optional();
5670
- var oboolean = () => booleanType().optional();
5671
- var coerce = {
5672
- string: ((arg) => ZodString.create({ ...arg, coerce: true })),
5673
- number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),
5674
- boolean: ((arg) => ZodBoolean.create({
5675
- ...arg,
5676
- coerce: true
5677
- })),
5678
- bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),
5679
- date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
6009
+ ZodNaN.create = (params) => {
6010
+ return new ZodNaN({
6011
+ typeName: ZodFirstPartyTypeKind.ZodNaN,
6012
+ ...processCreateParams(params)
6013
+ });
5680
6014
  };
5681
- var NEVER = INVALID;
5682
-
5683
- // ../platform/dist/utils/litespeed-vhost.js
5684
- var LITESPEED_STANDARD_APP_PATH = /^(\.|apps\/(backoffice|portal|web|api)(\/[\w-]+)?)$/;
5685
- var LitespeedVhostMappingSchema = external_exports.object({
5686
- domain: external_exports.string().min(1).transform((value) => normalizeVhostDomain(value)),
5687
- appPath: external_exports.string().min(1).regex(LITESPEED_STANDARD_APP_PATH, "Unsupported appPath")
5688
- });
5689
- external_exports.array(LitespeedVhostMappingSchema);
5690
- function normalizeVhostDomain(value) {
5691
- return value.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/\.+$/, "").toLowerCase();
5692
- }
5693
- var users = pgTable("user", {
5694
- id: uuid("id").primaryKey().defaultRandom(),
5695
- email: text("email").notNull().unique(),
5696
- fullName: text("full_name"),
5697
- avatarUrl: text("avatar_url"),
5698
- emailVerified: boolean("email_verified").notNull().default(false),
5699
- roleId: uuid("role_id"),
5700
- permissions: jsonb("permissions"),
5701
- /** Linked Refront user id (matched by email during refront-crm-sync). */
5702
- refrontUserId: uuid("refront_user_id"),
5703
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5704
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5705
- });
5706
- pgTable("session", {
5707
- id: uuid("id").primaryKey().defaultRandom(),
5708
- userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
5709
- token: text("token").notNull().unique(),
5710
- expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
5711
- ipAddress: text("ip_address"),
5712
- userAgent: text("user_agent"),
5713
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5714
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5715
- });
5716
- pgTable("account", {
5717
- id: uuid("id").primaryKey().defaultRandom(),
5718
- userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
5719
- providerId: text("provider_id").notNull(),
5720
- accountId: text("account_id").notNull(),
5721
- password: text("password"),
5722
- accessToken: text("access_token"),
5723
- refreshToken: text("refresh_token"),
5724
- accessTokenExpiresAt: timestamp("access_token_expires_at", {
5725
- withTimezone: true
5726
- }),
5727
- refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
5728
- withTimezone: true
5729
- }),
5730
- scope: text("scope"),
5731
- idToken: text("id_token"),
5732
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5733
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5734
- });
5735
- pgTable("verification", {
5736
- id: uuid("id").primaryKey().defaultRandom(),
5737
- identifier: text("identifier").notNull(),
5738
- value: text("value").notNull(),
5739
- expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
5740
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5741
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5742
- });
5743
- pgTable("two_factor", {
5744
- id: uuid("id").primaryKey().defaultRandom(),
5745
- userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
5746
- secret: text("secret").notNull(),
5747
- backupCodes: text("backup_codes")
5748
- });
5749
- var WIKI_EMBEDDING_DIMENSIONS = 1536;
5750
- var vector = customType({
5751
- dataType(config) {
5752
- return `vector(${config?.dimensions ?? WIKI_EMBEDDING_DIMENSIONS})`;
5753
- },
5754
- toDriver(value) {
5755
- return `[${value.join(",")}]`;
5756
- },
5757
- fromDriver(value) {
5758
- if (Array.isArray(value)) return value.map(Number);
5759
- if (typeof value !== "string") return [];
5760
- const trimmed = value.replace(/^\[/, "").replace(/\]$/, "");
5761
- if (!trimmed) return [];
5762
- return trimmed.split(",").map((part) => Number(part.trim()));
6015
+ var BRAND = /* @__PURE__ */ Symbol("zod_brand");
6016
+ var ZodBranded = class extends ZodType {
6017
+ _parse(input) {
6018
+ const { ctx } = this._processInputParams(input);
6019
+ const data = ctx.data;
6020
+ return this._def.type._parse({
6021
+ data,
6022
+ path: ctx.path,
6023
+ parent: ctx
6024
+ });
6025
+ }
6026
+ unwrap() {
6027
+ return this._def.type;
5763
6028
  }
5764
- });
5765
- pgTable(
5766
- "wiki_page",
5767
- {
5768
- id: uuid("id").primaryKey().defaultRandom(),
5769
- slug: text("slug").notNull().unique(),
5770
- title: text("title").notNull().default(""),
5771
- summary: text("summary"),
5772
- content: text("content").notNull().default(""),
5773
- frontmatter: jsonb("frontmatter").$type(),
5774
- sources: jsonb("sources").$type(),
5775
- sourcesArchive: jsonb("sources_archive").$type(),
5776
- tags: text("tags").array().notNull().default([]),
5777
- status: text("status").notNull().default("draft"),
5778
- pageUpdatedOn: date("page_updated_on"),
5779
- contentHash: text("content_hash"),
5780
- /** Legacy app-layer cosine storage; dual-written with embeddingVec. */
5781
- embedding: real("embedding").array(),
5782
- embeddingVec: vector("embedding_vec", {
5783
- dimensions: WIKI_EMBEDDING_DIMENSIONS
5784
- }),
5785
- embeddingModel: text("embedding_model"),
5786
- embeddedAt: timestamp("embedded_at", { withTimezone: true }),
5787
- reviewedBy: text("reviewed_by"),
5788
- reviewedOn: date("reviewed_on"),
5789
- lastVerifiedOn: date("last_verified_on"),
5790
- verifyCount: integer("verify_count").notNull().default(0),
5791
- verifyNote: text("verify_note"),
5792
- dirty: boolean("dirty").notNull().default(false),
5793
- vaultSyncedAt: timestamp("vault_synced_at", { withTimezone: true }),
5794
- /** Reviewer / itemized-phase lease holder. */
5795
- claimedBy: text("claimed_by"),
5796
- claimedAt: timestamp("claimed_at", { withTimezone: true }),
5797
- /** Expired leases are reclaimable by any runner. */
5798
- leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
5799
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5800
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5801
- },
5802
- (table) => [
5803
- index("idx_wiki_page_status").on(table.status),
5804
- index("idx_wiki_page_last_verified").on(table.lastVerifiedOn),
5805
- index("idx_wiki_page_dirty").on(table.dirty),
5806
- index("idx_wiki_page_lease_expires").on(table.leaseExpiresAt).where(sql`${table.leaseExpiresAt} IS NOT NULL`)
5807
- ]
5808
- );
5809
- pgTable(
5810
- "wiki_change_log",
5811
- {
5812
- id: uuid("id").primaryKey().defaultRandom(),
5813
- authorJob: text("author_job").notNull(),
5814
- summary: text("summary").notNull().default(""),
5815
- slugs: text("slugs").array().notNull().default([]),
5816
- syncedAt: timestamp("synced_at", { withTimezone: true }),
5817
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5818
- },
5819
- (table) => [index("idx_wiki_change_log_created").on(table.createdAt)]
5820
- );
5821
- pgTable(
5822
- "wiki_page_flag",
5823
- {
5824
- id: uuid("id").primaryKey().defaultRandom(),
5825
- pageSlug: text("page_slug").notNull(),
5826
- kind: text("kind").notNull(),
5827
- detail: text("detail").notNull().default(""),
5828
- sourceRef: text("source_ref"),
5829
- createdBy: text("created_by"),
5830
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5831
- resolvedAt: timestamp("resolved_at", { withTimezone: true }),
5832
- resolvedBy: text("resolved_by")
5833
- },
5834
- (table) => [index("idx_wiki_page_flag_created").on(table.createdAt)]
5835
- );
5836
- pgTable(
5837
- "wiki_page_evidence",
5838
- {
5839
- id: uuid("id").primaryKey().defaultRandom(),
5840
- pageSlug: text("page_slug").notNull(),
5841
- evidenceType: text("evidence_type").notNull(),
5842
- evidenceRef: text("evidence_ref").notNull(),
5843
- matchScore: real("match_score"),
5844
- matchReason: text("match_reason"),
5845
- snippet: text("snippet"),
5846
- status: text("status").notNull().default("pending"),
5847
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5848
- resolvedAt: timestamp("resolved_at", { withTimezone: true })
5849
- },
5850
- (table) => [
5851
- uniqueIndex(
5852
- "wiki_page_evidence_page_slug_evidence_type_evidence_ref_key"
5853
- ).on(table.pageSlug, table.evidenceType, table.evidenceRef),
5854
- index("idx_wiki_page_evidence_created").on(table.createdAt)
5855
- ]
5856
- );
5857
- pgTable(
5858
- "wiki_page_link",
5859
- {
5860
- id: uuid("id").primaryKey().defaultRandom(),
5861
- sourceSlug: text("source_slug").notNull(),
5862
- targetSlug: text("target_slug").notNull(),
5863
- kind: text("kind").notNull().default("wikilink"),
5864
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5865
- },
5866
- (table) => [
5867
- uniqueIndex("wiki_page_link_source_target_kind_key").on(
5868
- table.sourceSlug,
5869
- table.targetSlug,
5870
- table.kind
5871
- ),
5872
- index("idx_wiki_page_link_source").on(table.sourceSlug),
5873
- index("idx_wiki_page_link_target").on(table.targetSlug)
5874
- ]
5875
- );
5876
- pgTable(
5877
- "wiki_page_read",
5878
- {
5879
- id: uuid("id").primaryKey().defaultRandom(),
5880
- slug: text("slug").notNull(),
5881
- caller: text("caller"),
5882
- includedArchive: boolean("included_archive").notNull().default(false),
5883
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5884
- },
5885
- (table) => [
5886
- index("idx_wiki_page_read_slug").on(table.slug, table.createdAt),
5887
- index("idx_wiki_page_read_created").on(table.createdAt)
5888
- ]
5889
- );
5890
- pgTable(
5891
- "wiki_verify_event",
5892
- {
5893
- id: uuid("id").primaryKey().defaultRandom(),
5894
- pageSlug: text("page_slug").notNull(),
5895
- outcome: text("outcome").notNull(),
5896
- runMode: text("run_mode"),
5897
- note: text("note"),
5898
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5899
- },
5900
- (table) => [
5901
- index("idx_wiki_verify_event_created").on(table.createdAt),
5902
- index("idx_wiki_verify_event_outcome_created").on(
5903
- table.outcome,
5904
- table.createdAt
5905
- )
5906
- ]
5907
- );
5908
- pgTable(
5909
- "wiki_invariant_violation",
5910
- {
5911
- id: uuid("id").primaryKey().defaultRandom(),
5912
- slug: text("slug").notNull(),
5913
- code: text("code").notNull(),
5914
- severity: text("severity").notNull().default("warning"),
5915
- detail: text("detail").notNull().default(""),
5916
- suggestedFix: text("suggested_fix"),
5917
- suggestedSlug: text("suggested_slug"),
5918
- occurrenceCount: integer("occurrence_count").notNull().default(1),
5919
- reopenCount: integer("reopen_count").notNull().default(0),
5920
- firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull().defaultNow(),
5921
- lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
5922
- resolvedAt: timestamp("resolved_at", { withTimezone: true }),
5923
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5924
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5925
- },
5926
- (table) => [
5927
- uniqueIndex("wiki_invariant_violation_slug_code_key").on(
5928
- table.slug,
5929
- table.code
5930
- ),
5931
- index("idx_wiki_invariant_violation_severity").on(
5932
- table.severity,
5933
- table.resolvedAt
5934
- )
5935
- ]
5936
- );
5937
- pgTable(
5938
- "wiki_pipeline_rule",
5939
- {
5940
- id: uuid("id").primaryKey().defaultRandom(),
5941
- scope: text("scope").notNull(),
5942
- ruleKey: text("rule_key").notNull(),
5943
- text: text("text").notNull(),
5944
- /** A = deterministic auto-applied, B = judgement, gated by insight accept. */
5945
- tier: text("tier").notNull().default("B"),
5946
- status: text("status").notNull().default("proposed"),
5947
- sourceCode: text("source_code"),
5948
- evidenceRefs: text("evidence_refs").array().notNull().default([]),
5949
- violationCount: integer("violation_count").notNull().default(0),
5950
- lastViolationAt: timestamp("last_violation_at", { withTimezone: true }),
5951
- activatedAt: timestamp("activated_at", { withTimezone: true }),
5952
- retiredAt: timestamp("retired_at", { withTimezone: true }),
5953
- retiredReason: text("retired_reason"),
5954
- rejectedAt: timestamp("rejected_at", { withTimezone: true }),
5955
- rejectedReason: text("rejected_reason"),
5956
- approvedBy: text("approved_by"),
5957
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5958
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5959
- },
5960
- (table) => [
5961
- uniqueIndex("wiki_pipeline_rule_scope_key_key").on(
5962
- table.scope,
5963
- table.ruleKey
5964
- )
5965
- ]
5966
- );
5967
- pgTable(
5968
- "wiki_search_log",
5969
- {
5970
- id: uuid("id").primaryKey().defaultRandom(),
5971
- query: text("query").notNull(),
5972
- hitCount: integer("hit_count").notNull().default(0),
5973
- topScore: real("top_score"),
5974
- topSlug: text("top_slug"),
5975
- caller: text("caller"),
5976
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5977
- },
5978
- (table) => [index("idx_wiki_search_log_created").on(table.createdAt)]
5979
- );
5980
- pgTable(
5981
- "team_memory_search_log",
5982
- {
5983
- id: uuid("id").primaryKey().defaultRandom(),
5984
- query: text("query").notNull(),
5985
- hitCount: integer("hit_count").notNull().default(0),
5986
- topScore: real("top_score"),
5987
- topId: text("top_id"),
5988
- caller: text("caller"),
5989
- repo: text("repo"),
5990
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
5991
- },
5992
- (table) => [index("idx_team_memory_search_log_created").on(table.createdAt)]
5993
- );
5994
- pgTable(
5995
- "wiki_agent_run",
5996
- {
5997
- id: uuid("id").primaryKey().defaultRandom(),
5998
- phase: text("phase").notNull(),
5999
- model: text("model"),
6000
- claimed: jsonb("claimed").$type(),
6001
- proven: jsonb("proven").$type(),
6002
- violationCount: integer("violation_count").notNull().default(0),
6003
- violations: jsonb("violations").$type(),
6004
- outcome: text("outcome").notNull().default("ok"),
6005
- agentId: text("agent_id"),
6006
- runId: text("run_id"),
6007
- passId: text("pass_id"),
6008
- changedPaths: jsonb("changed_paths").$type(),
6009
- summary: text("summary"),
6010
- userVerdict: text("user_verdict"),
6011
- userVerdictNote: text("user_verdict_note"),
6012
- verdictAt: timestamp("verdict_at", { withTimezone: true }),
6013
- reviewed: boolean("reviewed").notNull().default(false),
6014
- reviewNotes: text("review_notes"),
6015
- reviewedAt: timestamp("reviewed_at", { withTimezone: true }),
6016
- startedAt: timestamp("started_at", { withTimezone: true }),
6017
- finishedAt: timestamp("finished_at", { withTimezone: true }).notNull().defaultNow(),
6018
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6029
+ };
6030
+ var ZodPipeline = class _ZodPipeline extends ZodType {
6031
+ _parse(input) {
6032
+ const { status, ctx } = this._processInputParams(input);
6033
+ if (ctx.common.async) {
6034
+ const handleAsync = async () => {
6035
+ const inResult = await this._def.in._parseAsync({
6036
+ data: ctx.data,
6037
+ path: ctx.path,
6038
+ parent: ctx
6039
+ });
6040
+ if (inResult.status === "aborted")
6041
+ return INVALID;
6042
+ if (inResult.status === "dirty") {
6043
+ status.dirty();
6044
+ return DIRTY(inResult.value);
6045
+ } else {
6046
+ return this._def.out._parseAsync({
6047
+ data: inResult.value,
6048
+ path: ctx.path,
6049
+ parent: ctx
6050
+ });
6051
+ }
6052
+ };
6053
+ return handleAsync();
6054
+ } else {
6055
+ const inResult = this._def.in._parseSync({
6056
+ data: ctx.data,
6057
+ path: ctx.path,
6058
+ parent: ctx
6059
+ });
6060
+ if (inResult.status === "aborted")
6061
+ return INVALID;
6062
+ if (inResult.status === "dirty") {
6063
+ status.dirty();
6064
+ return {
6065
+ status: "dirty",
6066
+ value: inResult.value
6067
+ };
6068
+ } else {
6069
+ return this._def.out._parseSync({
6070
+ data: inResult.value,
6071
+ path: ctx.path,
6072
+ parent: ctx
6073
+ });
6074
+ }
6075
+ }
6076
+ }
6077
+ static create(a, b) {
6078
+ return new _ZodPipeline({
6079
+ in: a,
6080
+ out: b,
6081
+ typeName: ZodFirstPartyTypeKind.ZodPipeline
6082
+ });
6083
+ }
6084
+ };
6085
+ var ZodReadonly = class extends ZodType {
6086
+ _parse(input) {
6087
+ const result = this._def.innerType._parse(input);
6088
+ const freeze = (data) => {
6089
+ if (isValid(data)) {
6090
+ data.value = Object.freeze(data.value);
6091
+ }
6092
+ return data;
6093
+ };
6094
+ return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result);
6095
+ }
6096
+ unwrap() {
6097
+ return this._def.innerType;
6098
+ }
6099
+ };
6100
+ ZodReadonly.create = (type, params) => {
6101
+ return new ZodReadonly({
6102
+ innerType: type,
6103
+ typeName: ZodFirstPartyTypeKind.ZodReadonly,
6104
+ ...processCreateParams(params)
6105
+ });
6106
+ };
6107
+ function cleanParams(params, data) {
6108
+ const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
6109
+ const p2 = typeof p === "string" ? { message: p } : p;
6110
+ return p2;
6111
+ }
6112
+ function custom(check3, _params = {}, fatal) {
6113
+ if (check3)
6114
+ return ZodAny.create().superRefine((data, ctx) => {
6115
+ const r = check3(data);
6116
+ if (r instanceof Promise) {
6117
+ return r.then((r2) => {
6118
+ if (!r2) {
6119
+ const params = cleanParams(_params, data);
6120
+ const _fatal = params.fatal ?? fatal ?? true;
6121
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
6122
+ }
6123
+ });
6124
+ }
6125
+ if (!r) {
6126
+ const params = cleanParams(_params, data);
6127
+ const _fatal = params.fatal ?? fatal ?? true;
6128
+ ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
6129
+ }
6130
+ return;
6131
+ });
6132
+ return ZodAny.create();
6133
+ }
6134
+ var late = {
6135
+ object: ZodObject.lazycreate
6136
+ };
6137
+ var ZodFirstPartyTypeKind;
6138
+ (function(ZodFirstPartyTypeKind2) {
6139
+ ZodFirstPartyTypeKind2["ZodString"] = "ZodString";
6140
+ ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber";
6141
+ ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN";
6142
+ ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt";
6143
+ ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean";
6144
+ ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate";
6145
+ ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol";
6146
+ ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined";
6147
+ ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull";
6148
+ ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny";
6149
+ ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown";
6150
+ ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever";
6151
+ ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid";
6152
+ ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray";
6153
+ ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject";
6154
+ ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion";
6155
+ ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion";
6156
+ ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection";
6157
+ ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple";
6158
+ ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord";
6159
+ ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap";
6160
+ ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet";
6161
+ ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction";
6162
+ ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy";
6163
+ ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral";
6164
+ ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum";
6165
+ ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects";
6166
+ ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum";
6167
+ ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional";
6168
+ ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable";
6169
+ ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault";
6170
+ ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch";
6171
+ ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise";
6172
+ ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded";
6173
+ ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline";
6174
+ ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly";
6175
+ })(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {}));
6176
+ var instanceOfType = (cls, params = {
6177
+ message: `Input not instance of ${cls.name}`
6178
+ }) => custom((data) => data instanceof cls, params);
6179
+ var stringType = ZodString.create;
6180
+ var numberType = ZodNumber.create;
6181
+ var nanType = ZodNaN.create;
6182
+ var bigIntType = ZodBigInt.create;
6183
+ var booleanType = ZodBoolean.create;
6184
+ var dateType = ZodDate.create;
6185
+ var symbolType = ZodSymbol.create;
6186
+ var undefinedType = ZodUndefined.create;
6187
+ var nullType = ZodNull.create;
6188
+ var anyType = ZodAny.create;
6189
+ var unknownType = ZodUnknown.create;
6190
+ var neverType = ZodNever.create;
6191
+ var voidType = ZodVoid.create;
6192
+ var arrayType = ZodArray.create;
6193
+ var objectType = ZodObject.create;
6194
+ var strictObjectType = ZodObject.strictCreate;
6195
+ var unionType = ZodUnion.create;
6196
+ var discriminatedUnionType = ZodDiscriminatedUnion.create;
6197
+ var intersectionType = ZodIntersection.create;
6198
+ var tupleType = ZodTuple.create;
6199
+ var recordType = ZodRecord.create;
6200
+ var mapType = ZodMap.create;
6201
+ var setType = ZodSet.create;
6202
+ var functionType = ZodFunction.create;
6203
+ var lazyType = ZodLazy.create;
6204
+ var literalType = ZodLiteral.create;
6205
+ var enumType = ZodEnum.create;
6206
+ var nativeEnumType = ZodNativeEnum.create;
6207
+ var promiseType = ZodPromise.create;
6208
+ var effectsType = ZodEffects.create;
6209
+ var optionalType = ZodOptional.create;
6210
+ var nullableType = ZodNullable.create;
6211
+ var preprocessType = ZodEffects.createWithPreprocess;
6212
+ var pipelineType = ZodPipeline.create;
6213
+ var ostring = () => stringType().optional();
6214
+ var onumber = () => numberType().optional();
6215
+ var oboolean = () => booleanType().optional();
6216
+ var coerce = {
6217
+ string: ((arg) => ZodString.create({ ...arg, coerce: true })),
6218
+ number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),
6219
+ boolean: ((arg) => ZodBoolean.create({
6220
+ ...arg,
6221
+ coerce: true
6222
+ })),
6223
+ bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),
6224
+ date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
6225
+ };
6226
+ var NEVER = INVALID;
6227
+
6228
+ // ../platform/dist/utils/litespeed-vhost.js
6229
+ var LITESPEED_STANDARD_APP_PATH = /^(\.|apps\/(backoffice|portal|web|api)(\/[\w-]+)?)$/;
6230
+ var LitespeedVhostMappingSchema = external_exports.object({
6231
+ domain: external_exports.string().min(1).transform((value) => normalizeVhostDomain(value)),
6232
+ appPath: external_exports.string().min(1).regex(LITESPEED_STANDARD_APP_PATH, "Unsupported appPath")
6233
+ });
6234
+ external_exports.array(LitespeedVhostMappingSchema);
6235
+ function normalizeVhostDomain(value) {
6236
+ return value.trim().replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/\.+$/, "").toLowerCase();
6237
+ }
6238
+ var users = pgTable("user", {
6239
+ id: uuid("id").primaryKey().defaultRandom(),
6240
+ email: text("email").notNull().unique(),
6241
+ fullName: text("full_name"),
6242
+ avatarUrl: text("avatar_url"),
6243
+ emailVerified: boolean("email_verified").notNull().default(false),
6244
+ roleId: uuid("role_id"),
6245
+ permissions: jsonb("permissions"),
6246
+ /** Linked Refront user id (matched by email during refront-crm-sync). */
6247
+ refrontUserId: uuid("refront_user_id"),
6248
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6249
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6250
+ });
6251
+ pgTable("session", {
6252
+ id: uuid("id").primaryKey().defaultRandom(),
6253
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
6254
+ token: text("token").notNull().unique(),
6255
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
6256
+ ipAddress: text("ip_address"),
6257
+ userAgent: text("user_agent"),
6258
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6259
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6260
+ });
6261
+ pgTable("account", {
6262
+ id: uuid("id").primaryKey().defaultRandom(),
6263
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
6264
+ providerId: text("provider_id").notNull(),
6265
+ accountId: text("account_id").notNull(),
6266
+ password: text("password"),
6267
+ accessToken: text("access_token"),
6268
+ refreshToken: text("refresh_token"),
6269
+ accessTokenExpiresAt: timestamp("access_token_expires_at", {
6270
+ withTimezone: true
6271
+ }),
6272
+ refreshTokenExpiresAt: timestamp("refresh_token_expires_at", {
6273
+ withTimezone: true
6274
+ }),
6275
+ scope: text("scope"),
6276
+ idToken: text("id_token"),
6277
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6278
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6279
+ });
6280
+ pgTable("verification", {
6281
+ id: uuid("id").primaryKey().defaultRandom(),
6282
+ identifier: text("identifier").notNull(),
6283
+ value: text("value").notNull(),
6284
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
6285
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6286
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6287
+ });
6288
+ pgTable("two_factor", {
6289
+ id: uuid("id").primaryKey().defaultRandom(),
6290
+ userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
6291
+ secret: text("secret").notNull(),
6292
+ backupCodes: text("backup_codes")
6293
+ });
6294
+ var WIKI_EMBEDDING_DIMENSIONS = 1536;
6295
+ var vector = customType({
6296
+ dataType(config) {
6297
+ return `vector(${config?.dimensions ?? WIKI_EMBEDDING_DIMENSIONS})`;
6019
6298
  },
6020
- (table) => [
6021
- index("idx_wiki_agent_run_phase_created").on(table.phase, table.createdAt),
6022
- index("idx_wiki_agent_run_created").on(table.createdAt),
6023
- index("idx_wiki_agent_run_reviewed").on(table.reviewed),
6024
- index("idx_wiki_agent_run_user_verdict").on(table.userVerdict),
6025
- index("idx_wiki_agent_run_pass_id").on(table.passId)
6026
- ]
6027
- );
6028
- pgTable(
6029
- "wiki_agent_review_suggestion",
6030
- {
6031
- id: uuid("id").primaryKey().defaultRandom(),
6032
- status: text("status").notNull().default("proposed"),
6033
- implementationClass: text("implementation_class").notNull(),
6034
- phase: text("phase"),
6035
- scope: text("scope"),
6036
- title: text("title").notNull(),
6037
- summary: text("summary").notNull(),
6038
- targetFile: text("target_file"),
6039
- targetKey: text("target_key"),
6040
- beforeText: text("before_text"),
6041
- afterText: text("after_text"),
6042
- evidenceRunIds: uuid("evidence_run_ids").array().notNull().default([]),
6043
- evidenceRefs: text("evidence_refs").array().notNull().default([]),
6044
- dedupeKey: text("dedupe_key").notNull(),
6045
- source: text("source").notNull().default("auto"),
6046
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6047
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6299
+ toDriver(value) {
6300
+ return `[${value.join(",")}]`;
6048
6301
  },
6049
- (table) => [
6050
- uniqueIndex("wiki_agent_review_suggestion_dedupe_key").on(table.dedupeKey),
6051
- index("idx_wiki_agent_review_suggestion_status_created").on(
6052
- table.status,
6053
- table.createdAt
6054
- )
6055
- ]
6056
- );
6057
-
6058
- // ../db/src/schema/agent-memory.ts
6302
+ fromDriver(value) {
6303
+ if (Array.isArray(value)) return value.map(Number);
6304
+ if (typeof value !== "string") return [];
6305
+ const trimmed = value.replace(/^\[/, "").replace(/\]$/, "");
6306
+ if (!trimmed) return [];
6307
+ return trimmed.split(",").map((part) => Number(part.trim()));
6308
+ }
6309
+ });
6059
6310
  pgTable(
6060
- "agent_memory",
6311
+ "wiki_page",
6061
6312
  {
6062
6313
  id: uuid("id").primaryKey().defaultRandom(),
6063
- conversationId: uuid("conversation_id"),
6064
- localConversationId: text("local_conversation_id"),
6065
- source: text("source").notNull().default("mirror"),
6066
- userId: text("user_id"),
6067
- hostId: uuid("host_id"),
6068
- workspaceId: uuid("workspace_id"),
6069
- gitRepository: text("git_repository"),
6314
+ slug: text("slug").notNull().unique(),
6070
6315
  title: text("title").notNull().default(""),
6071
6316
  summary: text("summary"),
6072
6317
  content: text("content").notNull().default(""),
6318
+ frontmatter: jsonb("frontmatter").$type(),
6319
+ sources: jsonb("sources").$type(),
6320
+ sourcesArchive: jsonb("sources_archive").$type(),
6321
+ tags: text("tags").array().notNull().default([]),
6322
+ status: text("status").notNull().default("draft"),
6323
+ pageUpdatedOn: date("page_updated_on"),
6073
6324
  contentHash: text("content_hash"),
6325
+ /** Legacy app-layer cosine storage; dual-written with embeddingVec. */
6074
6326
  embedding: real("embedding").array(),
6075
6327
  embeddingVec: vector("embedding_vec", {
6076
6328
  dimensions: WIKI_EMBEDDING_DIMENSIONS
6077
6329
  }),
6078
6330
  embeddingModel: text("embedding_model"),
6079
- messageCount: integer("message_count").notNull().default(0),
6080
- startedAt: timestamp("started_at", { withTimezone: true }),
6081
- lastMessageAt: timestamp("last_message_at", { withTimezone: true }),
6082
6331
  embeddedAt: timestamp("embedded_at", { withTimezone: true }),
6083
- resolved: boolean("resolved"),
6084
- category: text("category"),
6085
- tech: text("tech").array(),
6086
- kind: text("kind").notNull().default("human"),
6087
- symbols: text("symbols").array(),
6332
+ reviewedBy: text("reviewed_by"),
6333
+ reviewedOn: date("reviewed_on"),
6334
+ lastVerifiedOn: date("last_verified_on"),
6335
+ verifyCount: integer("verify_count").notNull().default(0),
6336
+ verifyNote: text("verify_note"),
6337
+ dirty: boolean("dirty").notNull().default(false),
6338
+ vaultSyncedAt: timestamp("vault_synced_at", { withTimezone: true }),
6339
+ /** Reviewer / itemized-phase lease holder. */
6340
+ claimedBy: text("claimed_by"),
6341
+ claimedAt: timestamp("claimed_at", { withTimezone: true }),
6342
+ /** Expired leases are reclaimable by any runner. */
6343
+ leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
6088
6344
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6089
6345
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6090
6346
  },
6091
6347
  (table) => [
6092
- uniqueIndex("uq_agent_memory_local_conversation").on(
6093
- table.localConversationId
6094
- ),
6095
- index("idx_agent_memory_repo").on(table.gitRepository),
6096
- index("idx_agent_memory_user").on(table.userId),
6097
- index("idx_agent_memory_kind").on(table.kind)
6348
+ index("idx_wiki_page_status").on(table.status),
6349
+ index("idx_wiki_page_last_verified").on(table.lastVerifiedOn),
6350
+ index("idx_wiki_page_dirty").on(table.dirty),
6351
+ index("idx_wiki_page_lease_expires").on(table.leaseExpiresAt).where(sql`${table.leaseExpiresAt} IS NOT NULL`)
6098
6352
  ]
6099
6353
  );
6100
- var contentSourceType = pgEnum("content_source_type", [
6101
- "own_site",
6102
- "client_site",
6103
- "external_source",
6104
- "competitor",
6105
- "news",
6106
- "government",
6107
- "supplier",
6108
- "unknown"
6109
- ]);
6110
- var contentSnapshotKind = pgEnum("content_snapshot_kind", [
6111
- "corpus",
6112
- "source"
6113
- ]);
6114
- var contentCandidateStatus = pgEnum("content_candidate_status", [
6115
- "candidate",
6116
- "used",
6117
- "rejected"
6118
- ]);
6119
- var contentSources = pgTable(
6120
- "content_sources",
6354
+ pgTable(
6355
+ "wiki_change_log",
6121
6356
  {
6122
6357
  id: uuid("id").primaryKey().defaultRandom(),
6123
- url: text("url").notNull(),
6124
- canonicalUrl: text("canonical_url"),
6125
- domain: text("domain").notNull(),
6126
- sourceType: contentSourceType("source_type").notNull().default("unknown"),
6127
- title: text("title"),
6128
- author: text("author"),
6129
- publishedAt: timestamp("published_at", { withTimezone: true }),
6130
- extractionMethod: text("extraction_method"),
6131
- summary: text("summary"),
6132
- facts: jsonb("facts").$type().notNull().default([]),
6133
- limitations: jsonb("limitations").$type().notNull().default([]),
6134
- fullText: text("full_text"),
6135
- textHash: text("text_hash"),
6136
- wordCount: integer("word_count"),
6137
- language: text("language"),
6138
- projectKey: text("project_key"),
6139
- siteKey: text("site_key"),
6140
- ticketNumber: text("ticket_number"),
6141
- metadata: jsonb("metadata").$type().notNull().default({}),
6142
- fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull().defaultNow(),
6143
- lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
6144
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6145
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6358
+ authorJob: text("author_job").notNull(),
6359
+ summary: text("summary").notNull().default(""),
6360
+ slugs: text("slugs").array().notNull().default([]),
6361
+ syncedAt: timestamp("synced_at", { withTimezone: true }),
6362
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6146
6363
  },
6147
- (table) => [
6148
- uniqueIndex("content_sources_canonical_url_uidx").on(table.canonicalUrl),
6149
- index("content_sources_domain_idx").on(table.domain),
6150
- index("content_sources_type_idx").on(table.sourceType),
6151
- index("content_sources_text_hash_idx").on(table.textHash),
6152
- index("content_sources_project_idx").on(table.projectKey),
6153
- index("content_sources_ticket_idx").on(table.ticketNumber),
6154
- index("content_sources_last_seen_idx").on(table.lastSeenAt.desc())
6155
- ]
6364
+ (table) => [index("idx_wiki_change_log_created").on(table.createdAt)]
6156
6365
  );
6157
- var contentEvidencePacks = pgTable(
6158
- "content_evidence_packs",
6366
+ pgTable(
6367
+ "wiki_page_flag",
6159
6368
  {
6160
6369
  id: uuid("id").primaryKey().defaultRandom(),
6161
- topic: text("topic"),
6162
- locale: text("locale"),
6163
- projectKey: text("project_key"),
6164
- siteKey: text("site_key"),
6165
- ticketNumber: text("ticket_number"),
6166
- generatedAt: timestamp("generated_at", { withTimezone: true }).notNull().defaultNow(),
6167
- sources: jsonb("sources").$type().notNull().default([]),
6168
- verifiedFacts: jsonb("verified_facts").$type().notNull().default([]),
6169
- conflicts: jsonb("conflicts").$type().notNull().default([]),
6170
- uniqueClaims: jsonb("unique_claims").$type().notNull().default([]),
6171
- openQuestions: jsonb("open_questions").$type().notNull().default([]),
6172
- verbatimWarnings: jsonb("verbatim_warnings").$type().notNull().default([]),
6173
- limitations: jsonb("limitations").$type().notNull().default([]),
6174
- /**
6175
- * Order-independent signature of topic + canonical URL set (packSignature).
6176
- * Lets the reuse-first save path return a fresh stored pack without
6177
- * re-researching when the same urls/topic are shared again.
6178
- */
6179
- sourceSignature: text("source_signature"),
6180
- metadata: jsonb("metadata").$type().notNull().default({}),
6370
+ pageSlug: text("page_slug").notNull(),
6371
+ kind: text("kind").notNull(),
6372
+ detail: text("detail").notNull().default(""),
6373
+ sourceRef: text("source_ref"),
6374
+ createdBy: text("created_by"),
6181
6375
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6182
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6376
+ resolvedAt: timestamp("resolved_at", { withTimezone: true }),
6377
+ resolvedBy: text("resolved_by")
6183
6378
  },
6184
- (table) => [
6185
- index("content_evidence_packs_project_idx").on(table.projectKey),
6186
- index("content_evidence_packs_ticket_idx").on(table.ticketNumber),
6187
- index("content_evidence_packs_generated_idx").on(table.generatedAt.desc()),
6188
- index("content_evidence_packs_signature_idx").on(table.sourceSignature)
6189
- ]
6379
+ (table) => [index("idx_wiki_page_flag_created").on(table.createdAt)]
6190
6380
  );
6191
6381
  pgTable(
6192
- "content_evidence_pack_source",
6382
+ "wiki_page_evidence",
6193
6383
  {
6194
6384
  id: uuid("id").primaryKey().defaultRandom(),
6195
- packId: uuid("pack_id").notNull().references(() => contentEvidencePacks.id, { onDelete: "cascade" }),
6196
- contentSourceId: uuid("content_source_id").notNull().references(() => contentSources.id, { onDelete: "cascade" }),
6197
- packSourceRef: text("pack_source_ref"),
6198
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6385
+ pageSlug: text("page_slug").notNull(),
6386
+ evidenceType: text("evidence_type").notNull(),
6387
+ evidenceRef: text("evidence_ref").notNull(),
6388
+ matchScore: real("match_score"),
6389
+ matchReason: text("match_reason"),
6390
+ snippet: text("snippet"),
6391
+ status: text("status").notNull().default("pending"),
6392
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6393
+ resolvedAt: timestamp("resolved_at", { withTimezone: true })
6199
6394
  },
6200
6395
  (table) => [
6201
- uniqueIndex("content_evidence_pack_source_uidx").on(
6202
- table.packId,
6203
- table.contentSourceId
6204
- ),
6205
- index("content_evidence_pack_source_source_idx").on(table.contentSourceId)
6206
- ]
6207
- );
6208
- var contentCorpusItems = pgTable(
6209
- "content_corpus_items",
6210
- {
6211
- id: uuid("id").primaryKey().defaultRandom(),
6212
- projectKey: text("project_key").notNull(),
6213
- siteKey: text("site_key"),
6214
- url: text("url").notNull(),
6215
- canonicalUrl: text("canonical_url"),
6216
- domain: text("domain").notNull(),
6217
- pagePath: text("page_path"),
6218
- sourceType: contentSourceType("source_type").notNull().default("own_site"),
6219
- title: text("title"),
6220
- h1: text("h1"),
6221
- headings: jsonb("headings").$type().notNull().default([]),
6222
- wordCount: integer("word_count"),
6223
- fullText: text("full_text"),
6224
- textHash: text("text_hash"),
6225
- internalLinks: jsonb("internal_links").$type().notNull().default([]),
6226
- externalLinks: jsonb("external_links").$type().notNull().default([]),
6227
- language: text("language"),
6228
- ticketNumber: text("ticket_number"),
6229
- metadata: jsonb("metadata").$type().notNull().default({}),
6230
- extractedAt: timestamp("extracted_at", { withTimezone: true }).notNull().defaultNow(),
6231
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6232
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6396
+ uniqueIndex(
6397
+ "wiki_page_evidence_page_slug_evidence_type_evidence_ref_key"
6398
+ ).on(table.pageSlug, table.evidenceType, table.evidenceRef),
6399
+ index("idx_wiki_page_evidence_created").on(table.createdAt)
6400
+ ]
6401
+ );
6402
+ pgTable(
6403
+ "wiki_page_link",
6404
+ {
6405
+ id: uuid("id").primaryKey().defaultRandom(),
6406
+ sourceSlug: text("source_slug").notNull(),
6407
+ targetSlug: text("target_slug").notNull(),
6408
+ kind: text("kind").notNull().default("wikilink"),
6409
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6233
6410
  },
6234
6411
  (table) => [
6235
- uniqueIndex("content_corpus_items_project_canonical_uidx").on(
6236
- table.projectKey,
6237
- table.canonicalUrl
6412
+ uniqueIndex("wiki_page_link_source_target_kind_key").on(
6413
+ table.sourceSlug,
6414
+ table.targetSlug,
6415
+ table.kind
6238
6416
  ),
6239
- index("content_corpus_items_domain_idx").on(table.domain),
6240
- index("content_corpus_items_project_idx").on(table.projectKey),
6241
- index("content_corpus_items_type_idx").on(table.sourceType),
6242
- index("content_corpus_items_text_hash_idx").on(table.textHash),
6243
- index("content_corpus_items_extracted_idx").on(table.extractedAt.desc())
6417
+ index("idx_wiki_page_link_source").on(table.sourceSlug),
6418
+ index("idx_wiki_page_link_target").on(table.targetSlug)
6244
6419
  ]
6245
6420
  );
6246
6421
  pgTable(
6247
- "content_quality_runs",
6422
+ "wiki_page_read",
6248
6423
  {
6249
6424
  id: uuid("id").primaryKey().defaultRandom(),
6250
- corpusItemId: uuid("corpus_item_id").references(
6251
- () => contentCorpusItems.id,
6252
- { onDelete: "set null" }
6253
- ),
6254
- contentSourceId: uuid("content_source_id").references(
6255
- () => contentSources.id,
6256
- { onDelete: "set null" }
6257
- ),
6258
- projectKey: text("project_key"),
6259
- siteKey: text("site_key"),
6260
- url: text("url"),
6261
- canonicalUrl: text("canonical_url"),
6262
- tool: text("tool").notNull(),
6263
- toolVersion: text("tool_version"),
6264
- score: integer("score"),
6265
- flags: jsonb("flags").$type().notNull().default([]),
6266
- report: jsonb("report").$type().notNull().default({}),
6267
- reportUrl: text("report_url"),
6268
- ticketNumber: text("ticket_number"),
6269
- runAt: timestamp("run_at", { withTimezone: true }).notNull().defaultNow(),
6425
+ slug: text("slug").notNull(),
6426
+ caller: text("caller"),
6427
+ includedArchive: boolean("included_archive").notNull().default(false),
6270
6428
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6271
6429
  },
6272
6430
  (table) => [
6273
- index("content_quality_runs_corpus_idx").on(table.corpusItemId),
6274
- index("content_quality_runs_source_idx").on(table.contentSourceId),
6275
- index("content_quality_runs_project_run_idx").on(
6276
- table.projectKey,
6277
- table.runAt.desc()
6278
- ),
6279
- index("content_quality_runs_url_idx").on(table.url)
6431
+ index("idx_wiki_page_read_slug").on(table.slug, table.createdAt),
6432
+ index("idx_wiki_page_read_created").on(table.createdAt)
6280
6433
  ]
6281
6434
  );
6282
6435
  pgTable(
6283
- "content_snapshots",
6436
+ "wiki_verify_event",
6284
6437
  {
6285
6438
  id: uuid("id").primaryKey().defaultRandom(),
6286
- corpusItemId: uuid("corpus_item_id").references(
6287
- () => contentCorpusItems.id,
6288
- { onDelete: "cascade" }
6289
- ),
6290
- contentSourceId: uuid("content_source_id").references(
6291
- () => contentSources.id,
6292
- { onDelete: "cascade" }
6293
- ),
6294
- kind: contentSnapshotKind("kind").notNull(),
6295
- url: text("url"),
6296
- canonicalUrl: text("canonical_url"),
6297
- domain: text("domain"),
6298
- title: text("title"),
6299
- fullText: text("full_text"),
6300
- textHash: text("text_hash"),
6301
- wordCount: integer("word_count"),
6302
- headings: jsonb("headings").$type().notNull().default([]),
6303
- internalLinks: jsonb("internal_links").$type().notNull().default([]),
6304
- metadata: jsonb("metadata").$type().notNull().default({}),
6305
- /**
6306
- * Run that captured this snapshot (managed site content sync, 2026-DASMG-042).
6307
- * Soft pointer (no Drizzle .references() to avoid a schema import cycle); the
6308
- * DB foreign key is declared in the migration.
6309
- */
6310
- ingestionRunId: uuid("ingestion_run_id"),
6311
- /** Source git commit / deploy id this snapshot was captured for, when known. */
6312
- sourceCommit: text("source_commit"),
6313
- capturedAt: timestamp("captured_at", { withTimezone: true }).notNull().defaultNow(),
6439
+ pageSlug: text("page_slug").notNull(),
6440
+ outcome: text("outcome").notNull(),
6441
+ runMode: text("run_mode"),
6442
+ note: text("note"),
6314
6443
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6315
6444
  },
6316
6445
  (table) => [
6317
- index("content_snapshots_corpus_idx").on(
6318
- table.corpusItemId,
6319
- table.capturedAt.desc()
6320
- ),
6321
- index("content_snapshots_source_idx").on(
6322
- table.contentSourceId,
6323
- table.capturedAt.desc()
6324
- ),
6325
- index("content_snapshots_text_hash_idx").on(table.textHash),
6326
- index("content_snapshots_ingestion_run_idx").on(table.ingestionRunId),
6327
- uniqueIndex("content_snapshots_corpus_hash_uidx").on(
6328
- table.corpusItemId,
6329
- table.textHash
6330
- ),
6331
- uniqueIndex("content_snapshots_source_hash_uidx").on(
6332
- table.contentSourceId,
6333
- table.textHash
6446
+ index("idx_wiki_verify_event_created").on(table.createdAt),
6447
+ index("idx_wiki_verify_event_outcome_created").on(
6448
+ table.outcome,
6449
+ table.createdAt
6334
6450
  )
6335
6451
  ]
6336
6452
  );
6337
6453
  pgTable(
6338
- "content_site_candidates",
6454
+ "wiki_invariant_violation",
6339
6455
  {
6340
6456
  id: uuid("id").primaryKey().defaultRandom(),
6341
- contentSourceId: uuid("content_source_id").references(
6342
- () => contentSources.id,
6343
- { onDelete: "cascade" }
6344
- ),
6345
- packId: uuid("pack_id").references(() => contentEvidencePacks.id, {
6346
- onDelete: "cascade"
6347
- }),
6348
- /** Managed site (2026-DASMG-042) when known; soft pointer, no FK to avoid a schema cycle. */
6349
- managedSiteId: uuid("managed_site_id"),
6350
- projectKey: text("project_key"),
6351
- siteKey: text("site_key"),
6352
- domain: text("domain"),
6353
- status: contentCandidateStatus("status").notNull().default("candidate"),
6354
- reason: text("reason"),
6355
- score: integer("score"),
6356
- /** The corpus page produced from this content, set when status becomes used. */
6357
- usedCorpusItemId: uuid("used_corpus_item_id").references(
6358
- () => contentCorpusItems.id,
6359
- { onDelete: "set null" }
6360
- ),
6361
- usedUrl: text("used_url"),
6362
- usedAt: timestamp("used_at", { withTimezone: true }),
6363
- ticketNumber: text("ticket_number"),
6364
- metadata: jsonb("metadata").$type().notNull().default({}),
6457
+ slug: text("slug").notNull(),
6458
+ code: text("code").notNull(),
6459
+ severity: text("severity").notNull().default("warning"),
6460
+ detail: text("detail").notNull().default(""),
6461
+ suggestedFix: text("suggested_fix"),
6462
+ suggestedSlug: text("suggested_slug"),
6463
+ occurrenceCount: integer("occurrence_count").notNull().default(1),
6464
+ reopenCount: integer("reopen_count").notNull().default(0),
6465
+ firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull().defaultNow(),
6466
+ lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
6467
+ resolvedAt: timestamp("resolved_at", { withTimezone: true }),
6365
6468
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6366
6469
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6367
6470
  },
6368
6471
  (table) => [
6369
- index("content_site_candidates_source_idx").on(table.contentSourceId),
6370
- index("content_site_candidates_pack_idx").on(table.packId),
6371
- index("content_site_candidates_managed_site_idx").on(table.managedSiteId),
6372
- index("content_site_candidates_project_idx").on(table.projectKey),
6373
- index("content_site_candidates_status_idx").on(table.status),
6374
- uniqueIndex("content_site_candidates_source_site_uidx").on(table.contentSourceId, table.managedSiteId).where(
6375
- sql`content_source_id IS NOT NULL AND managed_site_id IS NOT NULL`
6376
- ),
6377
- uniqueIndex("content_site_candidates_pack_site_uidx").on(table.packId, table.managedSiteId).where(sql`pack_id IS NOT NULL AND managed_site_id IS NOT NULL`),
6378
- check(
6379
- "content_site_candidates_subject_chk",
6380
- sql`content_source_id IS NOT NULL OR pack_id IS NOT NULL`
6472
+ uniqueIndex("wiki_invariant_violation_slug_code_key").on(
6473
+ table.slug,
6474
+ table.code
6381
6475
  ),
6382
- check(
6383
- "content_site_candidates_target_chk",
6384
- sql`managed_site_id IS NOT NULL OR project_key IS NOT NULL OR domain IS NOT NULL`
6476
+ index("idx_wiki_invariant_violation_severity").on(
6477
+ table.severity,
6478
+ table.resolvedAt
6385
6479
  )
6386
6480
  ]
6387
6481
  );
6388
- var managedServerOs = pgEnum("managed_server_os", [
6389
- "linux",
6390
- "windows",
6391
- "unknown"
6392
- ]);
6393
- var serverAgentStatus = pgEnum("server_agent_status", [
6394
- "not_installed",
6395
- "installing",
6396
- "online",
6397
- "degraded",
6398
- "offline",
6399
- "error"
6400
- ]);
6401
- var resourceKind = pgEnum("resource_kind", [
6402
- "wordpress",
6403
- "prestashop",
6404
- "postgres_cluster",
6405
- "postgres_database",
6406
- "mysql_database",
6407
- "mariadb_database",
6408
- "boiler_project",
6409
- "boiler_web_project",
6410
- "static_site",
6411
- "docker_app",
6412
- "domain",
6413
- "backup_policy",
6414
- "server_service"
6415
- ]);
6416
- var resourceStatus = pgEnum("resource_status", [
6417
- "unknown",
6418
- "healthy",
6419
- "degraded",
6420
- "failed",
6421
- "missing",
6422
- "provisioning",
6423
- "disabled"
6424
- ]);
6425
- var resourceOwnership = pgEnum("resource_ownership", [
6426
- "managed",
6427
- "discovered",
6428
- "external"
6429
- ]);
6430
- var operationStatus = pgEnum("operation_status", [
6431
- "queued",
6432
- "running",
6433
- "succeeded",
6434
- "failed",
6435
- "cancelled"
6436
- ]);
6437
- var alertSeverity = pgEnum("alert_severity", [
6438
- "info",
6439
- "warning",
6440
- "critical"
6441
- ]);
6442
- var managedServer = pgTable(
6443
- "managed_server",
6482
+ pgTable(
6483
+ "wiki_pipeline_rule",
6444
6484
  {
6445
6485
  id: uuid("id").primaryKey().defaultRandom(),
6446
- name: text("name").notNull(),
6447
- hostname: text("hostname").notNull(),
6448
- port: integer("port").notNull().default(22),
6449
- username: text("username").notNull(),
6450
- authMethod: text("auth_method").notNull().default("ssh_key"),
6451
- os: managedServerOs("os").notNull().default("unknown"),
6452
- provider: text("provider"),
6453
- region: text("region"),
6454
- tags: jsonb("tags").$type().notNull().default([]),
6455
- agentStatus: serverAgentStatus("agent_status").notNull().default("not_installed"),
6456
- agentVersion: text("agent_version"),
6457
- lastSeenAt: timestamp("last_seen_at", { withTimezone: true }),
6486
+ scope: text("scope").notNull(),
6487
+ ruleKey: text("rule_key").notNull(),
6488
+ text: text("text").notNull(),
6489
+ /** A = deterministic auto-applied, B = judgement, gated by insight accept. */
6490
+ tier: text("tier").notNull().default("B"),
6491
+ status: text("status").notNull().default("proposed"),
6492
+ sourceCode: text("source_code"),
6493
+ evidenceRefs: text("evidence_refs").array().notNull().default([]),
6494
+ violationCount: integer("violation_count").notNull().default(0),
6495
+ lastViolationAt: timestamp("last_violation_at", { withTimezone: true }),
6496
+ activatedAt: timestamp("activated_at", { withTimezone: true }),
6497
+ retiredAt: timestamp("retired_at", { withTimezone: true }),
6498
+ retiredReason: text("retired_reason"),
6499
+ rejectedAt: timestamp("rejected_at", { withTimezone: true }),
6500
+ rejectedReason: text("rejected_reason"),
6501
+ approvedBy: text("approved_by"),
6458
6502
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6459
6503
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6460
6504
  },
6461
6505
  (table) => [
6462
- uniqueIndex("managed_server_hostname_port_uidx").on(
6463
- table.hostname,
6464
- table.port
6465
- ),
6466
- index("managed_server_agent_status_idx").on(table.agentStatus)
6506
+ uniqueIndex("wiki_pipeline_rule_scope_key_key").on(
6507
+ table.scope,
6508
+ table.ruleKey
6509
+ )
6467
6510
  ]
6468
6511
  );
6469
- pgTable("server_credential", {
6470
- serverId: uuid("server_id").primaryKey().references(() => managedServer.id, { onDelete: "cascade" }),
6471
- passwordEncrypted: text("password_encrypted"),
6472
- sshKeyEncrypted: text("ssh_key_encrypted"),
6473
- sshKeyPassphraseEncrypted: text("ssh_key_passphrase_encrypted"),
6474
- dbRootPasswordEncrypted: text("db_root_password_encrypted"),
6475
- monitoringApiKeyEncrypted: text("monitoring_api_key_encrypted"),
6476
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6477
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6478
- });
6479
6512
  pgTable(
6480
- "server_connection_log",
6513
+ "wiki_search_log",
6481
6514
  {
6482
6515
  id: uuid("id").primaryKey().defaultRandom(),
6483
- serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
6484
- userId: uuid("user_id").notNull(),
6485
- connectedAt: timestamp("connected_at", { withTimezone: true }).notNull().defaultNow(),
6486
- disconnectedAt: timestamp("disconnected_at", { withTimezone: true }),
6487
- durationSeconds: integer("duration_seconds"),
6488
- status: text("status").notNull(),
6489
- errorMessage: text("error_message"),
6490
- clientIp: text("client_ip"),
6491
- terminalCols: integer("terminal_cols"),
6492
- terminalRows: integer("terminal_rows"),
6493
- outputSizeBytes: bigint("output_size_bytes", { mode: "number" }).notNull().default(0),
6494
- inputSizeBytes: bigint("input_size_bytes", { mode: "number" }).notNull().default(0),
6495
- commandCount: integer("command_count").notNull().default(0),
6496
- connectionType: text("connection_type").notNull().default("terminal"),
6497
- action: text("action"),
6498
- rawOutput: text("raw_output"),
6516
+ query: text("query").notNull(),
6517
+ hitCount: integer("hit_count").notNull().default(0),
6518
+ topScore: real("top_score"),
6519
+ topSlug: text("top_slug"),
6520
+ caller: text("caller"),
6499
6521
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6500
6522
  },
6501
- (table) => [
6502
- index("server_connection_log_server_idx").on(table.serverId),
6503
- index("server_connection_log_user_idx").on(table.userId),
6504
- index("server_connection_log_connected_idx").on(table.connectedAt),
6505
- index("server_connection_log_status_idx").on(table.status)
6506
- ]
6523
+ (table) => [index("idx_wiki_search_log_created").on(table.createdAt)]
6507
6524
  );
6508
6525
  pgTable(
6509
- "mcp_audit_log",
6526
+ "team_memory_search_log",
6510
6527
  {
6511
6528
  id: uuid("id").primaryKey().defaultRandom(),
6512
- apiKeyId: uuid("api_key_id"),
6513
- userId: uuid("user_id"),
6514
- toolName: text("tool_name"),
6515
- arguments: jsonb("arguments"),
6516
- ipAddress: text("ip_address"),
6517
- serverId: uuid("server_id"),
6518
- resultStatus: text("result_status").notNull().default("success"),
6519
- errorMessage: text("error_message"),
6520
- durationMs: integer("duration_ms"),
6529
+ query: text("query").notNull(),
6530
+ hitCount: integer("hit_count").notNull().default(0),
6531
+ topScore: real("top_score"),
6532
+ topId: text("top_id"),
6533
+ caller: text("caller"),
6534
+ repo: text("repo"),
6535
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6536
+ },
6537
+ (table) => [index("idx_team_memory_search_log_created").on(table.createdAt)]
6538
+ );
6539
+ pgTable(
6540
+ "wiki_agent_run",
6541
+ {
6542
+ id: uuid("id").primaryKey().defaultRandom(),
6543
+ phase: text("phase").notNull(),
6544
+ model: text("model"),
6545
+ claimed: jsonb("claimed").$type(),
6546
+ proven: jsonb("proven").$type(),
6547
+ violationCount: integer("violation_count").notNull().default(0),
6548
+ violations: jsonb("violations").$type(),
6549
+ outcome: text("outcome").notNull().default("ok"),
6550
+ agentId: text("agent_id"),
6551
+ runId: text("run_id"),
6552
+ passId: text("pass_id"),
6553
+ changedPaths: jsonb("changed_paths").$type(),
6554
+ summary: text("summary"),
6555
+ userVerdict: text("user_verdict"),
6556
+ userVerdictNote: text("user_verdict_note"),
6557
+ verdictAt: timestamp("verdict_at", { withTimezone: true }),
6558
+ reviewed: boolean("reviewed").notNull().default(false),
6559
+ reviewNotes: text("review_notes"),
6560
+ reviewedAt: timestamp("reviewed_at", { withTimezone: true }),
6561
+ startedAt: timestamp("started_at", { withTimezone: true }),
6562
+ finishedAt: timestamp("finished_at", { withTimezone: true }).notNull().defaultNow(),
6521
6563
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6522
6564
  },
6523
6565
  (table) => [
6524
- index("mcp_audit_log_created_at_idx").on(table.createdAt),
6525
- index("mcp_audit_log_user_idx").on(table.userId),
6526
- index("mcp_audit_log_tool_name_idx").on(table.toolName),
6527
- index("mcp_audit_log_server_idx").on(table.serverId),
6528
- index("mcp_audit_log_api_key_idx").on(table.apiKeyId),
6529
- index("mcp_audit_log_result_status_idx").on(table.resultStatus)
6566
+ index("idx_wiki_agent_run_phase_created").on(table.phase, table.createdAt),
6567
+ index("idx_wiki_agent_run_created").on(table.createdAt),
6568
+ index("idx_wiki_agent_run_reviewed").on(table.reviewed),
6569
+ index("idx_wiki_agent_run_user_verdict").on(table.userVerdict),
6570
+ index("idx_wiki_agent_run_pass_id").on(table.passId)
6530
6571
  ]
6531
6572
  );
6532
- var resource = pgTable(
6533
- "resource",
6573
+ pgTable(
6574
+ "wiki_agent_review_suggestion",
6534
6575
  {
6535
6576
  id: uuid("id").primaryKey().defaultRandom(),
6536
- serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
6537
- parentResourceId: uuid("parent_resource_id"),
6538
- kind: resourceKind("kind").notNull(),
6539
- slug: text("slug").notNull(),
6540
- displayName: text("display_name").notNull(),
6541
- spec: jsonb("spec").$type().notNull().default({}),
6542
- state: jsonb("state").$type().notNull().default({}),
6543
- status: resourceStatus("status").notNull().default("unknown"),
6544
- ownership: resourceOwnership("ownership").notNull().default("managed"),
6545
- lastDetectedAt: timestamp("last_detected_at", { withTimezone: true }),
6546
- lastEventId: uuid("last_event_id"),
6577
+ status: text("status").notNull().default("proposed"),
6578
+ implementationClass: text("implementation_class").notNull(),
6579
+ phase: text("phase"),
6580
+ scope: text("scope"),
6581
+ title: text("title").notNull(),
6582
+ summary: text("summary").notNull(),
6583
+ targetFile: text("target_file"),
6584
+ targetKey: text("target_key"),
6585
+ beforeText: text("before_text"),
6586
+ afterText: text("after_text"),
6587
+ evidenceRunIds: uuid("evidence_run_ids").array().notNull().default([]),
6588
+ evidenceRefs: text("evidence_refs").array().notNull().default([]),
6589
+ dedupeKey: text("dedupe_key").notNull(),
6590
+ source: text("source").notNull().default("auto"),
6547
6591
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6548
6592
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6549
6593
  },
6550
6594
  (table) => [
6551
- uniqueIndex("resource_server_kind_slug_uidx").on(
6552
- table.serverId,
6553
- table.kind,
6554
- table.slug
6555
- ),
6556
- index("resource_server_idx").on(table.serverId),
6557
- index("resource_parent_idx").on(table.parentResourceId),
6558
- index("resource_kind_status_idx").on(table.kind, table.status)
6595
+ uniqueIndex("wiki_agent_review_suggestion_dedupe_key").on(table.dedupeKey),
6596
+ index("idx_wiki_agent_review_suggestion_status_created").on(
6597
+ table.status,
6598
+ table.createdAt
6599
+ )
6559
6600
  ]
6560
6601
  );
6602
+
6603
+ // ../db/src/schema/agent-memory.ts
6561
6604
  pgTable(
6562
- "resource_event",
6605
+ "agent_memory",
6563
6606
  {
6564
6607
  id: uuid("id").primaryKey().defaultRandom(),
6565
- resourceId: uuid("resource_id").notNull().references(() => resource.id, { onDelete: "cascade" }),
6566
- kind: text("kind").notNull(),
6567
- before: jsonb("before").$type(),
6568
- after: jsonb("after").$type(),
6569
- actorUserId: text("actor_user_id"),
6570
- operationId: uuid("operation_id"),
6571
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6608
+ conversationId: uuid("conversation_id"),
6609
+ localConversationId: text("local_conversation_id"),
6610
+ source: text("source").notNull().default("mirror"),
6611
+ userId: text("user_id"),
6612
+ hostId: uuid("host_id"),
6613
+ workspaceId: uuid("workspace_id"),
6614
+ gitRepository: text("git_repository"),
6615
+ title: text("title").notNull().default(""),
6616
+ summary: text("summary"),
6617
+ content: text("content").notNull().default(""),
6618
+ contentHash: text("content_hash"),
6619
+ embedding: real("embedding").array(),
6620
+ embeddingVec: vector("embedding_vec", {
6621
+ dimensions: WIKI_EMBEDDING_DIMENSIONS
6622
+ }),
6623
+ embeddingModel: text("embedding_model"),
6624
+ messageCount: integer("message_count").notNull().default(0),
6625
+ startedAt: timestamp("started_at", { withTimezone: true }),
6626
+ lastMessageAt: timestamp("last_message_at", { withTimezone: true }),
6627
+ embeddedAt: timestamp("embedded_at", { withTimezone: true }),
6628
+ resolved: boolean("resolved"),
6629
+ category: text("category"),
6630
+ tech: text("tech").array(),
6631
+ kind: text("kind").notNull().default("human"),
6632
+ symbols: text("symbols").array(),
6633
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6634
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6572
6635
  },
6573
6636
  (table) => [
6574
- index("resource_event_resource_created_idx").on(
6575
- table.resourceId,
6576
- table.createdAt
6637
+ uniqueIndex("uq_agent_memory_local_conversation").on(
6638
+ table.localConversationId
6577
6639
  ),
6578
- index("resource_event_operation_idx").on(table.operationId)
6640
+ index("idx_agent_memory_repo").on(table.gitRepository),
6641
+ index("idx_agent_memory_user").on(table.userId),
6642
+ index("idx_agent_memory_kind").on(table.kind)
6579
6643
  ]
6580
6644
  );
6581
- var operation = pgTable(
6582
- "operation",
6645
+ var contentSourceType = pgEnum("content_source_type", [
6646
+ "own_site",
6647
+ "client_site",
6648
+ "external_source",
6649
+ "competitor",
6650
+ "news",
6651
+ "government",
6652
+ "supplier",
6653
+ "unknown"
6654
+ ]);
6655
+ var contentSnapshotKind = pgEnum("content_snapshot_kind", [
6656
+ "corpus",
6657
+ "source"
6658
+ ]);
6659
+ var contentCandidateStatus = pgEnum("content_candidate_status", [
6660
+ "candidate",
6661
+ "used",
6662
+ "rejected"
6663
+ ]);
6664
+ var contentSources = pgTable(
6665
+ "content_sources",
6583
6666
  {
6584
6667
  id: uuid("id").primaryKey().defaultRandom(),
6585
- kind: text("kind").notNull(),
6586
- resourceId: uuid("resource_id").references(() => resource.id, {
6587
- onDelete: "set null"
6588
- }),
6589
- serverId: uuid("server_id").references(() => managedServer.id, {
6590
- onDelete: "set null"
6591
- }),
6592
- status: operationStatus("status").notNull().default("queued"),
6593
- steps: jsonb("steps").$type().notNull().default([]),
6594
- idempotencyKey: text("idempotency_key").notNull(),
6595
- triggerRunId: text("trigger_run_id"),
6596
- logsR2Key: text("logs_r2_key"),
6597
- logs: text("logs"),
6598
- startedBy: text("started_by"),
6599
- startedAt: timestamp("started_at", { withTimezone: true }),
6600
- endedAt: timestamp("ended_at", { withTimezone: true }),
6668
+ url: text("url").notNull(),
6669
+ canonicalUrl: text("canonical_url"),
6670
+ domain: text("domain").notNull(),
6671
+ sourceType: contentSourceType("source_type").notNull().default("unknown"),
6672
+ title: text("title"),
6673
+ author: text("author"),
6674
+ publishedAt: timestamp("published_at", { withTimezone: true }),
6675
+ extractionMethod: text("extraction_method"),
6676
+ summary: text("summary"),
6677
+ facts: jsonb("facts").$type().notNull().default([]),
6678
+ limitations: jsonb("limitations").$type().notNull().default([]),
6679
+ fullText: text("full_text"),
6680
+ textHash: text("text_hash"),
6681
+ wordCount: integer("word_count"),
6682
+ language: text("language"),
6683
+ projectKey: text("project_key"),
6684
+ siteKey: text("site_key"),
6685
+ ticketNumber: text("ticket_number"),
6686
+ metadata: jsonb("metadata").$type().notNull().default({}),
6687
+ fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull().defaultNow(),
6688
+ lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
6601
6689
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6602
6690
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6603
6691
  },
6604
6692
  (table) => [
6605
- uniqueIndex("operation_idempotency_uidx").on(table.idempotencyKey),
6606
- index("operation_resource_idx").on(table.resourceId),
6607
- index("operation_server_idx").on(table.serverId),
6608
- index("operation_status_created_idx").on(table.status, table.createdAt)
6693
+ uniqueIndex("content_sources_canonical_url_uidx").on(table.canonicalUrl),
6694
+ index("content_sources_domain_idx").on(table.domain),
6695
+ index("content_sources_type_idx").on(table.sourceType),
6696
+ index("content_sources_text_hash_idx").on(table.textHash),
6697
+ index("content_sources_project_idx").on(table.projectKey),
6698
+ index("content_sources_ticket_idx").on(table.ticketNumber),
6699
+ index("content_sources_last_seen_idx").on(table.lastSeenAt.desc())
6609
6700
  ]
6610
6701
  );
6611
- pgTable(
6612
- "agent_installation",
6702
+ var contentEvidencePacks = pgTable(
6703
+ "content_evidence_packs",
6613
6704
  {
6614
6705
  id: uuid("id").primaryKey().defaultRandom(),
6615
- serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
6616
- agentId: text("agent_id"),
6617
- version: text("version").notNull(),
6618
- installPath: text("install_path").notNull().default("/usr/local/bin/mg-agent"),
6619
- configHash: text("config_hash").notNull(),
6620
- status: serverAgentStatus("status").notNull().default("installing"),
6621
- lastHeartbeatAt: timestamp("last_heartbeat_at", { withTimezone: true }),
6622
- lastError: text("last_error"),
6706
+ topic: text("topic"),
6707
+ locale: text("locale"),
6708
+ projectKey: text("project_key"),
6709
+ siteKey: text("site_key"),
6710
+ ticketNumber: text("ticket_number"),
6711
+ generatedAt: timestamp("generated_at", { withTimezone: true }).notNull().defaultNow(),
6712
+ sources: jsonb("sources").$type().notNull().default([]),
6713
+ verifiedFacts: jsonb("verified_facts").$type().notNull().default([]),
6714
+ conflicts: jsonb("conflicts").$type().notNull().default([]),
6715
+ uniqueClaims: jsonb("unique_claims").$type().notNull().default([]),
6716
+ openQuestions: jsonb("open_questions").$type().notNull().default([]),
6717
+ verbatimWarnings: jsonb("verbatim_warnings").$type().notNull().default([]),
6718
+ limitations: jsonb("limitations").$type().notNull().default([]),
6719
+ /**
6720
+ * Order-independent signature of topic + canonical URL set (packSignature).
6721
+ * Lets the reuse-first save path return a fresh stored pack without
6722
+ * re-researching when the same urls/topic are shared again.
6723
+ */
6724
+ sourceSignature: text("source_signature"),
6725
+ metadata: jsonb("metadata").$type().notNull().default({}),
6623
6726
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6624
6727
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6625
6728
  },
6626
6729
  (table) => [
6627
- uniqueIndex("agent_installation_server_uidx").on(table.serverId),
6628
- index("agent_installation_status_idx").on(table.status)
6730
+ index("content_evidence_packs_project_idx").on(table.projectKey),
6731
+ index("content_evidence_packs_ticket_idx").on(table.ticketNumber),
6732
+ index("content_evidence_packs_generated_idx").on(table.generatedAt.desc()),
6733
+ index("content_evidence_packs_signature_idx").on(table.sourceSignature)
6629
6734
  ]
6630
6735
  );
6631
6736
  pgTable(
6632
- "monitoring_sample",
6737
+ "content_evidence_pack_source",
6633
6738
  {
6634
6739
  id: uuid("id").primaryKey().defaultRandom(),
6635
- serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
6636
- resourceId: uuid("resource_id").references(() => resource.id, {
6637
- onDelete: "cascade"
6638
- }),
6639
- metric: text("metric").notNull(),
6640
- value: integer("value").notNull(),
6641
- unit: text("unit").notNull(),
6642
- tags: jsonb("tags").$type().notNull().default({}),
6643
- sampledAt: timestamp("sampled_at", { withTimezone: true }).notNull().defaultNow()
6740
+ packId: uuid("pack_id").notNull().references(() => contentEvidencePacks.id, { onDelete: "cascade" }),
6741
+ contentSourceId: uuid("content_source_id").notNull().references(() => contentSources.id, { onDelete: "cascade" }),
6742
+ packSourceRef: text("pack_source_ref"),
6743
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6644
6744
  },
6645
6745
  (table) => [
6646
- index("monitoring_sample_server_metric_time_idx").on(
6647
- table.serverId,
6648
- table.metric,
6649
- table.sampledAt
6650
- ),
6651
- // Descending on sampled_at so "latest metric per (server, metric)" lookups
6652
- // (DISTINCT ON ... ORDER BY sampled_at DESC) are served by an index-only
6653
- // scan instead of a full-table sort. The migration also adds
6654
- // INCLUDE (value, unit) to make it covering for the overview snapshot query
6655
- // (Drizzle 0.44 can't express INCLUDE here). See migration 20260529210000.
6656
- index("monitoring_sample_server_metric_time_desc_idx").on(
6657
- table.serverId,
6658
- table.metric,
6659
- table.sampledAt.desc()
6660
- ),
6661
- index("monitoring_sample_resource_metric_time_idx").on(
6662
- table.resourceId,
6663
- table.metric,
6664
- table.sampledAt
6746
+ uniqueIndex("content_evidence_pack_source_uidx").on(
6747
+ table.packId,
6748
+ table.contentSourceId
6665
6749
  ),
6666
- // Retention cleanup (monitoring-sample-retention-cleanup) filters only on
6667
- // sampled_at. Without this index Postgres seq-scans the full firehose.
6668
- // Live DB name uses idx_ prefix (see migration 20260731070000).
6669
- index("idx_monitoring_sample_sampled_at").on(table.sampledAt)
6750
+ index("content_evidence_pack_source_source_idx").on(table.contentSourceId)
6670
6751
  ]
6671
6752
  );
6672
- var appLogSource = pgTable(
6673
- "app_log_source",
6753
+ var contentCorpusItems = pgTable(
6754
+ "content_corpus_items",
6674
6755
  {
6675
6756
  id: uuid("id").primaryKey().defaultRandom(),
6676
- serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
6677
- resourceId: uuid("resource_id").references(() => resource.id, {
6678
- onDelete: "set null"
6679
- }),
6680
- sourceType: text("source_type").notNull(),
6681
- sourceKey: text("source_key").notNull(),
6682
- displayName: text("display_name").notNull(),
6683
- path: text("path").notNull(),
6684
- enabled: boolean("enabled").notNull().default(true),
6685
- mutedUntil: timestamp("muted_until", { withTimezone: true }),
6757
+ projectKey: text("project_key").notNull(),
6758
+ siteKey: text("site_key"),
6759
+ url: text("url").notNull(),
6760
+ canonicalUrl: text("canonical_url"),
6761
+ domain: text("domain").notNull(),
6762
+ pagePath: text("page_path"),
6763
+ sourceType: contentSourceType("source_type").notNull().default("own_site"),
6764
+ title: text("title"),
6765
+ h1: text("h1"),
6766
+ headings: jsonb("headings").$type().notNull().default([]),
6767
+ wordCount: integer("word_count"),
6768
+ fullText: text("full_text"),
6769
+ textHash: text("text_hash"),
6770
+ internalLinks: jsonb("internal_links").$type().notNull().default([]),
6771
+ externalLinks: jsonb("external_links").$type().notNull().default([]),
6772
+ language: text("language"),
6773
+ ticketNumber: text("ticket_number"),
6686
6774
  metadata: jsonb("metadata").$type().notNull().default({}),
6687
- lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
6775
+ extractedAt: timestamp("extracted_at", { withTimezone: true }).notNull().defaultNow(),
6688
6776
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6689
6777
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6690
6778
  },
6691
6779
  (table) => [
6692
- uniqueIndex("app_log_source_server_type_key_uidx").on(
6693
- table.serverId,
6694
- table.sourceType,
6695
- table.sourceKey
6780
+ uniqueIndex("content_corpus_items_project_canonical_uidx").on(
6781
+ table.projectKey,
6782
+ table.canonicalUrl
6696
6783
  ),
6697
- index("app_log_source_server_idx").on(table.serverId),
6698
- index("app_log_source_resource_idx").on(table.resourceId),
6699
- index("app_log_source_type_seen_idx").on(
6700
- table.sourceType,
6701
- table.lastSeenAt
6702
- )
6784
+ index("content_corpus_items_domain_idx").on(table.domain),
6785
+ index("content_corpus_items_project_idx").on(table.projectKey),
6786
+ index("content_corpus_items_type_idx").on(table.sourceType),
6787
+ index("content_corpus_items_text_hash_idx").on(table.textHash),
6788
+ index("content_corpus_items_extracted_idx").on(table.extractedAt.desc())
6703
6789
  ]
6704
6790
  );
6705
6791
  pgTable(
6706
- "app_error_event",
6792
+ "content_quality_runs",
6707
6793
  {
6708
6794
  id: uuid("id").primaryKey().defaultRandom(),
6709
- serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
6710
- resourceId: uuid("resource_id").references(() => resource.id, {
6711
- onDelete: "set null"
6712
- }),
6713
- logSourceId: uuid("log_source_id").references(() => appLogSource.id, {
6714
- onDelete: "set null"
6715
- }),
6716
- fingerprint: text("fingerprint").notNull(),
6717
- severity: text("severity").notNull().default("critical"),
6718
- category: text("category").notNull(),
6719
- sourceType: text("source_type").notNull(),
6720
- sourcePath: text("source_path").notNull(),
6721
- sampleMessage: text("sample_message").notNull(),
6722
- sampleLines: jsonb("sample_lines").$type().notNull().default([]),
6723
- signals: jsonb("signals").$type().notNull().default({}),
6724
- occurrences: integer("occurrences").notNull().default(1),
6725
- firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull().defaultNow(),
6726
- lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
6795
+ corpusItemId: uuid("corpus_item_id").references(
6796
+ () => contentCorpusItems.id,
6797
+ { onDelete: "set null" }
6798
+ ),
6799
+ contentSourceId: uuid("content_source_id").references(
6800
+ () => contentSources.id,
6801
+ { onDelete: "set null" }
6802
+ ),
6803
+ projectKey: text("project_key"),
6804
+ siteKey: text("site_key"),
6805
+ url: text("url"),
6806
+ canonicalUrl: text("canonical_url"),
6807
+ tool: text("tool").notNull(),
6808
+ toolVersion: text("tool_version"),
6809
+ score: integer("score"),
6810
+ flags: jsonb("flags").$type().notNull().default([]),
6811
+ report: jsonb("report").$type().notNull().default({}),
6812
+ reportUrl: text("report_url"),
6813
+ ticketNumber: text("ticket_number"),
6814
+ runAt: timestamp("run_at", { withTimezone: true }).notNull().defaultNow(),
6727
6815
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6728
6816
  },
6729
6817
  (table) => [
6730
- index("app_error_event_fingerprint_created_idx").on(
6731
- table.fingerprint,
6732
- table.createdAt
6733
- ),
6734
- index("app_error_event_server_created_idx").on(
6735
- table.serverId,
6736
- table.createdAt
6737
- ),
6738
- index("app_error_event_resource_created_idx").on(
6739
- table.resourceId,
6740
- table.createdAt
6818
+ index("content_quality_runs_corpus_idx").on(table.corpusItemId),
6819
+ index("content_quality_runs_source_idx").on(table.contentSourceId),
6820
+ index("content_quality_runs_project_run_idx").on(
6821
+ table.projectKey,
6822
+ table.runAt.desc()
6741
6823
  ),
6742
- index("app_error_event_severity_created_idx").on(
6743
- table.severity,
6744
- table.createdAt
6745
- )
6824
+ index("content_quality_runs_url_idx").on(table.url)
6746
6825
  ]
6747
6826
  );
6748
6827
  pgTable(
6749
- "app_error_alert",
6828
+ "content_snapshots",
6750
6829
  {
6751
6830
  id: uuid("id").primaryKey().defaultRandom(),
6752
- fingerprint: text("fingerprint").notNull(),
6753
- serverId: uuid("server_id").notNull().references(() => managedServer.id, { onDelete: "cascade" }),
6754
- resourceId: uuid("resource_id").references(() => resource.id, {
6755
- onDelete: "set null"
6756
- }),
6757
- logSourceId: uuid("log_source_id").references(() => appLogSource.id, {
6758
- onDelete: "set null"
6759
- }),
6760
- severity: text("severity").notNull().default("critical"),
6761
- status: text("status").notNull().default("open"),
6762
- category: text("category").notNull(),
6763
- title: text("title").notNull(),
6764
- lastError: text("last_error").notNull(),
6765
- sampleLines: jsonb("sample_lines").$type().notNull().default([]),
6766
- signals: jsonb("signals").$type().notNull().default({}),
6767
- failureCount: integer("failure_count").notNull().default(1),
6768
- firstSeenAt: timestamp("first_seen_at", { withTimezone: true }).notNull().defaultNow(),
6769
- lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
6770
- lastAlertAt: timestamp("last_alert_at", { withTimezone: true }),
6771
- resolvedAt: timestamp("resolved_at", { withTimezone: true }),
6772
- mutedUntil: timestamp("muted_until", { withTimezone: true }),
6773
- cursorPrompt: text("cursor_prompt"),
6774
- telegramChatId: text("telegram_chat_id"),
6775
- telegramMessageId: bigint("telegram_message_id", { mode: "number" }),
6776
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6777
- updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6831
+ corpusItemId: uuid("corpus_item_id").references(
6832
+ () => contentCorpusItems.id,
6833
+ { onDelete: "cascade" }
6834
+ ),
6835
+ contentSourceId: uuid("content_source_id").references(
6836
+ () => contentSources.id,
6837
+ { onDelete: "cascade" }
6838
+ ),
6839
+ kind: contentSnapshotKind("kind").notNull(),
6840
+ url: text("url"),
6841
+ canonicalUrl: text("canonical_url"),
6842
+ domain: text("domain"),
6843
+ title: text("title"),
6844
+ fullText: text("full_text"),
6845
+ textHash: text("text_hash"),
6846
+ wordCount: integer("word_count"),
6847
+ headings: jsonb("headings").$type().notNull().default([]),
6848
+ internalLinks: jsonb("internal_links").$type().notNull().default([]),
6849
+ metadata: jsonb("metadata").$type().notNull().default({}),
6850
+ /**
6851
+ * Run that captured this snapshot (managed site content sync, 2026-DASMG-042).
6852
+ * Soft pointer (no Drizzle .references() to avoid a schema import cycle); the
6853
+ * DB foreign key is declared in the migration.
6854
+ */
6855
+ ingestionRunId: uuid("ingestion_run_id"),
6856
+ /** Source git commit / deploy id this snapshot was captured for, when known. */
6857
+ sourceCommit: text("source_commit"),
6858
+ capturedAt: timestamp("captured_at", { withTimezone: true }).notNull().defaultNow(),
6859
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
6778
6860
  },
6779
6861
  (table) => [
6780
- uniqueIndex("app_error_alert_fingerprint_uidx").on(table.fingerprint),
6781
- index("app_error_alert_status_seen_idx").on(
6782
- table.status,
6783
- table.lastSeenAt
6862
+ index("content_snapshots_corpus_idx").on(
6863
+ table.corpusItemId,
6864
+ table.capturedAt.desc()
6784
6865
  ),
6785
- index("app_error_alert_server_seen_idx").on(
6786
- table.serverId,
6787
- table.lastSeenAt
6866
+ index("content_snapshots_source_idx").on(
6867
+ table.contentSourceId,
6868
+ table.capturedAt.desc()
6788
6869
  ),
6789
- index("app_error_alert_resource_seen_idx").on(
6790
- table.resourceId,
6791
- table.lastSeenAt
6870
+ index("content_snapshots_text_hash_idx").on(table.textHash),
6871
+ index("content_snapshots_ingestion_run_idx").on(table.ingestionRunId),
6872
+ uniqueIndex("content_snapshots_corpus_hash_uidx").on(
6873
+ table.corpusItemId,
6874
+ table.textHash
6792
6875
  ),
6793
- index("app_error_alert_alerted_idx").on(table.lastAlertAt)
6876
+ uniqueIndex("content_snapshots_source_hash_uidx").on(
6877
+ table.contentSourceId,
6878
+ table.textHash
6879
+ )
6794
6880
  ]
6795
6881
  );
6796
6882
  pgTable(
6797
- "alert_rule",
6883
+ "content_site_candidates",
6798
6884
  {
6799
6885
  id: uuid("id").primaryKey().defaultRandom(),
6800
- serverId: uuid("server_id").references(() => managedServer.id, {
6801
- onDelete: "cascade"
6802
- }),
6803
- resourceId: uuid("resource_id").references(() => resource.id, {
6886
+ contentSourceId: uuid("content_source_id").references(
6887
+ () => contentSources.id,
6888
+ { onDelete: "cascade" }
6889
+ ),
6890
+ packId: uuid("pack_id").references(() => contentEvidencePacks.id, {
6804
6891
  onDelete: "cascade"
6805
6892
  }),
6806
- metric: text("metric").notNull(),
6807
- expression: text("expression").notNull(),
6808
- severity: alertSeverity("severity").notNull().default("warning"),
6809
- cooldownSeconds: integer("cooldown_seconds").notNull().default(900),
6810
- enabled: boolean("enabled").notNull().default(true),
6811
- routes: jsonb("routes").$type().notNull().default({}),
6893
+ /** Managed site (2026-DASMG-042) when known; soft pointer, no FK to avoid a schema cycle. */
6894
+ managedSiteId: uuid("managed_site_id"),
6895
+ projectKey: text("project_key"),
6896
+ siteKey: text("site_key"),
6897
+ domain: text("domain"),
6898
+ status: contentCandidateStatus("status").notNull().default("candidate"),
6899
+ reason: text("reason"),
6900
+ score: integer("score"),
6901
+ /** The corpus page produced from this content, set when status becomes used. */
6902
+ usedCorpusItemId: uuid("used_corpus_item_id").references(
6903
+ () => contentCorpusItems.id,
6904
+ { onDelete: "set null" }
6905
+ ),
6906
+ usedUrl: text("used_url"),
6907
+ usedAt: timestamp("used_at", { withTimezone: true }),
6908
+ ticketNumber: text("ticket_number"),
6909
+ metadata: jsonb("metadata").$type().notNull().default({}),
6812
6910
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
6813
6911
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
6814
6912
  },
6815
6913
  (table) => [
6816
- index("alert_rule_server_idx").on(table.serverId),
6817
- index("alert_rule_resource_idx").on(table.resourceId),
6818
- index("alert_rule_enabled_severity_idx").on(table.enabled, table.severity)
6914
+ index("content_site_candidates_source_idx").on(table.contentSourceId),
6915
+ index("content_site_candidates_pack_idx").on(table.packId),
6916
+ index("content_site_candidates_managed_site_idx").on(table.managedSiteId),
6917
+ index("content_site_candidates_project_idx").on(table.projectKey),
6918
+ index("content_site_candidates_status_idx").on(table.status),
6919
+ uniqueIndex("content_site_candidates_source_site_uidx").on(table.contentSourceId, table.managedSiteId).where(
6920
+ sql`content_source_id IS NOT NULL AND managed_site_id IS NOT NULL`
6921
+ ),
6922
+ uniqueIndex("content_site_candidates_pack_site_uidx").on(table.packId, table.managedSiteId).where(sql`pack_id IS NOT NULL AND managed_site_id IS NOT NULL`),
6923
+ check(
6924
+ "content_site_candidates_subject_chk",
6925
+ sql`content_source_id IS NOT NULL OR pack_id IS NOT NULL`
6926
+ ),
6927
+ check(
6928
+ "content_site_candidates_target_chk",
6929
+ sql`managed_site_id IS NOT NULL OR project_key IS NOT NULL OR domain IS NOT NULL`
6930
+ )
6819
6931
  ]
6820
6932
  );
6821
6933
  var cursorSkill = pgTable(
@@ -7749,33 +7861,1188 @@ pgTable(
7749
7861
  index("idx_wiki_actor_affiliation_customer_folder").on(
7750
7862
  table.customerFolder
7751
7863
  )
7752
- ]
7753
- );
7754
- pgTable(
7755
- "intake_identity_unresolved",
7864
+ ]
7865
+ );
7866
+ pgTable(
7867
+ "intake_identity_unresolved",
7868
+ {
7869
+ id: uuid("id").primaryKey().defaultRandom(),
7870
+ identityHash: text("identity_hash").notNull(),
7871
+ kind: text("kind").notNull(),
7872
+ seenCount: integer("seen_count").notNull().default(1),
7873
+ sampleSignalIds: uuid("sample_signal_ids").array().notNull().default([]),
7874
+ sampleContext: text("sample_context").array().notNull().default([]),
7875
+ sampleSources: text("sample_sources").array().notNull().default([]),
7876
+ lastSeen: timestamp("last_seen", { withTimezone: true }).notNull().defaultNow(),
7877
+ insightFiledAt: timestamp("insight_filed_at", { withTimezone: true }),
7878
+ resolvedAt: timestamp("resolved_at", { withTimezone: true }),
7879
+ resolvedActorId: uuid("resolved_actor_id").references(() => wikiActor.id, {
7880
+ onDelete: "set null"
7881
+ }),
7882
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
7883
+ },
7884
+ (table) => [
7885
+ uniqueIndex("intake_identity_unresolved_kind_hash").on(
7886
+ table.kind,
7887
+ table.identityHash
7888
+ )
7889
+ ]
7890
+ );
7891
+ var SSH_POOL_IDLE_MS = 6e4;
7892
+ var sshPool = /* @__PURE__ */ new Map();
7893
+ function poolKey(options) {
7894
+ return `${options.hostname}:${options.port}:${options.username}`;
7895
+ }
7896
+ function dropPooled(key, entry) {
7897
+ if (sshPool.get(key) === entry)
7898
+ sshPool.delete(key);
7899
+ entry.closed = true;
7900
+ if (entry.idleTimer)
7901
+ clearTimeout(entry.idleTimer);
7902
+ try {
7903
+ entry.client.end();
7904
+ } catch {
7905
+ }
7906
+ }
7907
+ function getPooledConnection(options) {
7908
+ const key = poolKey(options);
7909
+ const existing = sshPool.get(key);
7910
+ if (existing && !existing.closed) {
7911
+ return { key, entry: existing, reused: true };
7912
+ }
7913
+ const client = new Client();
7914
+ const entry = {
7915
+ client,
7916
+ ready: new Promise((resolve, reject) => {
7917
+ client.on("ready", () => resolve());
7918
+ client.on("error", (err) => reject(err));
7919
+ }),
7920
+ inFlight: 0,
7921
+ idleTimer: null,
7922
+ closed: false
7923
+ };
7924
+ entry.ready.catch(() => {
7925
+ });
7926
+ client.on("close", () => dropPooled(key, entry));
7927
+ client.on("error", () => dropPooled(key, entry));
7928
+ client.connect({
7929
+ host: options.hostname,
7930
+ port: options.port,
7931
+ username: options.username,
7932
+ password: options.password,
7933
+ privateKey: options.privateKey,
7934
+ passphrase: options.passphrase,
7935
+ readyTimeout: options.timeout || 6e4,
7936
+ // Detect dead pooled connections between commands.
7937
+ keepaliveInterval: 15e3,
7938
+ keepaliveCountMax: 2
7939
+ });
7940
+ sshPool.set(key, entry);
7941
+ return { key, entry, reused: false };
7942
+ }
7943
+ function releasePooled(key, entry) {
7944
+ entry.inFlight = Math.max(0, entry.inFlight - 1);
7945
+ if (entry.inFlight === 0 && !entry.closed) {
7946
+ if (entry.idleTimer)
7947
+ clearTimeout(entry.idleTimer);
7948
+ entry.idleTimer = setTimeout(() => {
7949
+ if (entry.inFlight === 0)
7950
+ dropPooled(key, entry);
7951
+ }, SSH_POOL_IDLE_MS);
7952
+ entry.idleTimer.unref?.();
7953
+ }
7954
+ }
7955
+ async function executePooled(options, command, onData) {
7956
+ const attempt = (allowRetry) => {
7957
+ const { key, entry, reused } = getPooledConnection(options);
7958
+ entry.inFlight += 1;
7959
+ if (entry.idleTimer) {
7960
+ clearTimeout(entry.idleTimer);
7961
+ entry.idleTimer = null;
7962
+ }
7963
+ return new Promise((resolve) => {
7964
+ let stdout = "";
7965
+ let stderr = "";
7966
+ let resolved = false;
7967
+ let timedOut = false;
7968
+ const timeout = options.timeout || 6e4;
7969
+ const finish = (result, broken) => {
7970
+ if (resolved)
7971
+ return;
7972
+ resolved = true;
7973
+ clearTimeout(timeoutId);
7974
+ if (broken) {
7975
+ dropPooled(key, entry);
7976
+ } else {
7977
+ releasePooled(key, entry);
7978
+ }
7979
+ if (result.error && broken && reused && allowRetry && !timedOut) {
7980
+ resolve(attempt(false));
7981
+ return;
7982
+ }
7983
+ resolve(result);
7984
+ };
7985
+ const timeoutId = setTimeout(() => {
7986
+ timedOut = true;
7987
+ finish({
7988
+ stdout,
7989
+ stderr,
7990
+ exitCode: -1,
7991
+ error: new Error("SSH command timeout")
7992
+ }, true);
7993
+ }, timeout);
7994
+ entry.ready.then(() => {
7995
+ if (resolved)
7996
+ return;
7997
+ entry.client.exec(command, (err, stream) => {
7998
+ if (err) {
7999
+ finish({ stdout, stderr, exitCode: -1, error: err }, true);
8000
+ return;
8001
+ }
8002
+ stream.on("close", (code) => {
8003
+ finish({ stdout, stderr, exitCode: code || 0 }, false);
8004
+ });
8005
+ stream.on("data", (data) => {
8006
+ const chunk = data.toString();
8007
+ stdout += chunk;
8008
+ });
8009
+ stream.stderr.on("data", (data) => {
8010
+ stderr += data.toString();
8011
+ });
8012
+ });
8013
+ }).catch((err) => {
8014
+ finish({ stdout, stderr, exitCode: -1, error: err }, true);
8015
+ });
8016
+ });
8017
+ };
8018
+ return attempt(true);
8019
+ }
8020
+ async function executeSshCommand(options, command, proxy) {
8021
+ if (proxy) {
8022
+ return executeSshCommandViaProxy(proxy, options, command);
8023
+ }
8024
+ if (options.pool) {
8025
+ return executePooled(options, command);
8026
+ }
8027
+ return new Promise((resolve) => {
8028
+ const ssh2 = new Client();
8029
+ let stdout = "";
8030
+ let stderr = "";
8031
+ let exitCode = 0;
8032
+ let resolved = false;
8033
+ const timeout = options.timeout || 6e4;
8034
+ const timeoutId = setTimeout(() => {
8035
+ if (!resolved) {
8036
+ resolved = true;
8037
+ ssh2.end();
8038
+ resolve({
8039
+ stdout,
8040
+ stderr,
8041
+ exitCode: -1,
8042
+ error: new Error("SSH command timeout")
8043
+ });
8044
+ }
8045
+ }, timeout);
8046
+ ssh2.on("ready", () => {
8047
+ ssh2.exec(command, (err, stream) => {
8048
+ if (err) {
8049
+ if (!resolved) {
8050
+ resolved = true;
8051
+ clearTimeout(timeoutId);
8052
+ ssh2.end();
8053
+ resolve({
8054
+ stdout,
8055
+ stderr,
8056
+ exitCode: -1,
8057
+ error: err
8058
+ });
8059
+ }
8060
+ return;
8061
+ }
8062
+ stream.on("close", (code) => {
8063
+ if (!resolved) {
8064
+ resolved = true;
8065
+ clearTimeout(timeoutId);
8066
+ ssh2.end();
8067
+ exitCode = code || 0;
8068
+ resolve({
8069
+ stdout,
8070
+ stderr,
8071
+ exitCode
8072
+ });
8073
+ }
8074
+ });
8075
+ stream.on("data", (data) => {
8076
+ stdout += data.toString();
8077
+ });
8078
+ stream.stderr.on("data", (data) => {
8079
+ stderr += data.toString();
8080
+ });
8081
+ });
8082
+ });
8083
+ ssh2.on("error", (err) => {
8084
+ if (!resolved) {
8085
+ resolved = true;
8086
+ clearTimeout(timeoutId);
8087
+ resolve({
8088
+ stdout,
8089
+ stderr,
8090
+ exitCode: -1,
8091
+ error: err
8092
+ });
8093
+ }
8094
+ });
8095
+ ssh2.connect({
8096
+ host: options.hostname,
8097
+ port: options.port,
8098
+ username: options.username,
8099
+ password: options.password,
8100
+ privateKey: options.privateKey,
8101
+ passphrase: options.passphrase,
8102
+ readyTimeout: timeout
8103
+ });
8104
+ });
8105
+ }
8106
+ function executeSshCommandViaProxy(proxyOptions, targetOptions, command) {
8107
+ return new Promise((resolve) => {
8108
+ const proxyClient = new Client();
8109
+ let resolved = false;
8110
+ const timeout = targetOptions.timeout || 6e4;
8111
+ const timeoutId = setTimeout(() => {
8112
+ if (!resolved) {
8113
+ resolved = true;
8114
+ proxyClient.end();
8115
+ resolve({
8116
+ stdout: "",
8117
+ stderr: "",
8118
+ exitCode: -1,
8119
+ error: new Error("SSH proxy command timeout")
8120
+ });
8121
+ }
8122
+ }, timeout);
8123
+ const cleanup = () => {
8124
+ clearTimeout(timeoutId);
8125
+ proxyClient.end();
8126
+ };
8127
+ proxyClient.on("ready", () => {
8128
+ proxyClient.forwardOut("127.0.0.1", 0, targetOptions.hostname, targetOptions.port, (err, tunnel) => {
8129
+ if (err) {
8130
+ if (!resolved) {
8131
+ resolved = true;
8132
+ cleanup();
8133
+ resolve({ stdout: "", stderr: "", exitCode: -1, error: err });
8134
+ }
8135
+ return;
8136
+ }
8137
+ const targetClient = new Client();
8138
+ let stdout = "";
8139
+ let stderr = "";
8140
+ targetClient.on("ready", () => {
8141
+ targetClient.exec(command, (execErr, stream) => {
8142
+ if (execErr) {
8143
+ if (!resolved) {
8144
+ resolved = true;
8145
+ targetClient.end();
8146
+ cleanup();
8147
+ resolve({ stdout, stderr, exitCode: -1, error: execErr });
8148
+ }
8149
+ return;
8150
+ }
8151
+ stream.on("close", (code) => {
8152
+ if (!resolved) {
8153
+ resolved = true;
8154
+ targetClient.end();
8155
+ cleanup();
8156
+ resolve({ stdout, stderr, exitCode: code || 0 });
8157
+ }
8158
+ });
8159
+ stream.on("data", (data) => {
8160
+ stdout += data.toString();
8161
+ });
8162
+ stream.stderr.on("data", (data) => {
8163
+ stderr += data.toString();
8164
+ });
8165
+ });
8166
+ });
8167
+ targetClient.on("error", (targetErr) => {
8168
+ if (!resolved) {
8169
+ resolved = true;
8170
+ targetClient.end();
8171
+ cleanup();
8172
+ resolve({ stdout, stderr, exitCode: -1, error: targetErr });
8173
+ }
8174
+ });
8175
+ targetClient.connect({
8176
+ sock: tunnel,
8177
+ username: targetOptions.username,
8178
+ password: targetOptions.password,
8179
+ privateKey: targetOptions.privateKey,
8180
+ passphrase: targetOptions.passphrase,
8181
+ readyTimeout: timeout
8182
+ });
8183
+ });
8184
+ });
8185
+ proxyClient.on("error", (err) => {
8186
+ if (!resolved) {
8187
+ resolved = true;
8188
+ cleanup();
8189
+ resolve({ stdout: "", stderr: "", exitCode: -1, error: err });
8190
+ }
8191
+ });
8192
+ proxyClient.connect({
8193
+ host: proxyOptions.hostname,
8194
+ port: proxyOptions.port,
8195
+ username: proxyOptions.username,
8196
+ password: proxyOptions.password,
8197
+ privateKey: proxyOptions.privateKey,
8198
+ passphrase: proxyOptions.passphrase,
8199
+ readyTimeout: proxyOptions.timeout || 3e4
8200
+ });
8201
+ });
8202
+ }
8203
+ function serverToConnectionOptions(server2) {
8204
+ return {
8205
+ hostname: server2.hostname,
8206
+ port: server2.port,
8207
+ username: server2.username,
8208
+ password: server2.password_encrypted || void 0,
8209
+ privateKey: server2.ssh_key_encrypted || void 0,
8210
+ passphrase: server2.ssh_key_passphrase_encrypted || void 0
8211
+ };
8212
+ }
8213
+ async function appendOperationLog(operationId, message) {
8214
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}`;
8215
+ try {
8216
+ await getDb().execute(sql`
8217
+ UPDATE operation
8218
+ SET logs = CASE
8219
+ WHEN logs IS NULL OR logs = '' THEN ${line}
8220
+ ELSE logs || E'\n' || ${line}
8221
+ END,
8222
+ updated_at = now()
8223
+ WHERE id = ${operationId}
8224
+ `);
8225
+ } catch {
8226
+ }
8227
+ }
8228
+
8229
+ // ../jobs/src/refront-api-client.ts
8230
+ function stripEnvValue(raw) {
8231
+ if (!raw) return "";
8232
+ return raw.split("#")[0]?.trim() ?? "";
8233
+ }
8234
+ function getRefrontApiKeyEntries() {
8235
+ const fromList = (process.env.REFRONT_API_KEYS ?? "").split(",").map((part) => stripEnvValue(part)).filter(Boolean).map((part) => {
8236
+ const pipe = part.indexOf("|");
8237
+ if (pipe > 0) {
8238
+ return {
8239
+ label: part.slice(0, pipe).trim() || null,
8240
+ apiKey: part.slice(pipe + 1).trim()
8241
+ };
8242
+ }
8243
+ return { label: null, apiKey: part };
8244
+ }).filter((entry) => entry.apiKey.length > 0);
8245
+ const primary = stripEnvValue(
8246
+ process.env.REFRONT_API_KEY || process.env.REFRONT_ACCESS_TOKEN
8247
+ );
8248
+ const entries = [...fromList];
8249
+ if (primary && !entries.some((entry) => entry.apiKey === primary)) {
8250
+ entries.unshift({ apiKey: primary, label: null });
8251
+ }
8252
+ const seen = /* @__PURE__ */ new Set();
8253
+ return entries.filter((entry) => {
8254
+ if (seen.has(entry.apiKey)) return false;
8255
+ seen.add(entry.apiKey);
8256
+ return true;
8257
+ });
8258
+ }
8259
+ function getBaseUrl() {
8260
+ return (process.env.REFRONT_API_URL || "https://api.refront.nl").replace(
8261
+ /\/+$/,
8262
+ ""
8263
+ );
8264
+ }
8265
+ async function apiFetchWithKey(apiKey2, path, options = {}) {
8266
+ const url = `${getBaseUrl()}${path}`;
8267
+ const response = await fetch(url, {
8268
+ ...options,
8269
+ headers: {
8270
+ Authorization: `Bearer ${apiKey2}`,
8271
+ "Content-Type": "application/json",
8272
+ ...options.headers
8273
+ }
8274
+ });
8275
+ if (!response.ok) {
8276
+ const body = await response.text().catch(() => "");
8277
+ const retryAfter = response.headers.get("retry-after");
8278
+ const retrySuffix = response.status === 429 && retryAfter ? ` [retry-after=${retryAfter}]` : "";
8279
+ throw new Error(
8280
+ `Refront API ${options.method ?? "GET"} ${path} failed (${response.status}): ${body.slice(0, 300)}${retrySuffix}`
8281
+ );
8282
+ }
8283
+ if (response.status === 204) return void 0;
8284
+ return response.json();
8285
+ }
8286
+ function customerArchivedFlag(raw) {
8287
+ if (raw.isArchived === true) return true;
8288
+ if (raw.is_archived === true) return true;
8289
+ if (raw.archived === true) return true;
8290
+ if (typeof raw.archivedAt === "string" && raw.archivedAt.trim()) return true;
8291
+ if (typeof raw.archived_at === "string" && raw.archived_at.trim())
8292
+ return true;
8293
+ return false;
8294
+ }
8295
+ function resolveCustomerStatus(raw) {
8296
+ const explicit = typeof raw.status === "string" ? raw.status.trim() : raw.status;
8297
+ if (explicit) return explicit;
8298
+ if (customerArchivedFlag(raw)) return "archived";
8299
+ return raw.status ?? null;
8300
+ }
8301
+ function normalizeCustomer(raw, team) {
8302
+ const hasAliasesField = Object.prototype.hasOwnProperty.call(raw, "aliases");
8303
+ const aliases = hasAliasesField ? Array.isArray(raw.aliases) ? raw.aliases.map((a) => String(a).trim()).filter(Boolean) : [] : void 0;
8304
+ return {
8305
+ id: raw.id,
8306
+ name: raw.name,
8307
+ status: resolveCustomerStatus(raw),
8308
+ teamId: team?.id ?? null,
8309
+ teamName: team?.name ?? null,
8310
+ aliases
8311
+ };
8312
+ }
8313
+ async function resolveProviderTeam(apiKey2, preferredLabel) {
8314
+ try {
8315
+ const res = await apiFetchWithKey(apiKey2, "/teams");
8316
+ const teams = res.data ?? [];
8317
+ const providers = teams.filter(
8318
+ (team2) => team2.teamType === "provider" && !/e2e|eval/i.test(team2.name)
8319
+ );
8320
+ if (providers.length === 0) {
8321
+ if (preferredLabel) return { id: "", name: preferredLabel };
8322
+ return null;
8323
+ }
8324
+ if (preferredLabel) {
8325
+ const byLabel = providers.find(
8326
+ (team2) => team2.name.toLowerCase() === preferredLabel.toLowerCase() || team2.name.toLowerCase().includes(preferredLabel.toLowerCase())
8327
+ );
8328
+ if (byLabel) return { id: byLabel.id, name: byLabel.name };
8329
+ return { id: providers[0].id, name: preferredLabel };
8330
+ }
8331
+ const team = providers[0];
8332
+ if (!team) return null;
8333
+ return { id: team.id, name: team.name };
8334
+ } catch {
8335
+ if (preferredLabel) return { id: "", name: preferredLabel };
8336
+ return null;
8337
+ }
8338
+ }
8339
+ function customerMatchesSearch(customer, q) {
8340
+ const needle = q.trim().toLowerCase();
8341
+ if (!needle) return true;
8342
+ if (customer.name.toLowerCase().includes(needle)) return true;
8343
+ return (customer.teamName ?? "").toLowerCase().includes(needle);
8344
+ }
8345
+ function filterCustomersBySearch(customers, q) {
8346
+ const needle = q.trim();
8347
+ if (!needle) return customers;
8348
+ return customers.filter(
8349
+ (customer) => customerMatchesSearch(customer, needle)
8350
+ );
8351
+ }
8352
+ async function listCustomersForKey(apiKey2, options = {}, team) {
8353
+ const qs = new URLSearchParams({
8354
+ pageSize: String(options.pageSize ?? 20)
8355
+ });
8356
+ if (options.status && options.status !== "all") {
8357
+ qs.set("status", options.status);
8358
+ }
8359
+ const query = options.q?.trim();
8360
+ if (query) qs.set("q", query);
8361
+ const teamId = options.teamId?.trim() || "";
8362
+ if (teamId) qs.set("teamId", teamId);
8363
+ const res = await apiFetchWithKey(
8364
+ apiKey2,
8365
+ `/customers?${qs.toString()}`
8366
+ );
8367
+ return (res.data ?? []).map((customer) => normalizeCustomer(customer, team));
8368
+ }
8369
+ async function listCustomers(options = {}) {
8370
+ const entries = getRefrontApiKeyEntries();
8371
+ if (entries.length === 0) {
8372
+ throw new Error(
8373
+ "Missing REFRONT_API_KEYS, REFRONT_API_KEY, or REFRONT_ACCESS_TOKEN env var"
8374
+ );
8375
+ }
8376
+ if (options.teamId?.trim() && entries.length === 1) {
8377
+ return listCustomersForKey(entries[0].apiKey, options);
8378
+ }
8379
+ const settled = await Promise.allSettled(
8380
+ entries.map(async (entry) => {
8381
+ const team = await resolveProviderTeam(entry.apiKey, entry.label);
8382
+ return listCustomersForKey(entry.apiKey, options, team);
8383
+ })
8384
+ );
8385
+ const byId = /* @__PURE__ */ new Map();
8386
+ const errors = [];
8387
+ for (const result of settled) {
8388
+ if (result.status === "fulfilled") {
8389
+ for (const customer of result.value) {
8390
+ const existing = byId.get(customer.id);
8391
+ if (!existing) {
8392
+ byId.set(customer.id, customer);
8393
+ continue;
8394
+ }
8395
+ if (!existing.teamName && customer.teamName) {
8396
+ byId.set(customer.id, customer);
8397
+ continue;
8398
+ }
8399
+ if (customer.teamName && existing.teamName && customer.teamName !== existing.teamName) {
8400
+ byId.set(customer.id, customer);
8401
+ }
8402
+ }
8403
+ } else {
8404
+ const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
8405
+ errors.push(message);
8406
+ }
8407
+ }
8408
+ if (byId.size === 0 && errors.length > 0) {
8409
+ throw new Error(errors[0]);
8410
+ }
8411
+ if (errors.length > 0) {
8412
+ console.warn(
8413
+ `[refront] listCustomers partial failures (${errors.length}): ${errors.slice(0, 3).join(" | ")}`
8414
+ );
8415
+ }
8416
+ return [...byId.values()].sort((a, b) => a.name.localeCompare(b.name));
8417
+ }
8418
+ async function searchCustomersWithFallback(options = {}) {
8419
+ const pageSize = options.pageSize ?? 20;
8420
+ const status = options.status ?? "active";
8421
+ const raw = options.q?.trim() ?? "";
8422
+ if (!raw) {
8423
+ return listCustomers({ pageSize, status, teamId: options.teamId });
8424
+ }
8425
+ const exact = await listCustomers({
8426
+ q: raw,
8427
+ pageSize,
8428
+ status,
8429
+ teamId: options.teamId
8430
+ });
8431
+ const matchedExact = filterCustomersBySearch(exact, raw);
8432
+ if (matchedExact.length > 0) return matchedExact.slice(0, pageSize);
8433
+ const unfiltered = await listCustomers({
8434
+ pageSize: Math.max(pageSize, 100),
8435
+ status,
8436
+ teamId: options.teamId
8437
+ });
8438
+ return filterCustomersBySearch(unfiltered, raw).slice(0, pageSize);
8439
+ }
8440
+ async function getCustomer(id) {
8441
+ const entries = getRefrontApiKeyEntries();
8442
+ if (entries.length === 0) {
8443
+ throw new Error(
8444
+ "Missing REFRONT_API_KEYS, REFRONT_API_KEY, or REFRONT_ACCESS_TOKEN env var"
8445
+ );
8446
+ }
8447
+ let lastError = null;
8448
+ for (const entry of entries) {
8449
+ try {
8450
+ const team = await resolveProviderTeam(entry.apiKey, entry.label);
8451
+ const res = await apiFetchWithKey(
8452
+ entry.apiKey,
8453
+ `/customers/${id}`
8454
+ );
8455
+ return normalizeCustomer(res, team);
8456
+ } catch (error) {
8457
+ const message = error instanceof Error ? error.message : String(error);
8458
+ if (message.includes("(404)")) continue;
8459
+ lastError = error instanceof Error ? error : new Error(message);
8460
+ }
8461
+ }
8462
+ if (lastError) throw lastError;
8463
+ return null;
8464
+ }
8465
+
8466
+ // ../jobs/src/hermes-company.ts
8467
+ var MG_PROXY_SERVER_ID = process.env.MG_PROXY_SERVER_ID;
8468
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8469
+ var HermesCompanyError = class extends Error {
8470
+ constructor(code, message) {
8471
+ super(message);
8472
+ this.code = code;
8473
+ this.name = "HermesCompanyError";
8474
+ }
8475
+ };
8476
+ function containerNameFor(brandId) {
8477
+ return `hermes-${brandId}`;
8478
+ }
8479
+ 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(" ");
8487
+ }
8488
+ function parseBrandId(raw) {
8489
+ const brandId = raw.trim().toLowerCase();
8490
+ if (!/^[a-z0-9-]+$/.test(brandId)) {
8491
+ throw new HermesCompanyError(
8492
+ "BAD_REQUEST",
8493
+ "brand must be a lowercase slug (a-z, 0-9, hyphen)"
8494
+ );
8495
+ }
8496
+ return brandId;
8497
+ }
8498
+ function parseKitPath(raw) {
8499
+ const kitPath = (raw ?? "/opt/hermes-company-kit").trim();
8500
+ if (!/^\/[A-Za-z0-9._/-]+$/.test(kitPath)) {
8501
+ throw new HermesCompanyError(
8502
+ "BAD_REQUEST",
8503
+ "kit path must be an absolute unix path"
8504
+ );
8505
+ }
8506
+ return kitPath;
8507
+ }
8508
+ async function resolveServerId(serverIdOrName) {
8509
+ const trimmed = serverIdOrName.trim();
8510
+ if (!trimmed) {
8511
+ throw new HermesCompanyError("BAD_REQUEST", "serverId is required");
8512
+ }
8513
+ if (UUID_RE.test(trimmed)) return trimmed;
8514
+ const db2 = getDb();
8515
+ const exact = await db2.select({ id: managedServer.id, name: managedServer.name }).from(managedServer).where(eq(managedServer.name, trimmed)).limit(2);
8516
+ if (exact.length === 1) return exact[0].id;
8517
+ if (exact.length > 1) {
8518
+ throw new HermesCompanyError(
8519
+ "BAD_REQUEST",
8520
+ `Multiple servers named "${trimmed}". Pass the UUID.`
8521
+ );
8522
+ }
8523
+ const fuzzy = await db2.select({ id: managedServer.id, name: managedServer.name }).from(managedServer).where(sql`${managedServer.name} ILIKE ${`%${trimmed}%`}`).orderBy(managedServer.name).limit(5);
8524
+ if (fuzzy.length === 1) return fuzzy[0].id;
8525
+ if (fuzzy.length === 0) {
8526
+ throw new HermesCompanyError(
8527
+ "NOT_FOUND",
8528
+ `Server "${trimmed}" not found. Use list-servers.`
8529
+ );
8530
+ }
8531
+ throw new HermesCompanyError(
8532
+ "BAD_REQUEST",
8533
+ `Multiple servers match "${trimmed}": ${fuzzy.map((row) => row.name).join(", ")}.`
8534
+ );
8535
+ }
8536
+ async function resolveSsh(serverId) {
8537
+ const { server: server2, error } = await getSshServerByIdForUse(serverId);
8538
+ if (error || !server2) {
8539
+ throw new HermesCompanyError(
8540
+ "NOT_FOUND",
8541
+ error || "Server credentials not found"
8542
+ );
8543
+ }
8544
+ if (server2.os_type === "windows") {
8545
+ throw new HermesCompanyError(
8546
+ "BAD_REQUEST",
8547
+ "Hermes companies need a Linux Docker host"
8548
+ );
8549
+ }
8550
+ let proxy;
8551
+ if (MG_PROXY_SERVER_ID && server2.id !== MG_PROXY_SERVER_ID) {
8552
+ const { server: proxyServer } = await getSshServerByIdForUse(MG_PROXY_SERVER_ID);
8553
+ if (proxyServer) {
8554
+ proxy = serverToConnectionOptions(proxyServer);
8555
+ }
8556
+ }
8557
+ return {
8558
+ name: server2.name,
8559
+ target: serverToConnectionOptions(server2),
8560
+ proxy
8561
+ };
8562
+ }
8563
+ async function ssh(serverId, command, timeoutMs) {
8564
+ const conn = await resolveSsh(serverId);
8565
+ const result = await executeSshCommand(
8566
+ { ...conn.target, timeout: timeoutMs },
8567
+ command,
8568
+ conn.proxy
8569
+ );
8570
+ return {
8571
+ stdout: result.stdout,
8572
+ stderr: result.stderr,
8573
+ exitCode: result.exitCode
8574
+ };
8575
+ }
8576
+ async function dockerStatusByServer(serverIds) {
8577
+ const byServer = /* @__PURE__ */ new Map();
8578
+ const unique = [...new Set(serverIds)];
8579
+ await Promise.all(
8580
+ unique.map(async (serverId) => {
8581
+ const names = /* @__PURE__ */ new Map();
8582
+ try {
8583
+ const result = await ssh(
8584
+ serverId,
8585
+ "docker ps -a --filter name=hermes- --format '{{.Names}}\\t{{.Status}}'",
8586
+ 2e4
8587
+ );
8588
+ if (result.exitCode === 0) {
8589
+ for (const line of result.stdout.split("\n")) {
8590
+ const trimmed = line.trim();
8591
+ if (!trimmed) continue;
8592
+ const [name, ...rest] = trimmed.split(" ");
8593
+ if (name) names.set(name, rest.join(" ") || "unknown");
8594
+ }
8595
+ }
8596
+ } catch {
8597
+ }
8598
+ byServer.set(serverId, names);
8599
+ })
8600
+ );
8601
+ return byServer;
8602
+ }
8603
+ async function resolveCustomerCache(customerId) {
8604
+ if (!customerId) {
8605
+ return {
8606
+ refrontCustomerId: null,
8607
+ refrontCustomerName: null,
8608
+ refrontTeamLabel: null
8609
+ };
8610
+ }
8611
+ const customer = await getCustomer(customerId);
8612
+ if (!customer) {
8613
+ throw new HermesCompanyError("NOT_FOUND", "Refront customer not found");
8614
+ }
8615
+ return {
8616
+ refrontCustomerId: customer.id,
8617
+ refrontCustomerName: customer.name,
8618
+ refrontTeamLabel: customer.teamName ?? null
8619
+ };
8620
+ }
8621
+ function wrapMissingTable(error) {
8622
+ const message = error instanceof Error ? error.message : String(error);
8623
+ if (/hermes_company/i.test(message) && /does not exist/i.test(message)) {
8624
+ throw new HermesCompanyError(
8625
+ "PRECONDITION_FAILED",
8626
+ "Table hermes_company is missing. Run Dashboard migrations, then retry."
8627
+ );
8628
+ }
8629
+ throw error;
8630
+ }
8631
+ async function listHermesCompanies() {
8632
+ try {
8633
+ const rows = await getDb().select({
8634
+ company: hermesCompany,
8635
+ serverName: managedServer.name,
8636
+ serverHostname: managedServer.hostname
8637
+ }).from(hermesCompany).innerJoin(managedServer, eq(hermesCompany.serverId, managedServer.id)).orderBy(desc(hermesCompany.updatedAt));
8638
+ const docker = await dockerStatusByServer(
8639
+ rows.map((row) => row.company.serverId)
8640
+ );
8641
+ return rows.map((row) => {
8642
+ const name = containerNameFor(row.company.brandId);
8643
+ return {
8644
+ ...row.company,
8645
+ serverName: row.serverName,
8646
+ serverHostname: row.serverHostname,
8647
+ containerName: name,
8648
+ containerStatus: docker.get(row.company.serverId)?.get(name) ?? null
8649
+ };
8650
+ });
8651
+ } catch (error) {
8652
+ wrapMissingTable(error);
8653
+ }
8654
+ }
8655
+ async function upsertHermesCompany(input) {
8656
+ const brandId = parseBrandId(input.brandId);
8657
+ const displayName = input.displayName.trim();
8658
+ if (!displayName) {
8659
+ throw new HermesCompanyError("BAD_REQUEST", "displayName is required");
8660
+ }
8661
+ const serverId = await resolveServerId(input.serverId);
8662
+ const customer = await resolveCustomerCache(input.refrontCustomerId);
8663
+ const modules = input.modules ?? defaultHermesCompanyModules;
8664
+ const kitPath = parseKitPath(input.kitPath);
8665
+ const now = /* @__PURE__ */ new Date();
8666
+ const db2 = getDb();
8667
+ try {
8668
+ if (input.id) {
8669
+ const [updated] = await db2.update(hermesCompany).set({
8670
+ brandId,
8671
+ displayName,
8672
+ serverId,
8673
+ kitPath,
8674
+ modules,
8675
+ ...customer,
8676
+ updatedAt: now
8677
+ }).where(eq(hermesCompany.id, input.id)).returning();
8678
+ if (!updated) {
8679
+ throw new HermesCompanyError("NOT_FOUND", "Company not found");
8680
+ }
8681
+ return updated;
8682
+ }
8683
+ const [existing] = await db2.select({ id: hermesCompany.id }).from(hermesCompany).where(eq(hermesCompany.brandId, brandId)).limit(1);
8684
+ if (existing) {
8685
+ const [updated] = await db2.update(hermesCompany).set({
8686
+ displayName,
8687
+ serverId,
8688
+ kitPath,
8689
+ modules,
8690
+ ...customer,
8691
+ updatedAt: now
8692
+ }).where(eq(hermesCompany.id, existing.id)).returning();
8693
+ return updated;
8694
+ }
8695
+ const [created] = await db2.insert(hermesCompany).values({
8696
+ brandId,
8697
+ displayName,
8698
+ serverId,
8699
+ kitPath,
8700
+ modules,
8701
+ ...customer
8702
+ }).returning();
8703
+ return created;
8704
+ } catch (error) {
8705
+ if (error instanceof HermesCompanyError) throw error;
8706
+ const message = error instanceof Error ? error.message : String(error);
8707
+ if (message.includes("hermes_company_brand_id_uidx")) {
8708
+ throw new HermesCompanyError(
8709
+ "CONFLICT",
8710
+ `Brand ${brandId} already exists`
8711
+ );
8712
+ }
8713
+ wrapMissingTable(error);
8714
+ }
8715
+ }
8716
+ async function removeHermesCompany(id) {
8717
+ const [deleted] = await getDb().delete(hermesCompany).where(
8718
+ or(eq(hermesCompany.id, id), eq(hermesCompany.brandId, id.toLowerCase()))
8719
+ ).returning({ id: hermesCompany.id, brandId: hermesCompany.brandId });
8720
+ if (!deleted) {
8721
+ throw new HermesCompanyError("NOT_FOUND", "Company not found");
8722
+ }
8723
+ return { ok: true, ...deleted };
8724
+ }
8725
+ async function deployHermesCompany(input) {
8726
+ const key = input.idOrBrand.trim();
8727
+ const [row] = await getDb().select().from(hermesCompany).where(
8728
+ UUID_RE.test(key) ? eq(hermesCompany.id, key) : eq(hermesCompany.brandId, parseBrandId(key))
8729
+ ).limit(1);
8730
+ if (!row) {
8731
+ throw new HermesCompanyError("NOT_FOUND", "Company not found");
8732
+ }
8733
+ const kind = "hermes.company.deploy";
8734
+ const idempotencyPrefix = `${kind}:${row.id}:`;
8735
+ const [inFlight] = await getDb().select({ id: operation.id }).from(operation).where(
8736
+ and(
8737
+ eq(operation.serverId, row.serverId),
8738
+ eq(operation.kind, kind),
8739
+ inArray(operation.status, ["queued", "running"]),
8740
+ like(operation.idempotencyKey, `${idempotencyPrefix}%`)
8741
+ )
8742
+ ).limit(1);
8743
+ if (inFlight) {
8744
+ throw new HermesCompanyError(
8745
+ "CONFLICT",
8746
+ "A deploy is already running for this company"
8747
+ );
8748
+ }
8749
+ const [created] = await getDb().insert(operation).values({
8750
+ kind,
8751
+ serverId: row.serverId,
8752
+ status: "running",
8753
+ startedAt: /* @__PURE__ */ new Date(),
8754
+ idempotencyKey: `${idempotencyPrefix}${Date.now()}`,
8755
+ startedBy: input.actorUserId,
8756
+ steps: [
8757
+ { id: "probe", label: "Check kit checkout", status: "running" },
8758
+ {
8759
+ id: "install",
8760
+ label: "install-company.sh --up",
8761
+ status: "queued"
8762
+ }
8763
+ ]
8764
+ }).returning();
8765
+ if (!created) {
8766
+ throw new HermesCompanyError(
8767
+ "INTERNAL_SERVER_ERROR",
8768
+ "Failed to create deploy operation"
8769
+ );
8770
+ }
8771
+ await appendOperationLog(
8772
+ created.id,
8773
+ `Deploy ${row.brandId} queued by ${input.actorLabel ?? input.actorUserId} on kit ${row.kitPath}`
8774
+ );
8775
+ const markCompany = async (status, log) => {
8776
+ await getDb().update(hermesCompany).set({
8777
+ lastDeployAt: /* @__PURE__ */ new Date(),
8778
+ lastDeployStatus: status,
8779
+ lastDeployLog: log.slice(0, 4e3),
8780
+ updatedAt: /* @__PURE__ */ new Date()
8781
+ }).where(eq(hermesCompany.id, row.id));
8782
+ };
8783
+ const finishOperation = async (status, steps) => {
8784
+ await getDb().update(operation).set({
8785
+ status,
8786
+ steps,
8787
+ endedAt: /* @__PURE__ */ new Date(),
8788
+ updatedAt: /* @__PURE__ */ new Date()
8789
+ }).where(eq(operation.id, created.id));
8790
+ };
8791
+ try {
8792
+ const probe = await ssh(
8793
+ row.serverId,
8794
+ `if [ -x '${row.kitPath}/scripts/install-company.sh' ]; then echo OK; else echo MISSING; fi`,
8795
+ 2e4
8796
+ );
8797
+ if (probe.exitCode !== 0 || !probe.stdout.includes("OK")) {
8798
+ const message = `Hermes company kit not found at ${row.kitPath}. Clone MGSoftwareBV/hermes-company-kit there first.`;
8799
+ const log2 = (probe.stderr || probe.stdout || message).slice(0, 4e3);
8800
+ await appendOperationLog(created.id, message);
8801
+ await markCompany("missing_kit", log2);
8802
+ await finishOperation("failed", [
8803
+ {
8804
+ id: "probe",
8805
+ label: "Check kit checkout",
8806
+ status: "failed",
8807
+ error: message
8808
+ },
8809
+ { id: "install", label: "install-company.sh --up", status: "queued" }
8810
+ ]);
8811
+ throw new HermesCompanyError("PRECONDITION_FAILED", message);
8812
+ }
8813
+ await appendOperationLog(created.id, "Kit checkout found");
8814
+ const flags = modulesFlags(row.modules);
8815
+ const command = `cd '${row.kitPath}' && ./scripts/install-company.sh --brand ${row.brandId} --display-name ${JSON.stringify(row.displayName)} ${flags} --up`;
8816
+ await appendOperationLog(created.id, `Running ${command}`);
8817
+ const result = await ssh(row.serverId, command, 6e5);
8818
+ const log = `${result.stdout}
8819
+ ${result.stderr}`.trim().slice(0, 4e3);
8820
+ const ok = result.exitCode === 0;
8821
+ if (log) await appendOperationLog(created.id, log);
8822
+ await markCompany(ok ? "success" : "failed", log);
8823
+ await finishOperation(ok ? "succeeded" : "failed", [
8824
+ { id: "probe", label: "Check kit checkout", status: "succeeded" },
8825
+ {
8826
+ id: "install",
8827
+ label: "install-company.sh --up",
8828
+ status: ok ? "succeeded" : "failed",
8829
+ error: ok ? void 0 : log.slice(-800) || "install-company.sh failed"
8830
+ }
8831
+ ]);
8832
+ if (!ok) {
8833
+ throw new HermesCompanyError(
8834
+ "INTERNAL_SERVER_ERROR",
8835
+ log.slice(-800) || "install-company.sh failed"
8836
+ );
8837
+ }
8838
+ return {
8839
+ ok: true,
8840
+ operationId: created.id,
8841
+ brandId: row.brandId,
8842
+ containerName: containerNameFor(row.brandId),
8843
+ log
8844
+ };
8845
+ } catch (error) {
8846
+ if (error instanceof HermesCompanyError) throw error;
8847
+ const message = error instanceof Error ? error.message : "Deploy SSH failed";
8848
+ await appendOperationLog(created.id, message);
8849
+ await markCompany("failed", message);
8850
+ await finishOperation("failed", [
8851
+ {
8852
+ id: "probe",
8853
+ label: "Check kit checkout",
8854
+ status: "failed",
8855
+ error: message
8856
+ },
8857
+ { id: "install", label: "install-company.sh --up", status: "queued" }
8858
+ ]);
8859
+ throw new HermesCompanyError("INTERNAL_SERVER_ERROR", message);
8860
+ }
8861
+ }
8862
+ async function setupHermesCompany(input) {
8863
+ const company = await upsertHermesCompany(input);
8864
+ if (input.deploy === false) {
8865
+ return {
8866
+ company,
8867
+ deploy: null,
8868
+ next: "MCP keys + OAuth stay manual after --up. Call action=deploy when the kit is on the host."
8869
+ };
8870
+ }
8871
+ const deploy = await deployHermesCompany({
8872
+ idOrBrand: company.id,
8873
+ actorUserId: input.actorUserId,
8874
+ actorLabel: input.actorLabel
8875
+ });
8876
+ return {
8877
+ company,
8878
+ deploy,
8879
+ next: "Paste MCP_WIKI_KEY and MCP_REFRONT_KEY into secrets.env, then docker exec hermes-<brand> hermes setup for OAuth."
8880
+ };
8881
+ }
8882
+ async function searchHermesCustomers(q, pageSize = 20) {
8883
+ return searchCustomersWithFallback({
8884
+ q,
8885
+ pageSize,
8886
+ status: "active"
8887
+ });
8888
+ }
8889
+
8890
+ // src/hermes-company-tools.ts
8891
+ var HERMES_COMPANY_TOOL_NAME = "ai-company";
8892
+ var HERMES_COMPANY_ACTIONS = [
8893
+ "list",
8894
+ "search-customers",
8895
+ "upsert",
8896
+ "setup",
8897
+ "deploy",
8898
+ "remove"
8899
+ ];
8900
+ var HERMES_COMPANY_TOOLS = [
7756
8901
  {
7757
- id: uuid("id").primaryKey().defaultRandom(),
7758
- identityHash: text("identity_hash").notNull(),
7759
- kind: text("kind").notNull(),
7760
- seenCount: integer("seen_count").notNull().default(1),
7761
- sampleSignalIds: uuid("sample_signal_ids").array().notNull().default([]),
7762
- sampleContext: text("sample_context").array().notNull().default([]),
7763
- sampleSources: text("sample_sources").array().notNull().default([]),
7764
- lastSeen: timestamp("last_seen", { withTimezone: true }).notNull().defaultNow(),
7765
- insightFiledAt: timestamp("insight_filed_at", { withTimezone: true }),
7766
- resolvedAt: timestamp("resolved_at", { withTimezone: true }),
7767
- resolvedActorId: uuid("resolved_actor_id").references(() => wikiActor.id, {
7768
- onDelete: "set null"
7769
- }),
7770
- createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
7771
- },
7772
- (table) => [
7773
- uniqueIndex("intake_identity_unresolved_kind_hash").on(
7774
- table.kind,
7775
- table.identityHash
7776
- )
7777
- ]
7778
- );
8902
+ name: HERMES_COMPANY_TOOL_NAME,
8903
+ description: "Staff control plane for Hermes AI companies (one Docker container per brand). NOT for brand containers \u2014 those must not get SSH/Docker MCP. Dashboard UI is /ai-companies. yaml has no customerId; link the Refront customer here. Actions: list, search-customers (MG+AVARC), upsert (registry only), setup (registry + SSH install-company.sh --up), deploy, remove (registry only). MCP_WIKI_KEY / MCP_REFRONT_KEY paste and ChatGPT/Codex OAuth stay manual after --up. serverId accepts a managed-server UUID or name (e.g. MG Giga Server).",
8904
+ inputSchema: {
8905
+ type: "object",
8906
+ properties: {
8907
+ action: {
8908
+ type: "string",
8909
+ enum: [...HERMES_COMPANY_ACTIONS],
8910
+ description: "list | search-customers | upsert | setup | deploy | remove"
8911
+ },
8912
+ q: {
8913
+ type: "string",
8914
+ description: "search-customers: name or provider (wattago, avarc)."
8915
+ },
8916
+ brandId: {
8917
+ type: "string",
8918
+ description: "Lowercase slug. Container becomes hermes-<brandId>."
8919
+ },
8920
+ displayName: {
8921
+ type: "string",
8922
+ description: "Human name (Wattago, SolarFast)."
8923
+ },
8924
+ serverId: {
8925
+ type: "string",
8926
+ description: "Managed server UUID or name (list-servers)."
8927
+ },
8928
+ refrontCustomerId: {
8929
+ type: "string",
8930
+ description: "Optional Refront customer UUID. search-customers first. Empty is allowed."
8931
+ },
8932
+ kitPath: {
8933
+ type: "string",
8934
+ description: "Absolute unix path. Default /opt/hermes-company-kit."
8935
+ },
8936
+ seo: { type: "boolean", description: "Opt-in SEO specialist." },
8937
+ ads: { type: "boolean", description: "Opt-in ads specialist." },
8938
+ catalogus: { type: "boolean", description: "Opt-in catalogus (BM)." },
8939
+ customerFacing: {
8940
+ type: "boolean",
8941
+ description: "Opt-in customer-facing (Wattago)."
8942
+ },
8943
+ exact: {
8944
+ type: "boolean",
8945
+ description: "Opt-in Exact (BM, read-gated)."
8946
+ },
8947
+ deploy: {
8948
+ type: "boolean",
8949
+ description: "setup: SSH --up after save (default true)."
8950
+ },
8951
+ id: {
8952
+ type: "string",
8953
+ description: "Company UUID, or brand slug for deploy/remove."
8954
+ }
8955
+ },
8956
+ required: ["action"]
8957
+ }
8958
+ }
8959
+ ];
8960
+ function textResult(payload) {
8961
+ return {
8962
+ content: [
8963
+ {
8964
+ type: "text",
8965
+ text: typeof payload === "string" ? payload : JSON.stringify(payload, null, 2)
8966
+ }
8967
+ ]
8968
+ };
8969
+ }
8970
+ function errorResult(error) {
8971
+ const message = error instanceof HermesCompanyError ? error.message : error instanceof Error ? error.message : String(error);
8972
+ return textResult(`Error: ${message}`);
8973
+ }
8974
+ function modulesFromArgs(a) {
8975
+ return {
8976
+ seo: a.seo === true,
8977
+ ads: a.ads === true,
8978
+ catalogus: a.catalogus === true,
8979
+ customerFacing: a.customerFacing === true,
8980
+ exact: a.exact === true
8981
+ };
8982
+ }
8983
+ function str(a, key) {
8984
+ const value = a[key];
8985
+ return typeof value === "string" ? value.trim() : "";
8986
+ }
8987
+ async function handleHermesCompanyTool(a, ctx) {
8988
+ const action = str(a, "action");
8989
+ try {
8990
+ switch (action) {
8991
+ case "list":
8992
+ return textResult(await listHermesCompanies());
8993
+ case "search-customers":
8994
+ return textResult(await searchHermesCustomers(str(a, "q"), 30));
8995
+ case "upsert": {
8996
+ const company = await upsertHermesCompany({
8997
+ id: str(a, "id") || void 0,
8998
+ brandId: str(a, "brandId"),
8999
+ displayName: str(a, "displayName"),
9000
+ serverId: str(a, "serverId"),
9001
+ refrontCustomerId: str(a, "refrontCustomerId") || null,
9002
+ kitPath: str(a, "kitPath") || void 0,
9003
+ modules: modulesFromArgs(a)
9004
+ });
9005
+ return textResult({
9006
+ company,
9007
+ modulesFlags: modulesFlags(company.modules),
9008
+ next: "Call action=setup or action=deploy to run install-company.sh --up."
9009
+ });
9010
+ }
9011
+ case "setup": {
9012
+ const result = await setupHermesCompany({
9013
+ brandId: str(a, "brandId"),
9014
+ displayName: str(a, "displayName") || str(a, "brandId"),
9015
+ serverId: str(a, "serverId"),
9016
+ refrontCustomerId: str(a, "refrontCustomerId") || null,
9017
+ kitPath: str(a, "kitPath") || void 0,
9018
+ modules: modulesFromArgs(a),
9019
+ deploy: a.deploy !== false,
9020
+ actorUserId: ctx.userId,
9021
+ actorLabel: ctx.actorLabel
9022
+ });
9023
+ return textResult(result);
9024
+ }
9025
+ case "deploy":
9026
+ return textResult(
9027
+ await deployHermesCompany({
9028
+ idOrBrand: str(a, "id") || str(a, "brandId"),
9029
+ actorUserId: ctx.userId,
9030
+ actorLabel: ctx.actorLabel
9031
+ })
9032
+ );
9033
+ case "remove":
9034
+ return textResult(
9035
+ await removeHermesCompany(str(a, "id") || str(a, "brandId"))
9036
+ );
9037
+ default:
9038
+ return textResult(
9039
+ `Unknown action "${action}". Use: ${HERMES_COMPANY_ACTIONS.join(", ")}.`
9040
+ );
9041
+ }
9042
+ } catch (error) {
9043
+ return errorResult(error);
9044
+ }
9045
+ }
7779
9046
  var GITHUB_API = "https://api.github.com";
7780
9047
  var TRANSIENT_GITHUB_STATUSES = /* @__PURE__ */ new Set([
7781
9048
  429,
@@ -9203,6 +10470,7 @@ var TOOL_MODULE_MAP = {
9203
10470
  "dns-record": "domains",
9204
10471
  "cursor-remote-list": "cursor_remote",
9205
10472
  "cursor-remote-run": "cursor_remote",
10473
+ "ai-company": "ssh_servers",
9206
10474
  // cursor-skill intentionally unmapped — any valid API key may pull shared skills
9207
10475
  ...TRIGGER_TOOL_MODULE_MAP
9208
10476
  };
@@ -9772,7 +11040,7 @@ function getEncryptionKey() {
9772
11040
  throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
9773
11041
  return buf;
9774
11042
  }
9775
- function encrypt(text21) {
11043
+ function encrypt(text22) {
9776
11044
  const key = getEncryptionKey();
9777
11045
  const iv = randomBytes(ENC_IV_LENGTH);
9778
11046
  const cipher = createCipheriv(
@@ -9780,7 +11048,7 @@ function encrypt(text21) {
9780
11048
  new Uint8Array(key),
9781
11049
  new Uint8Array(iv)
9782
11050
  );
9783
- let encrypted = cipher.update(text21, "utf8", "hex");
11051
+ let encrypted = cipher.update(text22, "utf8", "hex");
9784
11052
  encrypted += cipher.final("hex");
9785
11053
  const authTag = cipher.getAuthTag();
9786
11054
  return Buffer.concat([
@@ -9879,11 +11147,11 @@ async function getProxyConnection() {
9879
11147
  };
9880
11148
  return _proxyConnCache;
9881
11149
  }
9882
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9883
- async function resolveServerId(input) {
11150
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
11151
+ async function resolveServerId2(input) {
9884
11152
  const trimmed = (input ?? "").trim();
9885
11153
  if (!trimmed) throw new Error("serverId is required");
9886
- if (UUID_RE.test(trimmed)) return trimmed;
11154
+ if (UUID_RE2.test(trimmed)) return trimmed;
9887
11155
  const exact = await db.execute(sql`
9888
11156
  SELECT id, name FROM managed_server WHERE name = ${trimmed} LIMIT 2
9889
11157
  `);
@@ -9923,7 +11191,7 @@ async function resolveServerId(input) {
9923
11191
  );
9924
11192
  }
9925
11193
  async function getServerConnection(serverIdOrName) {
9926
- const serverId = await resolveServerId(serverIdOrName);
11194
+ const serverId = await resolveServerId2(serverIdOrName);
9927
11195
  assertServerAccess(serverId);
9928
11196
  const rows = await db.execute(sql`
9929
11197
  SELECT
@@ -10085,7 +11353,7 @@ async function sshExec(opts, command, proxy, options) {
10085
11353
  async function sshExecOnce(opts, command, proxy, options) {
10086
11354
  if (proxy) return sshExecViaProxy(proxy, opts, command, options);
10087
11355
  return new Promise((resolve) => {
10088
- const ssh = new Client();
11356
+ const ssh2 = new Client();
10089
11357
  let stdout = "";
10090
11358
  let stderr = "";
10091
11359
  let done = false;
@@ -10095,7 +11363,7 @@ async function sshExecOnce(opts, command, proxy, options) {
10095
11363
  if (!done) {
10096
11364
  done = true;
10097
11365
  clearTimeout(idleTimer);
10098
- ssh.end();
11366
+ ssh2.end();
10099
11367
  resolve({
10100
11368
  stdout,
10101
11369
  stderr: stderr || `idle/wall timeout after ${wallTimeout}ms`,
@@ -10111,7 +11379,7 @@ async function sshExecOnce(opts, command, proxy, options) {
10111
11379
  if (!done) {
10112
11380
  done = true;
10113
11381
  clearTimeout(wallTimer);
10114
- ssh.end();
11382
+ ssh2.end();
10115
11383
  resolve({
10116
11384
  stdout,
10117
11385
  stderr: stderr || `idle timeout after ${idleTimeout}ms with no output`,
@@ -10121,15 +11389,15 @@ async function sshExecOnce(opts, command, proxy, options) {
10121
11389
  }, idleTimeout);
10122
11390
  };
10123
11391
  armIdle();
10124
- ssh.on("ready", () => {
11392
+ ssh2.on("ready", () => {
10125
11393
  const execOpts = options?.pty ? { pty: true } : {};
10126
- ssh.exec(command, execOpts, (err, stream) => {
11394
+ ssh2.exec(command, execOpts, (err, stream) => {
10127
11395
  if (err) {
10128
11396
  if (!done) {
10129
11397
  done = true;
10130
11398
  clearTimeout(wallTimer);
10131
11399
  if (idleTimer) clearTimeout(idleTimer);
10132
- ssh.end();
11400
+ ssh2.end();
10133
11401
  resolve({ stdout, stderr: err.message || stderr, exitCode: -1 });
10134
11402
  }
10135
11403
  return;
@@ -10147,7 +11415,7 @@ async function sshExecOnce(opts, command, proxy, options) {
10147
11415
  done = true;
10148
11416
  clearTimeout(wallTimer);
10149
11417
  if (idleTimer) clearTimeout(idleTimer);
10150
- ssh.end();
11418
+ ssh2.end();
10151
11419
  resolve({ stdout, stderr, exitCode: code ?? 0 });
10152
11420
  }
10153
11421
  });
@@ -10156,7 +11424,7 @@ async function sshExecOnce(opts, command, proxy, options) {
10156
11424
  }
10157
11425
  });
10158
11426
  });
10159
- ssh.on("error", (err) => {
11427
+ ssh2.on("error", (err) => {
10160
11428
  if (!done) {
10161
11429
  done = true;
10162
11430
  clearTimeout(wallTimer);
@@ -10164,7 +11432,7 @@ async function sshExecOnce(opts, command, proxy, options) {
10164
11432
  resolve({ stdout, stderr: err.message, exitCode: -1 });
10165
11433
  }
10166
11434
  });
10167
- ssh.connect({
11435
+ ssh2.connect({
10168
11436
  host: opts.hostname,
10169
11437
  port: opts.port,
10170
11438
  username: opts.username,
@@ -10320,10 +11588,10 @@ function connectSshClient(opts, proxy, readyTimeout = 6e4, extraConnect) {
10320
11588
  const compress = extraConnect?.compress;
10321
11589
  if (!proxy) {
10322
11590
  return new Promise((resolve, reject) => {
10323
- const ssh = new Client();
10324
- ssh.on("ready", () => resolve({ client: ssh, cleanup: () => ssh.end() }));
10325
- ssh.on("error", (e) => reject(e));
10326
- ssh.connect({
11591
+ const ssh2 = new Client();
11592
+ ssh2.on("ready", () => resolve({ client: ssh2, cleanup: () => ssh2.end() }));
11593
+ ssh2.on("error", (e) => reject(e));
11594
+ ssh2.connect({
10327
11595
  host: opts.hostname,
10328
11596
  port: opts.port,
10329
11597
  username: opts.username,
@@ -10517,10 +11785,10 @@ async function r2GetObjectRange(bucket, key, range) {
10517
11785
  const body = result.Body;
10518
11786
  if (!body?.transformToString)
10519
11787
  throw new Error("R2 returned no readable body");
10520
- const text21 = await body.transformToString();
11788
+ const text22 = await body.transformToString();
10521
11789
  const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
10522
11790
  return `${header}
10523
- ${text21}`;
11791
+ ${text22}`;
10524
11792
  } catch (e) {
10525
11793
  throw r2WrapError(bucket, key, e);
10526
11794
  }
@@ -10883,15 +12151,15 @@ async function sftpRead(opts, filePath, proxy, options) {
10883
12151
  clearTimeout(timer);
10884
12152
  cleanup?.();
10885
12153
  cleanup = void 0;
10886
- const text21 = Buffer.concat(
12154
+ const text22 = Buffer.concat(
10887
12155
  chunks.map((ch) => new Uint8Array(ch))
10888
12156
  ).toString("utf-8");
10889
12157
  if (!isWholeFileRequest) {
10890
12158
  const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
10891
12159
  resolve(`${header}
10892
- ${text21}`);
12160
+ ${text22}`);
10893
12161
  } else {
10894
- resolve(text21);
12162
+ resolve(text22);
10895
12163
  }
10896
12164
  });
10897
12165
  rs.on("error", (e) => {
@@ -10945,7 +12213,8 @@ var RAW_JSON_TOOLS = /* @__PURE__ */ new Set([
10945
12213
  // Skill registry metadata only (no file bodies) — must stay valid JSON.
10946
12214
  "cursor-skill",
10947
12215
  // GSC opportunity store (pass + items)
10948
- "gsc-opportunity"
12216
+ "gsc-opportunity",
12217
+ "ai-company"
10949
12218
  ]);
10950
12219
  var TOOL_CACHE_TTL_MS = {
10951
12220
  "list-servers": 6e4,
@@ -11046,11 +12315,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
11046
12315
  if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
11047
12316
  return e.names;
11048
12317
  }
11049
- function truncateForLLM(text21, maxBytes) {
11050
- const totalBytes = Buffer.byteLength(text21, "utf8");
12318
+ function truncateForLLM(text22, maxBytes) {
12319
+ const totalBytes = Buffer.byteLength(text22, "utf8");
11051
12320
  if (totalBytes <= maxBytes)
11052
- return { text: text21, truncated: false, totalBytes, shownBytes: totalBytes };
11053
- const buf = Buffer.from(text21, "utf8");
12321
+ return { text: text22, truncated: false, totalBytes, shownBytes: totalBytes };
12322
+ const buf = Buffer.from(text22, "utf8");
11054
12323
  let cut = maxBytes;
11055
12324
  while (cut > 0 && (buf[cut] & 192) === 128) cut--;
11056
12325
  const head = buf.subarray(0, cut).toString("utf8");
@@ -11081,10 +12350,10 @@ function postprocessResult(result, meta) {
11081
12350
  if (!result.content?.length) return result;
11082
12351
  if (RAW_JSON_TOOLS.has(meta.toolName)) return result;
11083
12352
  const block = result.content[0];
11084
- let text21 = String(block.text ?? "");
11085
- const trunc = truncateForLLM(text21, RESPONSE_MAX_BYTES);
12353
+ let text22 = String(block.text ?? "");
12354
+ const trunc = truncateForLLM(text22, RESPONSE_MAX_BYTES);
11086
12355
  if (trunc.truncated) {
11087
- text21 = trunc.text + "\n\n... " + buildTruncationHint(
12356
+ text22 = trunc.text + "\n\n... " + buildTruncationHint(
11088
12357
  meta.toolName,
11089
12358
  meta.args,
11090
12359
  trunc.totalBytes,
@@ -11098,11 +12367,11 @@ function postprocessResult(result, meta) {
11098
12367
  const parts = [`took ${tookStr}`, sizeStr];
11099
12368
  if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
11100
12369
  if (meta.cached) parts.push("cached");
11101
- text21 = `${text21}
12370
+ text22 = `${text22}
11102
12371
 
11103
12372
  [${parts.join(", ")}]`;
11104
12373
  }
11105
- return { ...result, content: [{ ...block, text: text21 }] };
12374
+ return { ...result, content: [{ ...block, text: text22 }] };
11106
12375
  }
11107
12376
  function buildPipelineScript(commands, shell, marker, stopOnError) {
11108
12377
  if (shell === "powershell") {
@@ -11888,11 +13157,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
11888
13157
  applied_by TEXT
11889
13158
  );
11890
13159
  `.trim();
11891
- function normaliseMigrationSql(sql31) {
11892
- return sql31.replace(/\r\n/g, "\n").trim() + "\n";
13160
+ function normaliseMigrationSql(sql33) {
13161
+ return sql33.replace(/\r\n/g, "\n").trim() + "\n";
11893
13162
  }
11894
- function migrationSha256(sql31) {
11895
- return createHash("sha256").update(sql31.replace(/\r\n/g, "\n"), "utf8").digest("hex");
13163
+ function migrationSha256(sql33) {
13164
+ return createHash("sha256").update(sql33.replace(/\r\n/g, "\n"), "utf8").digest("hex");
11896
13165
  }
11897
13166
  function dollarQuoteTag(value) {
11898
13167
  let tag = "_mcp";
@@ -13904,6 +15173,7 @@ var TOOLS = [
13904
15173
  }
13905
15174
  },
13906
15175
  ...MAILSERVER_TOOLS,
15176
+ ...HERMES_COMPANY_TOOLS,
13907
15177
  // ----- Trigger.dev -----
13908
15178
  ...TRIGGER_TOOLS
13909
15179
  ];
@@ -14224,7 +15494,7 @@ async function executeToolCall(name, a, _serverId) {
14224
15494
  ]
14225
15495
  };
14226
15496
  }
14227
- const lines = data.hits.map((hit, index18) => {
15497
+ const lines = data.hits.map((hit, index19) => {
14228
15498
  const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
14229
15499
  const repo = hit.repo ?? "unknown repo";
14230
15500
  const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
@@ -14234,7 +15504,7 @@ async function executeToolCall(name, a, _serverId) {
14234
15504
  ...Array.isArray(hit.tech) ? hit.tech.slice(0, 4) : []
14235
15505
  ].filter(Boolean);
14236
15506
  const tags = facets.length > 0 ? ` \xB7 ${facets.join(", ")}` : "";
14237
- return `${index18 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
15507
+ return `${index19 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
14238
15508
  id: ${hit.id}
14239
15509
  ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
14240
15510
  });
@@ -14441,12 +15711,12 @@ Searchable via search-team-memory once indexing completes (usually immediate).`
14441
15711
  ]
14442
15712
  };
14443
15713
  }
14444
- const lines = data.hits.map((hit, index18) => {
15714
+ const lines = data.hits.map((hit, index19) => {
14445
15715
  const sim = hit.similarity !== null ? ` \xB7 ${Math.round(hit.similarity * 100)}% match` : "";
14446
15716
  const tags = hit.tags.length ? ` \xB7 ${hit.tags.slice(0, 5).join(", ")}` : "";
14447
15717
  const sources = hit.sources.length ? `
14448
15718
  sources: ${hit.sources.slice(0, 6).map((s) => `${s.type}:${s.ref}`).join(", ")}` : "";
14449
- return `${index18 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
15719
+ return `${index19 + 1}. [${hit.status} \xB7 updated ${hit.pageUpdatedOn ?? "unknown"}${sim}${tags}] ${hit.title}
14450
15720
  slug: ${hit.slug}${sources}
14451
15721
  ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 400)}`;
14452
15722
  });
@@ -14624,7 +15894,7 @@ Archived sources (${archiveOnly.length}):
14624
15894
  let formatItems2 = function(items) {
14625
15895
  if (items.length === 0) return "(none)";
14626
15896
  return items.map(
14627
- (item, index18) => `${index18 + 1}. [${item.status} \xB7 ${item.folder} \xB7 updated ${item.updated ?? "unknown"}] ${item.title}
15897
+ (item, index19) => `${index19 + 1}. [${item.status} \xB7 ${item.folder} \xB7 updated ${item.updated ?? "unknown"}] ${item.title}
14628
15898
  slug: ${item.slug}`
14629
15899
  ).join("\n");
14630
15900
  };
@@ -14651,9 +15921,9 @@ Archived sources (${archiveOnly.length}):
14651
15921
  }
14652
15922
  const data = await res.json();
14653
15923
  const countLine = Object.entries(data.counts).sort(([left], [right]) => left.localeCompare(right)).map(([status, count]) => `${status} ${count}`).join(" \xB7 ");
14654
- const flagLines = data.flags.length === 0 ? "(none)" : data.flags.map((flag, index18) => {
15924
+ const flagLines = data.flags.length === 0 ? "(none)" : data.flags.map((flag, index19) => {
14655
15925
  const detail = flag.detail.replace(/\s+/g, " ").slice(0, 240);
14656
- return `${index18 + 1}. [${flag.kind} \xB7 ${flag.folder}${flag.status ? ` \xB7 page ${flag.status}` : ""}] ${flag.title}
15926
+ return `${index19 + 1}. [${flag.kind} \xB7 ${flag.folder}${flag.status ? ` \xB7 page ${flag.status}` : ""}] ${flag.title}
14657
15927
  slug: ${flag.slug}
14658
15928
  ${detail}`;
14659
15929
  }).join("\n");
@@ -15819,8 +17089,8 @@ ${sample.join("\n")}${files.length > 5 ? `
15819
17089
  };
15820
17090
  const filtered = sortRows(applyFilter(only.rows));
15821
17091
  if (format === "json") {
15822
- const text22 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
15823
- return { content: [{ type: "text", text: text22 }] };
17092
+ const text23 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
17093
+ return { content: [{ type: "text", text: text23 }] };
15824
17094
  }
15825
17095
  if (groupByProject) {
15826
17096
  const groups = /* @__PURE__ */ new Map();
@@ -15847,8 +17117,8 @@ ${sample.join("\n")}${files.length > 5 ? `
15847
17117
  }
15848
17118
  const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
15849
17119
  const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
15850
- const text21 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
15851
- return { content: [{ type: "text", text: text21 }] };
17120
+ const text22 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
17121
+ return { content: [{ type: "text", text: text22 }] };
15852
17122
  }
15853
17123
  if (format === "json") {
15854
17124
  const lines = [];
@@ -17671,6 +18941,12 @@ Install: GET https://dashboard.mgsoftware.nl/api/downloads/versions.txt then \u2
17671
18941
  token
17672
18942
  });
17673
18943
  }
18944
+ case HERMES_COMPANY_TOOL_NAME: {
18945
+ return handleHermesCompanyTool(a, {
18946
+ userId: ctx.userId,
18947
+ actorLabel: ctx.apiKeyName
18948
+ });
18949
+ }
17674
18950
  default:
17675
18951
  if (TRIGGER_TOOL_NAMES.has(name)) {
17676
18952
  return handleTriggerTool(name, a, { sshExec, getServerConnection });