create-workframe 0.1.0 → 0.1.1

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 (206) hide show
  1. package/LICENSE +201 -201
  2. package/NOTICE +12 -12
  3. package/README.md +8 -92
  4. package/SECURITY.md +40 -40
  5. package/bin/workframe.js +329 -329
  6. package/docs/workspace-instructions/WORKFRAME_ONBOARDING.md +1 -1
  7. package/docs/workspace-instructions/WORKFRAME_ROUTING.md +8 -8
  8. package/package.json +3 -6
  9. package/profiles/architect/AGENTS.md +29 -29
  10. package/profiles/architect/SOUL.md +2 -2
  11. package/profiles/architect/skills/devops/kanban-worker/SKILL.md +27 -27
  12. package/profiles/designer/AGENTS.md +26 -26
  13. package/profiles/designer/skills/devops/kanban-worker/SKILL.md +27 -27
  14. package/profiles/dev/AGENTS.md +28 -28
  15. package/profiles/dev/skills/devops/kanban-worker/SKILL.md +27 -27
  16. package/profiles/docs/AGENTS.md +27 -27
  17. package/profiles/docs/skills/devops/kanban-worker/SKILL.md +27 -27
  18. package/profiles/research/AGENTS.md +26 -26
  19. package/profiles/research/skills/devops/kanban-worker/SKILL.md +27 -27
  20. package/profiles/visionary/AGENTS.md +25 -25
  21. package/profiles/visionary/skills/devops/kanban-worker/SKILL.md +27 -27
  22. package/profiles/workframe-agent/AGENTS.md +37 -37
  23. package/profiles/workframe-agent/skills/devops/botfather/SKILL.md +85 -85
  24. package/profiles/workframe-agent/skills/devops/kanban-handoff-pattern/SKILL.md +58 -58
  25. package/profiles/workframe-agent/skills/devops/workframe-cohort/SKILL.md +54 -54
  26. package/rules/workspace-README.md +5 -5
  27. package/scripts/apply-update-hermes.sh +17 -17
  28. package/scripts/apply-update-workframe.sh +77 -77
  29. package/scripts/bootstrap-workspace-link.sh +8 -8
  30. package/scripts/bundle-workframe-ui.mjs +3 -3
  31. package/scripts/compose-docker-host.sh +37 -37
  32. package/scripts/ensure-compose-host-paths.mjs +51 -51
  33. package/scripts/fix-zk-encryption-key.sh +35 -35
  34. package/scripts/lib/install-identity.mjs +212 -212
  35. package/scripts/restart-gateway-hermes.sh +12 -12
  36. package/scripts/set-compose-public-url.mjs +92 -92
  37. package/scripts/setup-stack-secrets.sh +50 -50
  38. package/scripts/sync-canonical-to-package.mjs +8 -7
  39. package/scripts/verify-public-deploy.sh +105 -105
  40. package/shared/WORKFRAME_AGENT_LIBRARY.md +17 -17
  41. package/shared/WORKFRAME_AGENT_OPERATIONS.md +15 -15
  42. package/shared/WORKFRAME_AGENT_PACKS.json +18 -18
  43. package/shared/WORKFRAME_AGENT_PACKS.yaml +8 -8
  44. package/shared/WORKFRAME_SKILL_CURATION.md +4 -4
  45. package/workframe-api/README.md +28 -28
  46. package/workframe-api/action_proxy.py +131 -131
  47. package/workframe-api/auth_rate_limit.py +49 -49
  48. package/workframe-api/credential_vault.py +445 -445
  49. package/workframe-api/data/avatar-catalog.json +41 -41
  50. package/workframe-api/email_sender.py +220 -220
  51. package/workframe-api/google_auth.py +90 -90
  52. package/workframe-api/install_api.py +359 -359
  53. package/workframe-api/internal_proxy_auth.py +150 -150
  54. package/workframe-api/llm_proxy.py +277 -277
  55. package/workframe-api/oidc_jwt.py +108 -108
  56. package/workframe-api/package.json +12 -13
  57. package/workframe-api/public/assets/index-DPXu_lGn.css +1 -1
  58. package/workframe-api/public/assets/index-DYnLrCZZ.js +8 -8
  59. package/workframe-api/requirements.txt +2 -2
  60. package/workframe-api/site_meta.py +271 -271
  61. package/workframe-api/stack_config.py +427 -427
  62. package/workframe-api/time-bind-chat.py +99 -99
  63. package/workframe-api/turn_credentials.py +226 -226
  64. package/workframe-api/updates.py +417 -417
  65. package/workframe-api/vault_kek.py +159 -159
  66. package/workframe-api/zk_auth.py +633 -633
  67. package/workframe-supervisor/Dockerfile +11 -11
  68. package/workframe-supervisor/server.py +787 -787
  69. package/workframe-ui/docker/nginx.conf +85 -85
  70. package/workframe-ui/public/assets/{arc-CBDYvkAF.js → arc-COAT3laO.js} +1 -1
  71. package/workframe-ui/public/assets/architecture-7EHR7CIX-DUyH3hWG.js +1 -0
  72. package/workframe-ui/public/assets/{architectureDiagram-3BPJPVTR-XnBRKeW0.js → architectureDiagram-3BPJPVTR-BFjWV24l.js} +1 -1
  73. package/workframe-ui/public/assets/{blockDiagram-GPEHLZMM-VYHUfVhd.js → blockDiagram-GPEHLZMM-DSQLPfrj.js} +1 -1
  74. package/workframe-ui/public/assets/{c4Diagram-AAUBKEIU-BTjUcJpm.js → c4Diagram-AAUBKEIU-DKEHv1t2.js} +1 -1
  75. package/workframe-ui/public/assets/channel-g7r_RGaY.js +1 -0
  76. package/workframe-ui/public/assets/{chunk-2J33WTMH-w7uu7R-b.js → chunk-2J33WTMH-DHZg-DUi.js} +1 -1
  77. package/workframe-ui/public/assets/{chunk-3OPIFGDE-Cb9LtnDX.js → chunk-3OPIFGDE-BB-OYTfp.js} +1 -1
  78. package/workframe-ui/public/assets/{chunk-4BX2VUAB-DiQ-qCwH.js → chunk-4BX2VUAB-C93q0YIm.js} +1 -1
  79. package/workframe-ui/public/assets/{chunk-55IACEB6-C-mLFr7z.js → chunk-55IACEB6-MAYniqik.js} +1 -1
  80. package/workframe-ui/public/assets/{chunk-5ZQYHXKU-DOesfiCI.js → chunk-5ZQYHXKU-ChgN6YJs.js} +1 -1
  81. package/workframe-ui/public/assets/{chunk-727SXJPM-BJ3oBZuz.js → chunk-727SXJPM-B_FYwdAv.js} +1 -1
  82. package/workframe-ui/public/assets/{chunk-AQP2D5EJ-CCA6xpGs.js → chunk-AQP2D5EJ-1_Hw_h1A.js} +1 -1
  83. package/workframe-ui/public/assets/{chunk-BSJP7CBP-a0cMNFb2.js → chunk-BSJP7CBP-CFiDQ1Rv.js} +1 -1
  84. package/workframe-ui/public/assets/{chunk-CSCIHK7Q-kuqN8EIY.js → chunk-CSCIHK7Q-DZ9UMTlB.js} +1 -1
  85. package/workframe-ui/public/assets/{chunk-FMBD7UC4-DyPgYHCg.js → chunk-FMBD7UC4-DlMlyFgw.js} +1 -1
  86. package/workframe-ui/public/assets/{chunk-KSCS5N6A-CdUuvR0V.js → chunk-KSCS5N6A-DHXtQ_Hf.js} +1 -1
  87. package/workframe-ui/public/assets/{chunk-L5ZTLDWV-Dq9NoWmK.js → chunk-L5ZTLDWV-CuQzg-QG.js} +1 -1
  88. package/workframe-ui/public/assets/{chunk-LZXEDZCA-p74rddlO.js → chunk-LZXEDZCA-BHzjzCGg.js} +2 -2
  89. package/workframe-ui/public/assets/{chunk-ND2GUHAM-DBD2u1Gz.js → chunk-ND2GUHAM-DHXx05n2.js} +1 -1
  90. package/workframe-ui/public/assets/{chunk-NZK2D7GU-BeIeYFnd.js → chunk-NZK2D7GU-CV5pmDM_.js} +1 -1
  91. package/workframe-ui/public/assets/{chunk-O5CBEL6O-ClHc56ib.js → chunk-O5CBEL6O-6tkCHxsV.js} +1 -1
  92. package/workframe-ui/public/assets/chunk-QZHKN3VN-C5UQehWY.js +1 -0
  93. package/workframe-ui/public/assets/chunk-WU5MYG2G-DhWllrI8.js +1 -0
  94. package/workframe-ui/public/assets/{chunk-XPW4576I-EFr8R_1p.js → chunk-XPW4576I-BClwIiCp.js} +1 -1
  95. package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BBM_8T8E.js +1 -0
  96. package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BBM_8T8E.js +1 -0
  97. package/workframe-ui/public/assets/{cose-bilkent-S5V4N54A-C7aPBODd.js → cose-bilkent-S5V4N54A-DOrGV6DQ.js} +1 -1
  98. package/workframe-ui/public/assets/{dagre-BM42HDAG-BdU1Rv-H.js → dagre-BM42HDAG-DXTPvJkX.js} +1 -1
  99. package/workframe-ui/public/assets/{diagram-2AECGRRQ-DWowSo85.js → diagram-2AECGRRQ-xX_v-pbf.js} +1 -1
  100. package/workframe-ui/public/assets/{diagram-5GNKFQAL-MnxBbceO.js → diagram-5GNKFQAL-Cd2pXbBe.js} +1 -1
  101. package/workframe-ui/public/assets/{diagram-KO2AKTUF-DQaLRXFf.js → diagram-KO2AKTUF-Df3XvUtk.js} +1 -1
  102. package/workframe-ui/public/assets/{diagram-LMA3HP47-CQaBud9k.js → diagram-LMA3HP47-CsijIPaD.js} +1 -1
  103. package/workframe-ui/public/assets/{diagram-OG6HWLK6-D8bAXbY9.js → diagram-OG6HWLK6-aq5fmfHd.js} +1 -1
  104. package/workframe-ui/public/assets/{dist-DGpTLHr_.js → dist-D1c0mkbB.js} +1 -1
  105. package/workframe-ui/public/assets/{erDiagram-TEJ5UH35-1E-xSvBK.js → erDiagram-TEJ5UH35-DnFysVRY.js} +1 -1
  106. package/workframe-ui/public/assets/eventmodeling-FCH6USID-Ci8mdb44.js +1 -0
  107. package/workframe-ui/public/assets/{flowDiagram-I6XJVG4X-CgOVD5hu.js → flowDiagram-I6XJVG4X-C6Ebi3su.js} +1 -1
  108. package/workframe-ui/public/assets/{ganttDiagram-6RSMTGT7-JFYAIauo.js → ganttDiagram-6RSMTGT7-BQXQtUpa.js} +1 -1
  109. package/workframe-ui/public/assets/{gitGraph-WXDBUCRP-B9REenIl.js → gitGraph-WXDBUCRP-Dt0zIs_M.js} +1 -1
  110. package/workframe-ui/public/assets/{gitGraphDiagram-PVQCEYII-BQ7NcMSn.js → gitGraphDiagram-PVQCEYII-BF8gHzRn.js} +1 -1
  111. package/workframe-ui/public/assets/index-DpoUZAxh.css +1 -0
  112. package/workframe-ui/public/assets/{index-Dnw6vjqb.js → index-lRpzpNPT.js} +2 -2
  113. package/workframe-ui/public/assets/{info-J43DQDTF-CL6-eTjH.js → info-J43DQDTF-CSmszQJT.js} +1 -1
  114. package/workframe-ui/public/assets/{infoDiagram-5YYISTIA-LJTODW4W.js → infoDiagram-5YYISTIA-CVTKGW6p.js} +1 -1
  115. package/workframe-ui/public/assets/{ishikawaDiagram-YF4QCWOH-bchrQVuo.js → ishikawaDiagram-YF4QCWOH-Z8pT09Lv.js} +1 -1
  116. package/workframe-ui/public/assets/{journeyDiagram-JHISSGLW-DkrvYuxP.js → journeyDiagram-JHISSGLW-r3wD68_T.js} +1 -1
  117. package/workframe-ui/public/assets/{kanban-definition-UN3LZRKU-DFRbj0IG.js → kanban-definition-UN3LZRKU-Il8VglqN.js} +1 -1
  118. package/workframe-ui/public/assets/{line-Vd48P7-O.js → line-oyjpfz2A.js} +1 -1
  119. package/workframe-ui/public/assets/{linear-Ckizh2G7.js → linear-Cf7p5tVp.js} +1 -1
  120. package/workframe-ui/public/assets/{mermaid-parser.core-Bkimsnqj.js → mermaid-parser.core-YmbZ-AfY.js} +2 -2
  121. package/workframe-ui/public/assets/{mermaid.core-x0TvVuPo.js → mermaid.core-BFdCAqCo.js} +3 -3
  122. package/workframe-ui/public/assets/{mindmap-definition-RKZ34NQL-6ykAFPEz.js → mindmap-definition-RKZ34NQL-Cy2iCtEl.js} +1 -1
  123. package/workframe-ui/public/assets/{packet-YPE3B663-Dw3xgMDt.js → packet-YPE3B663-DwOBZL6K.js} +1 -1
  124. package/workframe-ui/public/assets/{pie-LRSECV5Y-DATysawG.js → pie-LRSECV5Y-04PPhnKK.js} +1 -1
  125. package/workframe-ui/public/assets/{pieDiagram-4H26LBE5-SJKD1S0S.js → pieDiagram-4H26LBE5-LxIpgHqi.js} +1 -1
  126. package/workframe-ui/public/assets/{quadrantDiagram-W4KKPZXB-BrYDZX8q.js → quadrantDiagram-W4KKPZXB-0nBYfYm4.js} +1 -1
  127. package/workframe-ui/public/assets/{radar-GUYGQ44K-BmWYPCds.js → radar-GUYGQ44K-D2-vBqps.js} +1 -1
  128. package/workframe-ui/public/assets/{requirementDiagram-4Y6WPE33-DwL9Mc8e.js → requirementDiagram-4Y6WPE33-DbuU0nlu.js} +1 -1
  129. package/workframe-ui/public/assets/{sankeyDiagram-5OEKKPKP-DYIFsL8h.js → sankeyDiagram-5OEKKPKP-B2hQ6B2x.js} +1 -1
  130. package/workframe-ui/public/assets/{sequenceDiagram-3UESZ5HK-0-FPkFk8.js → sequenceDiagram-3UESZ5HK-BBrU30e1.js} +1 -1
  131. package/workframe-ui/public/assets/{src-B_od6b6h.js → src-BJEDmV70.js} +1 -1
  132. package/workframe-ui/public/assets/{stateDiagram-AJRCARHV-BQCiBk6u.js → stateDiagram-AJRCARHV-7FGO4kkH.js} +1 -1
  133. package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-DLTSizMg.js +1 -0
  134. package/workframe-ui/public/assets/{timeline-definition-PNZ67QCA-DS3tFcXj.js → timeline-definition-PNZ67QCA-ptDm4rCN.js} +1 -1
  135. package/workframe-ui/public/assets/{treeView-BLDUP644-DSyUCKLY.js → treeView-BLDUP644-CS6Z-0q8.js} +1 -1
  136. package/workframe-ui/public/assets/{treemap-LRROVOQU-CEZaNh5Y.js → treemap-LRROVOQU-DqV4Y2VA.js} +1 -1
  137. package/workframe-ui/public/assets/{vennDiagram-CIIHVFJN-CD-Vc9NF.js → vennDiagram-CIIHVFJN-C0UrZJYt.js} +1 -1
  138. package/workframe-ui/public/assets/{wardley-L42UT6IY-Drq5w1Mc.js → wardley-L42UT6IY-bNDN3_Sa.js} +1 -1
  139. package/workframe-ui/public/assets/{wardleyDiagram-YWT4CUSO-DouXDJoF.js → wardleyDiagram-YWT4CUSO-jWiJsefM.js} +1 -1
  140. package/workframe-ui/public/assets/{xychartDiagram-2RQKCTM6-DDf_Lol5.js → xychartDiagram-2RQKCTM6-Dsh_fLCy.js} +1 -1
  141. package/workframe-ui/public/favicon.svg +7 -7
  142. package/workframe-ui/public/index.html +50 -50
  143. package/workframe-ui/public/workframe-config.json +3 -3
  144. package/scripts/security_audit.py +0 -156
  145. package/scripts/test-scaffold.mjs +0 -390
  146. package/workframe-api/tests/__init__.py +0 -0
  147. package/workframe-api/tests/db_setup.py +0 -13
  148. package/workframe-api/tests/test_admin_updates_gated.py +0 -30
  149. package/workframe-api/tests/test_agent_dm_bootstrap.py +0 -196
  150. package/workframe-api/tests/test_agent_profile_sync.py +0 -76
  151. package/workframe-api/tests/test_auth_email.py +0 -222
  152. package/workframe-api/tests/test_auth_hole_fix_selfcheck.py +0 -99
  153. package/workframe-api/tests/test_auth_rate_limit.py +0 -19
  154. package/workframe-api/tests/test_avatar_resolve.py +0 -77
  155. package/workframe-api/tests/test_child_soul_template.py +0 -71
  156. package/workframe-api/tests/test_credential_canary.py +0 -135
  157. package/workframe-api/tests/test_credential_isolation.py +0 -448
  158. package/workframe-api/tests/test_credential_resolution.py +0 -206
  159. package/workframe-api/tests/test_device_oauth.py +0 -108
  160. package/workframe-api/tests/test_doctor_repair.py +0 -103
  161. package/workframe-api/tests/test_ensure_profile_api.py +0 -77
  162. package/workframe-api/tests/test_gateway_compose_security.py +0 -136
  163. package/workframe-api/tests/test_install_secure_host.py +0 -39
  164. package/workframe-api/tests/test_internal_proxy_auth.py +0 -125
  165. package/workframe-api/tests/test_invite_runtime_bootstrap.py +0 -72
  166. package/workframe-api/tests/test_kanban_delegation.py +0 -185
  167. package/workframe-api/tests/test_llm_proxy.py +0 -155
  168. package/workframe-api/tests/test_login_access_policy.py +0 -183
  169. package/workframe-api/tests/test_mvp_model_bootstrap.py +0 -75
  170. package/workframe-api/tests/test_onboarding_bootstrap.py +0 -248
  171. package/workframe-api/tests/test_platform_auth.py +0 -47
  172. package/workframe-api/tests/test_profile_config_path.py +0 -56
  173. package/workframe-api/tests/test_profile_config_yaml_repair.py +0 -63
  174. package/workframe-api/tests/test_profile_create.py +0 -72
  175. package/workframe-api/tests/test_profile_identity_overlay.py +0 -61
  176. package/workframe-api/tests/test_profile_install_health.py +0 -45
  177. package/workframe-api/tests/test_profile_secret_policy.py +0 -57
  178. package/workframe-api/tests/test_profile_workspace_cwd.py +0 -34
  179. package/workframe-api/tests/test_provider_bootstrap.py +0 -75
  180. package/workframe-api/tests/test_provider_connect.py +0 -54
  181. package/workframe-api/tests/test_room_crud.py +0 -192
  182. package/workframe-api/tests/test_room_tenancy.py +0 -701
  183. package/workframe-api/tests/test_runtime_identity_backfill.py +0 -34
  184. package/workframe-api/tests/test_site_meta.py +0 -81
  185. package/workframe-api/tests/test_soul_stub.py +0 -42
  186. package/workframe-api/tests/test_space_member_sync.py +0 -99
  187. package/workframe-api/tests/test_stripe_stack_config.py +0 -37
  188. package/workframe-api/tests/test_supervisor_lifecycle.py +0 -52
  189. package/workframe-api/tests/test_turn_credential_vault.py +0 -125
  190. package/workframe-api/tests/test_updates.py +0 -176
  191. package/workframe-api/tests/test_user_cohort.py +0 -113
  192. package/workframe-api/tests/test_vault_envelope.py +0 -110
  193. package/workframe-api/tests/test_workspace_members.py +0 -183
  194. package/workframe-api/tests/test_workspace_messaging_sync.py +0 -125
  195. package/workframe-api/tests/test_workspace_provider_list.py +0 -57
  196. package/workframe-supervisor/tests/test_exec_guard.py +0 -42
  197. package/workframe-supervisor/tests/test_server_import.py +0 -21
  198. package/workframe-ui/public/assets/architecture-7EHR7CIX-CtbQKTuT.js +0 -1
  199. package/workframe-ui/public/assets/channel-Dy4Z4-jn.js +0 -1
  200. package/workframe-ui/public/assets/chunk-QZHKN3VN-CtBEchFK.js +0 -1
  201. package/workframe-ui/public/assets/chunk-WU5MYG2G-B9pBtriN.js +0 -1
  202. package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BMAEA8jI.js +0 -1
  203. package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BMAEA8jI.js +0 -1
  204. package/workframe-ui/public/assets/eventmodeling-FCH6USID-D75cstNT.js +0 -1
  205. package/workframe-ui/public/assets/index-DpAGxump.css +0 -1
  206. package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-B89jAMFF.js +0 -1
