@app-vitals/agent-admin 0.0.1 → 0.1.0
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/Dockerfile +43 -0
- package/README.md +72 -2
- package/e2e/agents-page.e2e.ts +338 -0
- package/e2e/chat-degraded.e2e.ts +153 -0
- package/e2e/chat-page.e2e.ts +169 -0
- package/e2e/chat-progress.e2e.ts +206 -0
- package/e2e/chat-thread-page.e2e.ts +444 -0
- package/e2e/login-page.e2e.ts +148 -0
- package/e2e/test-server.ts +427 -0
- package/openapi.json +6098 -0
- package/openapi.platform.json +5553 -0
- package/package.json +62 -4
- package/playwright.config.ts +25 -0
- package/prisma/client/browser.ts +134 -0
- package/prisma/client/client.ts +158 -0
- package/prisma/client/commonInputTypes.ts +578 -0
- package/prisma/client/enums.ts +15 -0
- package/prisma/client/internal/class.ts +424 -0
- package/prisma/client/internal/prismaNamespace.ts +2898 -0
- package/prisma/client/internal/prismaNamespaceBrowser.ts +476 -0
- package/prisma/client/models/Account.ts +1774 -0
- package/prisma/client/models/AccountInvite.ts +1386 -0
- package/prisma/client/models/AccountMember.ts +1351 -0
- package/prisma/client/models/Agent.ts +4554 -0
- package/prisma/client/models/AgentAsk.ts +1373 -0
- package/prisma/client/models/AgentChatTokenUsageDailyByModel.ts +1638 -0
- package/prisma/client/models/AgentCronJob.ts +2416 -0
- package/prisma/client/models/AgentCronRun.ts +2858 -0
- package/prisma/client/models/AgentCronRunModelBreakdown.ts +1532 -0
- package/prisma/client/models/AgentCronRunSkillUsage.ts +1642 -0
- package/prisma/client/models/AgentEnv.ts +1421 -0
- package/prisma/client/models/AgentGitHubInstallationsSnapshot.ts +1289 -0
- package/prisma/client/models/AgentMember.ts +1316 -0
- package/prisma/client/models/AgentPhaseMethodology.ts +1355 -0
- package/prisma/client/models/AgentPlugin.ts +1421 -0
- package/prisma/client/models/AgentToken.ts +1380 -0
- package/prisma/client/models/AgentTool.ts +1351 -0
- package/prisma/client/models/AgentWorkQueueSnapshot.ts +1297 -0
- package/prisma/client/models/ChatThreadWatch.ts +1183 -0
- package/prisma/client/models/PushSubscription.ts +1233 -0
- package/prisma/client/models/SessionAlertState.ts +1187 -0
- package/prisma/client/models/SessionFollow.ts +1187 -0
- package/prisma/client/models/UserNotificationPrefs.ts +1221 -0
- package/prisma/client/models.ts +34 -0
- package/prisma/migrations/20260608000000_init/migration.sql +112 -0
- package/prisma/migrations/20260616000000_agent_members/migration.sql +18 -0
- package/prisma/migrations/20260624000000_add_agent_self_hosted/migration.sql +2 -0
- package/prisma/migrations/20260624120000_add_agent_repos/migration.sql +3 -0
- package/prisma/migrations/20260625000000_add_agent_cron_run/migration.sql +26 -0
- package/prisma/migrations/20260626000000_add_cron_run_token_fields/migration.sql +10 -0
- package/prisma/migrations/20260627000000_add_agent_chat_token_usage_daily/migration.sql +18 -0
- package/prisma/migrations/20260627010000_add_agent_chat_token_usage_fk/migration.sql +2 -0
- package/prisma/migrations/20260701000000_add_chat_threads_messages/migration.sql +37 -0
- package/prisma/migrations/20260701010000_drop_chat_threads_messages/migration.sql +17 -0
- package/prisma/migrations/20260701020000_add_cron_run_model_breakdown/migration.sql +23 -0
- package/prisma/migrations/20260701020000_chat_token_by_model/migration.sql +25 -0
- package/prisma/migrations/20260701030000_add_secret_flag_to_agent_env/migration.sql +2 -0
- package/prisma/migrations/20260701030000_drop_cron_run_legacy_columns/migration.sql +5 -0
- package/prisma/migrations/20260706000000_consolidate_cron_run_token_accounting/migration.sql +54 -0
- package/prisma/migrations/20260709000000_add_phase_to_agent_cron_run/migration.sql +8 -0
- package/prisma/migrations/20260717000000_add_item_tracking_to_agent_cron_run/migration.sql +10 -0
- package/prisma/migrations/20260717010000_add_agent_work_queue_snapshot/migration.sql +20 -0
- package/prisma/migrations/20260718000000_add_cron_phase_columns/migration.sql +25 -0
- package/prisma/migrations/20260718100000_drop_agent_cron_run_phase_column/migration.sql +8 -0
- package/prisma/migrations/20260724000000_add_agent_type_name/migration.sql +3 -0
- package/prisma/migrations/20260725232433_add_agent_author_allowlist/migration.sql +2 -0
- package/prisma/migrations/20260727120000_add_not_null_to_agent_author_allowlist/migration.sql +3 -0
- package/prisma/migrations/20260731000000_add_session_id_to_agent_cron_run/migration.sql +2 -0
- package/prisma/migrations/20260821000000_add_agent_restrict_slack_to_members/migration.sql +3 -0
- package/prisma/migrations/20260901000000_add_push_subscription_and_chat_thread_watch/migration.sql +40 -0
- package/prisma/migrations/20260903000000_add_agent_patch_author_allowlist/migration.sql +2 -0
- package/prisma/migrations/20260903120000_add_agent_review_author_allowlist/migration.sql +18 -0
- package/prisma/migrations/20260903150000_drop_agent_author_allowlist/migration.sql +14 -0
- package/prisma/migrations/20260905000000_add_started_at_index_to_agent_cron_run/migration.sql +11 -0
- package/prisma/migrations/20260906000000_add_session_follow_notification_prefs_alert_state/migration.sql +47 -0
- package/prisma/migrations/20260911000000_backfill_auto_follow_since/migration.sql +16 -0
- package/prisma/migrations/20260923115814_add_last_heartbeat_at_to_agent_cron_run/migration.sql +2 -0
- package/prisma/migrations/20260924120000_add_trial_expiry_fields_to_agent/migration.sql +6 -0
- package/prisma/migrations/20260929000000_add_agent_phase_methodology/migration.sql +21 -0
- package/prisma/migrations/20260929120000_add_agent_policy_fields/migration.sql +15 -0
- package/prisma/migrations/20261005000000_add_agent_github_installations_snapshot/migration.sql +21 -0
- package/prisma/migrations/20261008000000_add_cron_run_telemetry/migration.sql +50 -0
- package/prisma/migrations/20261008010000_add_account_models/migration.sql +48 -0
- package/prisma/migrations/20261008020000_add_cron_lockdown_disabled_at/migration.sql +3 -0
- package/prisma/migrations/20261009000000_chat_token_usage_bigint/migration.sql +7 -0
- package/prisma/migrations/20261010000000_add_cron_run_command_variant/migration.sql +2 -0
- package/prisma/migrations/20261019000000_add_agent_anthropic_identity/migration.sql +9 -0
- package/prisma/migrations/20261025000000_add_agent_ask/migration.sql +23 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema/coding.prisma +85 -0
- package/prisma/schema/schema.prisma +506 -0
- package/prisma.config.ts +34 -0
- package/pwa-assets/icons/apple-touch-icon.png +0 -0
- package/pwa-assets/icons/favicon-32.png +0 -0
- package/pwa-assets/icons/icon-192.png +0 -0
- package/pwa-assets/icons/icon-512.png +0 -0
- package/pwa-assets/icons/icon-maskable-192.png +0 -0
- package/pwa-assets/icons/icon-maskable-512.png +0 -0
- package/src/account-agent-create.ts +42 -0
- package/src/account-agent-create.unit.test.ts +28 -0
- package/src/account-created-notifier.ts +71 -0
- package/src/account-created-notifier.unit.test.ts +70 -0
- package/src/account-email.ts +9 -0
- package/src/account-email.unit.test.ts +21 -0
- package/src/account-invites.ts +70 -0
- package/src/account-lifecycle.integration.test.ts +329 -0
- package/src/account-lifecycle.ts +206 -0
- package/src/account-lifecycle.unit.test.ts +108 -0
- package/src/account-members.ts +128 -0
- package/src/account-onboarding.integration.test.ts +101 -0
- package/src/account-onboarding.ts +95 -0
- package/src/account-trial-expiry-sweeper.ts +85 -0
- package/src/account-trial-warning-sweeper.integration.test.ts +228 -0
- package/src/account-trial-warning-sweeper.ts +216 -0
- package/src/accounts-api.integration.test.ts +196 -0
- package/src/accounts-api.smoke.test.ts +292 -0
- package/src/accounts-api.ts +289 -0
- package/src/accounts-api.unit.test.ts +93 -0
- package/src/accounts.integration.test.ts +178 -0
- package/src/accounts.ts +208 -0
- package/src/admin-boot-config.ts +574 -0
- package/src/admin-boot-config.unit.test.ts +28 -0
- package/src/admin-boot.boot-order.unit.test.ts +91 -0
- package/src/admin-boot.ts +690 -0
- package/src/admin-dev-login.smoke.test.ts +317 -0
- package/src/admin-extension.ts +432 -0
- package/src/admin-extension.unit.test.ts +532 -0
- package/src/admin-local-agent.smoke.test.ts +1643 -0
- package/src/admin-spec.smoke.test.ts +96 -0
- package/src/admin-spec.ts +186 -0
- package/src/admin-ui-account-nav.ts +20 -0
- package/src/admin-ui-account-pages.ts +131 -0
- package/src/admin-ui-account-pages.unit.test.ts +112 -0
- package/src/admin-ui-account.smoke.test.ts +286 -0
- package/src/admin-ui-account.ts +185 -0
- package/src/admin-ui-accounts-pages.ts +202 -0
- package/src/admin-ui-accounts-pages.unit.test.ts +138 -0
- package/src/admin-ui-accounts.smoke.test.ts +434 -0
- package/src/admin-ui-accounts.ts +266 -0
- package/src/admin-ui-layout.ts +60 -0
- package/src/admin-ui-layout.unit.test.ts +140 -0
- package/src/admin-ui-pages.ts +3489 -0
- package/src/admin-ui-pages.unit.test.ts +4355 -0
- package/src/admin-ui-push-test.smoke.test.ts +125 -0
- package/src/admin-ui-push-test.ts +102 -0
- package/src/admin-ui-styles.ts +656 -0
- package/src/admin-ui-styles.unit.test.ts +103 -0
- package/src/admin-ui.fixtures.ts +457 -0
- package/src/admin-ui.smoke.test.ts +7227 -0
- package/src/admin-ui.ts +3824 -0
- package/src/agent-account-assignment.ts +49 -0
- package/src/agent-account-assignment.unit.test.ts +99 -0
- package/src/agent-anthropic-identity.integration.test.ts +99 -0
- package/src/agent-anthropic-identity.ts +78 -0
- package/src/agent-anthropic-identity.unit.test.ts +77 -0
- package/src/agent-ask-config.ts +59 -0
- package/src/agent-ask-config.unit.test.ts +70 -0
- package/src/agent-ask.integration.test.ts +175 -0
- package/src/agent-ask.smoke.test.ts +457 -0
- package/src/agent-ask.ts +378 -0
- package/src/agent-chat-tokens.integration.test.ts +420 -0
- package/src/agent-chat-tokens.smoke.test.ts +551 -0
- package/src/agent-chat-tokens.ts +259 -0
- package/src/agent-cron-jobs.integration.test.ts +582 -0
- package/src/agent-cron-jobs.ts +615 -0
- package/src/agent-cron-jobs.unit.test.ts +420 -0
- package/src/agent-cron-run-stats.integration.test.ts +1551 -0
- package/src/agent-cron-run-stats.smoke.test.ts +953 -0
- package/src/agent-cron-run-stats.ts +752 -0
- package/src/agent-cron-runs.integration.test.ts +1491 -0
- package/src/agent-cron-runs.ts +489 -0
- package/src/agent-deletion-checklist.ts +128 -0
- package/src/agent-deletion-checklist.unit.test.ts +201 -0
- package/src/agent-deletion-per-agent.integration.test.ts +323 -0
- package/src/agent-deletion.integration.test.ts +323 -0
- package/src/agent-deletion.ts +329 -0
- package/src/agent-deletion.unit.test.ts +412 -0
- package/src/agent-envs.integration.test.ts +323 -0
- package/src/agent-envs.ts +244 -0
- package/src/agent-github-installations.integration.test.ts +115 -0
- package/src/agent-github-installations.ts +52 -0
- package/src/agent-manifest.ts +685 -0
- package/src/agent-manifest.unit.test.ts +870 -0
- package/src/agent-members.ts +69 -0
- package/src/agent-members.unit.test.ts +250 -0
- package/src/agent-patch-author-allowlist.integration.test.ts +218 -0
- package/src/agent-plugins.integration.test.ts +157 -0
- package/src/agent-plugins.ts +95 -0
- package/src/agent-plugins.unit.test.ts +43 -0
- package/src/agent-provisioner-per-agent.integration.test.ts +949 -0
- package/src/agent-provisioner.integration.test.ts +1245 -0
- package/src/agent-provisioner.ts +1650 -0
- package/src/agent-provisioner.unit.test.ts +1852 -0
- package/src/agent-tokens.integration.test.ts +150 -0
- package/src/agent-tokens.ts +112 -0
- package/src/agent-tools.integration.test.ts +129 -0
- package/src/agent-tools.ts +111 -0
- package/src/agents-api.integration.test.ts +705 -0
- package/src/agents-api.sentry.smoke.test.ts +478 -0
- package/src/agents-api.smoke.test.ts +5986 -0
- package/src/agents-api.ts +2826 -0
- package/src/agents.integration.test.ts +512 -0
- package/src/agents.ts +1097 -0
- package/src/agents.unit.test.ts +1736 -0
- package/src/anthropic-credential-guard.ts +44 -0
- package/src/anthropic-credential-guard.unit.test.ts +88 -0
- package/src/anthropic-federated-token-provider.integration.test.ts +248 -0
- package/src/anthropic-federated-token-provider.ts +249 -0
- package/src/anthropic-identity-client.fixtures.ts +237 -0
- package/src/anthropic-identity-client.integration.test.ts +342 -0
- package/src/anthropic-identity-client.ts +473 -0
- package/src/anthropic-identity-fixture-spec.integration.test.ts +169 -0
- package/src/anthropic-identity-name.integration.test.ts +83 -0
- package/src/anthropic-identity-name.ts +163 -0
- package/src/anthropic-identity-name.unit.test.ts +345 -0
- package/src/anthropic-openapi-contract.fixtures.ts +192 -0
- package/src/api-auth.ts +383 -0
- package/src/api-auth.unit.test.ts +837 -0
- package/src/api.smoke.test.ts +1225 -0
- package/src/api.ts +376 -0
- package/src/attachment-validation.ts +124 -0
- package/src/attachment-validation.unit.test.ts +218 -0
- package/src/caller-scope.ts +131 -0
- package/src/caller-scope.unit.test.ts +116 -0
- package/src/chat-credential-config.ts +22 -0
- package/src/chat-credentials-logging.unit.test.ts +92 -0
- package/src/chat-markers.ts +114 -0
- package/src/chat-markers.unit.test.ts +116 -0
- package/src/chat-service-provisioning-client.integration.test.ts +412 -0
- package/src/chat-service-provisioning-client.ts +221 -0
- package/src/chat.smoke.test.ts +1875 -0
- package/src/clock.ts +47 -0
- package/src/coding-task-store-provisioning-client.integration.test.ts +372 -0
- package/src/coding-task-store-provisioning-client.ts +156 -0
- package/src/crypto.ts +83 -0
- package/src/dev-auth-guard.ts +29 -0
- package/src/dev-auth-guard.unit.test.ts +51 -0
- package/src/errors.ts +77 -0
- package/src/errors.unit.test.ts +52 -0
- package/src/fixtures/anthropic-identity-recorded-fixtures.json +850 -0
- package/src/fixtures/anthropic-openapi/SDK_VERSION +1 -0
- package/src/fixtures/anthropic-openapi/mock-spec.json.gz +0 -0
- package/src/fixtures/chat-service-provisioning-cassette.json +156 -0
- package/src/fixtures/github-app-manifest-cassette.json +44 -0
- package/src/fixtures/google-auth-cassette.json +36 -0
- package/src/fixtures/http-chat-client-cassette.json +226 -0
- package/src/fixtures/k8s-cassette.json +470 -0
- package/src/fixtures/okta-auth-cassette.json +36 -0
- package/src/fixtures/slack-provision-cassette.json +12 -0
- package/src/fixtures/slack-provisioning-http-cassette.json +98 -0
- package/src/fixtures/task-store-provisioning-cassette.json +85 -0
- package/src/github-app-provisioning-client.integration.test.ts +149 -0
- package/src/github-app-provisioning-client.ts +161 -0
- package/src/github-app-provisioning-client.unit.test.ts +114 -0
- package/src/github-provisioning-service.ts +569 -0
- package/src/github-provisioning-service.unit.test.ts +123 -0
- package/src/google-auth-client.integration.test.ts +195 -0
- package/src/google-auth-client.ts +121 -0
- package/src/http-chat-client.integration.test.ts +554 -0
- package/src/http-chat-client.ts +533 -0
- package/src/http-chat-client.unit.test.ts +119 -0
- package/src/import-boundary.unit.test.ts +240 -0
- package/src/kubernetes-client.integration.test.ts +697 -0
- package/src/kubernetes-client.ts +1027 -0
- package/src/kubernetes-client.unit.test.ts +726 -0
- package/src/main.smoke.test.ts +91 -0
- package/src/main.ts +21 -0
- package/src/main.unit.test.ts +801 -0
- package/src/okta-auth-client.integration.test.ts +263 -0
- package/src/okta-auth-client.ts +152 -0
- package/src/openapi-schemas.ts +1310 -0
- package/src/openapi-schemas.unit.test.ts +348 -0
- package/src/per-agent-identity.fixtures.ts +305 -0
- package/src/platform-doc-route.ts +40 -0
- package/src/prisma-client.integration.test.ts +57 -0
- package/src/prisma-client.ts +187 -0
- package/src/prisma-client.unit.test.ts +178 -0
- package/src/prisma-tx.ts +23 -0
- package/src/push-content.ts +177 -0
- package/src/push-content.unit.test.ts +213 -0
- package/src/push-crypto.ts +135 -0
- package/src/push-crypto.unit.test.ts +66 -0
- package/src/push-sender.ts +169 -0
- package/src/push-sender.unit.test.ts +107 -0
- package/src/push-service.ts +332 -0
- package/src/push-service.unit.test.ts +679 -0
- package/src/push-toggle.ts +135 -0
- package/src/push-toggle.unit.test.ts +78 -0
- package/src/pwa-https-gating.smoke.test.ts +229 -0
- package/src/pwa-icons.unit.test.ts +41 -0
- package/src/pwa-routes.smoke.test.ts +276 -0
- package/src/pwa.ts +380 -0
- package/src/pwa.unit.test.ts +321 -0
- package/src/schema.unit.test.ts +122 -0
- package/src/self-serve-config.ts +52 -0
- package/src/self-serve-config.unit.test.ts +79 -0
- package/src/server.smoke.test.ts +273 -0
- package/src/server.ts +50 -0
- package/src/service-integration.ts +67 -0
- package/src/service-integration.unit.test.ts +129 -0
- package/src/slack-provisioning-client.integration.test.ts +308 -0
- package/src/slack-provisioning-client.ts +411 -0
- package/src/slack-provisioning-client.unit.test.ts +80 -0
- package/src/slack-provisioning-service.integration.test.ts +637 -0
- package/src/slack-provisioning-service.ts +451 -0
- package/src/slack-provisioning.integration.test.ts +395 -0
- package/src/specialty-manifest-loader.fixture.integration.test.ts +63 -0
- package/src/specialty-manifest-loader.ts +255 -0
- package/src/specialty-manifest-loader.unit.test.ts +301 -0
- package/src/specialty-registry.schema.unit.test.ts +35 -0
- package/src/specialty-registry.ts +236 -0
- package/src/specialty-registry.unit.test.ts +272 -0
- package/src/startup-policy-reconcile.ts +77 -0
- package/src/startup-policy-reconcile.unit.test.ts +251 -0
- package/src/theme-tokens.json +12 -0
- package/src/token-crypto.ts +60 -0
- package/src/token-crypto.unit.test.ts +182 -0
- package/src/trial-expiry-sweeper.integration.test.ts +318 -0
- package/src/trial-expiry-sweeper.ts +132 -0
- package/src/trial-expiry-sweeper.unit.test.ts +391 -0
- package/src/trial-expiry-warning-sweeper.integration.test.ts +468 -0
- package/src/trial-expiry-warning-sweeper.ts +447 -0
- package/src/trial-expiry-warning-sweeper.unit.test.ts +148 -0
- package/src/voice-contract.unit.test.ts +102 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin/e2e/test-server.ts
|
|
3
|
+
* Admin UI — E2E Test Server (ADM-3.2)
|
|
4
|
+
*
|
|
5
|
+
* Starts a minimal Hono server backed by in-memory mock services so that
|
|
6
|
+
* Playwright tests can exercise the admin UI without a real database or
|
|
7
|
+
* Google OAuth credentials.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* ADMIN_E2E_PORT=3490 bun run admin/e2e/test-server.ts
|
|
11
|
+
* ADMIN_E2E_AGENTS_MODE=1 ADMIN_E2E_PORT=3490 bun run admin/e2e/test-server.ts
|
|
12
|
+
*
|
|
13
|
+
* The server exposes:
|
|
14
|
+
* GET /health → 200 OK
|
|
15
|
+
* GET /admin/login → login page
|
|
16
|
+
* GET /admin/agents → agents list (requires admin_session cookie)
|
|
17
|
+
* GET /admin/agents/:id → agent detail (requires admin_session cookie)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { Hono } from "hono";
|
|
21
|
+
import { createAdminUIApp } from "../src/admin-ui.ts";
|
|
22
|
+
import type { AdminUIDeps } from "../src/admin-ui.ts";
|
|
23
|
+
import type {
|
|
24
|
+
ChatClient,
|
|
25
|
+
ChatMessage,
|
|
26
|
+
ChatThread,
|
|
27
|
+
} from "../src/http-chat-client.ts";
|
|
28
|
+
|
|
29
|
+
// ─── Config ───────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
const ADMIN_E2E_PORT = 3490;
|
|
32
|
+
const ADMIN_E2E_SESSION_SECRET =
|
|
33
|
+
process.env.ADMIN_E2E_SESSION_SECRET ?? "e2e-admin-test-secret-32chars!!!";
|
|
34
|
+
|
|
35
|
+
const ADMIN_E2E_AGENT = {
|
|
36
|
+
id: "agent-e2e-1",
|
|
37
|
+
name: "Test Agent",
|
|
38
|
+
slackId: "U999TEST",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ─── Mock data ────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
const MOCK_CRON = {
|
|
44
|
+
id: "cron-e2e-1",
|
|
45
|
+
agentId: ADMIN_E2E_AGENT.id,
|
|
46
|
+
schedule: "0 9 * * 1",
|
|
47
|
+
prompt: "Send the weekly standup summary to the team channel",
|
|
48
|
+
channel: "#general",
|
|
49
|
+
user: null,
|
|
50
|
+
enabled: true,
|
|
51
|
+
name: null,
|
|
52
|
+
system: false,
|
|
53
|
+
silent: false,
|
|
54
|
+
preCheck: null,
|
|
55
|
+
createdAt: new Date("2024-01-01"),
|
|
56
|
+
updatedAt: new Date("2024-01-01"),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const MOCK_TOOL = {
|
|
60
|
+
id: "tool-e2e-1",
|
|
61
|
+
agentId: ADMIN_E2E_AGENT.id,
|
|
62
|
+
pattern: "Bash(git:*)",
|
|
63
|
+
enabled: true,
|
|
64
|
+
createdAt: new Date("2024-01-01"),
|
|
65
|
+
updatedAt: new Date("2024-01-01"),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const MOCK_TOKEN = {
|
|
69
|
+
id: "token-e2e-1",
|
|
70
|
+
agentId: ADMIN_E2E_AGENT.id,
|
|
71
|
+
token: "hashed-token-value",
|
|
72
|
+
label: "Production token",
|
|
73
|
+
createdAt: new Date("2024-01-01"),
|
|
74
|
+
revokedAt: null,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// ─── Chat fixtures (CFB-1.3 — chat thread page e2e) ──────────────────────────
|
|
78
|
+
|
|
79
|
+
const MOCK_CHAT_THREAD: ChatThread = {
|
|
80
|
+
id: "thread-e2e-1",
|
|
81
|
+
agentId: ADMIN_E2E_AGENT.id,
|
|
82
|
+
title: "E2E Test Thread",
|
|
83
|
+
memberId: null,
|
|
84
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
85
|
+
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const MOCK_CHAT_MESSAGES: ChatMessage[] = [
|
|
89
|
+
{
|
|
90
|
+
id: "msg-e2e-1",
|
|
91
|
+
threadId: MOCK_CHAT_THREAD.id,
|
|
92
|
+
role: "user",
|
|
93
|
+
// Long enough to overflow any width well under the 70% max-width cap, so
|
|
94
|
+
// the e2e computed-width assertion (chat-thread-page.e2e.ts) exercises
|
|
95
|
+
// the CSS-driven cap rather than content-driven shrink-to-fit.
|
|
96
|
+
body: "Hello from the e2e test suite — this message body is intentionally long so that its rendered width is capped by the chat-bubble-inner max-width rule rather than shrinking to fit its own content, which lets the e2e test assert a stable 70% width ratio against the messages container.",
|
|
97
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
98
|
+
claimedBy: null,
|
|
99
|
+
repliedAt: null,
|
|
100
|
+
tokens: null,
|
|
101
|
+
costUsd: null,
|
|
102
|
+
errorKind: null,
|
|
103
|
+
attachmentFilename: null,
|
|
104
|
+
attachmentSize: null,
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
// ─── Live-progress fixture (CFB-2.3) ─────────────────────────────────────────
|
|
109
|
+
// When ADMIN_E2E_CHAT_PENDING=1, the mock thread's last message is an
|
|
110
|
+
// UNREPLIED user message whose progressSeq stays frozen — so the server
|
|
111
|
+
// renders the live status bubble and the client ticker + stall state can be
|
|
112
|
+
// asserted by chat-progress.e2e.ts without any real agent. createdAt is
|
|
113
|
+
// computed at boot so the elapsed value starts near 0 and visibly increments.
|
|
114
|
+
const MOCK_CHAT_PENDING_MESSAGES: ChatMessage[] = [
|
|
115
|
+
{
|
|
116
|
+
id: "msg-pending-e2e-1",
|
|
117
|
+
threadId: MOCK_CHAT_THREAD.id,
|
|
118
|
+
role: "user",
|
|
119
|
+
body: "Kick off a long-running task, please.",
|
|
120
|
+
// Anchored at boot so elapsed starts small and climbs across DOM reads.
|
|
121
|
+
createdAt: new Date().toISOString(),
|
|
122
|
+
claimedBy: "agent-e2e-1",
|
|
123
|
+
claimedAt: new Date().toISOString(),
|
|
124
|
+
heartbeatAt: new Date().toISOString(),
|
|
125
|
+
repliedAt: null,
|
|
126
|
+
tokens: null,
|
|
127
|
+
costUsd: null,
|
|
128
|
+
errorKind: null,
|
|
129
|
+
attachmentFilename: null,
|
|
130
|
+
attachmentSize: null,
|
|
131
|
+
// progressPhase set so a milestone label renders; progressSeq is FROZEN
|
|
132
|
+
// (never advances across polls) so the stall state trips once the
|
|
133
|
+
// (test-shortened) STALL_WARN_AFTER_MS elapses.
|
|
134
|
+
progressPhase: "reading",
|
|
135
|
+
progressSeq: 1,
|
|
136
|
+
cancelRequestedAt: null,
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const CHAT_PENDING_MODE = process.env.ADMIN_E2E_CHAT_PENDING === "1";
|
|
141
|
+
|
|
142
|
+
function chatMessages(): ChatMessage[] {
|
|
143
|
+
return CHAT_PENDING_MODE ? MOCK_CHAT_PENDING_MESSAGES : MOCK_CHAT_MESSAGES;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function buildMockChatClient(): ChatClient {
|
|
147
|
+
return {
|
|
148
|
+
listThreads: async () => ({
|
|
149
|
+
threads: [MOCK_CHAT_THREAD],
|
|
150
|
+
total: 1,
|
|
151
|
+
limit: 50,
|
|
152
|
+
offset: 0,
|
|
153
|
+
}),
|
|
154
|
+
getThread: async () => MOCK_CHAT_THREAD,
|
|
155
|
+
createThread: async () => MOCK_CHAT_THREAD,
|
|
156
|
+
updateThread: async () => MOCK_CHAT_THREAD,
|
|
157
|
+
deleteThread: async () => {},
|
|
158
|
+
listMessages: async () => {
|
|
159
|
+
const messages = chatMessages();
|
|
160
|
+
return {
|
|
161
|
+
messages,
|
|
162
|
+
total: messages.length,
|
|
163
|
+
limit: 50,
|
|
164
|
+
offset: 0,
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
createMessage: async () => chatMessages()[0],
|
|
168
|
+
getThreadStats: async () => {
|
|
169
|
+
const messages = chatMessages();
|
|
170
|
+
return {
|
|
171
|
+
messageCount: messages.length,
|
|
172
|
+
totalInputTokens: 0,
|
|
173
|
+
totalOutputTokens: 0,
|
|
174
|
+
totalCostUsd: 0,
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ─── Degraded-mode switch (CFB-2.3) ──────────────────────────────────────────
|
|
181
|
+
// When ADMIN_E2E_CHAT_CLIENT_ABSENT=1, buildTestApp() omits chatClient
|
|
182
|
+
// entirely — mirroring production when SHIPWRIGHT_CHAT_SERVICE_URL /
|
|
183
|
+
// SHIPWRIGHT_CHAT_SERVICE_ADMIN_TOKEN are unset — so the /admin/chat* routes
|
|
184
|
+
// fall back to the degraded alert instead of the real chat UI. This proves
|
|
185
|
+
// chatClient is a genuine injection point and that the other chat e2e suites
|
|
186
|
+
// are exercising the real UI, not a silently-degraded fallback.
|
|
187
|
+
const CHAT_CLIENT_ABSENT = process.env.ADMIN_E2E_CHAT_CLIENT_ABSENT === "1";
|
|
188
|
+
|
|
189
|
+
// ─── Mock deps factory ────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
function buildMockDeps(chatClient: ChatClient | undefined): AdminUIDeps {
|
|
192
|
+
return {
|
|
193
|
+
prisma: {
|
|
194
|
+
agent: {
|
|
195
|
+
findMany: async () => [
|
|
196
|
+
{
|
|
197
|
+
id: ADMIN_E2E_AGENT.id,
|
|
198
|
+
name: ADMIN_E2E_AGENT.name,
|
|
199
|
+
slackId: ADMIN_E2E_AGENT.slackId,
|
|
200
|
+
createdAt: new Date("2024-01-01"),
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
findUnique: async () => ({
|
|
204
|
+
id: ADMIN_E2E_AGENT.id,
|
|
205
|
+
name: ADMIN_E2E_AGENT.name,
|
|
206
|
+
slackId: ADMIN_E2E_AGENT.slackId,
|
|
207
|
+
selfHosted: false,
|
|
208
|
+
createdAt: new Date("2024-01-01"),
|
|
209
|
+
updatedAt: new Date("2024-01-01"),
|
|
210
|
+
repos: [],
|
|
211
|
+
}),
|
|
212
|
+
create: async () => ({
|
|
213
|
+
id: ADMIN_E2E_AGENT.id,
|
|
214
|
+
name: ADMIN_E2E_AGENT.name,
|
|
215
|
+
slackId: ADMIN_E2E_AGENT.slackId,
|
|
216
|
+
createdAt: new Date("2024-01-01"),
|
|
217
|
+
updatedAt: new Date("2024-01-01"),
|
|
218
|
+
repos: [],
|
|
219
|
+
}),
|
|
220
|
+
},
|
|
221
|
+
agentPlugin: {
|
|
222
|
+
findMany: async () => [],
|
|
223
|
+
},
|
|
224
|
+
// Referenced by the agent detail page's admin-only member list and by
|
|
225
|
+
// assertAgentAccess's non-admin membership check. Sessions minted for
|
|
226
|
+
// these e2e tests explicitly set `isAdmin: true`,
|
|
227
|
+
// so the render path deliberately takes the admin branch
|
|
228
|
+
// (prisma.agentMember.findMany), never the non-admin findUnique branch.
|
|
229
|
+
// No members fixture exists, so both resolve to empty/not-found. A
|
|
230
|
+
// non-admin session case isn't exercised here — that's an intentional
|
|
231
|
+
// test-coverage gap, not a fixture bug.
|
|
232
|
+
agentMember: {
|
|
233
|
+
findMany: async () => [],
|
|
234
|
+
findUnique: async () => null,
|
|
235
|
+
create: async () => {
|
|
236
|
+
throw new Error("not used in e2e tests");
|
|
237
|
+
},
|
|
238
|
+
deleteMany: async () => ({ count: 0 }),
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
agentEnvService: {
|
|
242
|
+
getByAgentId: async () => ({
|
|
243
|
+
SLACK_BOT_TOKEN: "xoxb-secret-value",
|
|
244
|
+
OPENAI_API_KEY: "sk-secret-value",
|
|
245
|
+
}),
|
|
246
|
+
upsert: async () => {},
|
|
247
|
+
patch: async () => {},
|
|
248
|
+
deleteKey: async () => {},
|
|
249
|
+
getConfigBundle: async () => null,
|
|
250
|
+
},
|
|
251
|
+
agentCronJobService: {
|
|
252
|
+
list: async () => [MOCK_CRON],
|
|
253
|
+
// Mirrors AgentCronJobService.listWithRunSummary: same rows as list(),
|
|
254
|
+
// plus a per-cron lastRun summary (null = "never run") and a
|
|
255
|
+
// runCountToday count. MOCK_CRON has never run, so both are the
|
|
256
|
+
// "no runs yet" defaults — the agent detail page renders that as
|
|
257
|
+
// "never" in the cron table, which doesn't affect the schedule/prompt
|
|
258
|
+
// assertions the existing e2e tests make.
|
|
259
|
+
listWithRunSummary: async () => [
|
|
260
|
+
{ ...MOCK_CRON, lastRun: null, runCountToday: 0 },
|
|
261
|
+
],
|
|
262
|
+
get: async () => MOCK_CRON,
|
|
263
|
+
create: async () => MOCK_CRON,
|
|
264
|
+
update: async () => MOCK_CRON,
|
|
265
|
+
setEnabled: async () => MOCK_CRON,
|
|
266
|
+
delete: async () => {},
|
|
267
|
+
reconcileSystemCrons: async () => ({
|
|
268
|
+
created: 0,
|
|
269
|
+
updated: 0,
|
|
270
|
+
deleted: 0,
|
|
271
|
+
}),
|
|
272
|
+
},
|
|
273
|
+
agentToolService: {
|
|
274
|
+
list: async () => [MOCK_TOOL],
|
|
275
|
+
add: async () => MOCK_TOOL,
|
|
276
|
+
toggle: async () => MOCK_TOOL,
|
|
277
|
+
remove: async () => {},
|
|
278
|
+
},
|
|
279
|
+
agentTokenService: {
|
|
280
|
+
listForAgent: async () => [MOCK_TOKEN],
|
|
281
|
+
create: async () => ({
|
|
282
|
+
token: MOCK_TOKEN,
|
|
283
|
+
rawToken: "sw_e2erawtokenvalue",
|
|
284
|
+
}),
|
|
285
|
+
revoke: async () => MOCK_TOKEN,
|
|
286
|
+
},
|
|
287
|
+
agentPluginService: {
|
|
288
|
+
list: async () => [],
|
|
289
|
+
add: async (agentId: string, name: string) => ({
|
|
290
|
+
id: "plugin-e2e-1",
|
|
291
|
+
agentId,
|
|
292
|
+
name,
|
|
293
|
+
version: null,
|
|
294
|
+
enabled: true,
|
|
295
|
+
createdAt: new Date("2024-01-01"),
|
|
296
|
+
updatedAt: new Date("2024-01-01"),
|
|
297
|
+
}),
|
|
298
|
+
},
|
|
299
|
+
agentMemberService: {
|
|
300
|
+
listByEmail: async () => [],
|
|
301
|
+
exists: async () => false,
|
|
302
|
+
add: async (agentId: string, email: string) => ({
|
|
303
|
+
id: "m1",
|
|
304
|
+
agentId,
|
|
305
|
+
email,
|
|
306
|
+
}),
|
|
307
|
+
remove: async () => {},
|
|
308
|
+
listByAgentId: async () => [],
|
|
309
|
+
},
|
|
310
|
+
agentService: {
|
|
311
|
+
listAll: async () => [
|
|
312
|
+
{
|
|
313
|
+
id: ADMIN_E2E_AGENT.id,
|
|
314
|
+
name: ADMIN_E2E_AGENT.name,
|
|
315
|
+
slackId: ADMIN_E2E_AGENT.slackId,
|
|
316
|
+
selfHosted: false,
|
|
317
|
+
typeName: "coding",
|
|
318
|
+
createdAt: new Date("2024-01-01"),
|
|
319
|
+
updatedAt: new Date("2024-01-01"),
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
listByIds: async () => [],
|
|
323
|
+
searchByName: async () => [],
|
|
324
|
+
listOptions: async () => [
|
|
325
|
+
{ id: ADMIN_E2E_AGENT.id, name: ADMIN_E2E_AGENT.name },
|
|
326
|
+
],
|
|
327
|
+
create: async (input: { name: string; typeName?: string }) => ({
|
|
328
|
+
id: ADMIN_E2E_AGENT.id,
|
|
329
|
+
name: input.name ?? ADMIN_E2E_AGENT.name,
|
|
330
|
+
slackId: ADMIN_E2E_AGENT.slackId,
|
|
331
|
+
selfHosted: false,
|
|
332
|
+
typeName: input.typeName ?? "coding",
|
|
333
|
+
createdAt: new Date("2024-01-01"),
|
|
334
|
+
updatedAt: new Date("2024-01-01"),
|
|
335
|
+
}),
|
|
336
|
+
delete: async () => {},
|
|
337
|
+
getDetail: async () => ({
|
|
338
|
+
id: ADMIN_E2E_AGENT.id,
|
|
339
|
+
name: ADMIN_E2E_AGENT.name,
|
|
340
|
+
slackId: ADMIN_E2E_AGENT.slackId,
|
|
341
|
+
selfHosted: false,
|
|
342
|
+
typeName: "coding",
|
|
343
|
+
createdAt: new Date("2024-01-01"),
|
|
344
|
+
updatedAt: new Date("2024-01-01"),
|
|
345
|
+
repos: [],
|
|
346
|
+
reviewAuthorAllowlist: [],
|
|
347
|
+
missingRequiredEnv: [],
|
|
348
|
+
}),
|
|
349
|
+
updateFields: async () => ({
|
|
350
|
+
id: ADMIN_E2E_AGENT.id,
|
|
351
|
+
name: ADMIN_E2E_AGENT.name,
|
|
352
|
+
slackId: ADMIN_E2E_AGENT.slackId,
|
|
353
|
+
selfHosted: false,
|
|
354
|
+
typeName: "coding",
|
|
355
|
+
createdAt: new Date("2024-01-01"),
|
|
356
|
+
updatedAt: new Date("2024-01-01"),
|
|
357
|
+
repos: [],
|
|
358
|
+
reviewAuthorAllowlist: [],
|
|
359
|
+
missingRequiredEnv: [],
|
|
360
|
+
}),
|
|
361
|
+
},
|
|
362
|
+
provisioner: {
|
|
363
|
+
canProvision: false,
|
|
364
|
+
provision: async () => ({
|
|
365
|
+
resourceName: "r",
|
|
366
|
+
secretName: "s",
|
|
367
|
+
deploymentName: "d",
|
|
368
|
+
}),
|
|
369
|
+
deprovision: async () => {},
|
|
370
|
+
reconcile: async () => ({
|
|
371
|
+
recreated: [],
|
|
372
|
+
updated: [],
|
|
373
|
+
orphans: [],
|
|
374
|
+
failed: [],
|
|
375
|
+
}),
|
|
376
|
+
},
|
|
377
|
+
sessionSecret: ADMIN_E2E_SESSION_SECRET,
|
|
378
|
+
googleClientId: "e2e-google-client-id",
|
|
379
|
+
googleClientSecret: "e2e-google-client-secret",
|
|
380
|
+
adminAllowedEmails: ["admin@example.com"],
|
|
381
|
+
googleClient: {
|
|
382
|
+
exchangeCode: () => Promise.reject(new Error("not used in e2e tests")),
|
|
383
|
+
getUserInfo: () => Promise.reject(new Error("not used in e2e tests")),
|
|
384
|
+
},
|
|
385
|
+
slackClient: {
|
|
386
|
+
createAppManifest: async () => ({
|
|
387
|
+
appId: "A_E2E_TEST",
|
|
388
|
+
oauthRedirectUrl: "https://slack.com/oauth/authorize?client_id=e2e",
|
|
389
|
+
}),
|
|
390
|
+
},
|
|
391
|
+
appBaseUrl: `http://localhost:${ADMIN_E2E_PORT}`,
|
|
392
|
+
chatClient,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// ─── Build app ────────────────────────────────────────────────────────────────
|
|
397
|
+
|
|
398
|
+
function buildTestApp(): Hono {
|
|
399
|
+
const app = new Hono();
|
|
400
|
+
|
|
401
|
+
// Health check
|
|
402
|
+
app.get("/health", (c) => c.text("ok"));
|
|
403
|
+
|
|
404
|
+
// Mount the admin UI
|
|
405
|
+
const chatClient = CHAT_CLIENT_ABSENT ? undefined : buildMockChatClient();
|
|
406
|
+
const adminApp = createAdminUIApp(buildMockDeps(chatClient));
|
|
407
|
+
app.route("/", adminApp);
|
|
408
|
+
|
|
409
|
+
return app;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// ─── Entrypoint ───────────────────────────────────────────────────────────────
|
|
413
|
+
|
|
414
|
+
const port = Number.parseInt(
|
|
415
|
+
process.env.ADMIN_E2E_PORT ?? String(ADMIN_E2E_PORT),
|
|
416
|
+
10,
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// Re-read secret at runtime in case it was overridden via env
|
|
420
|
+
const app = buildTestApp();
|
|
421
|
+
Bun.serve({ port, fetch: app.fetch });
|
|
422
|
+
console.log(
|
|
423
|
+
`[admin-e2e-server] Test server running on http://localhost:${port}`,
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
process.on("SIGTERM", () => process.exit(0));
|
|
427
|
+
process.on("SIGINT", () => process.exit(0));
|