From 64590d9fba76c20b49dd55fbfa4ed0f2ad41258b Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 11:34:52 +0800 Subject: [PATCH 01/13] upd: version app --- src/app/api/version-app/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index bebb874..edf4934 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "2.1.10", tahap: "beta", update: "-perbaikan grafik divisi" }, { status: 200 }); + return NextResponse.json({ success: true, version: "2.2.0", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); -- 2.49.1 From 5cd35dd534c9d0770e07981e28f0fb2e1a230706 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 12:12:22 +0800 Subject: [PATCH 02/13] bump: version 0.1.1 --- package.json | 2 +- src/app/api/version-app/route.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c571a11..d300917 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sistem-desa-mandiri", - "version": "0.1.0", + "version": "0.1.1", "private": true, "scripts": { "dev": "next dev --experimental-https", diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index edf4934..93d47ca 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "2.2.0", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); + return NextResponse.json({ success: true, version: "0.1.1", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); -- 2.49.1 From 7c37ae4ed8225055d531cd159060e445505551c4 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 12:14:05 +0800 Subject: [PATCH 03/13] bump: version 0.1.2 --- package.json | 2 +- src/app/api/version-app/route.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d300917..fdd4ff1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sistem-desa-mandiri", - "version": "0.1.1", + "version": "0.1.2", "private": true, "scripts": { "dev": "next dev --experimental-https", diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 93d47ca..cc0a05e 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "0.1.1", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); + return NextResponse.json({ success: true, version: "0.1.2", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); -- 2.49.1 From 43f7005d16a937b929158dff8e051b891c2f6e44 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 12:15:18 +0800 Subject: [PATCH 04/13] bump: version 0.1.3 --- package.json | 2 +- src/app/api/version-app/route.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fdd4ff1..dd4e28e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sistem-desa-mandiri", - "version": "0.1.2", + "version": "0.1.3", "private": true, "scripts": { "dev": "next dev --experimental-https", diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index cc0a05e..48e75a9 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "0.1.2", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); + return NextResponse.json({ success: true, version: "0.1.3", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); -- 2.49.1 From 58535ee7a67b5f1d871491da985352933f32d61d Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 12:17:27 +0800 Subject: [PATCH 05/13] bump: version 0.1.4 --- package.json | 2 +- src/app/api/version-app/route.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index dd4e28e..147e499 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sistem-desa-mandiri", - "version": "0.1.3", + "version": "0.1.4", "private": true, "scripts": { "dev": "next dev --experimental-https", diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 48e75a9..d712c82 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "0.1.3", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); + return NextResponse.json({ success: true, version: "0.1.4", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); -- 2.49.1 From 3f41155d406c4b00e62632fc3f01a28edb61fa39 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 13:58:05 +0800 Subject: [PATCH 06/13] refactor: version-app read from package.json --- src/app/api/version-app/route.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index d712c82..6813b1e 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -1,8 +1,9 @@ import { NextResponse } from "next/server"; +import { version } from "../../../../package.json"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "0.1.4", tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); + return NextResponse.json({ success: true, version, tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); -- 2.49.1 From 3e9fbacd949d04d3667cba3e0d614e820c481a33 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 13:58:11 +0800 Subject: [PATCH 07/13] bump: version 0.1.5 --- package.json | 2 +- src/app/api/version-app/route.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 147e499..fe46cf9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sistem-desa-mandiri", - "version": "0.1.4", + "version": "0.1.5", "private": true, "scripts": { "dev": "next dev --experimental-https", diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 6813b1e..f14d46d 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -6,6 +6,6 @@ export async function GET(request: Request) { return NextResponse.json({ success: true, version, tahap: "beta", update: "-perbaikan fitur diskusi dan perbaikan tampilan mobile" }, { status: 200 }); } catch (error) { console.error(error); - return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + return NextResponse.json({ success: false, version: "0.1.5", reason: (error as Error).message, }, { status: 500 }); } } -- 2.49.1 From 4f870a5c165b458bc1ff641c0434c76c681da7dd Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 14:28:26 +0800 Subject: [PATCH 08/13] fix: treat 524/504 timeout as accepted on repull --- .github/workflows/script/re-pull.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/script/re-pull.sh b/.github/workflows/script/re-pull.sh index 8097813..7c19ae3 100644 --- a/.github/workflows/script/re-pull.sh +++ b/.github/workflows/script/re-pull.sh @@ -48,9 +48,11 @@ HTTP_STATUS=$(curl -s -o /tmp/portainer_response.json -w "%{http_code}" \ -H "Content-Type: application/json" \ -d "$PAYLOAD") -if [ "$HTTP_STATUS" != "200" ]; then +if [ "$HTTP_STATUS" = "524" ] || [ "$HTTP_STATUS" = "504" ] || [ "$HTTP_STATUS" = "408" ]; then + echo "⚠️ HTTP $HTTP_STATUS (gateway timeout) — Portainer tetap memproses redeploy, lanjut polling container..." +elif [ "$HTTP_STATUS" != "200" ]; then echo "❌ Redeploy gagal! HTTP Status: $HTTP_STATUS" - cat /tmp/portainer_response.json | jq . + cat /tmp/portainer_response.json | jq . 2>/dev/null || true exit 1 fi -- 2.49.1 From d5a38eb0f5829b3ee8cb999a534c9e39c2bd7a72 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 14:30:13 +0800 Subject: [PATCH 09/13] =?UTF-8?q?fix:=20anti-zombie=20polling=20=E2=80=94?= =?UTF-8?q?=20curl=20timeout=20+=20adaptive=20MAX=5FRETRY?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/script/re-pull.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/script/re-pull.sh b/.github/workflows/script/re-pull.sh index 7c19ae3..1528e1d 100644 --- a/.github/workflows/script/re-pull.sh +++ b/.github/workflows/script/re-pull.sh @@ -50,22 +50,24 @@ HTTP_STATUS=$(curl -s -o /tmp/portainer_response.json -w "%{http_code}" \ if [ "$HTTP_STATUS" = "524" ] || [ "$HTTP_STATUS" = "504" ] || [ "$HTTP_STATUS" = "408" ]; then echo "⚠️ HTTP $HTTP_STATUS (gateway timeout) — Portainer tetap memproses redeploy, lanjut polling container..." + MAX_RETRY=60 elif [ "$HTTP_STATUS" != "200" ]; then echo "❌ Redeploy gagal! HTTP Status: $HTTP_STATUS" cat /tmp/portainer_response.json | jq . 2>/dev/null || true exit 1 +else + MAX_RETRY=30 fi -echo "⏳ Menunggu container running..." +echo "⏳ Menunggu container running (max $((MAX_RETRY * 10))s)..." -MAX_RETRY=15 COUNT=0 while [ $COUNT -lt $MAX_RETRY ]; do - sleep 5 + sleep 10 COUNT=$((COUNT + 1)) - CONTAINERS=$(curl -s -X GET \ + CONTAINERS=$(curl -s --max-time 10 -X GET \ "https://${PORTAINER_URL}/api/endpoints/${ENDPOINT_ID}/docker/containers/json?all=true&filters=%7B%22label%22%3A%5B%22com.docker.compose.project%3D${STACK_NAME}%22%5D%7D" \ -H "Authorization: Bearer ${TOKEN}") @@ -91,5 +93,5 @@ while [ $COUNT -lt $MAX_RETRY ]; do done echo "" -echo "❌ Timeout! Stack tidak kunjung running setelah $((MAX_RETRY * 5)) detik." +echo "❌ Timeout! Stack tidak kunjung running setelah $((MAX_RETRY * 10)) detik." exit 1 \ No newline at end of file -- 2.49.1 From a58441c4d6e8cf13424fca8766880b124e04fc4c Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 16:32:04 +0800 Subject: [PATCH 10/13] feat: run prisma migrate deploy on container startup --- Dockerfile | 4 +++- entrypoint.sh | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 entrypoint.sh diff --git a/Dockerfile b/Dockerfile index 9023548..faf828b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,11 +67,12 @@ COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/tsconfig.json ./tsconfig.json COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/src ./src +COPY entrypoint.sh ./entrypoint.sh # Env vars runtime dikelola oleh Portainer (stack env / container env). # Tidak perlu copy .env ke runner — image tetap bersih tanpa secrets. -RUN chown -R nextjs:nodejs /app +RUN chown -R nextjs:nodejs /app && chmod +x entrypoint.sh USER nextjs @@ -80,4 +81,5 @@ EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" +ENTRYPOINT ["./entrypoint.sh"] CMD ["bun", "run", "start"] \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..9a9a2b3 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +echo "⏳ Menjalankan migrasi database..." +./node_modules/.bin/prisma migrate deploy +echo "✅ Migrasi selesai." + +exec "$@" -- 2.49.1 From f9b2eb0a8089a82451e14a567f24860be3bc8f79 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 16:33:49 +0800 Subject: [PATCH 11/13] revert: remove entrypoint migration --- Dockerfile | 4 +--- entrypoint.sh | 8 -------- 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 entrypoint.sh diff --git a/Dockerfile b/Dockerfile index faf828b..9023548 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,12 +67,11 @@ COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/tsconfig.json ./tsconfig.json COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/src ./src -COPY entrypoint.sh ./entrypoint.sh # Env vars runtime dikelola oleh Portainer (stack env / container env). # Tidak perlu copy .env ke runner — image tetap bersih tanpa secrets. -RUN chown -R nextjs:nodejs /app && chmod +x entrypoint.sh +RUN chown -R nextjs:nodejs /app USER nextjs @@ -81,5 +80,4 @@ EXPOSE 3000 ENV PORT=3000 ENV HOSTNAME="0.0.0.0" -ENTRYPOINT ["./entrypoint.sh"] CMD ["bun", "run", "start"] \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100644 index 9a9a2b3..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -set -e - -echo "⏳ Menjalankan migrasi database..." -./node_modules/.bin/prisma migrate deploy -echo "✅ Migrasi selesai." - -exec "$@" -- 2.49.1 From c5c288328115703e56568a6b9c48a33d276f9733 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 16:42:51 +0800 Subject: [PATCH 12/13] bump: version 0.1.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fe46cf9..9f15aea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sistem-desa-mandiri", - "version": "0.1.5", + "version": "0.1.6", "private": true, "scripts": { "dev": "next dev --experimental-https", -- 2.49.1 From 81de073222de58d6d49009af419de39b9c4e320d Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Thu, 23 Apr 2026 17:26:16 +0800 Subject: [PATCH 13/13] feat: add deploy-stg MCP server --- .gitignore | 1 + .mcp.json | 13 ++ .mcp/deploy-stg/bun.lock | 194 +++++++++++++++++ .mcp/deploy-stg/package.json | 9 + .mcp/deploy-stg/server.ts | 393 +++++++++++++++++++++++++++++++++++ 5 files changed, 610 insertions(+) create mode 100644 .mcp.json create mode 100644 .mcp/deploy-stg/bun.lock create mode 100644 .mcp/deploy-stg/package.json create mode 100644 .mcp/deploy-stg/server.ts diff --git a/.gitignore b/.gitignore index c6e47ff..3fb6bb7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # dependencies /node_modules +.mcp/deploy-stg/node_modules /.pnp .pnp.js .yarn/install-state.gz diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..e2db79e --- /dev/null +++ b/.mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "deploy-stg": { + "type": "stdio", + "command": "bun", + "args": ["run", ".mcp/deploy-stg/server.ts"], + "env": { + "BASE_URL": "https://desa-plus-stg.wibudev.com", + "STACK_NAME": "desa-plus" + } + } + } +} diff --git a/.mcp/deploy-stg/bun.lock b/.mcp/deploy-stg/bun.lock new file mode 100644 index 0000000..f8a08f6 --- /dev/null +++ b/.mcp/deploy-stg/bun.lock @@ -0,0 +1,194 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "deploy-stg", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.11.0", + }, + }, + }, + "packages": { + "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], + + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="], + + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "express-rate-limit": ["express-rate-limit@8.4.0", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-gDK8yiqKxrGta+3WtON59arrrw6GLmadA1qoFgYXzdcch8fmKDID2XqO8itsi3f1wufXYPT51387dN6cvVBS3Q=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], + + "hono": ["hono@4.12.14", "", {}, "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w=="], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + } +} diff --git a/.mcp/deploy-stg/package.json b/.mcp/deploy-stg/package.json new file mode 100644 index 0000000..36b84d9 --- /dev/null +++ b/.mcp/deploy-stg/package.json @@ -0,0 +1,9 @@ +{ + "name": "deploy-stg", + "version": "1.0.0", + "private": true, + "type": "module", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.11.0" + } +} diff --git a/.mcp/deploy-stg/server.ts b/.mcp/deploy-stg/server.ts new file mode 100644 index 0000000..c3614e7 --- /dev/null +++ b/.mcp/deploy-stg/server.ts @@ -0,0 +1,393 @@ +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; +import { execFileSync, execSync } from "child_process"; +import { readFileSync, writeFileSync } from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const PROJECT_ROOT = path.resolve(__dirname, "../.."); +const REPO = "bipprojectbali/desa-plus"; +const STACK_ENV = "stg"; +const BASE_URL = process.env.BASE_URL ?? ""; +const DEFAULT_STACK_NAME = process.env.STACK_NAME ?? ""; + +const GH = (args: string[]) => + execFileSync("gh", args, { encoding: "utf-8", cwd: PROJECT_ROOT }).trim(); + +const GIT = (args: string[]) => + execFileSync("git", args, { encoding: "utf-8", cwd: PROJECT_ROOT }).trim(); + +// --- version helpers --- + +function bumpVersion(version: string, type: "patch" | "minor" | "major"): string { + const [maj, min, pat] = version.split(".").map(Number); + if (type === "major") return `${maj + 1}.0.0`; + if (type === "minor") return `${maj}.${min + 1}.0`; + return `${maj}.${min}.${pat + 1}`; +} + +function readPkgVersion(): string { + const pkg = JSON.parse(readFileSync(path.join(PROJECT_ROOT, "package.json"), "utf-8")); + return pkg.version as string; +} + +function applyVersionBump(newVersion: string): void { + const pkgPath = path.join(PROJECT_ROOT, "package.json"); + const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")); + pkg.version = newVersion; + writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n"); +} + +// --- deployed version check --- + +async function waitForDeployedVersion(expected: string, timeoutMs = 5 * 60 * 1000): Promise { + if (!BASE_URL) return "BASE_URL tidak di-set, skip cek versi stg."; + + const url = `${BASE_URL}/api/version-app`; + const interval = 15_000; + const maxAttempts = Math.ceil(timeoutMs / interval); + let last = ""; + + for (let i = 1; i <= maxAttempts; i++) { + await new Promise((r) => setTimeout(r, interval)); + try { + const res = await fetch(url); + const data = (await res.json()) as { version?: string }; + last = data.version ?? "?"; + if (last === expected) { + return `Versi terverifikasi di stg: ${last}`; + } + } catch { + last = "error fetch"; + } + } + return `Timeout: versi stg masih ${last}, expected ${expected}`; +} + +// --- MCP server --- + +const server = new Server( + { name: "deploy-stg", version: "1.0.0" }, + { capabilities: { tools: {} } } +); + +server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: "deploy", + description: + "Full deploy ke stg: bump version, commit, push ke build remote, publish Docker image, tunggu selesai, repull Portainer, verifikasi versi.", + inputSchema: { + type: "object", + properties: { + stack_name: { + type: "string", + description: "Nama stack Portainer. Jika tidak diisi, pakai env STACK_NAME.", + }, + bump: { + type: "string", + enum: ["patch", "minor", "major"], + description: "Jenis bump versi (default: patch)", + default: "patch", + }, + }, + required: [], + }, + }, + { + name: "publish", + description: + "Trigger workflow publish.yml: build & push Docker image ke GHCR (selalu stg, tag dari package.json). Kembalikan URL run.", + inputSchema: { type: "object", properties: {}, required: [] }, + }, + { + name: "repull", + description: + "Trigger workflow re-pull.yml: redeploy stack di Portainer stg dengan pull image terbaru. Kembalikan URL run.", + inputSchema: { + type: "object", + properties: { + stack_name: { + type: "string", + description: "Nama stack Portainer. Jika tidak diisi, pakai env STACK_NAME.", + }, + }, + required: [], + }, + }, + { + name: "run_status", + description: + "Cek status GitHub Actions run terbaru untuk workflow tertentu, atau semua workflow.", + inputSchema: { + type: "object", + properties: { + workflow: { + type: "string", + enum: ["publish.yml", "re-pull.yml", "all"], + description: "Nama workflow file atau 'all' untuk semua (default: all)", + default: "all", + }, + limit: { + type: "number", + description: "Jumlah run yang ditampilkan (default 5)", + default: 5, + }, + }, + required: [], + }, + }, + { + name: "check_version", + description: + "Bandingkan versi lokal (package.json) dengan versi yang berjalan di stg (/api/version-app).", + inputSchema: { type: "object", properties: {}, required: [] }, + }, + ], +})); + +server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + try { + // ── deploy ───────────────────────────────────────────────────────────── + if (name === "deploy") { + const { stack_name: _sn, bump = "patch" } = (args ?? {}) as { + stack_name?: string; + bump?: "patch" | "minor" | "major"; + }; + const stack_name = _sn || DEFAULT_STACK_NAME; + if (!stack_name) throw new Error("stack_name tidak diisi dan env STACK_NAME kosong."); + + // 0. Cek migrasi — buat otomatis jika schema ada perubahan + let migrationCreated = false; + try { + execFileSync( + "./node_modules/.bin/prisma", + ["migrate", "diff", "--from-migrations", "prisma/migrations", "--to-schema-datamodel", "prisma/schema.prisma", "--exit-code"], + { encoding: "utf-8", cwd: PROJECT_ROOT, stdio: "pipe" } + ); + } catch { + // Ada schema diff — buat migration otomatis + execFileSync( + "./node_modules/.bin/prisma", + ["migrate", "dev", "--create-only", "--name", "auto"], + { encoding: "utf-8", cwd: PROJECT_ROOT, stdio: "pipe" } + ); + migrationCreated = true; + } + + const oldVersion = readPkgVersion(); + const newVersion = bumpVersion(oldVersion, bump); + + // 1. Bump version in package.json + applyVersionBump(newVersion); + + // 2. Commit (version bump + migration jika ada) + GIT(["add", "package.json", "prisma/migrations"]); + GIT(["commit", "-m", migrationCreated + ? `bump: version ${newVersion} + migration` + : `bump: version ${newVersion}` + ]); + + // 3. Push to build remote (GitHub) + const currentBranch = GIT(["rev-parse", "--abbrev-ref", "HEAD"]); + GIT(["push", "build", `${currentBranch}:main`, "--force"]); + + // 4. Trigger publish + GH([ + "workflow", "run", "publish.yml", + "--repo", REPO, + "--field", `stack_env=${STACK_ENV}`, + "--field", `tag=${newVersion}`, + ]); + await new Promise((r) => setTimeout(r, 4000)); + + const publishRunId = GH([ + "run", "list", "--repo", REPO, + "--workflow", "publish.yml", + "--limit", "1", + "--json", "databaseId", + "--jq", ".[0].databaseId", + ]); + const publishUrl = GH([ + "run", "list", "--repo", REPO, + "--workflow", "publish.yml", + "--limit", "1", + "--json", "url", + "--jq", ".[0].url", + ]); + + // 5. Wait for publish to finish + execSync(`gh run watch ${publishRunId} --repo ${REPO} --exit-status`, { + encoding: "utf-8", + cwd: PROJECT_ROOT, + timeout: 30 * 60 * 1000, + stdio: "pipe", + }); + + // 6. Trigger repull + GH([ + "workflow", "run", "re-pull.yml", + "--repo", REPO, + "--field", `stack_name=${stack_name}`, + "--field", `stack_env=${STACK_ENV}`, + ]); + await new Promise((r) => setTimeout(r, 4000)); + + const repullUrl = GH([ + "run", "list", "--repo", REPO, + "--workflow", "re-pull.yml", + "--limit", "1", + "--json", "url", + "--jq", ".[0].url", + ]); + + // 7. Wait for repull, then verify version + await new Promise((r) => setTimeout(r, 30_000)); + const versionCheck = await waitForDeployedVersion(newVersion); + + const localVer = readPkgVersion(); + + return { + content: [ + { + type: "text", + text: [ + `Deploy selesai: ${stack_name}-${STACK_ENV} @ ${newVersion} (dari ${oldVersion})`, + `Publish run : ${publishUrl}`, + `Repull run : ${repullUrl}`, + ``, + `Versi lokal : ${localVer}`, + versionCheck, + ].join("\n"), + }, + ], + }; + } + + // ── publish ──────────────────────────────────────────────────────────── + if (name === "publish") { + const tag = readPkgVersion(); + + GH([ + "workflow", "run", "publish.yml", + "--repo", REPO, + "--field", `stack_env=${STACK_ENV}`, + "--field", `tag=${tag}`, + ]); + await new Promise((r) => setTimeout(r, 3000)); + + const runUrl = GH([ + "run", "list", "--repo", REPO, + "--workflow", "publish.yml", + "--limit", "1", + "--json", "url", + "--jq", ".[0].url", + ]); + + return { + content: [{ type: "text", text: `Publish triggered: ${STACK_ENV}-${tag}\nRun: ${runUrl}` }], + }; + } + + // ── repull ───────────────────────────────────────────────────────────── + if (name === "repull") { + const { stack_name: _sn } = (args ?? {}) as { stack_name?: string }; + const stack_name = _sn || DEFAULT_STACK_NAME; + if (!stack_name) throw new Error("stack_name tidak diisi dan env STACK_NAME kosong."); + + GH([ + "workflow", "run", "re-pull.yml", + "--repo", REPO, + "--field", `stack_name=${stack_name}`, + "--field", `stack_env=${STACK_ENV}`, + ]); + await new Promise((r) => setTimeout(r, 3000)); + + const runUrl = GH([ + "run", "list", "--repo", REPO, + "--workflow", "re-pull.yml", + "--limit", "1", + "--json", "url", + "--jq", ".[0].url", + ]); + + return { + content: [{ type: "text", text: `Repull triggered: ${stack_name}-${STACK_ENV}\nRun: ${runUrl}` }], + }; + } + + // ── run_status ───────────────────────────────────────────────────────── + if (name === "run_status") { + const { workflow = "all", limit = 5 } = (args ?? {}) as { + workflow?: string; + limit?: number; + }; + const workflowArgs = workflow === "all" ? [] : ["--workflow", workflow]; + + const output = GH([ + "run", "list", + "--repo", REPO, + ...workflowArgs, + "--limit", String(limit), + "--json", "workflowName,status,conclusion,startedAt,url,databaseId", + "--jq", + '.[] | "[\(.status)/\(.conclusion // "-")] \(.workflowName) — \(.startedAt)\n \(.url)"', + ]); + + return { + content: [{ type: "text", text: output || "Tidak ada run ditemukan." }], + }; + } + + // ── check_version ────────────────────────────────────────────────────── + if (name === "check_version") { + const localVersion = readPkgVersion(); + let stgVersion = "tidak dapat dijangkau"; + + if (BASE_URL) { + try { + const res = await fetch(`${BASE_URL}/api/version-app`); + const data = (await res.json()) as { version?: string }; + stgVersion = data.version ?? "?"; + } catch (e) { + stgVersion = `error: ${(e as Error).message}`; + } + } else { + stgVersion = "BASE_URL tidak di-set"; + } + + const match = localVersion === stgVersion ? "✓ sama" : "✗ beda"; + + return { + content: [ + { + type: "text", + text: [ + `Lokal (package.json) : ${localVersion}`, + `Stg (/api/version-app): ${stgVersion}`, + `Status : ${match}`, + ].join("\n"), + }, + ], + }; + } + + return { + content: [{ type: "text", text: `Unknown tool: ${name}` }], + isError: true, + }; + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true }; + } +}); + +const transport = new StdioServerTransport(); +await server.connect(transport); -- 2.49.1