Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e986247
test(docs): add comprehensive tests and monitoring for llms.txt endpo…
devin-ai-integration[bot] Nov 3, 2025
efbfae1
test(docs): remove middleware tests due to server-only constraints
devin-ai-integration[bot] Nov 3, 2025
a8466e7
Update scripts/monitor/check-llms-md-endpoints.ts
dsinghvi Nov 4, 2025
043ea04
chore(ci): align incident secret naming and enable PR testing
devin-ai-integration[bot] Nov 5, 2025
413d83c
Merge branch 'app' into devin/1762144039-add-llms-txt-tests-monitoring
devin-ai-integration[bot] Nov 5, 2025
dc7dd6d
fix(docs): use slug search parameter in markdown route
devin-ai-integration[bot] Nov 5, 2025
a423f9c
fix(ci): correct workflow trigger to use push instead of pull_request
devin-ai-integration[bot] Nov 5, 2025
9278b68
Merge branch 'devin/1762144039-add-llms-txt-tests-monitoring' of http…
devin-ai-integration[bot] Nov 5, 2025
9d1d53e
fix(ci): remove node-fetch install as Node 20 has global fetch
devin-ai-integration[bot] Nov 5, 2025
39da4a4
fix(ci): fix URL construction in monitoring script to prevent path du…
devin-ai-integration[bot] Nov 5, 2025
66bf31f
wip: add incident.io constants and interface for duplicate prevention
devin-ai-integration[bot] Nov 5, 2025
5192121
Merge branch 'devin/1762144039-add-llms-txt-tests-monitoring' of http…
devin-ai-integration[bot] Nov 5, 2025
07ec50a
fix(monitor): implement incident.io duplicate prevention and add DRY_…
devin-ai-integration[bot] Nov 5, 2025
d9e655a
feat(monitor): add TEST_MODE support to label incidents as tests
devin-ai-integration[bot] Nov 5, 2025
423abe0
fix(monitor): use CANCELED status, add DRY_RUN guard to updateInciden…
devin-ai-integration[bot] Nov 5, 2025
926ee56
Merge updates from app branch
devin-ai-integration[bot] Nov 5, 2025
d9c164b
fix(monitor): add response.ok check and make Slack alerts opt-in
devin-ai-integration[bot] Nov 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/monitor-llms-md-endpoints.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Monitor LLM-friendly Docs Endpoints

on:
schedule:
# Run every 5 minutes
- cron: '*/5 * * * *'
workflow_dispatch: # Allow manual triggering

jobs:
monitor:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: |
npm install -g tsx
npm install node-fetch
- name: Run monitoring script
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_DOCS_INCIDENTS }}
INCIDENT_IO_API_KEY: ${{ secrets.INCIDENT_IO_API_KEY }}
run: |
tsx scripts/monitor/check-llms-md-endpoints.ts
- name: Report status
if: always()
run: |
if [ $? -eq 0 ]; then
echo "✅ All endpoints healthy"
else
echo "❌ Some endpoints failed - alerts sent"
fi
3 changes: 2 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,8 @@
"packages/workers/proxy/src/websocket.ts",
"packages/generator-cli/src/**/*.ts",
"packages/commons/visual-editor-server/src/mongodb-client.ts",
"servers/fdr-lambda/src/index.ts"
"servers/fdr-lambda/src/index.ts",
"scripts/monitor/**/*.ts"
],
"linter": {
"rules": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { NextRequest } from "next/server";
import { describe, expect, it, vi } from "vitest";

describe("markdown route slug handling", () => {
const createMockRequest = (pathname: string, searchParams: Record<string, string> = {}) => {
const url = new URL(`https://example.com${pathname}`);
Object.entries(searchParams).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
return new NextRequest(url);
};

it("should prefer slug from search params over pathname", () => {
const request = createMockRequest("/api/fern-docs/markdown", { slug: "docs/quickstart" });

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("docs/quickstart");
});

it("should fallback to pathname parsing when slug param is not present", () => {
const request = createMockRequest("/docs/quickstart.md");

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("/docs/quickstart");
});

it("should handle .mdx extension in pathname fallback", () => {
const request = createMockRequest("/docs/quickstart.mdx");

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("/docs/quickstart");
});

it("should handle nested paths in slug param", () => {
const request = createMockRequest("/api/fern-docs/markdown", {
slug: "learn/sdks/overview/quickstart"
});

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("learn/sdks/overview/quickstart");
});

it("should handle empty slug param", () => {
const request = createMockRequest("/api/fern-docs/markdown", { slug: "" });

const slugParam = request.nextUrl.searchParams.get("slug");
const slug = slugParam ?? request.nextUrl.pathname.replace(/\.(md|mdx)$/, "");

expect(slug).toBe("");
});
});
Loading