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,77 +0,0 @@
1
- import unittest
2
-
3
- import server
4
-
5
-
6
- class AvatarResolveTests(unittest.TestCase):
7
- def test_avatar_url_for_id_uses_catalog_file(self) -> None:
8
- url = server._avatar_url_for_id("ada")
9
- self.assertTrue(url.endswith("/ada.png"))
10
-
11
- def test_resolve_avatar_fields_from_id(self) -> None:
12
- row = {"avatar_id": "grace"}
13
- server._resolve_avatar_fields(row)
14
- self.assertIn("avatar_url", row)
15
- self.assertTrue(str(row["avatar_url"]).endswith("/grace.png"))
16
-
17
- def test_avatar_id_from_catalog_url(self) -> None:
18
- catalog_id = str((server._load_avatar_catalog().get("avatars") or [{}])[0].get("id") or "")
19
- self.assertTrue(catalog_id)
20
- url = server._avatar_url_for_id(catalog_id)
21
- self.assertEqual(server._avatar_id_from_url(url), catalog_id)
22
-
23
- def test_avatar_id_from_custom_url_is_empty(self) -> None:
24
- self.assertEqual(server._avatar_id_from_url("/files/uploads/my-face.png"), "")
25
-
26
- def test_explicit_url_survives_resolve_after_reconcile(self) -> None:
27
- # Reconciled catalog pick: id matches url, resolver reproduces the chosen url.
28
- catalog_id = str((server._load_avatar_catalog().get("avatars") or [{}])[0].get("id") or "")
29
- chosen = server._avatar_url_for_id(catalog_id)
30
- row = {"avatar_url": chosen, "avatar_id": server._avatar_id_from_url(chosen)}
31
- server._resolve_avatar_fields(row)
32
- self.assertEqual(row["avatar_url"], chosen)
33
-
34
- def test_custom_url_survives_resolve_when_id_cleared(self) -> None:
35
- row = {"avatar_url": "/files/uploads/my-face.png", "avatar_id": ""}
36
- server._resolve_avatar_fields(row)
37
- self.assertEqual(row["avatar_url"], "/files/uploads/my-face.png")
38
-
39
- def test_avatar_id_from_vite_hashed_url(self) -> None:
40
- self.assertEqual(server._avatar_id_from_url("/assets/frida-DAkwYbrd.png"), "frida")
41
- self.assertEqual(
42
- server._avatar_id_from_url("http://127.0.0.1:18644/assets/steve-CvzbBEg_.png"),
43
- "steve",
44
- )
45
-
46
- def test_normalize_agent_avatar_patch_stable(self) -> None:
47
- out = server._normalize_agent_avatar_patch("/assets/frida-DAkwYbrd.png", "")
48
- self.assertEqual(out["avatar_id"], "frida")
49
- self.assertTrue(out["avatar_url"].endswith("/frida.png"))
50
-
51
- def test_native_assign_uses_steve(self) -> None:
52
- native = str(server.NATIVE_PROFILE or "workframe-agent")
53
- picked = server._assign_agent_avatar(native)
54
- self.assertEqual(picked["avatar_id"], "steve")
55
- self.assertTrue(picked["avatar_url"].endswith("/steve.png"))
56
-
57
- def test_normalize_user_avatar_vite_hash(self) -> None:
58
- out = server._normalize_user_avatar_url("/assets/grace-CnKLjc4q.png")
59
- self.assertTrue(out.endswith("/grace.png"))
60
-
61
- def test_normalize_logo_vite_hash(self) -> None:
62
- out = server._normalize_logo_url("/assets/4-DGVBPxhf.png")
63
- self.assertTrue(out.endswith("/4.png"))
64
-
65
- def test_pick_logo_url_from_catalog(self) -> None:
66
- url = server._pick_logo_url()
67
- self.assertTrue(url.startswith("/assets/project-logos/"))
68
- self.assertTrue(url.endswith(".png"))
69
-
70
- def test_pick_user_avatar_url_from_catalog(self) -> None:
71
- url = server._pick_user_avatar_url()
72
- self.assertTrue(url.startswith("/assets/avatars/"))
73
- self.assertTrue(url.endswith(".png"))
74
-
75
-
76
- if __name__ == "__main__":
77
- unittest.main()
@@ -1,71 +0,0 @@
1
- """Child agent SOUL templates and native overlay seeding."""
2
- import tempfile
3
- import unittest
4
- from pathlib import Path
5
- from unittest import mock
6
-
7
- import server
8
-
9
-
10
- class ChildSoulTemplateTests(unittest.TestCase):
11
- def test_format_child_template_expands_native_agent_name(self) -> None:
12
- body = server._format_child_template(
13
- server._CHILD_SOUL_TEMPLATE,
14
- display_name="Elvis",
15
- role="Rock legend",
16
- )
17
- self.assertNotIn("{nativeAgentName}", body)
18
- self.assertIn("Workframe Agent", body)
19
- self.assertIn("Elvis", body)
20
-
21
- def test_install_child_base_artifacts_no_placeholder_error(self) -> None:
22
- with tempfile.TemporaryDirectory() as tmp:
23
- root = Path(tmp)
24
- prof = "elvis"
25
- prof_dir = root / "profiles" / prof
26
- prof_dir.mkdir(parents=True)
27
- (prof_dir / "SOUL.md").write_text("test", encoding="utf-8")
28
- with mock.patch.object(server, "_profile_dir", side_effect=lambda p: root / "profiles" / p), mock.patch.object(
29
- server, "PROJECT_NAME", "Workframe"
30
- ), mock.patch.object(server, "_is_native_profile", return_value=False):
31
- ok = server._install_child_base_artifacts(prof, display_name="Elvis", role="Rock legend")
32
- self.assertTrue(ok)
33
- agents = (prof_dir / "AGENTS.md").read_text(encoding="utf-8")
34
- self.assertNotIn("{nativeAgentName}", agents)
35
-
36
- def test_seed_native_user_overlay_keeps_base_soul(self) -> None:
37
- with tempfile.TemporaryDirectory() as tmp:
38
- root = Path(tmp)
39
- template = root / "profiles" / "workframe-agent"
40
- runtime = root / "profiles" / "u-user-workframe-agent"
41
- template.mkdir(parents=True)
42
- runtime.mkdir(parents=True)
43
- (template / "SOUL.md").write_text(
44
- "# Workframe Agent\n\nBase native system prompt.\n" + ("operate. " * 20),
45
- encoding="utf-8",
46
- )
47
- (runtime / "SOUL.md").write_text("test", encoding="utf-8")
48
- agents = root / "workframe" / "agents.json"
49
- agents.parent.mkdir(parents=True)
50
- agents.write_text('{"version":1,"agents":{}}', encoding="utf-8")
51
- with mock.patch.object(server, "_profile_dir", side_effect=lambda p: root / "profiles" / p), mock.patch.object(
52
- server, "AGENTS_JSON", agents
53
- ), mock.patch.object(server, "_is_runtime_profile_slug", return_value=True), mock.patch.object(
54
- server, "_runtime_template_slug", return_value="workframe-agent"
55
- ):
56
- server._seed_native_user_overlay(
57
- "u-user-workframe-agent",
58
- "workframe-agent",
59
- display_name="Andy",
60
- user_soul="Always be cheerful.",
61
- )
62
- base = (runtime / "SOUL.md").read_text(encoding="utf-8")
63
- merged = server._profile_soul_text("u-user-workframe-agent")
64
- self.assertIn("Base native system prompt", base)
65
- self.assertIn("Base native system prompt", merged)
66
- self.assertIn("## Additional instructions", merged)
67
- self.assertIn("Always be cheerful.", merged)
68
-
69
-
70
- if __name__ == "__main__":
71
- unittest.main()
@@ -1,135 +0,0 @@
1
- """Canary / red-team tests: cross-profile reads, exec guards, public tool gating."""
2
-
3
- from __future__ import annotations
4
-
5
- import tempfile
6
- import unittest
7
- from pathlib import Path
8
- from unittest.mock import patch
9
-
10
- import server
11
- from db_setup import ensure_workframe_schemas
12
-
13
-
14
- class CredentialCanaryTests(unittest.TestCase):
15
- def setUp(self) -> None:
16
- self._tmp = tempfile.TemporaryDirectory()
17
- self.addCleanup(self._tmp.cleanup)
18
- self._old_data_dir = server.DATA_DIR
19
- self._old_auth_db_path = server.AUTH_DB_PATH
20
- self._old_hermes_data = server.HERMES_DATA
21
- server.DATA_DIR = Path(self._tmp.name)
22
- server.AUTH_DB_PATH = Path(self._tmp.name) / "auth.db"
23
- server.HERMES_DATA = Path(self._tmp.name) / "hermes"
24
- (server.HERMES_DATA / "profiles").mkdir(parents=True)
25
- ensure_workframe_schemas()
26
- self.workspace_id = "ws-canary"
27
- self.user_a = "cb6a2db4-ac86-4c49-8247-14a1d68aca72"
28
- self.user_b = "44fb344c-0954-47b6-a19a-ebbcf20e9680"
29
- conn = server._workframe_db()
30
- try:
31
- now = "1"
32
- conn.execute(
33
- "INSERT INTO workspaces (id, slug, display_name, owner_id, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?)",
34
- (self.workspace_id, "canary", "Canary", self.user_a, "active", now, now),
35
- )
36
- for uid, name, role in (
37
- (self.user_a, "Alice", "owner"),
38
- (self.user_b, "Bob", "member"),
39
- ):
40
- conn.execute(
41
- "INSERT INTO users (id, display_name, role, status, created_at, updated_at) VALUES (?,?,?,?,?,?)",
42
- (uid, name, "member", "active", now, now),
43
- )
44
- conn.execute(
45
- """
46
- INSERT INTO workspace_memberships (id, workspace_id, user_id, role, status, created_at, updated_at)
47
- VALUES (?, ?, ?, ?, 'active', ?, ?)
48
- """,
49
- (f"wm-{uid}", self.workspace_id, uid, role, now, now),
50
- )
51
- conn.execute(
52
- """
53
- INSERT INTO agent_profiles (id, workspace_id, slug, display_name, is_native, status, created_at, updated_at)
54
- VALUES (?, ?, ?, ?, ?, 'available', ?, ?)
55
- """,
56
- ("ap-architect", self.workspace_id, "architect", "Architect", 0, now, now),
57
- )
58
- conn.commit()
59
- finally:
60
- conn.close()
61
- self.runtime_a = server._runtime_profile_slug(self.user_a, "architect")
62
- self.runtime_b = server._runtime_profile_slug(self.user_b, "architect")
63
-
64
- def tearDown(self) -> None:
65
- server.DATA_DIR = self._old_data_dir
66
- server.AUTH_DB_PATH = self._old_auth_db_path
67
- server.HERMES_DATA = self._old_hermes_data
68
-
69
- def test_member_cannot_access_peer_runtime_profile_public_mode(self) -> None:
70
- with patch.object(server, "DEPLOYMENT_MODE", "public_multi_user"):
71
- self.assertFalse(
72
- server._user_may_access_runtime_profile(self.user_b, self.runtime_a, self.workspace_id)
73
- )
74
- self.assertTrue(
75
- server._user_may_access_runtime_profile(self.user_a, self.runtime_a, self.workspace_id)
76
- )
77
-
78
- def test_owner_can_access_peer_runtime_in_trusted_team(self) -> None:
79
- with patch.object(server, "DEPLOYMENT_MODE", "trusted_team"):
80
- self.assertTrue(
81
- server._user_may_access_runtime_profile(self.user_a, self.runtime_b, self.workspace_id)
82
- )
83
- self.assertFalse(
84
- server._user_may_access_runtime_profile(self.user_b, self.runtime_a, self.workspace_id)
85
- )
86
-
87
- def test_gateway_exec_blocks_cross_profile_env_cat(self) -> None:
88
- victim_env = f"/opt/data/profiles/{self.runtime_a}/.env"
89
- with patch.object(server, "DEPLOYMENT_MODE", "trusted_team"):
90
- self.assertTrue(
91
- server._exec_targets_runtime_profile_secrets(["sh", "-lc", f"cat {victim_env}"])
92
- )
93
-
94
- def test_gateway_exec_denies_foreign_runtime_profile(self) -> None:
95
- with patch.object(server, "DEPLOYMENT_MODE", "public_multi_user"), patch.object(
96
- server, "resolve_validated_profile", return_value=self.runtime_a
97
- ), patch.object(server, "_gateway_exec") as gateway_exec:
98
- out = server.hermes_gateway_exec(
99
- "/help",
100
- self.runtime_a,
101
- user_id=self.user_b,
102
- workspace_id=self.workspace_id,
103
- )
104
- gateway_exec.assert_not_called()
105
- self.assertFalse(out.get("ok"))
106
- self.assertEqual(out.get("error"), "profile_access_denied")
107
-
108
- def test_docker_exec_guard_returns_blocked_without_running(self) -> None:
109
- victim_env = f"/opt/data/profiles/{self.runtime_b}/.env"
110
- with patch.object(server, "DEPLOYMENT_MODE", "trusted_team"), patch.object(
111
- server, "_docker_request"
112
- ) as docker_request:
113
- code, out = server._docker_exec("workframe-gateway", ["cat", victim_env])
114
- docker_request.assert_not_called()
115
- self.assertEqual(code, 1)
116
- self.assertIn("blocked", out)
117
-
118
- def test_public_mode_toolsets_include_terminal(self) -> None:
119
- with patch.object(server, "DEPLOYMENT_MODE", "public_multi_user"):
120
- self.assertEqual(server._chat_toolsets_for_profile("architect"), ("hermes-cli", "terminal"))
121
-
122
- def test_public_mode_ensure_toolsets_enables_terminal(self) -> None:
123
- prof_dir = server._profile_dir("architect")
124
- prof_dir.mkdir(parents=True, exist_ok=True)
125
- cfg_path = prof_dir / "config.yaml"
126
- cfg_path.write_text("toolsets: []\n", encoding="utf-8")
127
- with patch.object(server, "DEPLOYMENT_MODE", "public_multi_user"):
128
- server._ensure_profile_toolsets("architect")
129
- text = cfg_path.read_text(encoding="utf-8")
130
- self.assertIn("hermes-cli", text)
131
- self.assertIn("terminal", text)
132
-
133
-
134
- if __name__ == "__main__":
135
- unittest.main()