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,92 +1,92 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Set public URL keys in compose .env (APP_BASE_URL, WORKFRAME_PUBLIC_HOST, CORS, ALLOWED_HOSTS).
4
- * HERMES_DASHBOARD_PUBLIC_URL is derived from APP_BASE_URL in docker-compose.yml.
5
- * Usage: node set-compose-public-url.mjs https://dev.example.com [--env path/to/.env]
6
- */
7
- import fs from 'node:fs';
8
- import path from 'node:path';
9
- import { fileURLToPath } from 'node:url';
10
-
11
- const args = process.argv.slice(2);
12
-
13
- if (args.includes('--self-check')) {
14
- function normalizePublicUrl(raw) {
15
- let u = String(raw || '').trim();
16
- if (!u) throw new Error('url required');
17
- if (!/^https?:\/\//i.test(u)) u = `https://${u}`;
18
- const parsed = new URL(u);
19
- if (!parsed.hostname) throw new Error('invalid hostname');
20
- return `https://${parsed.hostname}`;
21
- }
22
- if (normalizePublicUrl('dev.example.com') !== 'https://dev.example.com') {
23
- throw new Error('normalizePublicUrl failed');
24
- }
25
- console.log('self-check ok');
26
- process.exit(0);
27
- }
28
-
29
- const envFlag = args.indexOf('--env');
30
- let envPath =
31
- envFlag >= 0 ? args[envFlag + 1] : path.join(path.dirname(fileURLToPath(import.meta.url)), '../../infra/compose/workframe/.env');
32
- const urlArg = args.find((a) => !a.startsWith('--') && a !== envPath);
33
-
34
- if (!urlArg?.trim()) {
35
- console.error('Usage: node set-compose-public-url.mjs <https://host> [--env path/to/.env]');
36
- process.exit(1);
37
- }
38
-
39
- function normalizePublicUrl(raw) {
40
- let u = String(raw || '').trim();
41
- if (!u) throw new Error('url required');
42
- if (!/^https?:\/\//i.test(u)) u = `https://${u}`;
43
- const parsed = new URL(u);
44
- if (!parsed.hostname) throw new Error('invalid hostname');
45
- return `https://${parsed.hostname}`;
46
- }
47
-
48
- function hostnameFromUrl(url) {
49
- return new URL(url).hostname;
50
- }
51
-
52
- function setKv(text, key, val) {
53
- const line = `${key}=${val}`;
54
- const re = new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}=.*$`, 'm');
55
- if (re.test(text)) return text.replace(re, line);
56
- return `${text}${text.endsWith('\n') || !text ? '' : '\n'}${line}\n`;
57
- }
58
-
59
- const publicUrl = normalizePublicUrl(urlArg);
60
- const host = hostnameFromUrl(publicUrl);
61
-
62
- if (!fs.existsSync(envPath)) {
63
- const example = `${envPath}.example`;
64
- if (fs.existsSync(example)) {
65
- fs.mkdirSync(path.dirname(envPath), { recursive: true });
66
- fs.copyFileSync(example, envPath);
67
- console.log(`Created ${envPath} from example`);
68
- } else {
69
- throw new Error(`Missing env file: ${envPath}`);
70
- }
71
- }
72
-
73
- let text = fs.readFileSync(envPath, 'utf8');
74
- text = setKv(text, 'APP_BASE_URL', publicUrl);
75
- text = setKv(text, 'WORKFRAME_PUBLIC_HOST', host);
76
- text = setKv(text, 'ALLOWED_HOSTS', host);
77
- text = setKv(text, 'CORS_ALLOW_ORIGIN', publicUrl);
78
- fs.writeFileSync(envPath, text);
79
-
80
- console.log(
81
- JSON.stringify(
82
- {
83
- ok: true,
84
- env: envPath,
85
- app_base_url: publicUrl,
86
- hermes_dashboard_public_url: `${publicUrl}/hermes-dashboard`,
87
- host,
88
- },
89
- null,
90
- 2,
91
- ),
92
- );
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Set public URL keys in compose .env (APP_BASE_URL, WORKFRAME_PUBLIC_HOST, CORS, ALLOWED_HOSTS).
4
+ * HERMES_DASHBOARD_PUBLIC_URL is derived from APP_BASE_URL in docker-compose.yml.
5
+ * Usage: node set-compose-public-url.mjs https://dev.example.com [--env path/to/.env]
6
+ */
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const args = process.argv.slice(2);
12
+
13
+ if (args.includes('--self-check')) {
14
+ function normalizePublicUrl(raw) {
15
+ let u = String(raw || '').trim();
16
+ if (!u) throw new Error('url required');
17
+ if (!/^https?:\/\//i.test(u)) u = `https://${u}`;
18
+ const parsed = new URL(u);
19
+ if (!parsed.hostname) throw new Error('invalid hostname');
20
+ return `https://${parsed.hostname}`;
21
+ }
22
+ if (normalizePublicUrl('dev.example.com') !== 'https://dev.example.com') {
23
+ throw new Error('normalizePublicUrl failed');
24
+ }
25
+ console.log('self-check ok');
26
+ process.exit(0);
27
+ }
28
+
29
+ const envFlag = args.indexOf('--env');
30
+ let envPath =
31
+ envFlag >= 0 ? args[envFlag + 1] : path.join(path.dirname(fileURLToPath(import.meta.url)), '../../infra/compose/workframe/.env');
32
+ const urlArg = args.find((a) => !a.startsWith('--') && a !== envPath);
33
+
34
+ if (!urlArg?.trim()) {
35
+ console.error('Usage: node set-compose-public-url.mjs <https://host> [--env path/to/.env]');
36
+ process.exit(1);
37
+ }
38
+
39
+ function normalizePublicUrl(raw) {
40
+ let u = String(raw || '').trim();
41
+ if (!u) throw new Error('url required');
42
+ if (!/^https?:\/\//i.test(u)) u = `https://${u}`;
43
+ const parsed = new URL(u);
44
+ if (!parsed.hostname) throw new Error('invalid hostname');
45
+ return `https://${parsed.hostname}`;
46
+ }
47
+
48
+ function hostnameFromUrl(url) {
49
+ return new URL(url).hostname;
50
+ }
51
+
52
+ function setKv(text, key, val) {
53
+ const line = `${key}=${val}`;
54
+ const re = new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}=.*$`, 'm');
55
+ if (re.test(text)) return text.replace(re, line);
56
+ return `${text}${text.endsWith('\n') || !text ? '' : '\n'}${line}\n`;
57
+ }
58
+
59
+ const publicUrl = normalizePublicUrl(urlArg);
60
+ const host = hostnameFromUrl(publicUrl);
61
+
62
+ if (!fs.existsSync(envPath)) {
63
+ const example = `${envPath}.example`;
64
+ if (fs.existsSync(example)) {
65
+ fs.mkdirSync(path.dirname(envPath), { recursive: true });
66
+ fs.copyFileSync(example, envPath);
67
+ console.log(`Created ${envPath} from example`);
68
+ } else {
69
+ throw new Error(`Missing env file: ${envPath}`);
70
+ }
71
+ }
72
+
73
+ let text = fs.readFileSync(envPath, 'utf8');
74
+ text = setKv(text, 'APP_BASE_URL', publicUrl);
75
+ text = setKv(text, 'WORKFRAME_PUBLIC_HOST', host);
76
+ text = setKv(text, 'ALLOWED_HOSTS', host);
77
+ text = setKv(text, 'CORS_ALLOW_ORIGIN', publicUrl);
78
+ fs.writeFileSync(envPath, text);
79
+
80
+ console.log(
81
+ JSON.stringify(
82
+ {
83
+ ok: true,
84
+ env: envPath,
85
+ app_base_url: publicUrl,
86
+ hermes_dashboard_public_url: `${publicUrl}/hermes-dashboard`,
87
+ host,
88
+ },
89
+ null,
90
+ 2,
91
+ ),
92
+ );
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Copy canonical Workframe BFF into create-workframe package tree before npm pack.
4
- * Run from ProjectX root: node packages/create-workframe/scripts/sync-canonical-to-package.mjs
4
+ * Run from repository root: node packages/create-workframe/scripts/sync-canonical-to-package.mjs
5
5
  */
6
6
  import fs from 'node:fs';
7
7
  import path from 'node:path';
@@ -9,14 +9,15 @@ import { fileURLToPath } from 'node:url';
9
9
 
10
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
11
  const PKG_ROOT = path.resolve(__dirname, '..');
12
- const PROJECTX_ROOT = path.resolve(PKG_ROOT, '../..');
13
- const CANONICAL_API = path.join(PROJECTX_ROOT, 'services/workframe-api');
12
+ const REPO_ROOT = path.resolve(PKG_ROOT, '../..');
13
+ const CANONICAL_API = path.join(REPO_ROOT, 'services/workframe-api');
14
14
  const PKG_API = path.join(PKG_ROOT, 'workframe-api');
15
- const CANONICAL_SUPERVISOR = path.join(PROJECTX_ROOT, 'services/workframe-supervisor');
15
+ const CANONICAL_SUPERVISOR = path.join(REPO_ROOT, 'services/workframe-supervisor');
16
16
  const PKG_SUPERVISOR = path.join(PKG_ROOT, 'workframe-supervisor');
17
17
 
18
18
  const SKIP_DIRS = new Set([
19
19
  'data',
20
+ 'tests',
20
21
  '__pycache__',
21
22
  '.pytest_cache',
22
23
  '.venv',
@@ -56,6 +57,17 @@ function removeIfExists(p) {
56
57
  if (fs.existsSync(p)) fs.rmSync(p, { recursive: true, force: true });
57
58
  }
58
59
 
60
+ /** Alpine sh breaks on CRLF (then\r). Normalize at pack time — Windows checkout is CRLF. */
61
+ function copyIntoPackage(src, dst) {
62
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
63
+ if (dst.endsWith('.sh')) {
64
+ const text = fs.readFileSync(src, 'utf8').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
65
+ fs.writeFileSync(dst, text, 'utf8');
66
+ } else {
67
+ fs.copyFileSync(src, dst);
68
+ }
69
+ }
70
+
59
71
  console.log(`Sync canonical BFF: ${CANONICAL_API} -> ${PKG_API}`);
60
72
  copyTree(CANONICAL_API, PKG_API);
61
73
 
@@ -103,15 +115,21 @@ const applyScripts = [
103
115
  'ensure-compose-host-paths.mjs',
104
116
  ];
105
117
  for (const name of applyScripts) {
106
- const src = path.join(PROJECTX_ROOT, 'scripts/workframe', name);
118
+ const src = path.join(REPO_ROOT, 'scripts/workframe', name);
107
119
  const dst = path.join(PKG_ROOT, 'scripts', name);
108
120
  if (!fs.existsSync(src)) throw new Error(`Missing apply script: ${src}`);
109
- fs.mkdirSync(path.dirname(dst), { recursive: true });
110
- fs.copyFileSync(src, dst);
121
+ copyIntoPackage(src, dst);
111
122
  console.log(`Synced ${name} -> package/scripts/`);
112
123
  }
113
124
 
114
- const publicDeploySrc = path.join(PROJECTX_ROOT, 'infra/compose/workframe/PUBLIC_DEPLOY.md');
125
+ for (const sh of fs.readdirSync(path.join(PKG_ROOT, 'scripts')).filter((n) => n.endsWith('.sh'))) {
126
+ const p = path.join(PKG_ROOT, 'scripts', sh);
127
+ if (fs.readFileSync(p).includes(0x0d)) {
128
+ throw new Error(`CRLF remains in package script after sync: ${sh}`);
129
+ }
130
+ }
131
+
132
+ const publicDeploySrc = path.join(REPO_ROOT, 'infra/compose/workframe/PUBLIC_DEPLOY.md');
115
133
  const publicDeployDst = path.join(PKG_ROOT, 'docs/PUBLIC_DEPLOY.md');
116
134
  if (fs.existsSync(publicDeploySrc)) {
117
135
  fs.mkdirSync(path.dirname(publicDeployDst), { recursive: true });
@@ -120,7 +138,7 @@ if (fs.existsSync(publicDeploySrc)) {
120
138
  }
121
139
 
122
140
  for (const name of ['LICENSE', 'NOTICE', 'SECURITY.md']) {
123
- const src = path.join(PROJECTX_ROOT, name);
141
+ const src = path.join(REPO_ROOT, name);
124
142
  const dst = path.join(PKG_ROOT, name);
125
143
  if (!fs.existsSync(src)) throw new Error(`Missing publish file: ${src}`);
126
144
  fs.copyFileSync(src, dst);
@@ -8,23 +8,23 @@ Canonical behavior location
8
8
  - Runtime: `Agents/profiles/{nativeProfileSlug}/SOUL.md`
9
9
  - Bootstrap seed: `scripts/seed/profiles/{nativeProfileSlug}/SOUL.md`
10
10
 
11
- Default specialist catalog
12
- - visionary
13
- - architect
14
- - docs
15
- - dev
16
- - research
17
- - designer
18
-
19
- Install-time starter packs
20
- - Recorded in `workframe-manifest.json` (`pack`, `profiles`, `native_agent`)
21
- - Default scaffold pack is `native` (native agent only)
22
- - Reference packs can still be chosen explicitly
23
- - See `docs/SETUP.md` for the pack chosen at install
24
-
25
- Extension pattern
26
- - Add niche specialists only when recurring demand exists.
27
- - Preferred path: spawn specialists on demand through botfather/lifecycle flows instead of preinstalling the whole crew.
11
+ Default specialist catalog
12
+ - visionary
13
+ - architect
14
+ - docs
15
+ - dev
16
+ - research
17
+ - designer
18
+
19
+ Install-time starter packs
20
+ - Recorded in `workframe-manifest.json` (`pack`, `profiles`, `native_agent`)
21
+ - Default scaffold pack is `native` (native agent only)
22
+ - Reference packs can still be chosen explicitly
23
+ - See `docs/SETUP.md` for the pack chosen at install
24
+
25
+ Extension pattern
26
+ - Add niche specialists only when recurring demand exists.
27
+ - Preferred path: spawn specialists on demand through botfather/lifecycle flows instead of preinstalling the whole crew.
28
28
 
29
29
  Curation rule
30
30
  - Keep role-specific skill/tool bundles lean.
@@ -3,11 +3,11 @@
3
3
  Goal
4
4
  - keep the agent library dynamic but controlled.
5
5
 
6
- Concierge responsibilities
7
- - detect recurring specialist needs
8
- - propose profile add/remove/swap
9
- - route work through existing profiles until approved change lands
10
- - spawn specialists only when a real role gap exists
6
+ Concierge responsibilities
7
+ - detect recurring specialist needs
8
+ - propose profile add/remove/swap
9
+ - route work through existing profiles until approved change lands
10
+ - spawn specialists only when a real role gap exists
11
11
 
12
12
  Profile operations policy
13
13
  - Add profile: when repeated tasks do not fit existing specialists.
@@ -17,13 +17,13 @@ Profile operations policy
17
17
  Approval
18
18
  - owner/admin approves profile topology changes in team environments.
19
19
 
20
- Preferred implementation path
21
- 1) Update runtime profile SOUL under `Agents/profiles/`.
22
- 2) Update bootstrap seeds under `scripts/seed/profiles/` when templates change.
23
- 3) Spawn or update the runtime profile set through lifecycle/botfather flows.
24
- 4) Announce changed routing behavior in docs and chat status.
25
-
26
- Default topology policy
27
- - Start native-only.
28
- - Treat specialist packs as reference presets, not mandatory first boot state.
29
- - Keep the installed crew as small as possible until real demand appears.
20
+ Preferred implementation path
21
+ 1) Update runtime profile SOUL under `Agents/profiles/`.
22
+ 2) Update bootstrap seeds under `scripts/seed/profiles/` when templates change.
23
+ 3) Spawn or update the runtime profile set through lifecycle/botfather flows.
24
+ 4) Announce changed routing behavior in docs and chat status.
25
+
26
+ Default topology policy
27
+ - Start native-only.
28
+ - Treat specialist packs as reference presets, not mandatory first boot state.
29
+ - Keep the installed crew as small as possible until real demand appears.
@@ -1,17 +1,17 @@
1
1
  {
2
- "version": 2,
3
- "packs": {
4
- "native": {
5
- "description": "One native project agent only. Spawn specialists later through Workframe botfather flows.",
6
- "profiles": [
7
- "project-agent"
8
- ]
9
- },
10
- "vanilla": {
11
- "description": "Reference default crew pack (native project agent + all core specialists)",
12
- "profiles": [
13
- "project-agent",
14
- "visionary",
2
+ "version": 2,
3
+ "packs": {
4
+ "native": {
5
+ "description": "One native project agent only. Spawn specialists later through Workframe botfather flows.",
6
+ "profiles": [
7
+ "project-agent"
8
+ ]
9
+ },
10
+ "vanilla": {
11
+ "description": "Reference default crew pack (native project agent + all core specialists)",
12
+ "profiles": [
13
+ "project-agent",
14
+ "visionary",
15
15
  "architect",
16
16
  "docs",
17
17
  "dev",
@@ -48,11 +48,11 @@
48
48
  "research"
49
49
  ]
50
50
  },
51
- "full": {
52
- "description": "Alias of vanilla for compatibility",
53
- "profiles": [
54
- "project-agent",
55
- "visionary",
51
+ "full": {
52
+ "description": "Alias of vanilla for compatibility",
53
+ "profiles": [
54
+ "project-agent",
55
+ "visionary",
56
56
  "architect",
57
57
  "docs",
58
58
  "dev",
@@ -1,11 +1,11 @@
1
- version: 2
2
- packs:
3
- native:
4
- description: One native project agent only. Spawn specialists later through Workframe botfather flows.
5
- profiles: [project-agent]
6
- vanilla:
7
- description: Reference default crew pack (native project agent + all core specialists)
8
- profiles: [project-agent, visionary, architect, docs, dev, research, designer]
1
+ version: 2
2
+ packs:
3
+ native:
4
+ description: One native project agent only. Spawn specialists later through Workframe botfather flows.
5
+ profiles: [project-agent]
6
+ vanilla:
7
+ description: Reference default crew pack (native project agent + all core specialists)
8
+ profiles: [project-agent, visionary, architect, docs, dev, research, designer]
9
9
  core:
10
10
  description: Minimal operational baseline
11
11
  profiles: [project-agent, docs, dev]
@@ -21,7 +21,7 @@ Tool curation rules
21
21
  3) Add a skill only after repeated need.
22
22
  4) Remove stale skills quarterly.
23
23
 
24
- Install-time selection
25
- - default to the `native` starter pack unless a larger reference pack is explicitly requested
26
- - reference packs remain available (`core` / `product` / `engineering` / `full`)
27
- - concierge can request profile add/remove later with owner approval.
24
+ Install-time selection
25
+ - default to the `native` starter pack unless a larger reference pack is explicitly requested
26
+ - reference packs remain available (`core` / `product` / `engineering` / `full`)
27
+ - concierge can request profile add/remove later with owner approval.
@@ -1,28 +1,26 @@
1
- # Workframe API Service
2
-
3
- **Version:** `0.1.0` (`workframe-api-0.1.0`). See `docs/VERSION.md`.
4
-
5
- Active backend service for the transplanted Workframe vertical slice.
6
-
7
- This is intentionally preserved as the current Python BFF instead of being rewritten into `apps/api`.
8
-
9
- ## Local
10
-
11
- ```bash
12
- cd services/workframe-api
13
- python3 -m venv .venv
14
- . .venv/bin/activate
15
- pip install -r requirements.txt
16
- HOST=0.0.0.0 PORT=8080 HERMES_DATA=/opt/data WORKSPACE=/workspace python3 server.py
17
- ```
18
-
19
- ## Runtime state
20
-
21
- Runtime files are intentionally not committed:
22
-
23
- - `data/*.db`
24
- - `data/.auth_keys`
25
- - Hermes `Agents/`
26
- - workspace `Files/`
27
-
28
- For VPS deployment, mount clean persistent volumes into `/opt/data` and `/workspace`.
1
+ # Workframe API Service
2
+
3
+ Python BFF for the Workframe UI. See [docs/VERSION.md](../../docs/VERSION.md) for release version.
4
+
5
+ Canonical implementation not rewritten into a separate `apps/api` service.
6
+
7
+ ## Local (outside Docker)
8
+
9
+ ```bash
10
+ cd services/workframe-api
11
+ python3 -m venv .venv
12
+ . .venv/bin/activate # Windows: .venv\Scripts\activate
13
+ pip install -r requirements.txt
14
+ HOST=0.0.0.0 PORT=8080 HERMES_DATA=/opt/data WORKSPACE=/workspace python3 server.py
15
+ ```
16
+
17
+ ## Runtime state (not committed)
18
+
19
+ - `data/*.db`, vault files under `WORKFRAME_API_DATA_DIR`
20
+ - Hermes `Agents/` tree on mounted volume
21
+
22
+ ## Docs
23
+
24
+ - [Runtime operations](../../docs/public/runtime-operations.md)
25
+ - [Security](../../docs/public/security.md)
26
+ - [BFF route map](../../docs/public/bff-route-map.md)