@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.
Files changed (325) hide show
  1. package/Dockerfile +43 -0
  2. package/README.md +72 -2
  3. package/e2e/agents-page.e2e.ts +338 -0
  4. package/e2e/chat-degraded.e2e.ts +153 -0
  5. package/e2e/chat-page.e2e.ts +169 -0
  6. package/e2e/chat-progress.e2e.ts +206 -0
  7. package/e2e/chat-thread-page.e2e.ts +444 -0
  8. package/e2e/login-page.e2e.ts +148 -0
  9. package/e2e/test-server.ts +427 -0
  10. package/openapi.json +6098 -0
  11. package/openapi.platform.json +5553 -0
  12. package/package.json +62 -4
  13. package/playwright.config.ts +25 -0
  14. package/prisma/client/browser.ts +134 -0
  15. package/prisma/client/client.ts +158 -0
  16. package/prisma/client/commonInputTypes.ts +578 -0
  17. package/prisma/client/enums.ts +15 -0
  18. package/prisma/client/internal/class.ts +424 -0
  19. package/prisma/client/internal/prismaNamespace.ts +2898 -0
  20. package/prisma/client/internal/prismaNamespaceBrowser.ts +476 -0
  21. package/prisma/client/models/Account.ts +1774 -0
  22. package/prisma/client/models/AccountInvite.ts +1386 -0
  23. package/prisma/client/models/AccountMember.ts +1351 -0
  24. package/prisma/client/models/Agent.ts +4554 -0
  25. package/prisma/client/models/AgentAsk.ts +1373 -0
  26. package/prisma/client/models/AgentChatTokenUsageDailyByModel.ts +1638 -0
  27. package/prisma/client/models/AgentCronJob.ts +2416 -0
  28. package/prisma/client/models/AgentCronRun.ts +2858 -0
  29. package/prisma/client/models/AgentCronRunModelBreakdown.ts +1532 -0
  30. package/prisma/client/models/AgentCronRunSkillUsage.ts +1642 -0
  31. package/prisma/client/models/AgentEnv.ts +1421 -0
  32. package/prisma/client/models/AgentGitHubInstallationsSnapshot.ts +1289 -0
  33. package/prisma/client/models/AgentMember.ts +1316 -0
  34. package/prisma/client/models/AgentPhaseMethodology.ts +1355 -0
  35. package/prisma/client/models/AgentPlugin.ts +1421 -0
  36. package/prisma/client/models/AgentToken.ts +1380 -0
  37. package/prisma/client/models/AgentTool.ts +1351 -0
  38. package/prisma/client/models/AgentWorkQueueSnapshot.ts +1297 -0
  39. package/prisma/client/models/ChatThreadWatch.ts +1183 -0
  40. package/prisma/client/models/PushSubscription.ts +1233 -0
  41. package/prisma/client/models/SessionAlertState.ts +1187 -0
  42. package/prisma/client/models/SessionFollow.ts +1187 -0
  43. package/prisma/client/models/UserNotificationPrefs.ts +1221 -0
  44. package/prisma/client/models.ts +34 -0
  45. package/prisma/migrations/20260608000000_init/migration.sql +112 -0
  46. package/prisma/migrations/20260616000000_agent_members/migration.sql +18 -0
  47. package/prisma/migrations/20260624000000_add_agent_self_hosted/migration.sql +2 -0
  48. package/prisma/migrations/20260624120000_add_agent_repos/migration.sql +3 -0
  49. package/prisma/migrations/20260625000000_add_agent_cron_run/migration.sql +26 -0
  50. package/prisma/migrations/20260626000000_add_cron_run_token_fields/migration.sql +10 -0
  51. package/prisma/migrations/20260627000000_add_agent_chat_token_usage_daily/migration.sql +18 -0
  52. package/prisma/migrations/20260627010000_add_agent_chat_token_usage_fk/migration.sql +2 -0
  53. package/prisma/migrations/20260701000000_add_chat_threads_messages/migration.sql +37 -0
  54. package/prisma/migrations/20260701010000_drop_chat_threads_messages/migration.sql +17 -0
  55. package/prisma/migrations/20260701020000_add_cron_run_model_breakdown/migration.sql +23 -0
  56. package/prisma/migrations/20260701020000_chat_token_by_model/migration.sql +25 -0
  57. package/prisma/migrations/20260701030000_add_secret_flag_to_agent_env/migration.sql +2 -0
  58. package/prisma/migrations/20260701030000_drop_cron_run_legacy_columns/migration.sql +5 -0
  59. package/prisma/migrations/20260706000000_consolidate_cron_run_token_accounting/migration.sql +54 -0
  60. package/prisma/migrations/20260709000000_add_phase_to_agent_cron_run/migration.sql +8 -0
  61. package/prisma/migrations/20260717000000_add_item_tracking_to_agent_cron_run/migration.sql +10 -0
  62. package/prisma/migrations/20260717010000_add_agent_work_queue_snapshot/migration.sql +20 -0
  63. package/prisma/migrations/20260718000000_add_cron_phase_columns/migration.sql +25 -0
  64. package/prisma/migrations/20260718100000_drop_agent_cron_run_phase_column/migration.sql +8 -0
  65. package/prisma/migrations/20260724000000_add_agent_type_name/migration.sql +3 -0
  66. package/prisma/migrations/20260725232433_add_agent_author_allowlist/migration.sql +2 -0
  67. package/prisma/migrations/20260727120000_add_not_null_to_agent_author_allowlist/migration.sql +3 -0
  68. package/prisma/migrations/20260731000000_add_session_id_to_agent_cron_run/migration.sql +2 -0
  69. package/prisma/migrations/20260821000000_add_agent_restrict_slack_to_members/migration.sql +3 -0
  70. package/prisma/migrations/20260901000000_add_push_subscription_and_chat_thread_watch/migration.sql +40 -0
  71. package/prisma/migrations/20260903000000_add_agent_patch_author_allowlist/migration.sql +2 -0
  72. package/prisma/migrations/20260903120000_add_agent_review_author_allowlist/migration.sql +18 -0
  73. package/prisma/migrations/20260903150000_drop_agent_author_allowlist/migration.sql +14 -0
  74. package/prisma/migrations/20260905000000_add_started_at_index_to_agent_cron_run/migration.sql +11 -0
  75. package/prisma/migrations/20260906000000_add_session_follow_notification_prefs_alert_state/migration.sql +47 -0
  76. package/prisma/migrations/20260911000000_backfill_auto_follow_since/migration.sql +16 -0
  77. package/prisma/migrations/20260923115814_add_last_heartbeat_at_to_agent_cron_run/migration.sql +2 -0
  78. package/prisma/migrations/20260924120000_add_trial_expiry_fields_to_agent/migration.sql +6 -0
  79. package/prisma/migrations/20260929000000_add_agent_phase_methodology/migration.sql +21 -0
  80. package/prisma/migrations/20260929120000_add_agent_policy_fields/migration.sql +15 -0
  81. package/prisma/migrations/20261005000000_add_agent_github_installations_snapshot/migration.sql +21 -0
  82. package/prisma/migrations/20261008000000_add_cron_run_telemetry/migration.sql +50 -0
  83. package/prisma/migrations/20261008010000_add_account_models/migration.sql +48 -0
  84. package/prisma/migrations/20261008020000_add_cron_lockdown_disabled_at/migration.sql +3 -0
  85. package/prisma/migrations/20261009000000_chat_token_usage_bigint/migration.sql +7 -0
  86. package/prisma/migrations/20261010000000_add_cron_run_command_variant/migration.sql +2 -0
  87. package/prisma/migrations/20261019000000_add_agent_anthropic_identity/migration.sql +9 -0
  88. package/prisma/migrations/20261025000000_add_agent_ask/migration.sql +23 -0
  89. package/prisma/migrations/migration_lock.toml +3 -0
  90. package/prisma/schema/coding.prisma +85 -0
  91. package/prisma/schema/schema.prisma +506 -0
  92. package/prisma.config.ts +34 -0
  93. package/pwa-assets/icons/apple-touch-icon.png +0 -0
  94. package/pwa-assets/icons/favicon-32.png +0 -0
  95. package/pwa-assets/icons/icon-192.png +0 -0
  96. package/pwa-assets/icons/icon-512.png +0 -0
  97. package/pwa-assets/icons/icon-maskable-192.png +0 -0
  98. package/pwa-assets/icons/icon-maskable-512.png +0 -0
  99. package/src/account-agent-create.ts +42 -0
  100. package/src/account-agent-create.unit.test.ts +28 -0
  101. package/src/account-created-notifier.ts +71 -0
  102. package/src/account-created-notifier.unit.test.ts +70 -0
  103. package/src/account-email.ts +9 -0
  104. package/src/account-email.unit.test.ts +21 -0
  105. package/src/account-invites.ts +70 -0
  106. package/src/account-lifecycle.integration.test.ts +329 -0
  107. package/src/account-lifecycle.ts +206 -0
  108. package/src/account-lifecycle.unit.test.ts +108 -0
  109. package/src/account-members.ts +128 -0
  110. package/src/account-onboarding.integration.test.ts +101 -0
  111. package/src/account-onboarding.ts +95 -0
  112. package/src/account-trial-expiry-sweeper.ts +85 -0
  113. package/src/account-trial-warning-sweeper.integration.test.ts +228 -0
  114. package/src/account-trial-warning-sweeper.ts +216 -0
  115. package/src/accounts-api.integration.test.ts +196 -0
  116. package/src/accounts-api.smoke.test.ts +292 -0
  117. package/src/accounts-api.ts +289 -0
  118. package/src/accounts-api.unit.test.ts +93 -0
  119. package/src/accounts.integration.test.ts +178 -0
  120. package/src/accounts.ts +208 -0
  121. package/src/admin-boot-config.ts +574 -0
  122. package/src/admin-boot-config.unit.test.ts +28 -0
  123. package/src/admin-boot.boot-order.unit.test.ts +91 -0
  124. package/src/admin-boot.ts +690 -0
  125. package/src/admin-dev-login.smoke.test.ts +317 -0
  126. package/src/admin-extension.ts +432 -0
  127. package/src/admin-extension.unit.test.ts +532 -0
  128. package/src/admin-local-agent.smoke.test.ts +1643 -0
  129. package/src/admin-spec.smoke.test.ts +96 -0
  130. package/src/admin-spec.ts +186 -0
  131. package/src/admin-ui-account-nav.ts +20 -0
  132. package/src/admin-ui-account-pages.ts +131 -0
  133. package/src/admin-ui-account-pages.unit.test.ts +112 -0
  134. package/src/admin-ui-account.smoke.test.ts +286 -0
  135. package/src/admin-ui-account.ts +185 -0
  136. package/src/admin-ui-accounts-pages.ts +202 -0
  137. package/src/admin-ui-accounts-pages.unit.test.ts +138 -0
  138. package/src/admin-ui-accounts.smoke.test.ts +434 -0
  139. package/src/admin-ui-accounts.ts +266 -0
  140. package/src/admin-ui-layout.ts +60 -0
  141. package/src/admin-ui-layout.unit.test.ts +140 -0
  142. package/src/admin-ui-pages.ts +3489 -0
  143. package/src/admin-ui-pages.unit.test.ts +4355 -0
  144. package/src/admin-ui-push-test.smoke.test.ts +125 -0
  145. package/src/admin-ui-push-test.ts +102 -0
  146. package/src/admin-ui-styles.ts +656 -0
  147. package/src/admin-ui-styles.unit.test.ts +103 -0
  148. package/src/admin-ui.fixtures.ts +457 -0
  149. package/src/admin-ui.smoke.test.ts +7227 -0
  150. package/src/admin-ui.ts +3824 -0
  151. package/src/agent-account-assignment.ts +49 -0
  152. package/src/agent-account-assignment.unit.test.ts +99 -0
  153. package/src/agent-anthropic-identity.integration.test.ts +99 -0
  154. package/src/agent-anthropic-identity.ts +78 -0
  155. package/src/agent-anthropic-identity.unit.test.ts +77 -0
  156. package/src/agent-ask-config.ts +59 -0
  157. package/src/agent-ask-config.unit.test.ts +70 -0
  158. package/src/agent-ask.integration.test.ts +175 -0
  159. package/src/agent-ask.smoke.test.ts +457 -0
  160. package/src/agent-ask.ts +378 -0
  161. package/src/agent-chat-tokens.integration.test.ts +420 -0
  162. package/src/agent-chat-tokens.smoke.test.ts +551 -0
  163. package/src/agent-chat-tokens.ts +259 -0
  164. package/src/agent-cron-jobs.integration.test.ts +582 -0
  165. package/src/agent-cron-jobs.ts +615 -0
  166. package/src/agent-cron-jobs.unit.test.ts +420 -0
  167. package/src/agent-cron-run-stats.integration.test.ts +1551 -0
  168. package/src/agent-cron-run-stats.smoke.test.ts +953 -0
  169. package/src/agent-cron-run-stats.ts +752 -0
  170. package/src/agent-cron-runs.integration.test.ts +1491 -0
  171. package/src/agent-cron-runs.ts +489 -0
  172. package/src/agent-deletion-checklist.ts +128 -0
  173. package/src/agent-deletion-checklist.unit.test.ts +201 -0
  174. package/src/agent-deletion-per-agent.integration.test.ts +323 -0
  175. package/src/agent-deletion.integration.test.ts +323 -0
  176. package/src/agent-deletion.ts +329 -0
  177. package/src/agent-deletion.unit.test.ts +412 -0
  178. package/src/agent-envs.integration.test.ts +323 -0
  179. package/src/agent-envs.ts +244 -0
  180. package/src/agent-github-installations.integration.test.ts +115 -0
  181. package/src/agent-github-installations.ts +52 -0
  182. package/src/agent-manifest.ts +685 -0
  183. package/src/agent-manifest.unit.test.ts +870 -0
  184. package/src/agent-members.ts +69 -0
  185. package/src/agent-members.unit.test.ts +250 -0
  186. package/src/agent-patch-author-allowlist.integration.test.ts +218 -0
  187. package/src/agent-plugins.integration.test.ts +157 -0
  188. package/src/agent-plugins.ts +95 -0
  189. package/src/agent-plugins.unit.test.ts +43 -0
  190. package/src/agent-provisioner-per-agent.integration.test.ts +949 -0
  191. package/src/agent-provisioner.integration.test.ts +1245 -0
  192. package/src/agent-provisioner.ts +1650 -0
  193. package/src/agent-provisioner.unit.test.ts +1852 -0
  194. package/src/agent-tokens.integration.test.ts +150 -0
  195. package/src/agent-tokens.ts +112 -0
  196. package/src/agent-tools.integration.test.ts +129 -0
  197. package/src/agent-tools.ts +111 -0
  198. package/src/agents-api.integration.test.ts +705 -0
  199. package/src/agents-api.sentry.smoke.test.ts +478 -0
  200. package/src/agents-api.smoke.test.ts +5986 -0
  201. package/src/agents-api.ts +2826 -0
  202. package/src/agents.integration.test.ts +512 -0
  203. package/src/agents.ts +1097 -0
  204. package/src/agents.unit.test.ts +1736 -0
  205. package/src/anthropic-credential-guard.ts +44 -0
  206. package/src/anthropic-credential-guard.unit.test.ts +88 -0
  207. package/src/anthropic-federated-token-provider.integration.test.ts +248 -0
  208. package/src/anthropic-federated-token-provider.ts +249 -0
  209. package/src/anthropic-identity-client.fixtures.ts +237 -0
  210. package/src/anthropic-identity-client.integration.test.ts +342 -0
  211. package/src/anthropic-identity-client.ts +473 -0
  212. package/src/anthropic-identity-fixture-spec.integration.test.ts +169 -0
  213. package/src/anthropic-identity-name.integration.test.ts +83 -0
  214. package/src/anthropic-identity-name.ts +163 -0
  215. package/src/anthropic-identity-name.unit.test.ts +345 -0
  216. package/src/anthropic-openapi-contract.fixtures.ts +192 -0
  217. package/src/api-auth.ts +383 -0
  218. package/src/api-auth.unit.test.ts +837 -0
  219. package/src/api.smoke.test.ts +1225 -0
  220. package/src/api.ts +376 -0
  221. package/src/attachment-validation.ts +124 -0
  222. package/src/attachment-validation.unit.test.ts +218 -0
  223. package/src/caller-scope.ts +131 -0
  224. package/src/caller-scope.unit.test.ts +116 -0
  225. package/src/chat-credential-config.ts +22 -0
  226. package/src/chat-credentials-logging.unit.test.ts +92 -0
  227. package/src/chat-markers.ts +114 -0
  228. package/src/chat-markers.unit.test.ts +116 -0
  229. package/src/chat-service-provisioning-client.integration.test.ts +412 -0
  230. package/src/chat-service-provisioning-client.ts +221 -0
  231. package/src/chat.smoke.test.ts +1875 -0
  232. package/src/clock.ts +47 -0
  233. package/src/coding-task-store-provisioning-client.integration.test.ts +372 -0
  234. package/src/coding-task-store-provisioning-client.ts +156 -0
  235. package/src/crypto.ts +83 -0
  236. package/src/dev-auth-guard.ts +29 -0
  237. package/src/dev-auth-guard.unit.test.ts +51 -0
  238. package/src/errors.ts +77 -0
  239. package/src/errors.unit.test.ts +52 -0
  240. package/src/fixtures/anthropic-identity-recorded-fixtures.json +850 -0
  241. package/src/fixtures/anthropic-openapi/SDK_VERSION +1 -0
  242. package/src/fixtures/anthropic-openapi/mock-spec.json.gz +0 -0
  243. package/src/fixtures/chat-service-provisioning-cassette.json +156 -0
  244. package/src/fixtures/github-app-manifest-cassette.json +44 -0
  245. package/src/fixtures/google-auth-cassette.json +36 -0
  246. package/src/fixtures/http-chat-client-cassette.json +226 -0
  247. package/src/fixtures/k8s-cassette.json +470 -0
  248. package/src/fixtures/okta-auth-cassette.json +36 -0
  249. package/src/fixtures/slack-provision-cassette.json +12 -0
  250. package/src/fixtures/slack-provisioning-http-cassette.json +98 -0
  251. package/src/fixtures/task-store-provisioning-cassette.json +85 -0
  252. package/src/github-app-provisioning-client.integration.test.ts +149 -0
  253. package/src/github-app-provisioning-client.ts +161 -0
  254. package/src/github-app-provisioning-client.unit.test.ts +114 -0
  255. package/src/github-provisioning-service.ts +569 -0
  256. package/src/github-provisioning-service.unit.test.ts +123 -0
  257. package/src/google-auth-client.integration.test.ts +195 -0
  258. package/src/google-auth-client.ts +121 -0
  259. package/src/http-chat-client.integration.test.ts +554 -0
  260. package/src/http-chat-client.ts +533 -0
  261. package/src/http-chat-client.unit.test.ts +119 -0
  262. package/src/import-boundary.unit.test.ts +240 -0
  263. package/src/kubernetes-client.integration.test.ts +697 -0
  264. package/src/kubernetes-client.ts +1027 -0
  265. package/src/kubernetes-client.unit.test.ts +726 -0
  266. package/src/main.smoke.test.ts +91 -0
  267. package/src/main.ts +21 -0
  268. package/src/main.unit.test.ts +801 -0
  269. package/src/okta-auth-client.integration.test.ts +263 -0
  270. package/src/okta-auth-client.ts +152 -0
  271. package/src/openapi-schemas.ts +1310 -0
  272. package/src/openapi-schemas.unit.test.ts +348 -0
  273. package/src/per-agent-identity.fixtures.ts +305 -0
  274. package/src/platform-doc-route.ts +40 -0
  275. package/src/prisma-client.integration.test.ts +57 -0
  276. package/src/prisma-client.ts +187 -0
  277. package/src/prisma-client.unit.test.ts +178 -0
  278. package/src/prisma-tx.ts +23 -0
  279. package/src/push-content.ts +177 -0
  280. package/src/push-content.unit.test.ts +213 -0
  281. package/src/push-crypto.ts +135 -0
  282. package/src/push-crypto.unit.test.ts +66 -0
  283. package/src/push-sender.ts +169 -0
  284. package/src/push-sender.unit.test.ts +107 -0
  285. package/src/push-service.ts +332 -0
  286. package/src/push-service.unit.test.ts +679 -0
  287. package/src/push-toggle.ts +135 -0
  288. package/src/push-toggle.unit.test.ts +78 -0
  289. package/src/pwa-https-gating.smoke.test.ts +229 -0
  290. package/src/pwa-icons.unit.test.ts +41 -0
  291. package/src/pwa-routes.smoke.test.ts +276 -0
  292. package/src/pwa.ts +380 -0
  293. package/src/pwa.unit.test.ts +321 -0
  294. package/src/schema.unit.test.ts +122 -0
  295. package/src/self-serve-config.ts +52 -0
  296. package/src/self-serve-config.unit.test.ts +79 -0
  297. package/src/server.smoke.test.ts +273 -0
  298. package/src/server.ts +50 -0
  299. package/src/service-integration.ts +67 -0
  300. package/src/service-integration.unit.test.ts +129 -0
  301. package/src/slack-provisioning-client.integration.test.ts +308 -0
  302. package/src/slack-provisioning-client.ts +411 -0
  303. package/src/slack-provisioning-client.unit.test.ts +80 -0
  304. package/src/slack-provisioning-service.integration.test.ts +637 -0
  305. package/src/slack-provisioning-service.ts +451 -0
  306. package/src/slack-provisioning.integration.test.ts +395 -0
  307. package/src/specialty-manifest-loader.fixture.integration.test.ts +63 -0
  308. package/src/specialty-manifest-loader.ts +255 -0
  309. package/src/specialty-manifest-loader.unit.test.ts +301 -0
  310. package/src/specialty-registry.schema.unit.test.ts +35 -0
  311. package/src/specialty-registry.ts +236 -0
  312. package/src/specialty-registry.unit.test.ts +272 -0
  313. package/src/startup-policy-reconcile.ts +77 -0
  314. package/src/startup-policy-reconcile.unit.test.ts +251 -0
  315. package/src/theme-tokens.json +12 -0
  316. package/src/token-crypto.ts +60 -0
  317. package/src/token-crypto.unit.test.ts +182 -0
  318. package/src/trial-expiry-sweeper.integration.test.ts +318 -0
  319. package/src/trial-expiry-sweeper.ts +132 -0
  320. package/src/trial-expiry-sweeper.unit.test.ts +391 -0
  321. package/src/trial-expiry-warning-sweeper.integration.test.ts +468 -0
  322. package/src/trial-expiry-warning-sweeper.ts +447 -0
  323. package/src/trial-expiry-warning-sweeper.unit.test.ts +148 -0
  324. package/src/voice-contract.unit.test.ts +102 -0
  325. package/tsconfig.json +9 -0
