diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e5b92f..a4cffe6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: name: Lint & Typecheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: pnpm/action-setup@v4 with: @@ -55,7 +55,7 @@ jobs: BETTER_AUTH_SECRET: ci-test-secret-32chars-xxxxxxxxx ENCRYPTION_KEY: ci-test-encrypt-key-32chars-xxxx steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: pnpm/action-setup@v4 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7ff1453..10b49e8 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,7 +18,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -42,7 +42,7 @@ jobs: env: APP_VERSION: ${{ steps.version.outputs.version }} - - uses: actions/upload-pages-artifact@v4 + - uses: actions/upload-pages-artifact@v5 with: path: apps/docs/dist/ diff --git a/.github/workflows/pr-docker-build.yml b/.github/workflows/pr-docker-build.yml index 9631fab..df06ea7 100644 --- a/.github/workflows/pr-docker-build.yml +++ b/.github/workflows/pr-docker-build.yml @@ -13,7 +13,7 @@ jobs: packages: write pull-requests: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: docker/setup-qemu-action@v4 @@ -38,7 +38,7 @@ jobs: cache-to: type=gha,mode=max - name: Comment on PR - uses: actions/github-script@v8 + uses: actions/github-script@v9 with: script: | const image = `ghcr.io/${{ github.repository }}:pr-${{ github.event.pull_request.number }}`; @@ -85,7 +85,7 @@ jobs: env: APP_IMAGE: ghcr.io/${{ github.repository }}:pr-${{ github.event.pull_request.number }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Generate random credentials run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4731e3..f17357d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: version: ${{ steps.version.outputs.version }} tag: ${{ steps.version.outputs.tag }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -73,7 +73,7 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: docker/setup-qemu-action@v4 diff --git a/Dockerfile b/Dockerfile index 835e385..aeda1d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ # ---------- base ---------- -FROM node:24-slim AS base +FROM node:25-slim AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable && corepack prepare pnpm@10.30.3 --activate +# Node 25 dropped bundled corepack, so install pnpm directly. +RUN npm install -g pnpm@10.30.3 # ---------- deps ---------- FROM base AS deps @@ -97,7 +98,6 @@ COPY --from=build-worker /app/apps/worker/worker.mjs ./apps/worker/worker.mjs COPY --from=build-spa /app/apps/frontend/dist /usr/share/nginx/html RUN useradd -r -M -d /data -s /bin/false archmax \ - && corepack disable \ && mkdir -p /data /tmp/redis \ && chown -R archmax:archmax /data /tmp/redis /var/log diff --git a/apps/api/package.json b/apps/api/package.json index d76fa36..e9942b9 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -17,23 +17,23 @@ }, "dependencies": { "@archmax/core": "workspace:*", - "@hono/node-server": "^1", + "@hono/node-server": "^1.19.14", "@hono/zod-validator": "^0.7.6", - "@langchain/core": "^1.1.39", - "@langchain/openai": "^1.4.2", + "@langchain/core": "^1.1.40", + "@langchain/openai": "^1.4.4", "@modelcontextprotocol/hono": "2.0.0-alpha.2", "@modelcontextprotocol/server": "2.0.0-alpha.2", - "better-auth": "^1.5.6", - "deepagents": "^1.8.8", - "dotenv": "^17.3.1", - "hono": "^4", + "better-auth": "^1.6.5", + "deepagents": "^1.9.0", + "dotenv": "^17.4.2", + "hono": "^4.12.14", "js-yaml": "^4.1.1", "mongodb": "^7.1.1", "mongoose": "^9.4.1", "zod": "^4.3.6" }, "devDependencies": { - "@types/node": "^20", + "@types/node": "^25", "tsx": "^4.19.2", "typescript": "5.9.3" } diff --git a/apps/api/src/routes/conversations.ts b/apps/api/src/routes/conversations.ts index 8a183cc..d7f9d6f 100644 --- a/apps/api/src/routes/conversations.ts +++ b/apps/api/src/routes/conversations.ts @@ -1,15 +1,23 @@ import { Hono } from "hono"; +import { zValidator } from "@hono/zod-validator"; +import { z } from "zod/v4"; import { connectDB } from "@archmax/core/infra/db"; import { Conversation } from "@archmax/core/models/index"; import { isStreamActive } from "@archmax/core/streaming/stream-bridge"; import { AppError } from "../utils/errors"; +const listQuerySchema = z.object({ + limit: z.string().optional(), + skip: z.string().optional(), +}); + const app = new Hono() - .get("/", async (c) => { + .get("/", zValidator("query", listQuerySchema), async (c) => { await connectDB(); const projectId = c.req.param("projectId")!; - const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "10", 10) || 10, 1), 100); - const skip = Math.max(parseInt(c.req.query("skip") ?? "0", 10) || 0, 0); + const q = c.req.valid("query"); + const limit = Math.min(Math.max(parseInt(q.limit ?? "10", 10) || 10, 1), 100); + const skip = Math.max(parseInt(q.skip ?? "0", 10) || 0, 0); const filter = { project: projectId, testAgent: null }; const [rawItems, total] = await Promise.all([ diff --git a/apps/api/src/routes/mcp-logs.ts b/apps/api/src/routes/mcp-logs.ts index 3cd3442..fa788b5 100644 --- a/apps/api/src/routes/mcp-logs.ts +++ b/apps/api/src/routes/mcp-logs.ts @@ -1,18 +1,31 @@ import { Hono } from "hono"; +import { zValidator } from "@hono/zod-validator"; +import { z } from "zod/v4"; import { connectDB } from "@archmax/core/infra/db"; import { McpCallLog } from "@archmax/core/models/index"; -const app = new Hono().get("/", async (c) => { +const listQuerySchema = z.object({ + page: z.string().optional(), + limit: z.string().optional(), + toolName: z.string().optional(), + tokenId: z.string().optional(), + errorOnly: z.string().optional(), + from: z.string().optional(), + to: z.string().optional(), +}); + +const app = new Hono().get("/", zValidator("query", listQuerySchema), async (c) => { await connectDB(); const projectId = c.req.param("projectId")!; - const page = Math.max(1, parseInt(c.req.query("page") || "1", 10)); - const limit = Math.min(200, Math.max(1, parseInt(c.req.query("limit") || "50", 10))); - const toolName = c.req.query("toolName"); - const tokenId = c.req.query("tokenId"); - const errorOnly = c.req.query("errorOnly") === "true"; - const from = c.req.query("from"); - const to = c.req.query("to"); + const q = c.req.valid("query"); + const page = Math.max(1, parseInt(q.page || "1", 10)); + const limit = Math.min(200, Math.max(1, parseInt(q.limit || "50", 10))); + const toolName = q.toolName; + const tokenId = q.tokenId; + const errorOnly = q.errorOnly === "true"; + const from = q.from; + const to = q.to; const filter: Record = { project: projectId }; diff --git a/apps/api/src/routes/test-cases.ts b/apps/api/src/routes/test-cases.ts index 15859a1..a8b2bca 100644 --- a/apps/api/src/routes/test-cases.ts +++ b/apps/api/src/routes/test-cases.ts @@ -17,18 +17,27 @@ const createSchema = z.object({ const updateSchema = createSchema.partial(); +const listQuerySchema = z.object({ + page: z.string().optional(), + limit: z.string().optional(), + agentId: z.string().optional(), + semanticModel: z.string().optional(), + tags: z.string().optional(), +}); + const app = new Hono() - .get("/", async (c) => { + .get("/", zValidator("query", listQuerySchema), async (c) => { await connectDB(); const projectId = c.req.param("projectId")!; - const page = Math.max(parseInt(c.req.query("page") ?? "1", 10) || 1, 1); - const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "25", 10) || 25, 1), 100); + const q = c.req.valid("query"); + const page = Math.max(parseInt(q.page ?? "1", 10) || 1, 1); + const limit = Math.min(Math.max(parseInt(q.limit ?? "25", 10) || 25, 1), 100); const skip = (page - 1) * limit; const filter: Record = { project: projectId }; - const agentId = c.req.query("agentId"); - const semanticModel = c.req.query("semanticModel"); - const tagsParam = c.req.query("tags"); + const agentId = q.agentId; + const semanticModel = q.semanticModel; + const tagsParam = q.tags; if (agentId) filter.testAgent = agentId; if (semanticModel) filter.semanticModel = semanticModel; if (tagsParam) filter.tags = { $in: tagsParam.split(",").map((t) => t.trim().toLowerCase()).filter(Boolean) }; diff --git a/apps/api/src/routes/test-runs.ts b/apps/api/src/routes/test-runs.ts index e3912bc..d40a7d4 100644 --- a/apps/api/src/routes/test-runs.ts +++ b/apps/api/src/routes/test-runs.ts @@ -12,12 +12,18 @@ const createSchema = z.object({ testCaseIds: z.array(z.string().min(1)).min(1), }); +const listQuerySchema = z.object({ + page: z.string().optional(), + limit: z.string().optional(), +}); + const app = new Hono() - .get("/", async (c) => { + .get("/", zValidator("query", listQuerySchema), async (c) => { await connectDB(); const projectId = c.req.param("projectId")!; - const page = Math.max(parseInt(c.req.query("page") ?? "1", 10) || 1, 1); - const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "25", 10) || 25, 1), 100); + const q = c.req.valid("query"); + const page = Math.max(parseInt(q.page ?? "1", 10) || 1, 1); + const limit = Math.min(Math.max(parseInt(q.limit ?? "25", 10) || 25, 1), 100); const skip = (page - 1) * limit; const filter = { project: projectId }; diff --git a/apps/docs/package.json b/apps/docs/package.json index efeea56..ee5013d 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@astrojs/starlight": "^0.38.3", - "astro": "^6.1.4", + "astro": "^6.1.8", "sharp": "^0.34.5", "starlight-image-zoom": "^0.14.1" } diff --git a/apps/docs/public/images/screenshot-graph-view.png b/apps/docs/public/images/screenshot-graph-view.png index 353f757..fe12b70 100644 Binary files a/apps/docs/public/images/screenshot-graph-view.png and b/apps/docs/public/images/screenshot-graph-view.png differ diff --git a/apps/docs/public/images/screenshot-home.png b/apps/docs/public/images/screenshot-home.png index 68ba50a..82cd508 100644 Binary files a/apps/docs/public/images/screenshot-home.png and b/apps/docs/public/images/screenshot-home.png differ diff --git a/apps/docs/public/images/screenshot-settings.png b/apps/docs/public/images/screenshot-settings.png index 09f7d61..1af2823 100644 Binary files a/apps/docs/public/images/screenshot-settings.png and b/apps/docs/public/images/screenshot-settings.png differ diff --git a/apps/docs/src/content/docs/reference/docker.mdx b/apps/docs/src/content/docs/reference/docker.mdx index 2d23e16..6b55a7f 100644 --- a/apps/docs/src/content/docs/reference/docker.mdx +++ b/apps/docs/src/content/docs/reference/docker.mdx @@ -18,7 +18,7 @@ The `ghcr.io/archmaxai/archmax` Docker image bundles the application components | **MongoDB** | Embedded `mongod`; starts automatically when `MONGODB_URI` is not set | | **Redis** | Embedded `redis-server`; starts automatically when `REDIS_URL` is not set | -Base image: `node:24-slim` (Debian Bookworm). +Base image: `node:25-slim` (Debian Bookworm).