@oneuptime/common 11.5.7 → 11.5.9
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/Models/DatabaseModels/AIAgentTaskPullRequest.ts +15 -6
- package/Models/DatabaseModels/AIRun.ts +68 -0
- package/Models/DatabaseModels/AIRunEvent.ts +34 -1
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +215 -0
- package/Models/DatabaseModels/Project.ts +25 -0
- package/Models/DatabaseModels/StatusPage.ts +42 -0
- package/Server/API/AIAgentTaskAPI.ts +38 -12
- package/Server/API/AIAgentTaskLogAPI.ts +49 -0
- package/Server/API/AIChatAPI.ts +16 -0
- package/Server/API/AIReadinessAPI.ts +84 -0
- package/Server/API/CodeFixRunAPI.ts +190 -57
- package/Server/API/TelemetryExceptionAPI.ts +6 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784105912819-BackfillCodeFixTaskType.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784124919694-AddAITaskNumberAndRunTranscript.ts +95 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784135099754-AllowNullAiAgentOnPullRequest.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784137457184-AddEnableMcpServerToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784200000000-AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +1 -1
- package/Server/Middleware/ProjectAuthorization.ts +1 -1
- package/Server/Services/AIRunEventService.ts +15 -1
- package/Server/Services/AIRunService.ts +48 -12
- package/Server/Services/LlmProviderService.ts +2 -0
- package/Server/Services/ProjectService.ts +42 -0
- package/Server/Services/StatusPageService.ts +87 -0
- package/Server/Services/TelemetryExceptionService.ts +19 -90
- package/Server/Types/Markdown.ts +9 -9
- package/Server/Types/MarkdownSlugify.ts +47 -0
- package/Server/Utils/AI/AIRunPrivacyFilter.ts +106 -0
- package/Server/Utils/AI/AIRunTranscript.ts +240 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +9 -0
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +119 -5
- package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +75 -0
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +235 -0
- package/Server/Utils/AI/SRE/Insights/FixRouting.ts +2 -3
- package/Server/Utils/AI/Toolbox/CodeTools.ts +883 -0
- package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +519 -0
- package/Server/Utils/AI/Toolbox/Index.ts +27 -0
- package/Server/Utils/AI/Toolbox/ScheduledMaintenanceTools.ts +279 -0
- package/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.ts +19 -1
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +600 -0
- package/Server/Utils/CodeRepository/StackTraceRepoResolver.ts +7 -2
- package/Server/Utils/LLM/LLMService.ts +397 -18
- package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +4 -11
- package/Tests/Models/StatusPageEnableMcpServer.test.ts +125 -0
- package/Tests/Server/Services/StatusPageServiceMcp.test.ts +223 -0
- package/Tests/Server/Services/TelemetryExceptionAIFixReadiness.test.ts +26 -3
- package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +12 -0
- package/Tests/Server/Types/Markdown.test.ts +106 -0
- package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +295 -0
- package/Tests/Server/Utils/AI/AIRunTranscript.test.ts +182 -0
- package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +411 -0
- package/Tests/Server/Utils/AI/CodeTools.test.ts +596 -0
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +475 -0
- package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +548 -0
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +142 -0
- package/Tests/Server/Utils/AI/ScheduledMaintenanceTools.test.ts +176 -0
- package/Tests/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.test.ts +63 -0
- package/Tests/Server/Utils/GitHubGetFileContent.test.ts +197 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +502 -0
- package/Tests/Types/AI/AIAgentTaskStatus.test.ts +7 -6
- package/Tests/Types/AI/AIChatPageContext.test.ts +0 -0
- package/Tests/Types/AI/AIRunStatus.test.ts +42 -0
- package/Tests/Types/Log/LogSeverity.test.ts +78 -0
- package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +167 -0
- package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +66 -0
- package/Tests/UI/Utils/AIChatExport/ChatWidgetData.test.ts +319 -0
- package/Tests/UI/Utils/AIChatExport/ChatWidgetMarkdown.test.ts +188 -0
- package/Types/AI/AIAgentTaskStatus.ts +13 -0
- package/Types/AI/AIChatPageContext.ts +137 -0
- package/Types/AI/AIChatTypes.ts +44 -0
- package/Types/AI/AIFixReadiness.ts +46 -0
- package/Types/AI/AIRunStatus.ts +11 -0
- package/Types/Email/EmailTemplateType.ts +2 -0
- package/Types/Log/LogSeverity.ts +61 -0
- package/Types/Monitor/SnmpMonitor/SnmpVersion.ts +40 -0
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +8 -2
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +3 -1
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +8 -2
- package/UI/Components/EditionLabel/EditionLabel.tsx +821 -331
- package/UI/Components/Forms/BasicForm.tsx +26 -8
- package/UI/Styles/Theme.css +1314 -0
- package/UI/Utils/AIChatExport/ChatWidgetData.ts +316 -0
- package/UI/Utils/AIChatExport/ChatWidgetMarkdown.ts +349 -0
- package/UI/Utils/AIChatExport/ConversationMarkdown.ts +177 -0
- package/UI/Utils/AIChatExport/MarkdownBlocks.ts +327 -0
- package/UI/Utils/AIChatExport/MarkdownSafety.ts +215 -0
- package/UI/Utils/DownloadFile.ts +54 -0
- package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js +15 -7
- package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIRun.js +69 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIRunEvent.js +31 -0
- package/build/dist/Models/DatabaseModels/AIRunEvent.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +225 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +27 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +43 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskAPI.js +37 -11
- package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskLogAPI.js +32 -7
- package/build/dist/Server/API/AIAgentTaskLogAPI.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +9 -0
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIReadinessAPI.js +57 -0
- package/build/dist/Server/API/AIReadinessAPI.js.map +1 -0
- package/build/dist/Server/API/CodeFixRunAPI.js +165 -51
- package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryExceptionAPI.js +4 -0
- package/build/dist/Server/API/TelemetryExceptionAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784105912819-BackfillCodeFixTaskType.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784105912819-BackfillCodeFixTaskType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784124919694-AddAITaskNumberAndRunTranscript.js +80 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784124919694-AddAITaskNumberAndRunTranscript.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784135099754-AllowNullAiAgentOnPullRequest.js +37 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784135099754-AllowNullAiAgentOnPullRequest.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784137457184-AddEnableMcpServerToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784137457184-AddEnableMcpServerToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784200000000-AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784200000000-AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +1 -1
- package/build/dist/Server/Services/AIRunEventService.js +8 -0
- package/build/dist/Server/Services/AIRunEventService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +39 -3
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +2 -0
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +39 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +81 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +14 -61
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +8 -8
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/MarkdownSlugify.js +47 -0
- package/build/dist/Server/Types/MarkdownSlugify.js.map +1 -0
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +82 -0
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -0
- package/build/dist/Server/Utils/AI/AIRunTranscript.js +180 -0
- package/build/dist/Server/Utils/AI/AIRunTranscript.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +103 -5
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +55 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +211 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +654 -0
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +389 -0
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +19 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ScheduledMaintenanceTools.js +239 -0
- package/build/dist/Server/Utils/AI/Toolbox/ScheduledMaintenanceTools.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.js +19 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +439 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/StackTraceRepoResolver.js +1 -1
- package/build/dist/Server/Utils/CodeRepository/StackTraceRepoResolver.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +275 -19
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +2 -11
- package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
- package/build/dist/Types/AI/AIAgentTaskStatus.js +12 -0
- package/build/dist/Types/AI/AIAgentTaskStatus.js.map +1 -1
- package/build/dist/Types/AI/AIChatPageContext.js +101 -0
- package/build/dist/Types/AI/AIChatPageContext.js.map +1 -0
- package/build/dist/Types/AI/AIChatTypes.js +2 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
- package/build/dist/Types/AI/AIFixReadiness.js +9 -0
- package/build/dist/Types/AI/AIFixReadiness.js.map +1 -0
- package/build/dist/Types/AI/AIRunStatus.js +11 -0
- package/build/dist/Types/AI/AIRunStatus.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Log/LogSeverity.js +56 -0
- package/build/dist/Types/Log/LogSeverity.js.map +1 -1
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpVersion.js +38 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpVersion.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +9 -2
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +3 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +9 -2
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +424 -139
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +24 -6
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Utils/AIChatExport/ChatWidgetData.js +217 -0
- package/build/dist/UI/Utils/AIChatExport/ChatWidgetData.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/ChatWidgetMarkdown.js +234 -0
- package/build/dist/UI/Utils/AIChatExport/ChatWidgetMarkdown.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +111 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/MarkdownBlocks.js +233 -0
- package/build/dist/UI/Utils/AIChatExport/MarkdownBlocks.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/MarkdownSafety.js +134 -0
- package/build/dist/UI/Utils/AIChatExport/MarkdownSafety.js.map +1 -0
- package/build/dist/UI/Utils/DownloadFile.js +41 -0
- package/build/dist/UI/Utils/DownloadFile.js.map +1 -0
- package/package.json +5 -1
package/Server/Infrastructure/Postgres/SchemaMigrations/1784105912819-BackfillCodeFixTaskType.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class BackfillCodeFixTaskType1784105912819
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name = "BackfillCodeFixTaskType1784105912819";
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* A null AIRun.codeFixTaskType on a CodeFix run has always MEANT
|
|
10
|
+
* FixException: every code-fix run created before task recipes existed was
|
|
11
|
+
* an exception fix, and both the server's read path
|
|
12
|
+
* (CodeFixTaskTypeHelper.fromDatabaseValue) and the dashboard normalize the
|
|
13
|
+
* null to FixException so nobody ever sees it.
|
|
14
|
+
*
|
|
15
|
+
* That normalization only covers reads. Now that the AI Tasks list filters
|
|
16
|
+
* through the standard CRUD, a "Fix Exception" filter compiles to
|
|
17
|
+
* `codeFixTaskType IN ('FixException')`, which NULL never matches — the
|
|
18
|
+
* oldest runs would silently vanish from the filtered list. The bespoke
|
|
19
|
+
* endpoint papered over this with an equalToOrNull query it built itself;
|
|
20
|
+
* that endpoint is gone, so the meaning gets written into the data instead.
|
|
21
|
+
*
|
|
22
|
+
* Scoped to runType = 'CodeFix': codeFixTaskType is meaningless on chat and
|
|
23
|
+
* investigation runs and must stay null there.
|
|
24
|
+
*/
|
|
25
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
26
|
+
await queryRunner.query(
|
|
27
|
+
`UPDATE "AIRun" SET "codeFixTaskType" = 'FixException' WHERE "runType" = 'CodeFix' AND "codeFixTaskType" IS NULL`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* Deliberately a no-op. The rows this set are indistinguishable from rows
|
|
33
|
+
* that already said FixException, so nulling them back would corrupt data
|
|
34
|
+
* this migration never touched. Reverting is also unnecessary: the read path
|
|
35
|
+
* still normalizes null to FixException, so an older build reads these rows
|
|
36
|
+
* exactly as it did before.
|
|
37
|
+
*/
|
|
38
|
+
public async down(_queryRunner: QueryRunner): Promise<void> {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Two additions, both in service of debugging a code-fix run that went wrong.
|
|
5
|
+
*
|
|
6
|
+
* 1. AIRun.taskNumber + Project.aiRunCounter — the per-project "#42" handle,
|
|
7
|
+
* allocated exactly like Incident.incidentNumber (ProjectService holds the
|
|
8
|
+
* counter behind a Redis semaphore; see incrementAndGetAIRunCounter).
|
|
9
|
+
* 2. AIRunEvent.contentPayload — the verbatim prompt/response/tool transcript.
|
|
10
|
+
*
|
|
11
|
+
* The DDL here was produced by `npm run generate-postgres-migration`. Two
|
|
12
|
+
* unrelated OnCallDutyPolicyScheduleLayer default changes the generator also
|
|
13
|
+
* emitted (pre-existing whitespace drift between the entity defaults and the
|
|
14
|
+
* local schema) were dropped: they are not part of this change.
|
|
15
|
+
*
|
|
16
|
+
* The backfill rides along in the same migration rather than a follow-up so
|
|
17
|
+
* there is no window in which existing tasks are on-screen with a blank
|
|
18
|
+
* number.
|
|
19
|
+
*/
|
|
20
|
+
export class AddAITaskNumberAndRunTranscript1784124919694
|
|
21
|
+
implements MigrationInterface
|
|
22
|
+
{
|
|
23
|
+
public name: string = "AddAITaskNumberAndRunTranscript1784124919694";
|
|
24
|
+
|
|
25
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
26
|
+
await queryRunner.query(
|
|
27
|
+
`ALTER TABLE "Project" ADD "aiRunCounter" integer NOT NULL DEFAULT '0'`,
|
|
28
|
+
);
|
|
29
|
+
await queryRunner.query(`ALTER TABLE "AIRun" ADD "taskNumber" integer`);
|
|
30
|
+
await queryRunner.query(
|
|
31
|
+
`ALTER TABLE "AIRunEvent" ADD "contentPayload" jsonb`,
|
|
32
|
+
);
|
|
33
|
+
await queryRunner.query(
|
|
34
|
+
`CREATE INDEX "IDX_ede30aa8b14a167586bd555fa9" ON "AIRun" ("taskNumber") `,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Number the tasks that already exist, oldest first, per project — so the
|
|
39
|
+
* list does not read "#1, -, -, #2" the day this ships. Scoped to
|
|
40
|
+
* runType = 'CodeFix' because only code-fix runs are tasks; chat and
|
|
41
|
+
* investigation runs share this table and must stay null.
|
|
42
|
+
*
|
|
43
|
+
* Soft-deleted rows are numbered too. Not to prevent reuse (a skipped row
|
|
44
|
+
* would hold no number to reuse) but to keep the sequence a stable
|
|
45
|
+
* function of creation order: a run that is soft-deleted later must not
|
|
46
|
+
* renumber the runs after it, and an undeleted row must not collide with
|
|
47
|
+
* a number already handed out. The cost is gaps in what the list shows,
|
|
48
|
+
* which is the same thing incidents do.
|
|
49
|
+
*/
|
|
50
|
+
await queryRunner.query(`
|
|
51
|
+
WITH numbered AS (
|
|
52
|
+
SELECT
|
|
53
|
+
"_id",
|
|
54
|
+
ROW_NUMBER() OVER (
|
|
55
|
+
PARTITION BY "projectId"
|
|
56
|
+
ORDER BY "createdAt" ASC, "_id" ASC
|
|
57
|
+
) AS "rowNumber"
|
|
58
|
+
FROM "AIRun"
|
|
59
|
+
WHERE "runType" = 'CodeFix'
|
|
60
|
+
)
|
|
61
|
+
UPDATE "AIRun"
|
|
62
|
+
SET "taskNumber" = numbered."rowNumber"
|
|
63
|
+
FROM numbered
|
|
64
|
+
WHERE "AIRun"."_id" = numbered."_id"
|
|
65
|
+
`);
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* Advance each project's counter past what the backfill just handed out,
|
|
69
|
+
* so the next allocation continues the sequence instead of restarting at
|
|
70
|
+
* #1 and colliding. Projects with no code-fix runs keep the 0 default.
|
|
71
|
+
*/
|
|
72
|
+
await queryRunner.query(`
|
|
73
|
+
UPDATE "Project"
|
|
74
|
+
SET "aiRunCounter" = "maxPerProject"."maxTaskNumber"
|
|
75
|
+
FROM (
|
|
76
|
+
SELECT "projectId", MAX("taskNumber") AS "maxTaskNumber"
|
|
77
|
+
FROM "AIRun"
|
|
78
|
+
WHERE "runType" = 'CodeFix' AND "taskNumber" IS NOT NULL
|
|
79
|
+
GROUP BY "projectId"
|
|
80
|
+
) AS "maxPerProject"
|
|
81
|
+
WHERE "Project"."_id" = "maxPerProject"."projectId"
|
|
82
|
+
`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
86
|
+
await queryRunner.query(
|
|
87
|
+
`DROP INDEX "public"."IDX_ede30aa8b14a167586bd555fa9"`,
|
|
88
|
+
);
|
|
89
|
+
await queryRunner.query(
|
|
90
|
+
`ALTER TABLE "AIRunEvent" DROP COLUMN "contentPayload"`,
|
|
91
|
+
);
|
|
92
|
+
await queryRunner.query(`ALTER TABLE "AIRun" DROP COLUMN "taskNumber"`);
|
|
93
|
+
await queryRunner.query(`ALTER TABLE "Project" DROP COLUMN "aiRunCounter"`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* A pull request can now be proposed from an AI chat conversation, which has
|
|
5
|
+
* no AIAgent behind it (the AIAgent record belongs to the autonomous fix
|
|
6
|
+
* worker). aiAgentId becomes nullable so chat-authored PRs can be recorded at
|
|
7
|
+
* all — and recording them is what makes them count against the per-repository
|
|
8
|
+
* open-PR cap, which reads AIAgentTaskPullRequest rows.
|
|
9
|
+
*
|
|
10
|
+
* A null aiAgentId therefore means "proposed from chat". Existing rows all
|
|
11
|
+
* have an agent, so dropping NOT NULL needs no backfill and the down migration
|
|
12
|
+
* is safe until the first chat-authored PR exists.
|
|
13
|
+
*/
|
|
14
|
+
export class AllowNullAiAgentOnPullRequest1784135099754
|
|
15
|
+
implements MigrationInterface
|
|
16
|
+
{
|
|
17
|
+
public name = "AllowNullAiAgentOnPullRequest1784135099754";
|
|
18
|
+
|
|
19
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
20
|
+
/*
|
|
21
|
+
* The FK is dropped and re-added because Postgres will not alter a column's
|
|
22
|
+
* nullability while a foreign key references it. ON DELETE CASCADE is
|
|
23
|
+
* preserved exactly as it was.
|
|
24
|
+
*/
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "AIAgentTaskPullRequest" DROP CONSTRAINT "FK_b1e01ecfe9eecfb555428471073"`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "AIAgentTaskPullRequest" ALTER COLUMN "aiAgentId" DROP NOT NULL`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "AIAgentTaskPullRequest" ADD CONSTRAINT "FK_b1e01ecfe9eecfb555428471073" FOREIGN KEY ("aiAgentId") REFERENCES "AIAgent"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
37
|
+
/*
|
|
38
|
+
* Restoring NOT NULL fails if any chat-authored (null-agent) rows exist.
|
|
39
|
+
* That is deliberate: silently deleting or reassigning a customer's pull
|
|
40
|
+
* request records to satisfy a rollback would be worse than a loud failure.
|
|
41
|
+
*/
|
|
42
|
+
await queryRunner.query(
|
|
43
|
+
`ALTER TABLE "AIAgentTaskPullRequest" DROP CONSTRAINT "FK_b1e01ecfe9eecfb555428471073"`,
|
|
44
|
+
);
|
|
45
|
+
await queryRunner.query(
|
|
46
|
+
`ALTER TABLE "AIAgentTaskPullRequest" ALTER COLUMN "aiAgentId" SET NOT NULL`,
|
|
47
|
+
);
|
|
48
|
+
await queryRunner.query(
|
|
49
|
+
`ALTER TABLE "AIAgentTaskPullRequest" ADD CONSTRAINT "FK_b1e01ecfe9eecfb555428471073" FOREIGN KEY ("aiAgentId") REFERENCES "AIAgent"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Status page owners can now stop AI agents from reading their status page over
|
|
5
|
+
* the public OneUptime MCP server. MCP access stays on unless it is turned off,
|
|
6
|
+
* so the column defaults to true.
|
|
7
|
+
*
|
|
8
|
+
* ADD COLUMN ... NOT NULL DEFAULT true populates every existing row with true in
|
|
9
|
+
* the same statement, so no backfill is needed and existing status pages keep
|
|
10
|
+
* working exactly as they do today. That is what makes the
|
|
11
|
+
* `enableMcpServer: true` predicate in StatusPageService.isMcpServerEnabled
|
|
12
|
+
* safe — there are no NULL rows for it to silently drop.
|
|
13
|
+
*/
|
|
14
|
+
export class AddEnableMcpServerToStatusPage1784137457184
|
|
15
|
+
implements MigrationInterface
|
|
16
|
+
{
|
|
17
|
+
public name = "AddEnableMcpServerToStatusPage1784137457184";
|
|
18
|
+
|
|
19
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
20
|
+
await queryRunner.query(
|
|
21
|
+
`ALTER TABLE "StatusPage" ADD "enableMcpServer" boolean NOT NULL DEFAULT true`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
26
|
+
await queryRunner.query(
|
|
27
|
+
`ALTER TABLE "StatusPage" DROP COLUMN "enableMcpServer"`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan1784200000000
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name = "AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan1784200000000";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(
|
|
10
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" ADD "snmpV3SecurityLevel" character varying(100)`,
|
|
11
|
+
);
|
|
12
|
+
await queryRunner.query(
|
|
13
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" ADD "snmpV3Username" character varying(100)`,
|
|
14
|
+
);
|
|
15
|
+
await queryRunner.query(
|
|
16
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" ADD "snmpV3AuthProtocol" character varying(100)`,
|
|
17
|
+
);
|
|
18
|
+
await queryRunner.query(
|
|
19
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" ADD "snmpV3AuthKey" text`,
|
|
20
|
+
);
|
|
21
|
+
await queryRunner.query(
|
|
22
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" ADD "snmpV3PrivProtocol" character varying(100)`,
|
|
23
|
+
);
|
|
24
|
+
await queryRunner.query(
|
|
25
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" ADD "snmpV3PrivKey" text`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
30
|
+
await queryRunner.query(
|
|
31
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" DROP COLUMN "snmpV3PrivKey"`,
|
|
32
|
+
);
|
|
33
|
+
await queryRunner.query(
|
|
34
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" DROP COLUMN "snmpV3PrivProtocol"`,
|
|
35
|
+
);
|
|
36
|
+
await queryRunner.query(
|
|
37
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" DROP COLUMN "snmpV3AuthKey"`,
|
|
38
|
+
);
|
|
39
|
+
await queryRunner.query(
|
|
40
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" DROP COLUMN "snmpV3AuthProtocol"`,
|
|
41
|
+
);
|
|
42
|
+
await queryRunner.query(
|
|
43
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" DROP COLUMN "snmpV3Username"`,
|
|
44
|
+
);
|
|
45
|
+
await queryRunner.query(
|
|
46
|
+
`ALTER TABLE "NetworkDeviceDiscoveryScan" DROP COLUMN "snmpV3SecurityLevel"`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -450,6 +450,11 @@ import { AddSentinelInsightFlags1784010274994 } from "./1784010274994-AddSentine
|
|
|
450
450
|
import { RenameSentinelToAI1784030612266 } from "./1784030612266-RenameSentinelToAI";
|
|
451
451
|
import { MigrationName1784033837629 } from "./1784033837629-MigrationName";
|
|
452
452
|
import { AddCreatedByUserToUser1784048917994 } from "./1784048917994-AddCreatedByUserToUser";
|
|
453
|
+
import { BackfillCodeFixTaskType1784105912819 } from "./1784105912819-BackfillCodeFixTaskType";
|
|
454
|
+
import { AddAITaskNumberAndRunTranscript1784124919694 } from "./1784124919694-AddAITaskNumberAndRunTranscript";
|
|
455
|
+
import { AllowNullAiAgentOnPullRequest1784135099754 } from "./1784135099754-AllowNullAiAgentOnPullRequest";
|
|
456
|
+
import { AddEnableMcpServerToStatusPage1784137457184 } from "./1784137457184-AddEnableMcpServerToStatusPage";
|
|
457
|
+
import { AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan1784200000000 } from "./1784200000000-AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan";
|
|
453
458
|
|
|
454
459
|
export default [
|
|
455
460
|
InitialMigration,
|
|
@@ -904,4 +909,9 @@ export default [
|
|
|
904
909
|
RenameSentinelToAI1784030612266,
|
|
905
910
|
MigrationName1784033837629,
|
|
906
911
|
AddCreatedByUserToUser1784048917994,
|
|
912
|
+
BackfillCodeFixTaskType1784105912819,
|
|
913
|
+
AddAITaskNumberAndRunTranscript1784124919694,
|
|
914
|
+
AllowNullAiAgentOnPullRequest1784135099754,
|
|
915
|
+
AddEnableMcpServerToStatusPage1784137457184,
|
|
916
|
+
AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan1784200000000,
|
|
907
917
|
];
|
|
@@ -59,7 +59,7 @@ export default class MasterAdminAuthorization {
|
|
|
59
59
|
* Same as isAuthorizedMasterAdminMiddleware, but ALSO accepts the instance-wide
|
|
60
60
|
* master API key (Admin Dashboard → Settings → API Key) supplied in the
|
|
61
61
|
* `apikey` header. The master key has root/master-admin access, so this lets
|
|
62
|
-
* automated callers reach the master-admin
|
|
62
|
+
* automated callers reach the master-admin OneUptime Health endpoints with the
|
|
63
63
|
* key instead of a logged-in master-admin session.
|
|
64
64
|
*
|
|
65
65
|
* Deliberately scoped to the read-only health / diagnostics routes only. It is
|
|
@@ -65,7 +65,7 @@ export default class ProjectMiddleware {
|
|
|
65
65
|
* (Admin Dashboard → Settings → API Key) and that key is currently enabled.
|
|
66
66
|
* The master key has root/master-admin access, so this is shared with
|
|
67
67
|
* MasterAdminAuthorization to let the key reach master-admin-only endpoints
|
|
68
|
-
* (e.g.
|
|
68
|
+
* (e.g. OneUptime Health) as well.
|
|
69
69
|
*/
|
|
70
70
|
@CaptureSpan()
|
|
71
71
|
public static async isMasterApiKey(apiKey: ObjectID): Promise<boolean> {
|
|
@@ -2,13 +2,17 @@ import PositiveNumber from "../../Types/PositiveNumber";
|
|
|
2
2
|
import ObjectID from "../../Types/ObjectID";
|
|
3
3
|
import { JSONObject } from "../../Types/JSON";
|
|
4
4
|
import AIRunEventType from "../../Types/AI/AIRunEventType";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
AIRunEventContentPayload,
|
|
7
|
+
AIRunEventResultSummary,
|
|
8
|
+
} from "../../Types/AI/AIChatTypes";
|
|
6
9
|
import CountBy from "../Types/Database/CountBy";
|
|
7
10
|
import FindBy from "../Types/Database/FindBy";
|
|
8
11
|
import { OnFind } from "../Types/Database/Hooks";
|
|
9
12
|
import DatabaseService from "./DatabaseService";
|
|
10
13
|
import Model from "../../Models/DatabaseModels/AIRunEvent";
|
|
11
14
|
import { pinQueryToRequestingUser } from "../Utils/AI/AIChatPrivacyFilter";
|
|
15
|
+
import AIRunTranscript from "../Utils/AI/AIRunTranscript";
|
|
12
16
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
13
17
|
import logger from "../Utils/Logger";
|
|
14
18
|
|
|
@@ -36,6 +40,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
36
40
|
toolArguments?: JSONObject | undefined;
|
|
37
41
|
resultSummary?: AIRunEventResultSummary | undefined;
|
|
38
42
|
citationId?: string | undefined;
|
|
43
|
+
contentPayload?: AIRunEventContentPayload | undefined;
|
|
39
44
|
}): Promise<void> {
|
|
40
45
|
try {
|
|
41
46
|
const sequence: number = (
|
|
@@ -63,6 +68,15 @@ export class Service extends DatabaseService<Model> {
|
|
|
63
68
|
if (data.citationId) {
|
|
64
69
|
event.citationId = data.citationId;
|
|
65
70
|
}
|
|
71
|
+
/*
|
|
72
|
+
* Capped here rather than at the call sites, so no caller can write an
|
|
73
|
+
* unbounded transcript row by forgetting to clamp.
|
|
74
|
+
*/
|
|
75
|
+
if (data.contentPayload) {
|
|
76
|
+
event.contentPayload = AIRunTranscript.clampPayload(
|
|
77
|
+
data.contentPayload,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
66
80
|
|
|
67
81
|
await this.create({
|
|
68
82
|
data: event,
|
|
@@ -12,12 +12,15 @@ import CodeFixTaskType, {
|
|
|
12
12
|
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
13
13
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
14
14
|
import CountBy from "../Types/Database/CountBy";
|
|
15
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
15
16
|
import FindBy from "../Types/Database/FindBy";
|
|
16
|
-
import { OnFind } from "../Types/Database/Hooks";
|
|
17
|
+
import { OnCreate, OnFind } from "../Types/Database/Hooks";
|
|
17
18
|
import DatabaseService from "./DatabaseService";
|
|
19
|
+
import ProjectService from "./ProjectService";
|
|
18
20
|
import Model from "../../Models/DatabaseModels/AIRun";
|
|
19
|
-
import {
|
|
21
|
+
import { applyAIRunPrivacyFilter } from "../Utils/AI/AIRunPrivacyFilter";
|
|
20
22
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
23
|
+
import logger from "../Utils/Logger";
|
|
21
24
|
import { UpdateQueryBuilder, UpdateResult } from "typeorm";
|
|
22
25
|
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
|
|
23
26
|
|
|
@@ -328,26 +331,59 @@ export class Service extends DatabaseService<Model> {
|
|
|
328
331
|
return { runId: run.id, verdict: data.verdict };
|
|
329
332
|
}
|
|
330
333
|
|
|
334
|
+
/*
|
|
335
|
+
* Stamp CodeFix runs with the project's next task number, so every AI task
|
|
336
|
+
* has the short human-citable handle "#42" that incidents and alerts have.
|
|
337
|
+
*
|
|
338
|
+
* CodeFix only, on purpose: chat turns and investigations share this table
|
|
339
|
+
* but are not tasks, and numbering them would advance the counter on every
|
|
340
|
+
* Ask-AI message — the AI Tasks list would then read #7, #31, #244.
|
|
341
|
+
*
|
|
342
|
+
* Best-effort. The counter needs Redis (Semaphore) and a second write; a
|
|
343
|
+
* blip there must not lose a fix run, because the number is a display
|
|
344
|
+
* convenience and the run itself is the work. On failure the run is created
|
|
345
|
+
* with a null taskNumber, which the list renders as "-" and the detail page
|
|
346
|
+
* hides — the run stays fully usable, just uncitable.
|
|
347
|
+
*/
|
|
348
|
+
protected override async onBeforeCreate(
|
|
349
|
+
createBy: CreateBy<Model>,
|
|
350
|
+
): Promise<OnCreate<Model>> {
|
|
351
|
+
const projectId: ObjectID | undefined =
|
|
352
|
+
createBy.data.projectId || createBy.props.tenantId || undefined;
|
|
353
|
+
|
|
354
|
+
if (createBy.data.runType === AIRunType.CodeFix && projectId) {
|
|
355
|
+
try {
|
|
356
|
+
createBy.data.taskNumber =
|
|
357
|
+
await ProjectService.incrementAndGetAIRunCounter(projectId);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
logger.error(
|
|
360
|
+
`Could not allocate a task number for a code-fix run in project ${projectId.toString()}; creating it unnumbered: ${error}`,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return { createBy, carryForward: null };
|
|
366
|
+
}
|
|
367
|
+
|
|
331
368
|
protected override async onBeforeFind(
|
|
332
369
|
findBy: FindBy<Model>,
|
|
333
370
|
): Promise<OnFind<Model>> {
|
|
334
|
-
findBy.query =
|
|
335
|
-
findBy.query,
|
|
336
|
-
findBy.props,
|
|
337
|
-
"userId",
|
|
338
|
-
);
|
|
371
|
+
findBy.query = applyAIRunPrivacyFilter(findBy.query, findBy.props);
|
|
339
372
|
return { findBy, carryForward: null };
|
|
340
373
|
}
|
|
341
374
|
|
|
375
|
+
/*
|
|
376
|
+
* countBy MUST repeat the filter. DatabaseService has no onBeforeCount hook
|
|
377
|
+
* (only onCountSuccess / onCountError), and BaseAPI.getList issues findBy
|
|
378
|
+
* and countBy with the SAME client query, so filtering in onBeforeFind alone
|
|
379
|
+
* would return a correctly-scoped list beside an unscoped project-wide
|
|
380
|
+
* count on every page. IncidentService repeats it for the same reason.
|
|
381
|
+
*/
|
|
342
382
|
@CaptureSpan()
|
|
343
383
|
public override async countBy(
|
|
344
384
|
countBy: CountBy<Model>,
|
|
345
385
|
): Promise<PositiveNumber> {
|
|
346
|
-
countBy.query =
|
|
347
|
-
countBy.query,
|
|
348
|
-
countBy.props,
|
|
349
|
-
"userId",
|
|
350
|
-
);
|
|
386
|
+
countBy.query = applyAIRunPrivacyFilter(countBy.query, countBy.props);
|
|
351
387
|
return super.countBy(countBy);
|
|
352
388
|
}
|
|
353
389
|
}
|
|
@@ -333,6 +333,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
333
333
|
select: {
|
|
334
334
|
_id: true,
|
|
335
335
|
name: true,
|
|
336
|
+
description: true,
|
|
336
337
|
llmType: true,
|
|
337
338
|
modelName: true,
|
|
338
339
|
isDefault: true,
|
|
@@ -356,6 +357,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
356
357
|
select: {
|
|
357
358
|
_id: true,
|
|
358
359
|
name: true,
|
|
360
|
+
description: true,
|
|
359
361
|
llmType: true,
|
|
360
362
|
modelName: true,
|
|
361
363
|
isDefault: true,
|
|
@@ -2144,6 +2144,48 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
2144
2144
|
}
|
|
2145
2145
|
}
|
|
2146
2146
|
|
|
2147
|
+
/*
|
|
2148
|
+
* Allocate the next AI task number for a project. Unlike the counters
|
|
2149
|
+
* above there is no companion prefix column — AI task numbers are not
|
|
2150
|
+
* user-customizable, so the caller renders a plain "#N".
|
|
2151
|
+
*/
|
|
2152
|
+
@CaptureSpan()
|
|
2153
|
+
public async incrementAndGetAIRunCounter(
|
|
2154
|
+
projectId: ObjectID,
|
|
2155
|
+
): Promise<number> {
|
|
2156
|
+
const mutex: SemaphoreMutex = await Semaphore.lock({
|
|
2157
|
+
key: projectId.toString(),
|
|
2158
|
+
namespace: "ProjectService.aiRunCounter",
|
|
2159
|
+
});
|
|
2160
|
+
|
|
2161
|
+
try {
|
|
2162
|
+
await this.atomicIncrementColumnValueByOne({
|
|
2163
|
+
id: projectId,
|
|
2164
|
+
columnName: "aiRunCounter",
|
|
2165
|
+
});
|
|
2166
|
+
|
|
2167
|
+
const project: Model | null = await this.findOneById({
|
|
2168
|
+
id: projectId,
|
|
2169
|
+
select: {
|
|
2170
|
+
aiRunCounter: true,
|
|
2171
|
+
},
|
|
2172
|
+
props: {
|
|
2173
|
+
isRoot: true,
|
|
2174
|
+
},
|
|
2175
|
+
});
|
|
2176
|
+
|
|
2177
|
+
if (!project || project.aiRunCounter === undefined) {
|
|
2178
|
+
throw new BadDataException(
|
|
2179
|
+
`Could not read aiRunCounter for project ${projectId.toString()}`,
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
return project.aiRunCounter;
|
|
2184
|
+
} finally {
|
|
2185
|
+
await Semaphore.release(mutex);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2147
2189
|
@CaptureSpan()
|
|
2148
2190
|
public async isSMSNotificationsEnabled(
|
|
2149
2191
|
projectId: ObjectID,
|
|
@@ -118,6 +118,93 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
118
118
|
this.statusPageUrlCache.clear();
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/*
|
|
122
|
+
* Mirrors `resolveStatusPageIdOrThrow` in `Common/Server/API/StatusPageAPI.ts`
|
|
123
|
+
* (module-private there), but returns null instead of throwing so callers can
|
|
124
|
+
* collapse "no such page" and "page exists but is gated" into one answer.
|
|
125
|
+
* The dot check — not a UUID check — is the discriminator, so both resolvers
|
|
126
|
+
* agree on dotless hostnames like "localhost".
|
|
127
|
+
*/
|
|
128
|
+
@CaptureSpan()
|
|
129
|
+
public async resolveStatusPageIdOrNull(
|
|
130
|
+
statusPageIdOrDomain: string,
|
|
131
|
+
): Promise<ObjectID | null> {
|
|
132
|
+
if (!statusPageIdOrDomain) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (statusPageIdOrDomain.includes(".")) {
|
|
137
|
+
const statusPageDomain: StatusPageDomain | null =
|
|
138
|
+
await StatusPageDomainService.findOneBy({
|
|
139
|
+
query: {
|
|
140
|
+
fullDomain: statusPageIdOrDomain,
|
|
141
|
+
domain: {
|
|
142
|
+
isVerified: true,
|
|
143
|
+
} as any,
|
|
144
|
+
},
|
|
145
|
+
select: {
|
|
146
|
+
statusPageId: true,
|
|
147
|
+
},
|
|
148
|
+
props: {
|
|
149
|
+
isRoot: true,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (!statusPageDomain || !statusPageDomain.statusPageId) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return statusPageDomain.statusPageId;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
ObjectID.validateUUID(statusPageIdOrDomain);
|
|
162
|
+
return new ObjectID(statusPageIdOrDomain);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
logger.error(
|
|
165
|
+
`Error converting statusPageIdOrDomain to ObjectID: ${statusPageIdOrDomain}`,
|
|
166
|
+
);
|
|
167
|
+
logger.error(err);
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/*
|
|
173
|
+
* Folds existence and the `enableMcpServer` flag into a single query so the
|
|
174
|
+
* caller cannot distinguish "not found" from "disabled". The public MCP tools
|
|
175
|
+
* need no API key, so a distinct "disabled" message would be a status page
|
|
176
|
+
* enumeration oracle.
|
|
177
|
+
*
|
|
178
|
+
* Throws on database failure rather than returning false, so the caller
|
|
179
|
+
* reports an error instead of reporting the page as gated.
|
|
180
|
+
*/
|
|
181
|
+
@CaptureSpan()
|
|
182
|
+
public async isMcpServerEnabled(
|
|
183
|
+
statusPageIdOrDomain: string,
|
|
184
|
+
): Promise<boolean> {
|
|
185
|
+
const statusPageId: ObjectID | null =
|
|
186
|
+
await this.resolveStatusPageIdOrNull(statusPageIdOrDomain);
|
|
187
|
+
|
|
188
|
+
if (!statusPageId) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const statusPage: StatusPage | null = await this.findOneBy({
|
|
193
|
+
query: {
|
|
194
|
+
_id: statusPageId,
|
|
195
|
+
enableMcpServer: true,
|
|
196
|
+
},
|
|
197
|
+
select: {
|
|
198
|
+
_id: true,
|
|
199
|
+
},
|
|
200
|
+
props: {
|
|
201
|
+
isRoot: true,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return Boolean(statusPage);
|
|
206
|
+
}
|
|
207
|
+
|
|
121
208
|
public static getDefaultEmailFooterText(): string {
|
|
122
209
|
return "This is an automated email sent to you because you are subscribed to this Status Page.";
|
|
123
210
|
}
|