@@ -0,0 +1,273 @@
1
+ /**
2
+ * admin/src/server.smoke.test.ts
3
+ * Thin composition smoke test for admin/src/server.ts — the Hono entrypoint
4
+ * barrel.
5
+ *
6
+ * Confirms createAdminUIApp + createAdminApp + createAgentRuntimeApp (all
7
+ * imported via the server.ts barrel) are reachable once mounted together the
8
+ * same way main.ts's startServer() composes them. Deliberately does NOT
9
+ * assert on any sub-app's route behavior — that's covered by
10
+ * admin-ui.smoke.test.ts, agents-api.smoke.test.ts, and api.smoke.test.ts.
11
+ */
12
+
13
+ import { describe, expect, it } from "bun:test";
14
+ import { Hono } from "hono";
15
+ import {
16
+ createAdminApp,
17
+ createAdminUIApp,
18
+ createAgentRuntimeApp,
19
+ } from "./server.ts";
20
+
21
+ const SESSION_SECRET = "test-server-smoke-session-secret!!";
22
+
23
+ const notImplemented = () => {
24
+ throw new Error("not implemented — smoke test only exercises routing");
25
+ };
26
+
27
+ // Shared stub prisma satisfying every prisma-shaped field the three deps
28
+ // interfaces require (agent, agentEnv, agentPlugin, agentMember). Unauthenticated
29
+ // requests never reach a handler that calls these, so throwing is safe.
30
+ const stubPrisma = {
31
+ agent: {
32
+ findMany: notImplemented,
33
+ findUnique: notImplemented,
34
+ create: notImplemented,
35
+ update: notImplemented,
36
+ delete: notImplemented,
37
+ },
38
+ agentEnv: { findMany: notImplemented },
39
+ agentPlugin: { findMany: notImplemented },
40
+ agentMember: {
41
+ findMany: notImplemented,
42
+ findUnique: notImplemented,
43
+ create: notImplemented,
44
+ deleteMany: notImplemented,
45
+ },
46
+ } as never;
47
+
48
+ const stubAgentTokenService = { validate: notImplemented } as never;
49
+
50
+ function buildComposedApp() {
51
+ const root = new Hono();
52
+
53
+ const runtimeApp = createAgentRuntimeApp({
54
+ agentEnvService: { getConfigBundle: notImplemented },
55
+ agentCronJobService: {
56
+ list: notImplemented,
57
+ listWithRunSummary: notImplemented,
58
+ },
59
+ agentService: { getById: notImplemented },
60
+ agentPluginService: { listEnabled: notImplemented },
61
+ agentPhaseMethodologyService: { list: notImplemented },
62
+ sessionSecret: SESSION_SECRET,
63
+ agentTokenService: stubAgentTokenService,
64
+ });
65
+ root.route("/agents", runtimeApp);
66
+
67
+ const adminApiApp = createAdminApp({
68
+ agentService: {
69
+ create: notImplemented,
70
+ delete: notImplemented,
71
+ list: notImplemented,
72
+ getSummary: notImplemented,
73
+ getDetail: notImplemented,
74
+ exists: notImplemented,
75
+ updateSelfHosted: notImplemented,
76
+ updateFields: notImplemented,
77
+ runTransaction: notImplemented,
78
+ },
79
+ specialtyRegistry: {
80
+ getManifest: notImplemented,
81
+ tryGetManifest: notImplemented,
82
+ listTypes: notImplemented,
83
+ },
84
+ agentEnvService: {
85
+ upsert: notImplemented,
86
+ patch: notImplemented,
87
+ getByAgentId: notImplemented,
88
+ deleteKey: notImplemented,
89
+ },
90
+ agentCronJobService: {
91
+ list: notImplemented,
92
+ listWithRunSummary: notImplemented,
93
+ create: notImplemented,
94
+ update: notImplemented,
95
+ delete: notImplemented,
96
+ reconcileSystemCrons: notImplemented,
97
+ get: notImplemented,
98
+ setEnabled: notImplemented,
99
+ updatePreCheck: notImplemented,
100
+ },
101
+ agentCronRunService: {
102
+ create: notImplemented,
103
+ list: notImplemented,
104
+ patch: notImplemented,
105
+ },
106
+ agentCronRunStatsService: {
107
+ query: notImplemented,
108
+ outcomes: notImplemented,
109
+ },
110
+ agentToolService: {
111
+ list: notImplemented,
112
+ add: notImplemented,
113
+ remove: notImplemented,
114
+ toggle: notImplemented,
115
+ },
116
+ agentTokenService: {
117
+ create: notImplemented,
118
+ listForAgent: notImplemented,
119
+ revoke: notImplemented,
120
+ validate: notImplemented,
121
+ },
122
+ agentPluginService: {
123
+ list: notImplemented,
124
+ add: notImplemented,
125
+ remove: notImplemented,
126
+ removeByName: notImplemented,
127
+ },
128
+ agentMemberService: {
129
+ add: notImplemented,
130
+ listByAgentId: notImplemented,
131
+ exists: async () => false,
132
+ listByEmail: async () => [],
133
+ },
134
+ agentChatTokenService: {
135
+ upsertDailyByModel: notImplemented,
136
+ queryStats: notImplemented,
137
+ },
138
+ agentGitHubInstallationsService: {
139
+ push: notImplemented,
140
+ get: notImplemented,
141
+ },
142
+ prisma: stubPrisma,
143
+ provisioner: {
144
+ canProvision: false,
145
+ provision: notImplemented,
146
+ deprovision: notImplemented,
147
+ reconcile: notImplemented,
148
+ },
149
+ serviceIntegrations: [
150
+ {
151
+ name: "task-store",
152
+ listTokensForAgent: notImplemented,
153
+ revokeToken: notImplemented,
154
+ },
155
+ ],
156
+ chatService: {
157
+ listTokensForAgent: notImplemented,
158
+ revokeToken: notImplemented,
159
+ deleteThreadsForAgent: notImplemented,
160
+ },
161
+ slack: { deleteApp: notImplemented },
162
+ decrypt: notImplemented,
163
+ sessionSecret: SESSION_SECRET,
164
+ });
165
+ root.route("/", adminApiApp);
166
+
167
+ const adminUIApp = createAdminUIApp({
168
+ prisma: stubPrisma,
169
+ agentEnvService: {
170
+ getByAgentId: notImplemented,
171
+ upsert: notImplemented,
172
+ patch: notImplemented,
173
+ deleteKey: notImplemented,
174
+ getConfigBundle: notImplemented,
175
+ },
176
+ agentCronJobService: {
177
+ list: notImplemented,
178
+ listWithRunSummary: notImplemented,
179
+ create: notImplemented,
180
+ update: notImplemented,
181
+ setEnabled: notImplemented,
182
+ delete: notImplemented,
183
+ get: notImplemented,
184
+ reconcileSystemCrons: notImplemented,
185
+ },
186
+ agentToolService: {
187
+ list: notImplemented,
188
+ add: notImplemented,
189
+ toggle: notImplemented,
190
+ remove: notImplemented,
191
+ },
192
+ agentTokenService: {
193
+ listForAgent: notImplemented,
194
+ create: notImplemented,
195
+ revoke: notImplemented,
196
+ },
197
+ agentPluginService: { list: notImplemented, add: notImplemented },
198
+ agentMemberService: {
199
+ listByEmail: notImplemented,
200
+ exists: notImplemented,
201
+ add: notImplemented,
202
+ remove: notImplemented,
203
+ listByAgentId: notImplemented,
204
+ },
205
+ agentService: {
206
+ listAll: notImplemented,
207
+ listByIds: notImplemented,
208
+ searchByName: notImplemented,
209
+ listOptions: notImplemented,
210
+ create: notImplemented,
211
+ delete: notImplemented,
212
+ getDetail: notImplemented,
213
+ updateFields: notImplemented,
214
+ },
215
+ provisioner: {
216
+ canProvision: false,
217
+ provision: notImplemented,
218
+ deprovision: notImplemented,
219
+ reconcile: notImplemented,
220
+ },
221
+ serviceIntegrations: [
222
+ {
223
+ name: "task-store",
224
+ listTokensForAgent: notImplemented,
225
+ revokeToken: notImplemented,
226
+ },
227
+ ],
228
+ chatService: {
229
+ listTokensForAgent: notImplemented,
230
+ revokeToken: notImplemented,
231
+ deleteThreadsForAgent: notImplemented,
232
+ },
233
+ slack: { deleteApp: notImplemented },
234
+ decrypt: notImplemented,
235
+ sessionSecret: SESSION_SECRET,
236
+ googleClient: { exchangeCode: notImplemented, getUserInfo: notImplemented },
237
+ googleClientId: "test-google-client-id",
238
+ googleClientSecret: "test-google-client-secret",
239
+ adminAllowedEmails: ["admin@example.com"],
240
+ slackClient: {
241
+ createAppManifest: notImplemented,
242
+ updateAppManifest: notImplemented,
243
+ exchangeOAuthCode: notImplemented,
244
+ authTest: notImplemented,
245
+ },
246
+ githubAppClient: {
247
+ exchangeManifestCode: notImplemented,
248
+ },
249
+ appBaseUrl: "http://localhost:3000",
250
+ });
251
+ root.route("/", adminUIApp);
252
+
253
+ return root;
254
+ }
255
+
256
+ describe("server.ts composed app mounts correctly", () => {
257
+ const app = buildComposedApp();
258
+
259
+ it("mounts createAdminUIApp — GET /admin/login is reachable (public route)", async () => {
260
+ const res = await app.request("/admin/login");
261
+ expect(res.status).toBe(200);
262
+ });
263
+
264
+ it("mounts createAgentRuntimeApp under /agents — GET /agents/:id/config requires auth", async () => {
265
+ const res = await app.request("/agents/agent-test-123/config");
266
+ expect(res.status).toBe(401);
267
+ });
268
+
269
+ it("mounts createAdminApp — GET /agents (admin CRUD API) requires auth", async () => {
270
+ const res = await app.request("/agents");
271
+ expect(res.status).toBe(401);
272
+ });
273
+ });
package/src/server.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * admin/src/server.ts
3
+ * Shipwright admin service — Hono entrypoint.
4
+ *
5
+ * Mounts:
6
+ * - Admin UI routes at /admin/* (server-rendered)
7
+ * - Admin CRUD API at /agents/:id/* (JSON)
8
+ * - Agent runtime API at /agents/* (JSON, internal)
9
+ */
10
+
11
+ export { createAdminUIApp } from "./admin-ui.ts";
12
+ export type { AdminUIDeps } from "./admin-ui.ts";
13
+ export { createAdminApp } from "./agents-api.ts";
14
+ export type { AdminDeps } from "./agents-api.ts";
15
+ export { createAgentRuntimeApp } from "./api.ts";
16
+ export type {
17
+ AgentRuntimeDeps,
18
+ AgentConfigResponse,
19
+ AgentPlugin,
20
+ } from "./api.ts";
21
+ export { AgentEnvService } from "./agent-envs.ts";
22
+ export type { AgentEnvBundle, AgentEnvEntry } from "./agent-envs.ts";
23
+ export { AgentCronJobService } from "./agent-cron-jobs.ts";
24
+ export type {
25
+ AgentCronJob,
26
+ CreateAgentCronJobInput,
27
+ } from "./agent-cron-jobs.ts";
28
+ export { AgentToolService } from "./agent-tools.ts";
29
+ export type { AgentTool } from "./agent-tools.ts";
30
+ export { AgentTokenService } from "./agent-tokens.ts";
31
+ export type { AgentToken, AgentTokenValidated } from "./agent-tokens.ts";
32
+ export { AgentPluginService } from "./agent-plugins.ts";
33
+ export type { AgentPlugin as AgentPluginRecord } from "./agent-plugins.ts";
34
+ export { makeTokenCrypto, identityCrypto } from "./token-crypto.ts";
35
+ export type { TokenCrypto } from "./token-crypto.ts";
36
+ export {
37
+ ApiError,
38
+ NotFoundError,
39
+ ConflictError,
40
+ BadRequestError,
41
+ ForbiddenError,
42
+ UnprocessableEntityError,
43
+ BadGatewayError,
44
+ } from "./errors.ts";
45
+ export { HttpSlackProvisioningClient } from "./slack-provisioning-client.ts";
46
+ export type { SlackProvisioningClient } from "./slack-provisioning-client.ts";
47
+ export { HttpGoogleAuthClient } from "./google-auth-client.ts";
48
+ export type { GoogleAuthClient } from "./google-auth-client.ts";
49
+ export { PrismaClient } from "../prisma/client/client.ts";
50
+ export { createAdminPrismaClient } from "./prisma-client.ts";
@@ -0,0 +1,67 @@
1
+ /**
2
+ * admin/src/service-integration.ts
3
+ *
4
+ * The seam for optional per-agent service integrations in agent provisioning.
5
+ *
6
+ * An integration is an external service (task-store today) for which the
7
+ * provisioner mints a per-agent token on create, stores it in the agent Secret
8
+ * under `secretKey`, and injects `tokenEnv` (secretKeyRef) + `urlEnv` (plain
9
+ * value) into the agent Deployment; agent deletion revokes every token scoped
10
+ * to the agent. Integrations are supplied by the composition root through
11
+ * AdminExtension.serviceIntegrations(env) — the platform never names one.
12
+ *
13
+ * Chat-service stays a built-in platform integration (it has extra thread
14
+ * cleanup on delete) and is handled alongside, not through this list.
15
+ *
16
+ * Ordering contract: extension integrations are processed in list order and
17
+ * ALWAYS before the built-in chat-service integration — minting, Secret keys,
18
+ * Deployment env entries, rollback revocation and deletion steps all follow
19
+ * that order. (This reproduces the original task-store-before-chat order.)
20
+ */
21
+
22
+ /** Token operations a service exposes to the provisioner and deletion flow. */
23
+ export interface ServiceTokenClient {
24
+ /** Mint a per-agent token; `id` is needed to revoke it on rollback. */
25
+ mintToken(
26
+ label: string,
27
+ agentId?: string,
28
+ ): Promise<{ id: string; rawToken: string }>;
29
+ /** Revoke a previously-minted token. Idempotent (already-gone is a no-op). */
30
+ revokeToken(id: string): Promise<void>;
31
+ /** List the ids of every token scoped to `agentId` (for deletion cleanup). */
32
+ listTokensForAgent(agentId: string): Promise<{ id: string }[]>;
33
+ }
34
+
35
+ /** How an integration shows up in the agent Secret and Deployment. */
36
+ export interface ServiceEnvSpec {
37
+ /** Env var carrying the per-agent token (secretKeyRef into the agent Secret). */
38
+ tokenEnv: string;
39
+ /** Env var carrying the service base URL (plain value). */
40
+ urlEnv: string;
41
+ /** Key within the agent Secret under which the token is stored. */
42
+ secretKey: string;
43
+ /** In-cluster base URL. Absent = integration not configured for provisioning. */
44
+ url?: string;
45
+ }
46
+
47
+ export interface ServiceIntegration extends ServiceTokenClient, ServiceEnvSpec {
48
+ /** Stable name; deletion reports the step as `${name}-tokens`. */
49
+ name: string;
50
+ }
51
+
52
+ /** The subset of an integration the deletion flow needs. */
53
+ export type DeletionServiceIntegration = Pick<
54
+ ServiceIntegration,
55
+ "name" | "listTokensForAgent" | "revokeToken"
56
+ >;
57
+
58
+ /**
59
+ * The integrations the provisioner should actively wire (mint + env): those
60
+ * with a URL. Unconfigured integrations are kept in the full list so deletion
61
+ * still runs (and reports) their step, matching the former no-op client.
62
+ */
63
+ export function provisionableIntegrations(
64
+ integrations: readonly ServiceIntegration[] | undefined,
65
+ ): ServiceIntegration[] {
66
+ return (integrations ?? []).filter((i) => Boolean(i.url));
67
+ }
@@ -0,0 +1,129 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { deleteAgentFully } from "./agent-deletion.ts";
3
+ import { buildAgentDeploymentManifest } from "./agent-manifest.ts";
4
+ import {
5
+ provisionableIntegrations,
6
+ type ServiceIntegration,
7
+ } from "./service-integration.ts";
8
+
9
+ function integration(
10
+ name: string,
11
+ url: string | undefined,
12
+ log: string[] = [],
13
+ ): ServiceIntegration {
14
+ return {
15
+ name,
16
+ url,
17
+ tokenEnv: `SVC_${name.toUpperCase()}_TOKEN`,
18
+ urlEnv: `SVC_${name.toUpperCase()}_URL`,
19
+ secretKey: `${name}-token`,
20
+ mintToken: async () => ({ id: `${name}-id`, rawToken: `${name}-raw` }),
21
+ revokeToken: async (id) => {
22
+ log.push(`revoke:${id}`);
23
+ },
24
+ listTokensForAgent: async () => [{ id: `${name}-id` }],
25
+ };
26
+ }
27
+
28
+ describe("provisionableIntegrations", () => {
29
+ it("returns [] for undefined or empty input", () => {
30
+ expect(provisionableIntegrations(undefined)).toEqual([]);
31
+ expect(provisionableIntegrations([])).toEqual([]);
32
+ });
33
+
34
+ it("keeps list order", () => {
35
+ const list = [integration("b", "http://b"), integration("a", "http://a")];
36
+ expect(provisionableIntegrations(list).map((i) => i.name)).toEqual([
37
+ "b",
38
+ "a",
39
+ ]);
40
+ });
41
+
42
+ it("drops integrations without a url (optionality)", () => {
43
+ const list = [
44
+ integration("a", "http://a"),
45
+ integration("b", undefined),
46
+ integration("c", ""),
47
+ ];
48
+ expect(provisionableIntegrations(list).map((i) => i.name)).toEqual(["a"]);
49
+ });
50
+ });
51
+
52
+ describe("service integrations in the Deployment manifest", () => {
53
+ const base = {
54
+ agentId: "agent-1",
55
+ namespace: "ns",
56
+ image: "img",
57
+ imageTag: "t",
58
+ apiUrl: "http://api",
59
+ secretName: "agent-1-token",
60
+ pvcName: "agent-1-home",
61
+ };
62
+
63
+ it("emits token then url env per integration, in list order, before chat", () => {
64
+ const d = buildAgentDeploymentManifest({
65
+ ...base,
66
+ serviceIntegrations: [
67
+ integration("a", "http://a"),
68
+ integration("b", "http://b"),
69
+ ],
70
+ chatServiceUrl: "http://chat",
71
+ });
72
+ const names = (d.spec.template.spec.containers[0].env ?? []).map(
73
+ (e) => e.name,
74
+ );
75
+ expect(names.slice(6)).toEqual([
76
+ "SVC_A_TOKEN",
77
+ "SVC_A_URL",
78
+ "SVC_B_TOKEN",
79
+ "SVC_B_URL",
80
+ "SHIPWRIGHT_CHAT_SERVICE_TOKEN",
81
+ "SHIPWRIGHT_CHAT_SERVICE_URL",
82
+ ]);
83
+ });
84
+
85
+ it("omits an integration without a url", () => {
86
+ const d = buildAgentDeploymentManifest({
87
+ ...base,
88
+ serviceIntegrations: [integration("a", undefined)],
89
+ });
90
+ const names = (d.spec.template.spec.containers[0].env ?? []).map(
91
+ (e) => e.name,
92
+ );
93
+ expect(names).toHaveLength(6);
94
+ });
95
+ });
96
+
97
+ describe("service integrations in agent deletion", () => {
98
+ it("runs one name-tokens step per integration, in order, before chat", async () => {
99
+ const log: string[] = [];
100
+ const result = await deleteAgentFully("agent-1", {
101
+ prisma: {
102
+ agent: {
103
+ findUnique: async () => ({ id: "agent-1", name: "agent-1" }),
104
+ delete: async () => ({}),
105
+ },
106
+ agentEnv: { findMany: async () => [] },
107
+ } as never,
108
+ provisioner: { deprovision: async () => {} } as never,
109
+ serviceIntegrations: [
110
+ integration("a", "http://a", log),
111
+ integration("b", undefined, log),
112
+ ],
113
+ chatService: {
114
+ listTokensForAgent: async () => [],
115
+ revokeToken: async () => {},
116
+ deleteThreadsForAgent: async () => ({ deleted: 0 }),
117
+ },
118
+ slack: { deleteApp: async () => {} },
119
+ decrypt: (v) => v,
120
+ });
121
+ expect(result.completed).toEqual([
122
+ "k8s",
123
+ "a-tokens",
124
+ "b-tokens",
125
+ "chat-service-tokens-and-threads",
126
+ ]);
127
+ expect(log).toEqual(["revoke:a-id", "revoke:b-id"]);
128
+ });
129
+ });