@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.
- package/dist/index.js +2606 -1330
- 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,
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 "${
|
|
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
|
|
753
|
-
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
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
|
|
836
|
-
const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${
|
|
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
|
|
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:
|
|
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
|
|
1104
|
-
if (logs)
|
|
1105
|
-
return { content: [{ type: "text", text:
|
|
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
|
|
1127
|
+
let text22 = formatRunDetail(run);
|
|
1128
1128
|
const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
|
|
1129
|
-
if (logs)
|
|
1130
|
-
return { content: [{ type: "text", text:
|
|
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
|
|
1395
|
-
if (
|
|
1396
|
-
const key = trimmed.slice(0,
|
|
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(
|
|
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
|
|
1504
|
-
let json =
|
|
1503
|
+
const text22 = await res.text();
|
|
1504
|
+
let json = text22;
|
|
1505
1505
|
try {
|
|
1506
|
-
json =
|
|
1506
|
+
json = text22 ? JSON.parse(text22) : null;
|
|
1507
1507
|
} catch {
|
|
1508
|
-
json = { error:
|
|
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(
|
|
4792
|
+
catchall(index19) {
|
|
4248
4793
|
return new _ZodObject({
|
|
4249
4794
|
...this._def,
|
|
4250
|
-
catchall:
|
|
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
|
|
4569
|
-
const itemA = a[
|
|
4570
|
-
const itemB = b[
|
|
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],
|
|
5321
|
+
const pairs = [...ctx.data.entries()].map(([key, value], index19) => {
|
|
4777
5322
|
return {
|
|
4778
|
-
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [
|
|
4779
|
-
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [
|
|
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
|
-
|
|
5982
|
+
removeCatch() {
|
|
5552
5983
|
return this._def.innerType;
|
|
5553
5984
|
}
|
|
5554
5985
|
};
|
|
5555
|
-
|
|
5556
|
-
return new
|
|
5986
|
+
ZodCatch.create = (type, params) => {
|
|
5987
|
+
return new ZodCatch({
|
|
5557
5988
|
innerType: type,
|
|
5558
|
-
typeName: ZodFirstPartyTypeKind.
|
|
5989
|
+
typeName: ZodFirstPartyTypeKind.ZodCatch,
|
|
5990
|
+
catchValue: typeof params.catch === "function" ? params.catch : () => params.catch,
|
|
5559
5991
|
...processCreateParams(params)
|
|
5560
5992
|
});
|
|
5561
5993
|
};
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
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
|
-
|
|
5593
|
-
(
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
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
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
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
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
);
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
}
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
}
|
|
5834
|
-
|
|
5835
|
-
)
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
);
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
]
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
"
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
]
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
"
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
]
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
"
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
}
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
)
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
)
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
)
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
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
|
-
(
|
|
6021
|
-
|
|
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
|
-
(
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
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
|
-
"
|
|
6311
|
+
"wiki_page",
|
|
6061
6312
|
{
|
|
6062
6313
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6063
|
-
|
|
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
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
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
|
-
|
|
6093
|
-
|
|
6094
|
-
),
|
|
6095
|
-
index("
|
|
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
|
-
|
|
6101
|
-
"
|
|
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
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
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
|
-
|
|
6158
|
-
"
|
|
6366
|
+
pgTable(
|
|
6367
|
+
"wiki_page_flag",
|
|
6159
6368
|
{
|
|
6160
6369
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
6382
|
+
"wiki_page_evidence",
|
|
6193
6383
|
{
|
|
6194
6384
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
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(
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
)
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
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("
|
|
6236
|
-
table.
|
|
6237
|
-
table.
|
|
6412
|
+
uniqueIndex("wiki_page_link_source_target_kind_key").on(
|
|
6413
|
+
table.sourceSlug,
|
|
6414
|
+
table.targetSlug,
|
|
6415
|
+
table.kind
|
|
6238
6416
|
),
|
|
6239
|
-
index("
|
|
6240
|
-
index("
|
|
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
|
-
"
|
|
6422
|
+
"wiki_page_read",
|
|
6248
6423
|
{
|
|
6249
6424
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
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("
|
|
6274
|
-
index("
|
|
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
|
-
"
|
|
6436
|
+
"wiki_verify_event",
|
|
6284
6437
|
{
|
|
6285
6438
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
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("
|
|
6318
|
-
|
|
6319
|
-
table.
|
|
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
|
-
"
|
|
6454
|
+
"wiki_invariant_violation",
|
|
6339
6455
|
{
|
|
6340
6456
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
),
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
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
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
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
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6476
|
+
index("idx_wiki_invariant_violation_severity").on(
|
|
6477
|
+
table.severity,
|
|
6478
|
+
table.resolvedAt
|
|
6385
6479
|
)
|
|
6386
6480
|
]
|
|
6387
6481
|
);
|
|
6388
|
-
|
|
6389
|
-
"
|
|
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
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
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("
|
|
6463
|
-
table.
|
|
6464
|
-
table.
|
|
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
|
-
"
|
|
6513
|
+
"wiki_search_log",
|
|
6481
6514
|
{
|
|
6482
6515
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
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
|
-
"
|
|
6526
|
+
"team_memory_search_log",
|
|
6510
6527
|
{
|
|
6511
6528
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
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("
|
|
6525
|
-
index("
|
|
6526
|
-
index("
|
|
6527
|
-
index("
|
|
6528
|
-
index("
|
|
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
|
-
|
|
6533
|
-
"
|
|
6573
|
+
pgTable(
|
|
6574
|
+
"wiki_agent_review_suggestion",
|
|
6534
6575
|
{
|
|
6535
6576
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
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("
|
|
6552
|
-
|
|
6553
|
-
table.
|
|
6554
|
-
table.
|
|
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
|
-
"
|
|
6605
|
+
"agent_memory",
|
|
6563
6606
|
{
|
|
6564
6607
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
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
|
-
|
|
6575
|
-
table.
|
|
6576
|
-
table.createdAt
|
|
6637
|
+
uniqueIndex("uq_agent_memory_local_conversation").on(
|
|
6638
|
+
table.localConversationId
|
|
6577
6639
|
),
|
|
6578
|
-
index("
|
|
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
|
|
6582
|
-
"
|
|
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
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
}),
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
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("
|
|
6606
|
-
index("
|
|
6607
|
-
index("
|
|
6608
|
-
index("
|
|
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
|
-
"
|
|
6702
|
+
var contentEvidencePacks = pgTable(
|
|
6703
|
+
"content_evidence_packs",
|
|
6613
6704
|
{
|
|
6614
6705
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
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
|
-
|
|
6628
|
-
index("
|
|
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
|
-
"
|
|
6737
|
+
"content_evidence_pack_source",
|
|
6633
6738
|
{
|
|
6634
6739
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
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
|
-
|
|
6647
|
-
table.
|
|
6648
|
-
table.
|
|
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
|
-
|
|
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
|
|
6673
|
-
"
|
|
6753
|
+
var contentCorpusItems = pgTable(
|
|
6754
|
+
"content_corpus_items",
|
|
6674
6755
|
{
|
|
6675
6756
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
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
|
-
|
|
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("
|
|
6693
|
-
table.
|
|
6694
|
-
table.
|
|
6695
|
-
table.sourceKey
|
|
6780
|
+
uniqueIndex("content_corpus_items_project_canonical_uidx").on(
|
|
6781
|
+
table.projectKey,
|
|
6782
|
+
table.canonicalUrl
|
|
6696
6783
|
),
|
|
6697
|
-
index("
|
|
6698
|
-
index("
|
|
6699
|
-
index("
|
|
6700
|
-
|
|
6701
|
-
|
|
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
|
-
"
|
|
6792
|
+
"content_quality_runs",
|
|
6707
6793
|
{
|
|
6708
6794
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
onDelete: "set null"
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
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("
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
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("
|
|
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
|
-
"
|
|
6828
|
+
"content_snapshots",
|
|
6750
6829
|
{
|
|
6751
6830
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
onDelete: "
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
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
|
-
|
|
6781
|
-
|
|
6782
|
-
table.
|
|
6783
|
-
table.lastSeenAt
|
|
6862
|
+
index("content_snapshots_corpus_idx").on(
|
|
6863
|
+
table.corpusItemId,
|
|
6864
|
+
table.capturedAt.desc()
|
|
6784
6865
|
),
|
|
6785
|
-
index("
|
|
6786
|
-
table.
|
|
6787
|
-
table.
|
|
6866
|
+
index("content_snapshots_source_idx").on(
|
|
6867
|
+
table.contentSourceId,
|
|
6868
|
+
table.capturedAt.desc()
|
|
6788
6869
|
),
|
|
6789
|
-
index("
|
|
6790
|
-
|
|
6791
|
-
|
|
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
|
-
|
|
6876
|
+
uniqueIndex("content_snapshots_source_hash_uidx").on(
|
|
6877
|
+
table.contentSourceId,
|
|
6878
|
+
table.textHash
|
|
6879
|
+
)
|
|
6794
6880
|
]
|
|
6795
6881
|
);
|
|
6796
6882
|
pgTable(
|
|
6797
|
-
"
|
|
6883
|
+
"content_site_candidates",
|
|
6798
6884
|
{
|
|
6799
6885
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
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
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
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("
|
|
6817
|
-
index("
|
|
6818
|
-
index("
|
|
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
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
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(
|
|
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(
|
|
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
|
|
9883
|
-
async function
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11392
|
+
ssh2.on("ready", () => {
|
|
10125
11393
|
const execOpts = options?.pty ? { pty: true } : {};
|
|
10126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
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
|
|
11788
|
+
const text22 = await body.transformToString();
|
|
10521
11789
|
const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
|
|
10522
11790
|
return `${header}
|
|
10523
|
-
${
|
|
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
|
|
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
|
-
${
|
|
12160
|
+
${text22}`);
|
|
10893
12161
|
} else {
|
|
10894
|
-
resolve(
|
|
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(
|
|
11050
|
-
const totalBytes = Buffer.byteLength(
|
|
12318
|
+
function truncateForLLM(text22, maxBytes) {
|
|
12319
|
+
const totalBytes = Buffer.byteLength(text22, "utf8");
|
|
11051
12320
|
if (totalBytes <= maxBytes)
|
|
11052
|
-
return { text:
|
|
11053
|
-
const buf = Buffer.from(
|
|
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
|
|
11085
|
-
const trunc = truncateForLLM(
|
|
12353
|
+
let text22 = String(block.text ?? "");
|
|
12354
|
+
const trunc = truncateForLLM(text22, RESPONSE_MAX_BYTES);
|
|
11086
12355
|
if (trunc.truncated) {
|
|
11087
|
-
|
|
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
|
-
|
|
12370
|
+
text22 = `${text22}
|
|
11102
12371
|
|
|
11103
12372
|
[${parts.join(", ")}]`;
|
|
11104
12373
|
}
|
|
11105
|
-
return { ...result, content: [{ ...block, text:
|
|
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(
|
|
11892
|
-
return
|
|
13160
|
+
function normaliseMigrationSql(sql33) {
|
|
13161
|
+
return sql33.replace(/\r\n/g, "\n").trim() + "\n";
|
|
11893
13162
|
}
|
|
11894
|
-
function migrationSha256(
|
|
11895
|
-
return createHash("sha256").update(
|
|
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,
|
|
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 `${
|
|
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,
|
|
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 `${
|
|
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,
|
|
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,
|
|
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 `${
|
|
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
|
|
15823
|
-
return { content: [{ type: "text", text:
|
|
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
|
|
15851
|
-
return { content: [{ type: "text", text:
|
|
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 });
|