@@ -1,113 +0,0 @@
1
- import tempfile
2
- import unittest
3
- from pathlib import Path
4
- from unittest.mock import patch
5
-
6
- import server
7
- from db_setup import ensure_workframe_schemas
8
-
9
-
10
- class UserCohortTests(unittest.TestCase):
11
- def setUp(self) -> None:
12
- self._tmp = tempfile.TemporaryDirectory()
13
- self.addCleanup(self._tmp.cleanup)
14
- self._old_data_dir = server.DATA_DIR
15
- self._old_auth_db_path = server.AUTH_DB_PATH
16
- self._old_hermes_data = server.HERMES_DATA
17
- server.DATA_DIR = Path(self._tmp.name)
18
- server.AUTH_DB_PATH = Path(self._tmp.name) / "auth.db"
19
- server.HERMES_DATA = Path(self._tmp.name) / "hermes"
20
- (server.HERMES_DATA / "profiles").mkdir(parents=True)
21
- ensure_workframe_schemas()
22
- self.workspace_id = "ws-1"
23
- self.user_id = "cb6a2db4-ac86-4c49-8aaa-user"
24
- conn = server._workframe_db()
25
- try:
26
- now = "1"
27
- conn.execute(
28
- "INSERT INTO users (id, display_name, email, role, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?)",
29
- (self.user_id, "Fab", "fab@example.com", "member", "active", now, now),
30
- )
31
- conn.execute(
32
- "INSERT INTO workspaces (id, slug, display_name, owner_id, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?)",
33
- (self.workspace_id, "proj", "Project", self.user_id, "active", now, now),
34
- )
35
- for slug, name, is_native in (
36
- ("workframe-agent", "Workframe Agent", 1),
37
- ("architect", "Architect", 0),
38
- ("dev", "Dev", 0),
39
- ):
40
- conn.execute(
41
- """
42
- INSERT INTO agent_profiles (id, workspace_id, slug, display_name, is_native, status, created_at, updated_at)
43
- VALUES (?, ?, ?, ?, ?, 'available', ?, ?)
44
- """,
45
- (f"ap-{slug}", self.workspace_id, slug, name, is_native, now, now),
46
- )
47
- conn.commit()
48
- finally:
49
- conn.close()
50
-
51
- def tearDown(self) -> None:
52
- server.DATA_DIR = self._old_data_dir
53
- server.AUTH_DB_PATH = self._old_auth_db_path
54
- server.HERMES_DATA = self._old_hermes_data
55
-
56
- def test_user_handle_from_display_name(self) -> None:
57
- self.assertEqual(server._user_handle(self.user_id), "fab")
58
-
59
- def test_cohort_alias(self) -> None:
60
- self.assertEqual(server._cohort_alias(self.user_id, "architect"), "fab-architect")
61
-
62
- def test_runtime_display_label_specialist(self) -> None:
63
- label = server._runtime_display_label(self.user_id, "architect", self.workspace_id)
64
- self.assertEqual(label, "Architect")
65
-
66
- def test_runtime_display_label_concierge(self) -> None:
67
- label = server._runtime_display_label(self.user_id, "workframe-agent", self.workspace_id)
68
- self.assertEqual(label, "Workframe Agent")
69
-
70
- def test_runtime_display_label_personalized_without_db_name(self) -> None:
71
- label = server._runtime_display_label(self.user_id, "research", self.workspace_id)
72
- self.assertEqual(label, "Fab's Research")
73
-
74
- def test_resolve_runtime_assignee_slug(self) -> None:
75
- with patch.object(server, "ensure_runtime_profile") as ensure:
76
- slug = server.resolve_runtime_assignee("architect", self.user_id, self.workspace_id)
77
- self.assertTrue(slug.startswith("u-"))
78
- self.assertTrue(slug.endswith("-architect"))
79
- ensure.assert_called_once()
80
-
81
- @patch.object(server, "ensure_runtime_profile")
82
- def test_ensure_user_agent_cohort_writes_manifest(self, _ensure: object) -> None:
83
- cohort = server.ensure_user_agent_cohort(self.user_id, self.workspace_id)
84
- self.assertEqual(len(cohort), 3)
85
- assignees = {row["template_slug"]: row["runtime_slug"] for row in cohort}
86
- self.assertTrue(assignees["architect"].startswith("u-"))
87
- concierge = assignees["workframe-agent"]
88
- manifest = server._profile_dir(concierge) / "WORKFRAME_COHORT.md"
89
- self.assertTrue(manifest.is_file())
90
- text = manifest.read_text(encoding="utf-8")
91
- self.assertIn("Fab's Workframe cohort", text)
92
- self.assertIn(assignees["architect"], text)
93
- self.assertIn("fab-architect", text)
94
- self.assertIn("workspace_kind", text)
95
-
96
- def test_cohort_manifest_markdown_includes_rules(self) -> None:
97
- body = server._cohort_manifest_markdown(
98
- self.user_id,
99
- [
100
- {
101
- "role": "Architect",
102
- "display_name": "Fab's Architect",
103
- "runtime_slug": "u-cb6a2db4-architect",
104
- "alias": "fab-architect",
105
- }
106
- ],
107
- )
108
- self.assertIn("never bare template names", body)
109
- self.assertIn("`scratch`", body)
110
-
111
-
112
- if __name__ == "__main__":
113
- unittest.main()
@@ -1,110 +0,0 @@
1
- """Vault envelope encryption (KEK + per-secret DEK)."""
2
-
3
- from __future__ import annotations
4
-
5
- import json
6
- import tempfile
7
- import unittest
8
- from pathlib import Path
9
-
10
- import credential_vault
11
- import vault_kek
12
- import zk_auth
13
-
14
-
15
- class VaultEnvelopeTests(unittest.TestCase):
16
- def setUp(self) -> None:
17
- self._tmp = tempfile.TemporaryDirectory()
18
- self.addCleanup(self._tmp.cleanup)
19
- self._old_data = credential_vault.DATA_DIR
20
- self._old_db = credential_vault.VAULT_DB
21
- self._old_kek_file = vault_kek.VAULT_KEK_FILE
22
- credential_vault.DATA_DIR = Path(self._tmp.name)
23
- credential_vault.VAULT_DB = credential_vault.DATA_DIR / "credential_vault.db"
24
- vault_kek.DATA_DIR = credential_vault.DATA_DIR
25
- vault_kek.VAULT_KEK_FILE = credential_vault.DATA_DIR / ".vault_kek"
26
- vault_kek.clear_kek()
27
- credential_vault.ensure_schema()
28
-
29
- def tearDown(self) -> None:
30
- vault_kek.clear_kek()
31
- credential_vault.DATA_DIR = self._old_data
32
- credential_vault.VAULT_DB = self._old_db
33
- vault_kek.VAULT_KEK_FILE = self._old_kek_file
34
-
35
- def test_v2_envelope_roundtrip(self) -> None:
36
- credential_vault.unseal_for_tests()
37
- credential_vault.store_secret("bid-1", "sk-live-secret", provider="openrouter")
38
- conn = credential_vault._connect()
39
- try:
40
- row = conn.execute(
41
- "SELECT encrypted_secret FROM credential_secrets WHERE binding_id = ?",
42
- ("bid-1",),
43
- ).fetchone()
44
- blob = str(row["encrypted_secret"])
45
- parsed = json.loads(blob)
46
- self.assertEqual(parsed["v"], 2)
47
- self.assertIn("wrapped_dek", parsed)
48
- finally:
49
- conn.close()
50
- self.assertEqual(credential_vault.read_secret("bid-1"), "sk-live-secret")
51
-
52
- def test_legacy_v1_readable_after_unseal(self) -> None:
53
- legacy = json.dumps(zk_auth.encrypt_string("legacy-key", zk_auth.ZK_AUTH_ENCRYPTION_KEY))
54
- conn = credential_vault._connect()
55
- now = "1"
56
- conn.execute(
57
- """
58
- INSERT INTO credential_secrets (
59
- binding_id, encrypted_secret, env_var, provider, scope,
60
- user_id, workspace_id, created_at, updated_at
61
- ) VALUES (?,?,?,?,?,?,?,?,?)
62
- """,
63
- ("legacy-1", legacy, "OPENROUTER_API_KEY", "openrouter", "user", "u1", None, now, now),
64
- )
65
- conn.commit()
66
- conn.close()
67
- credential_vault.unseal_for_tests()
68
- self.assertEqual(credential_vault.read_secret("legacy-1"), "legacy-key")
69
- conn = credential_vault._connect()
70
- try:
71
- row = conn.execute(
72
- "SELECT encrypted_secret FROM credential_secrets WHERE binding_id = ?",
73
- ("legacy-1",),
74
- ).fetchone()
75
- self.assertEqual(json.loads(str(row["encrypted_secret"]))["v"], 2)
76
- finally:
77
- conn.close()
78
-
79
- def test_sealed_vault_blocks_store_without_passphrase_bootstrap(self) -> None:
80
- credential_vault.unseal_for_tests()
81
- credential_vault.init_vault_passphrase("long-passphrase-1")
82
- credential_vault.seal_vault()
83
- with self.assertRaises(RuntimeError):
84
- credential_vault.store_secret("x", "secret")
85
-
86
- def test_passphrase_unlock_restores_access(self) -> None:
87
- credential_vault.unseal_for_tests()
88
- credential_vault.store_secret("k1", "before-seal")
89
- credential_vault.init_vault_passphrase("another-long-pass")
90
- credential_vault.seal_vault()
91
- status = credential_vault.unlock_vault("another-long-pass")
92
- self.assertFalse(status["sealed"])
93
- self.assertEqual(credential_vault.read_secret("k1"), "before-seal")
94
-
95
- def test_wipe_deletes_ciphertext(self) -> None:
96
- credential_vault.unseal_for_tests()
97
- credential_vault.store_secret("w1", "to-delete")
98
- deleted = credential_vault.wipe_all_secrets()
99
- self.assertEqual(deleted, 1)
100
- self.assertEqual(credential_vault.read_secret("w1"), "")
101
-
102
- def test_bootstrap_generates_kek_file_for_local(self) -> None:
103
- vault_kek.clear_kek()
104
- status = credential_vault.bootstrap_vault(allow_generate_file=True)
105
- self.assertFalse(status["sealed"])
106
- self.assertTrue(vault_kek.VAULT_KEK_FILE.is_file())
107
-
108
-
109
- if __name__ == "__main__":
110
- unittest.main()
@@ -1,183 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import importlib.util
4
- from pathlib import Path
5
- from types import SimpleNamespace
6
- from typing import Any
7
-
8
- import pytest
9
-
10
- SERVER_PATH = Path(__file__).resolve().parents[1] / "server.py"
11
- spec = importlib.util.spec_from_file_location("workframe_server", SERVER_PATH)
12
- server = importlib.util.module_from_spec(spec)
13
- assert spec.loader is not None
14
- spec.loader.exec_module(server)
15
-
16
-
17
- class FakeHandler:
18
- def __init__(self, auth_user: str = "u_owner", auth_role: str = "owner") -> None:
19
- self.auth_user = auth_user
20
- self.auth_role = auth_role
21
- self.headers = {}
22
- self.client_address = ("127.0.0.1", 12345)
23
-
24
-
25
- @pytest.fixture(autouse=True)
26
- def isolate_server(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
27
- monkeypatch.setattr(server, "DATA_DIR", tmp_path)
28
- monkeypatch.setattr(server, "AUTH_DB_PATH", tmp_path / "auth.db")
29
- monkeypatch.setattr(server, "SECURE_MODE", False)
30
- monkeypatch.setattr(server, "DEV_LOCAL_UNSAFE", True)
31
- server._ensure_workframe_db_schema()
32
- server._ensure_agent_runs_schema()
33
- server._ensure_invites_schema()
34
- server._ensure_memory_schema()
35
- server._ensure_policies_schema()
36
- server._ensure_budgets_grants_schema()
37
- server._ensure_user_prefs_schema()
38
- server._ensure_runtime_tokens_table()
39
- seed_db(tmp_path)
40
- yield
41
-
42
-
43
- def seed_db(tmp_path: Path) -> None:
44
- conn = server._workframe_db()
45
- try:
46
- now = "1700000000"
47
- conn.execute(
48
- "INSERT INTO workspaces (id, slug, display_name, owner_id, created_at, updated_at) VALUES (?,?,?,?,?,?)",
49
- ("ws", "default", "Default", "u_owner", now, now),
50
- )
51
- conn.executemany(
52
- "INSERT INTO users (id, email, display_name, avatar_url, role, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)",
53
- [
54
- ("u_owner", "owner@example.com", "Owner", None, "owner", "active", now, now),
55
- ("u_bob", "bob@example.com", "Bob", None, "member", "active", now, now),
56
- ("u_carol", "carol@example.com", "Carol", None, "member", "active", now, now),
57
- ("u_outsider", "outsider@example.com", "Outsider", None, "member", "active", now, now),
58
- ],
59
- )
60
- conn.executemany(
61
- "INSERT INTO workspace_memberships (id, workspace_id, user_id, role, status, invited_by, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)",
62
- [
63
- ("m_owner", "ws", "u_owner", "owner", "active", None, now, now),
64
- ("m_bob", "ws", "u_bob", "member", "active", "u_owner", now, now),
65
- ("m_carol", "ws", "u_carol", "member", "active", "u_owner", now, now),
66
- ],
67
- )
68
- conn.commit()
69
- finally:
70
- conn.close()
71
-
72
-
73
- def membership_by_user(payload: dict[str, Any], user_id: str) -> dict[str, Any]:
74
- return next(item for item in payload["members"] if item["user_id"] == user_id)
75
-
76
-
77
- def test_list_workspace_members_returns_active_memberships_with_user_fields() -> None:
78
- status, payload = server._list_workspace_members("ws", FakeHandler())
79
-
80
- assert status == 200
81
- assert payload["ok"] is True
82
- assert payload["workspace_id"] == "ws"
83
- assert [item["user_id"] for item in payload["members"]] == ["u_bob", "u_carol", "u_owner"]
84
- bob = membership_by_user(payload, "u_bob")
85
- assert bob["membership_id"] == "m_bob"
86
- assert bob["email"] == "bob@example.com"
87
- assert bob["display_name"] == "Bob"
88
- assert bob["role"] == "member"
89
- assert bob["status"] == "active"
90
-
91
-
92
- def test_patch_updates_member_role_and_status() -> None:
93
- status, payload = server._patch_workspace_members(
94
- "ws",
95
- {"user_id": "u_bob", "role": "admin", "status": "active"},
96
- FakeHandler(),
97
- )
98
-
99
- assert status == 200
100
- assert payload["membership"]["user_id"] == "u_bob"
101
- assert payload["membership"]["role"] == "admin"
102
- assert payload["membership"]["status"] == "active"
103
- assert payload["membership"]["updated_at"] != "1700000000"
104
-
105
- _, listed = server._list_workspace_members("ws", FakeHandler())
106
- assert membership_by_user(listed, "u_bob")["role"] == "admin"
107
-
108
-
109
- def test_patch_status_removed_soft_deletes_member() -> None:
110
- status, payload = server._patch_workspace_members(
111
- "ws",
112
- {"user_id": "u_carol", "status": "removed"},
113
- FakeHandler(),
114
- )
115
-
116
- assert status == 200
117
- assert payload["membership"]["status"] == "removed"
118
-
119
- _, listed = server._list_workspace_members("ws", FakeHandler())
120
- assert "u_carol" not in [item["user_id"] for item in listed["members"]]
121
-
122
-
123
- def test_patch_bulk_updates_memberships() -> None:
124
- status, payload = server._patch_workspace_members(
125
- "ws",
126
- {
127
- "memberships": [
128
- {"user_id": "u_bob", "role": "admin"},
129
- {"user_id": "u_carol", "role": "member"},
130
- ],
131
- },
132
- FakeHandler(),
133
- )
134
-
135
- assert status == 200
136
- assert [item["user_id"] for item in payload["memberships"]] == ["u_bob", "u_carol"]
137
- assert payload["memberships"][0]["role"] == "admin"
138
- assert payload["memberships"][1]["role"] == "member"
139
-
140
-
141
- def test_patch_requires_owner_or_admin_when_secure(monkeypatch: pytest.MonkeyPatch) -> None:
142
- monkeypatch.setattr(server, "SECURE_MODE", True)
143
-
144
- status, payload = server._patch_workspace_members(
145
- "ws",
146
- {"user_id": "u_bob", "role": "admin"},
147
- FakeHandler(auth_user="u_bob", auth_role="member"),
148
- )
149
-
150
- assert status == 403
151
- assert payload["error"] == "forbidden"
152
-
153
-
154
- def test_patch_rejects_invalid_role() -> None:
155
- status, payload = server._patch_workspace_members(
156
- "ws",
157
- {"user_id": "u_bob", "role": "superadmin"},
158
- FakeHandler(),
159
- )
160
-
161
- assert status == 400
162
- assert payload["error"] == "invalid_role"
163
- assert "superadmin" not in payload["allowed"]
164
-
165
-
166
- def test_patch_prevents_removing_last_owner() -> None:
167
- status, payload = server._patch_workspace_members(
168
- "ws",
169
- {"user_id": "u_owner", "status": "removed"},
170
- FakeHandler(),
171
- )
172
-
173
- assert status == 409
174
- assert payload["error"] == "cannot_remove_last_owner"
175
-
176
-
177
- def test_list_workspace_members_requires_workspace_access_when_secure(monkeypatch: pytest.MonkeyPatch) -> None:
178
- monkeypatch.setattr(server, "SECURE_MODE", True)
179
-
180
- status, payload = server._list_workspace_members("ws", FakeHandler(auth_user="u_outsider", auth_role="member"))
181
-
182
- assert status == 403
183
- assert payload["error"] == "forbidden"
@@ -1,125 +0,0 @@
1
- """Workspace messaging: vault store, settings merge, gateway env overlay."""
2
-
3
- import json
4
- import tempfile
5
- import unittest
6
- from pathlib import Path
7
- from unittest.mock import patch
8
-
9
- import credential_vault
10
- import server
11
- import vault_kek
12
- from db_setup import ensure_workframe_schemas
13
-
14
-
15
- class WorkspaceMessagingSyncTests(unittest.TestCase):
16
- def setUp(self) -> None:
17
- self._tmp = tempfile.TemporaryDirectory()
18
- self.addCleanup(self._tmp.cleanup)
19
- self._old_data_dir = server.DATA_DIR
20
- self._old_auth_db_path = server.AUTH_DB_PATH
21
- self._old_hermes_data = server.HERMES_DATA
22
- self._old_vault_data_dir = credential_vault.DATA_DIR
23
- self._old_vault_db = credential_vault.VAULT_DB
24
- self._old_kek_data_dir = vault_kek.DATA_DIR
25
- self._old_kek_file = vault_kek.VAULT_KEK_FILE
26
- server.DATA_DIR = Path(self._tmp.name)
27
- server.AUTH_DB_PATH = Path(self._tmp.name) / "auth.db"
28
- server.HERMES_DATA = Path(self._tmp.name) / "hermes"
29
- profile_dir = server._profile_dir("workframe-agent")
30
- profile_dir.mkdir(parents=True, exist_ok=True)
31
- credential_vault.DATA_DIR = server.DATA_DIR
32
- credential_vault.VAULT_DB = server.DATA_DIR / "credential_vault.db"
33
- vault_kek.DATA_DIR = server.DATA_DIR
34
- vault_kek.VAULT_KEK_FILE = server.DATA_DIR / ".vault_kek"
35
- credential_vault.ensure_schema()
36
- credential_vault.unseal_for_tests()
37
- ensure_workframe_schemas()
38
- self.workspace_id = "ws-msg"
39
- self.admin_id = "admin-msg-user"
40
- conn = server._workframe_db()
41
- try:
42
- now = "1"
43
- conn.execute(
44
- "INSERT INTO workspaces (id, slug, display_name, owner_id, status, settings_json, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)",
45
- (self.workspace_id, "default", "Default", self.admin_id, "active", "{}", now, now),
46
- )
47
- conn.execute(
48
- "INSERT INTO users (id, display_name, platform_ids, role, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?)",
49
- (self.admin_id, "Admin", json.dumps({"discord": "999888777"}), "owner", "active", now, now),
50
- )
51
- conn.execute(
52
- """
53
- INSERT INTO workspace_memberships (id, workspace_id, user_id, role, status, created_at, updated_at)
54
- VALUES (?, ?, ?, 'owner', 'active', ?, ?)
55
- """,
56
- ("wm-admin", self.workspace_id, self.admin_id, now, now),
57
- )
58
- conn.execute(
59
- """
60
- INSERT INTO agent_profiles (id, workspace_id, slug, display_name, is_native, status, created_at, updated_at)
61
- VALUES (?, ?, 'workframe-agent', 'Workframe Agent', 1, 'available', ?, ?)
62
- """,
63
- ("ap-native", self.workspace_id, now, now),
64
- )
65
- conn.commit()
66
- finally:
67
- conn.close()
68
-
69
- def tearDown(self) -> None:
70
- server.DATA_DIR = self._old_data_dir
71
- server.AUTH_DB_PATH = self._old_auth_db_path
72
- server.HERMES_DATA = self._old_hermes_data
73
- credential_vault.DATA_DIR = self._old_vault_data_dir
74
- credential_vault.VAULT_DB = self._old_vault_db
75
- vault_kek.DATA_DIR = self._old_kek_data_dir
76
- vault_kek.VAULT_KEK_FILE = self._old_kek_file
77
-
78
- @patch.object(server, "_primary_profile", return_value="workframe-agent")
79
- @patch.object(server, "_restart_stack_gateway", return_value={"ok": True})
80
- @patch.object(server, "_set_primary_messaging_platforms", return_value=(True, "ok"))
81
- def test_store_and_sync_writes_gateway_env(self, _platforms, _restart, _primary) -> None:
82
- server._store_workspace_credential(
83
- self.workspace_id,
84
- "discord",
85
- "api_key",
86
- "discord-bot-secret",
87
- "DISCORD_BOT_TOKEN",
88
- "Discord",
89
- self.admin_id,
90
- )
91
- status, payload = server._patch_workspace_integrations(
92
- self.workspace_id,
93
- {
94
- "messaging": {
95
- "discord": {
96
- "home_channel": "chan-123",
97
- "allowed_users": "111",
98
- },
99
- },
100
- },
101
- self.admin_id,
102
- )
103
- self.assertEqual(status, 200)
104
- self.assertTrue(payload.get("ok"))
105
- env = server._read_env_map(server._profile_dir("workframe-agent") / ".env")
106
- self.assertEqual(env.get("DISCORD_BOT_TOKEN"), "discord-bot-secret")
107
- self.assertEqual(env.get("DISCORD_HOME_CHANNEL"), "chan-123")
108
- self.assertIn("111", env.get("DISCORD_ALLOWED_USERS", ""))
109
- self.assertIn("999888777", env.get("DISCORD_ALLOWED_USERS", ""))
110
-
111
- def test_workspace_store_rejects_user_only_providers(self) -> None:
112
- with self.assertRaises(ValueError):
113
- server._store_workspace_credential(
114
- self.workspace_id,
115
- "vercel",
116
- "api_key",
117
- "pat-secret",
118
- "VERCEL_TOKEN",
119
- "Vercel",
120
- self.admin_id,
121
- )
122
-
123
-
124
- if __name__ == "__main__":
125
- unittest.main()
@@ -1,57 +0,0 @@
1
- """Workspace LLM keys surface in provider list for company-pay onboarding."""
2
- import os
3
- import tempfile
4
- import unittest
5
- from pathlib import Path
6
- from unittest import mock
7
-
8
- import server
9
-
10
-
11
- class WorkspaceProviderListTests(unittest.TestCase):
12
- def setUp(self) -> None:
13
- self.tmp = tempfile.TemporaryDirectory()
14
- self.addCleanup(self.tmp.cleanup)
15
- data = Path(self.tmp.name) / "data"
16
- agents = Path(self.tmp.name) / "Agents"
17
- profiles = agents / "profiles" / "workframe-agent"
18
- profiles.mkdir(parents=True)
19
- (profiles / "profile.yaml").write_text("description: test\n", encoding="utf-8")
20
- (profiles / ".env").write_text("OPENROUTER_API_KEY=sk-test-workspace\n", encoding="utf-8")
21
- self.patches = [
22
- mock.patch.object(server, "DATA_DIR", data),
23
- mock.patch.object(server, "HERMES_DATA", agents),
24
- mock.patch.object(server, "WORKSPACE", Path(self.tmp.name) / "Files"),
25
- mock.patch.object(server, "_workframe_db_path", return_value=data / "workframe.db"),
26
- mock.patch.dict(os.environ, {"WORKFRAME_PROJECT": "Workframe", "WORKFRAME_NATIVE_PROFILE": "workframe-agent"}, clear=False),
27
- ]
28
- for patch in self.patches:
29
- patch.start()
30
- self.addCleanup(patch.stop)
31
- server._ensure_workframe_db_schema()
32
- server._ensure_default_workspace()
33
-
34
- def test_list_user_providers_shows_workspace_llm_connected(self) -> None:
35
- conn = server._workframe_db()
36
- ws = conn.execute("SELECT id FROM workspaces WHERE slug='default'").fetchone()
37
- now = server._utc_now()
38
- conn.execute(
39
- """INSERT INTO credential_bindings
40
- (id, workspace_id, user_id, agent_profile_id, provider, credential_type,
41
- credential_ref, label, is_active, created_by, created_at, updated_at)
42
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
43
- (
44
- "cred-ws-1", ws["id"], None, None, "openrouter", "api_key",
45
- "env:OPENROUTER_API_KEY", "OpenRouter", 1, "admin", now, now,
46
- ),
47
- )
48
- conn.commit()
49
- conn.close()
50
- payload = server.list_user_providers("user-no-keys", str(ws["id"]))
51
- row = next(p for p in payload["providers"] if p["id"] == "openrouter")
52
- self.assertTrue(row["connected"])
53
- self.assertEqual(row["source"], "workspace")
54
-
55
-
56
- if __name__ == "__main__":
57
- unittest.main()
@@ -1,42 +0,0 @@
1
- """Supervisor gateway exec must not expose sibling u-* credential files."""
2
-
3
- from __future__ import annotations
4
-
5
- import unittest
6
- from unittest.mock import patch
7
-
8
- import server
9
-
10
-
11
- class SupervisorExecGuardTests(unittest.TestCase):
12
- def test_blocks_cat_peer_env(self) -> None:
13
- cmd = ["sh", "-lc", "cat /opt/data/profiles/u-deadbeef-architect/.env"]
14
- with patch.object(server, "DEPLOYMENT_MODE", "trusted_team"):
15
- self.assertTrue(server._exec_targets_runtime_profile_secrets(cmd))
16
-
17
- def test_allows_single_user_local(self) -> None:
18
- cmd = ["sh", "-lc", "cat /opt/data/profiles/u-deadbeef-architect/.env"]
19
- with patch.object(server, "DEPLOYMENT_MODE", "single_user_local"):
20
- self.assertFalse(server._exec_targets_runtime_profile_secrets(cmd))
21
-
22
- def test_docker_exec_short_circuits(self) -> None:
23
- cmd = ["cat", "/opt/data/profiles/u-abc-architect/auth.json"]
24
- with patch.object(server, "DEPLOYMENT_MODE", "public_multi_user"), patch.object(
25
- server, "_docker_request"
26
- ) as docker_request:
27
- code, out = server._docker_exec("workframe-gateway", cmd)
28
- docker_request.assert_not_called()
29
- self.assertEqual(code, 1)
30
- self.assertIn("blocked", out)
31
-
32
- def test_profile_api_health_uses_gateway_container_exec(self) -> None:
33
- with patch.object(server, "_docker_exec", return_value=(0, "ok")) as docker_exec:
34
- self.assertTrue(server._profile_api_healthy("architect"))
35
- docker_exec.assert_called_once()
36
- self.assertEqual(docker_exec.call_args[0][0], server.GATEWAY_CONTAINER_NAME)
37
- script = docker_exec.call_args[0][1][-1]
38
- self.assertIn("127.0.0.1", script)
39
-
40
-
41
- if __name__ == "__main__":
42
- unittest.main()
@@ -1,21 +0,0 @@
1
- """Supervisor server must import cleanly (catches missing profile_secret_policy import)."""
2
-
3
- from __future__ import annotations
4
-
5
- import importlib.util
6
- import unittest
7
- from pathlib import Path
8
-
9
-
10
- class SupervisorImportTests(unittest.TestCase):
11
- def test_server_module_loads(self) -> None:
12
- root = Path(__file__).resolve().parents[1]
13
- spec = importlib.util.spec_from_file_location("workframe_supervisor_server", root / "server.py")
14
- assert spec and spec.loader
15
- mod = importlib.util.module_from_spec(spec)
16
- spec.loader.exec_module(mod)
17
- self.assertTrue(mod._exec_targets_runtime_profile_secrets(["cat", "profiles/u-x/.env"]))
18
-
19
-
20
- if __name__ == "__main__":
21
- unittest.main()
@@ -1 +0,0 @@
1
- import"./chunk-NNHCCRGN-DlpIbxXb.js";import{x as e}from"./mermaid-parser.core-Bkimsnqj.js";export{e as createArchitectureServices};
@@ -1 +0,0 @@
1
- import{at as e,it as t}from"./chunk-CSCIHK7Q-kuqN8EIY.js";var n=(n,r)=>e.lang.round(t.parse(n)[r]);export{n as t};
@@ -1 +0,0 @@
1
- import{h as e}from"./src-B_od6b6h.js";var t=class{constructor(e){this.init=e,this.records=this.init()}static{e(this,`ImperativeState`)}reset(){this.records=this.init()}};export{t};
@@ -1 +0,0 @@
1
- import{h as e,p as t}from"./src-B_od6b6h.js";import{x as n}from"./chunk-CSCIHK7Q-kuqN8EIY.js";var r=e(e=>{let{securityLevel:r}=n(),i=t(`body`);return r===`sandbox`&&(i=t((t(`#i${e}`).node()?.contentDocument??document).body)),i.select(`#${e}`)},`selectSvgElement`);export{r as t};
@@ -1 +0,0 @@
1
- import{h as e}from"./src-B_od6b6h.js";import"./chunk-CSCIHK7Q-kuqN8EIY.js";import"./chunk-5ZQYHXKU-DOesfiCI.js";import"./chunk-O5CBEL6O-ClHc56ib.js";import"./chunk-FMBD7UC4-DyPgYHCg.js";import"./chunk-BSJP7CBP-a0cMNFb2.js";import"./chunk-L5ZTLDWV-Dq9NoWmK.js";import"./chunk-ND2GUHAM-DBD2u1Gz.js";import"./chunk-55IACEB6-C-mLFr7z.js";import"./chunk-2J33WTMH-w7uu7R-b.js";import"./chunk-NZK2D7GU-BeIeYFnd.js";import"./chunk-3OPIFGDE-Cb9LtnDX.js";import"./chunk-KSCS5N6A-CdUuvR0V.js";import"./chunk-LZXEDZCA-p74rddlO.js";import{i as t,n,r,t as i}from"./chunk-727SXJPM-BJ3oBZuz.js";var a={parser:n,get db(){return new i},renderer:r,styles:t,init:e(e=>{e.class||={},e.class.arrowMarkerAbsolute=e.arrowMarkerAbsolute},`init`)};export{a as diagram};