create-workframe 0.1.0 → 0.1.2

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 (198) hide show
  1. package/LICENSE +201 -201
  2. package/NOTICE +12 -12
  3. package/README.md +8 -92
  4. package/SECURITY.md +38 -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/bundle-workframe-ui.mjs +3 -3
  28. package/scripts/ensure-compose-host-paths.mjs +51 -51
  29. package/scripts/lib/install-identity.mjs +212 -212
  30. package/scripts/set-compose-public-url.mjs +92 -92
  31. package/scripts/sync-canonical-to-package.mjs +27 -9
  32. package/shared/WORKFRAME_AGENT_LIBRARY.md +17 -17
  33. package/shared/WORKFRAME_AGENT_OPERATIONS.md +15 -15
  34. package/shared/WORKFRAME_AGENT_PACKS.json +18 -18
  35. package/shared/WORKFRAME_AGENT_PACKS.yaml +8 -8
  36. package/shared/WORKFRAME_SKILL_CURATION.md +4 -4
  37. package/workframe-api/README.md +26 -28
  38. package/workframe-api/action_proxy.py +131 -131
  39. package/workframe-api/auth_rate_limit.py +49 -49
  40. package/workframe-api/credential_vault.py +445 -445
  41. package/workframe-api/data/avatar-catalog.json +41 -41
  42. package/workframe-api/email_sender.py +220 -220
  43. package/workframe-api/google_auth.py +90 -90
  44. package/workframe-api/install_api.py +359 -359
  45. package/workframe-api/internal_proxy_auth.py +150 -150
  46. package/workframe-api/llm_proxy.py +277 -277
  47. package/workframe-api/oidc_jwt.py +108 -108
  48. package/workframe-api/package.json +12 -13
  49. package/workframe-api/public/assets/index-DPXu_lGn.css +1 -1
  50. package/workframe-api/public/assets/index-DYnLrCZZ.js +8 -8
  51. package/workframe-api/requirements.txt +2 -2
  52. package/workframe-api/site_meta.py +271 -271
  53. package/workframe-api/stack_config.py +427 -427
  54. package/workframe-api/time-bind-chat.py +99 -99
  55. package/workframe-api/turn_credentials.py +226 -226
  56. package/workframe-api/updates.py +417 -417
  57. package/workframe-api/vault_kek.py +159 -159
  58. package/workframe-api/zk_auth.py +633 -633
  59. package/workframe-supervisor/Dockerfile +11 -11
  60. package/workframe-supervisor/server.py +787 -787
  61. package/workframe-ui/docker/nginx.conf +85 -85
  62. package/workframe-ui/public/assets/{arc-CBDYvkAF.js → arc-COAT3laO.js} +1 -1
  63. package/workframe-ui/public/assets/architecture-7EHR7CIX-DUyH3hWG.js +1 -0
  64. package/workframe-ui/public/assets/{architectureDiagram-3BPJPVTR-XnBRKeW0.js → architectureDiagram-3BPJPVTR-BFjWV24l.js} +1 -1
  65. package/workframe-ui/public/assets/{blockDiagram-GPEHLZMM-VYHUfVhd.js → blockDiagram-GPEHLZMM-DSQLPfrj.js} +1 -1
  66. package/workframe-ui/public/assets/{c4Diagram-AAUBKEIU-BTjUcJpm.js → c4Diagram-AAUBKEIU-DKEHv1t2.js} +1 -1
  67. package/workframe-ui/public/assets/channel-g7r_RGaY.js +1 -0
  68. package/workframe-ui/public/assets/{chunk-2J33WTMH-w7uu7R-b.js → chunk-2J33WTMH-DHZg-DUi.js} +1 -1
  69. package/workframe-ui/public/assets/{chunk-3OPIFGDE-Cb9LtnDX.js → chunk-3OPIFGDE-BB-OYTfp.js} +1 -1
  70. package/workframe-ui/public/assets/{chunk-4BX2VUAB-DiQ-qCwH.js → chunk-4BX2VUAB-C93q0YIm.js} +1 -1
  71. package/workframe-ui/public/assets/{chunk-55IACEB6-C-mLFr7z.js → chunk-55IACEB6-MAYniqik.js} +1 -1
  72. package/workframe-ui/public/assets/{chunk-5ZQYHXKU-DOesfiCI.js → chunk-5ZQYHXKU-ChgN6YJs.js} +1 -1
  73. package/workframe-ui/public/assets/{chunk-727SXJPM-BJ3oBZuz.js → chunk-727SXJPM-B_FYwdAv.js} +1 -1
  74. package/workframe-ui/public/assets/{chunk-AQP2D5EJ-CCA6xpGs.js → chunk-AQP2D5EJ-1_Hw_h1A.js} +1 -1
  75. package/workframe-ui/public/assets/{chunk-BSJP7CBP-a0cMNFb2.js → chunk-BSJP7CBP-CFiDQ1Rv.js} +1 -1
  76. package/workframe-ui/public/assets/{chunk-CSCIHK7Q-kuqN8EIY.js → chunk-CSCIHK7Q-DZ9UMTlB.js} +1 -1
  77. package/workframe-ui/public/assets/{chunk-FMBD7UC4-DyPgYHCg.js → chunk-FMBD7UC4-DlMlyFgw.js} +1 -1
  78. package/workframe-ui/public/assets/{chunk-KSCS5N6A-CdUuvR0V.js → chunk-KSCS5N6A-DHXtQ_Hf.js} +1 -1
  79. package/workframe-ui/public/assets/{chunk-L5ZTLDWV-Dq9NoWmK.js → chunk-L5ZTLDWV-CuQzg-QG.js} +1 -1
  80. package/workframe-ui/public/assets/{chunk-LZXEDZCA-p74rddlO.js → chunk-LZXEDZCA-BHzjzCGg.js} +2 -2
  81. package/workframe-ui/public/assets/{chunk-ND2GUHAM-DBD2u1Gz.js → chunk-ND2GUHAM-DHXx05n2.js} +1 -1
  82. package/workframe-ui/public/assets/{chunk-NZK2D7GU-BeIeYFnd.js → chunk-NZK2D7GU-CV5pmDM_.js} +1 -1
  83. package/workframe-ui/public/assets/{chunk-O5CBEL6O-ClHc56ib.js → chunk-O5CBEL6O-6tkCHxsV.js} +1 -1
  84. package/workframe-ui/public/assets/chunk-QZHKN3VN-C5UQehWY.js +1 -0
  85. package/workframe-ui/public/assets/chunk-WU5MYG2G-DhWllrI8.js +1 -0
  86. package/workframe-ui/public/assets/{chunk-XPW4576I-EFr8R_1p.js → chunk-XPW4576I-BClwIiCp.js} +1 -1
  87. package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BBM_8T8E.js +1 -0
  88. package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BBM_8T8E.js +1 -0
  89. package/workframe-ui/public/assets/{cose-bilkent-S5V4N54A-C7aPBODd.js → cose-bilkent-S5V4N54A-DOrGV6DQ.js} +1 -1
  90. package/workframe-ui/public/assets/{dagre-BM42HDAG-BdU1Rv-H.js → dagre-BM42HDAG-DXTPvJkX.js} +1 -1
  91. package/workframe-ui/public/assets/{diagram-2AECGRRQ-DWowSo85.js → diagram-2AECGRRQ-xX_v-pbf.js} +1 -1
  92. package/workframe-ui/public/assets/{diagram-5GNKFQAL-MnxBbceO.js → diagram-5GNKFQAL-Cd2pXbBe.js} +1 -1
  93. package/workframe-ui/public/assets/{diagram-KO2AKTUF-DQaLRXFf.js → diagram-KO2AKTUF-Df3XvUtk.js} +1 -1
  94. package/workframe-ui/public/assets/{diagram-LMA3HP47-CQaBud9k.js → diagram-LMA3HP47-CsijIPaD.js} +1 -1
  95. package/workframe-ui/public/assets/{diagram-OG6HWLK6-D8bAXbY9.js → diagram-OG6HWLK6-aq5fmfHd.js} +1 -1
  96. package/workframe-ui/public/assets/{dist-DGpTLHr_.js → dist-D1c0mkbB.js} +1 -1
  97. package/workframe-ui/public/assets/{erDiagram-TEJ5UH35-1E-xSvBK.js → erDiagram-TEJ5UH35-DnFysVRY.js} +1 -1
  98. package/workframe-ui/public/assets/eventmodeling-FCH6USID-Ci8mdb44.js +1 -0
  99. package/workframe-ui/public/assets/{flowDiagram-I6XJVG4X-CgOVD5hu.js → flowDiagram-I6XJVG4X-C6Ebi3su.js} +1 -1
  100. package/workframe-ui/public/assets/{ganttDiagram-6RSMTGT7-JFYAIauo.js → ganttDiagram-6RSMTGT7-BQXQtUpa.js} +1 -1
  101. package/workframe-ui/public/assets/{gitGraph-WXDBUCRP-B9REenIl.js → gitGraph-WXDBUCRP-Dt0zIs_M.js} +1 -1
  102. package/workframe-ui/public/assets/{gitGraphDiagram-PVQCEYII-BQ7NcMSn.js → gitGraphDiagram-PVQCEYII-BF8gHzRn.js} +1 -1
  103. package/workframe-ui/public/assets/index-DpoUZAxh.css +1 -0
  104. package/workframe-ui/public/assets/{index-Dnw6vjqb.js → index-lRpzpNPT.js} +2 -2
  105. package/workframe-ui/public/assets/{info-J43DQDTF-CL6-eTjH.js → info-J43DQDTF-CSmszQJT.js} +1 -1
  106. package/workframe-ui/public/assets/{infoDiagram-5YYISTIA-LJTODW4W.js → infoDiagram-5YYISTIA-CVTKGW6p.js} +1 -1
  107. package/workframe-ui/public/assets/{ishikawaDiagram-YF4QCWOH-bchrQVuo.js → ishikawaDiagram-YF4QCWOH-Z8pT09Lv.js} +1 -1
  108. package/workframe-ui/public/assets/{journeyDiagram-JHISSGLW-DkrvYuxP.js → journeyDiagram-JHISSGLW-r3wD68_T.js} +1 -1
  109. package/workframe-ui/public/assets/{kanban-definition-UN3LZRKU-DFRbj0IG.js → kanban-definition-UN3LZRKU-Il8VglqN.js} +1 -1
  110. package/workframe-ui/public/assets/{line-Vd48P7-O.js → line-oyjpfz2A.js} +1 -1
  111. package/workframe-ui/public/assets/{linear-Ckizh2G7.js → linear-Cf7p5tVp.js} +1 -1
  112. package/workframe-ui/public/assets/{mermaid-parser.core-Bkimsnqj.js → mermaid-parser.core-YmbZ-AfY.js} +2 -2
  113. package/workframe-ui/public/assets/{mermaid.core-x0TvVuPo.js → mermaid.core-BFdCAqCo.js} +3 -3
  114. package/workframe-ui/public/assets/{mindmap-definition-RKZ34NQL-6ykAFPEz.js → mindmap-definition-RKZ34NQL-Cy2iCtEl.js} +1 -1
  115. package/workframe-ui/public/assets/{packet-YPE3B663-Dw3xgMDt.js → packet-YPE3B663-DwOBZL6K.js} +1 -1
  116. package/workframe-ui/public/assets/{pie-LRSECV5Y-DATysawG.js → pie-LRSECV5Y-04PPhnKK.js} +1 -1
  117. package/workframe-ui/public/assets/{pieDiagram-4H26LBE5-SJKD1S0S.js → pieDiagram-4H26LBE5-LxIpgHqi.js} +1 -1
  118. package/workframe-ui/public/assets/{quadrantDiagram-W4KKPZXB-BrYDZX8q.js → quadrantDiagram-W4KKPZXB-0nBYfYm4.js} +1 -1
  119. package/workframe-ui/public/assets/{radar-GUYGQ44K-BmWYPCds.js → radar-GUYGQ44K-D2-vBqps.js} +1 -1
  120. package/workframe-ui/public/assets/{requirementDiagram-4Y6WPE33-DwL9Mc8e.js → requirementDiagram-4Y6WPE33-DbuU0nlu.js} +1 -1
  121. package/workframe-ui/public/assets/{sankeyDiagram-5OEKKPKP-DYIFsL8h.js → sankeyDiagram-5OEKKPKP-B2hQ6B2x.js} +1 -1
  122. package/workframe-ui/public/assets/{sequenceDiagram-3UESZ5HK-0-FPkFk8.js → sequenceDiagram-3UESZ5HK-BBrU30e1.js} +1 -1
  123. package/workframe-ui/public/assets/{src-B_od6b6h.js → src-BJEDmV70.js} +1 -1
  124. package/workframe-ui/public/assets/{stateDiagram-AJRCARHV-BQCiBk6u.js → stateDiagram-AJRCARHV-7FGO4kkH.js} +1 -1
  125. package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-DLTSizMg.js +1 -0
  126. package/workframe-ui/public/assets/{timeline-definition-PNZ67QCA-DS3tFcXj.js → timeline-definition-PNZ67QCA-ptDm4rCN.js} +1 -1
  127. package/workframe-ui/public/assets/{treeView-BLDUP644-DSyUCKLY.js → treeView-BLDUP644-CS6Z-0q8.js} +1 -1
  128. package/workframe-ui/public/assets/{treemap-LRROVOQU-CEZaNh5Y.js → treemap-LRROVOQU-DqV4Y2VA.js} +1 -1
  129. package/workframe-ui/public/assets/{vennDiagram-CIIHVFJN-CD-Vc9NF.js → vennDiagram-CIIHVFJN-C0UrZJYt.js} +1 -1
  130. package/workframe-ui/public/assets/{wardley-L42UT6IY-Drq5w1Mc.js → wardley-L42UT6IY-bNDN3_Sa.js} +1 -1
  131. package/workframe-ui/public/assets/{wardleyDiagram-YWT4CUSO-DouXDJoF.js → wardleyDiagram-YWT4CUSO-jWiJsefM.js} +1 -1
  132. package/workframe-ui/public/assets/{xychartDiagram-2RQKCTM6-DDf_Lol5.js → xychartDiagram-2RQKCTM6-Dsh_fLCy.js} +1 -1
  133. package/workframe-ui/public/favicon.svg +7 -7
  134. package/workframe-ui/public/index.html +50 -50
  135. package/workframe-ui/public/workframe-config.json +3 -3
  136. package/scripts/security_audit.py +0 -156
  137. package/scripts/test-scaffold.mjs +0 -390
  138. package/workframe-api/tests/__init__.py +0 -0
  139. package/workframe-api/tests/db_setup.py +0 -13
  140. package/workframe-api/tests/test_admin_updates_gated.py +0 -30
  141. package/workframe-api/tests/test_agent_dm_bootstrap.py +0 -196
  142. package/workframe-api/tests/test_agent_profile_sync.py +0 -76
  143. package/workframe-api/tests/test_auth_email.py +0 -222
  144. package/workframe-api/tests/test_auth_hole_fix_selfcheck.py +0 -99
  145. package/workframe-api/tests/test_auth_rate_limit.py +0 -19
  146. package/workframe-api/tests/test_avatar_resolve.py +0 -77
  147. package/workframe-api/tests/test_child_soul_template.py +0 -71
  148. package/workframe-api/tests/test_credential_canary.py +0 -135
  149. package/workframe-api/tests/test_credential_isolation.py +0 -448
  150. package/workframe-api/tests/test_credential_resolution.py +0 -206
  151. package/workframe-api/tests/test_device_oauth.py +0 -108
  152. package/workframe-api/tests/test_doctor_repair.py +0 -103
  153. package/workframe-api/tests/test_ensure_profile_api.py +0 -77
  154. package/workframe-api/tests/test_gateway_compose_security.py +0 -136
  155. package/workframe-api/tests/test_install_secure_host.py +0 -39
  156. package/workframe-api/tests/test_internal_proxy_auth.py +0 -125
  157. package/workframe-api/tests/test_invite_runtime_bootstrap.py +0 -72
  158. package/workframe-api/tests/test_kanban_delegation.py +0 -185
  159. package/workframe-api/tests/test_llm_proxy.py +0 -155
  160. package/workframe-api/tests/test_login_access_policy.py +0 -183
  161. package/workframe-api/tests/test_mvp_model_bootstrap.py +0 -75
  162. package/workframe-api/tests/test_onboarding_bootstrap.py +0 -248
  163. package/workframe-api/tests/test_platform_auth.py +0 -47
  164. package/workframe-api/tests/test_profile_config_path.py +0 -56
  165. package/workframe-api/tests/test_profile_config_yaml_repair.py +0 -63
  166. package/workframe-api/tests/test_profile_create.py +0 -72
  167. package/workframe-api/tests/test_profile_identity_overlay.py +0 -61
  168. package/workframe-api/tests/test_profile_install_health.py +0 -45
  169. package/workframe-api/tests/test_profile_secret_policy.py +0 -57
  170. package/workframe-api/tests/test_profile_workspace_cwd.py +0 -34
  171. package/workframe-api/tests/test_provider_bootstrap.py +0 -75
  172. package/workframe-api/tests/test_provider_connect.py +0 -54
  173. package/workframe-api/tests/test_room_crud.py +0 -192
  174. package/workframe-api/tests/test_room_tenancy.py +0 -701
  175. package/workframe-api/tests/test_runtime_identity_backfill.py +0 -34
  176. package/workframe-api/tests/test_site_meta.py +0 -81
  177. package/workframe-api/tests/test_soul_stub.py +0 -42
  178. package/workframe-api/tests/test_space_member_sync.py +0 -99
  179. package/workframe-api/tests/test_stripe_stack_config.py +0 -37
  180. package/workframe-api/tests/test_supervisor_lifecycle.py +0 -52
  181. package/workframe-api/tests/test_turn_credential_vault.py +0 -125
  182. package/workframe-api/tests/test_updates.py +0 -176
  183. package/workframe-api/tests/test_user_cohort.py +0 -113
  184. package/workframe-api/tests/test_vault_envelope.py +0 -110
  185. package/workframe-api/tests/test_workspace_members.py +0 -183
  186. package/workframe-api/tests/test_workspace_messaging_sync.py +0 -125
  187. package/workframe-api/tests/test_workspace_provider_list.py +0 -57
  188. package/workframe-supervisor/tests/test_exec_guard.py +0 -42
  189. package/workframe-supervisor/tests/test_server_import.py +0 -21
  190. package/workframe-ui/public/assets/architecture-7EHR7CIX-CtbQKTuT.js +0 -1
  191. package/workframe-ui/public/assets/channel-Dy4Z4-jn.js +0 -1
  192. package/workframe-ui/public/assets/chunk-QZHKN3VN-CtBEchFK.js +0 -1
  193. package/workframe-ui/public/assets/chunk-WU5MYG2G-B9pBtriN.js +0 -1
  194. package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BMAEA8jI.js +0 -1
  195. package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BMAEA8jI.js +0 -1
  196. package/workframe-ui/public/assets/eventmodeling-FCH6USID-D75cstNT.js +0 -1
  197. package/workframe-ui/public/assets/index-DpAGxump.css +0 -1
  198. 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};