create-workframe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +22 -0
- package/.gitignore +73 -0
- package/LICENSE +201 -0
- package/NOTICE +12 -0
- package/README.md +111 -0
- package/SECURITY.md +40 -0
- package/bin/create-workframe.js +2814 -0
- package/bin/workframe.js +329 -0
- package/docs/workspace-instructions/WORKFRAME_DISCORD.md +20 -0
- package/docs/workspace-instructions/WORKFRAME_DOCUMENTS_AND_ARTIFACTS.md +20 -0
- package/docs/workspace-instructions/WORKFRAME_KANBAN.md +20 -0
- package/docs/workspace-instructions/WORKFRAME_ONBOARDING.md +21 -0
- package/docs/workspace-instructions/WORKFRAME_ROUTING.md +29 -0
- package/docs/workspace-instructions/WORKFRAME_TELEGRAM.md +19 -0
- package/package.json +67 -0
- package/profiles/README.md +15 -0
- package/profiles/architect/AGENTS.md +29 -0
- package/profiles/architect/SOUL.md +44 -0
- package/profiles/architect/skills/devops/kanban-worker/SKILL.md +27 -0
- package/profiles/designer/AGENTS.md +26 -0
- package/profiles/designer/SOUL.md +31 -0
- package/profiles/designer/skills/devops/kanban-worker/SKILL.md +27 -0
- package/profiles/dev/AGENTS.md +28 -0
- package/profiles/dev/SOUL.md +31 -0
- package/profiles/dev/skills/devops/kanban-worker/SKILL.md +27 -0
- package/profiles/docs/AGENTS.md +27 -0
- package/profiles/docs/SOUL.md +30 -0
- package/profiles/docs/skills/devops/kanban-worker/SKILL.md +27 -0
- package/profiles/research/AGENTS.md +26 -0
- package/profiles/research/SOUL.md +31 -0
- package/profiles/research/skills/devops/kanban-worker/SKILL.md +27 -0
- package/profiles/visionary/AGENTS.md +25 -0
- package/profiles/visionary/SOUL.md +31 -0
- package/profiles/visionary/skills/devops/kanban-worker/SKILL.md +27 -0
- package/profiles/workframe-agent/AGENTS.md +37 -0
- package/profiles/workframe-agent/SETUP.md +185 -0
- package/profiles/workframe-agent/SOUL.md +61 -0
- package/profiles/workframe-agent/skills/devops/botfather/SKILL.md +85 -0
- package/profiles/workframe-agent/skills/devops/kanban-handoff-pattern/SKILL.md +58 -0
- package/profiles/workframe-agent/skills/devops/workframe-cohort/SKILL.md +54 -0
- package/prompts/WORKFRAME_PROMPT_TEMPLATES.md +16 -0
- package/rules/.hermes.md +11 -0
- package/rules/AGENTS.md +22 -0
- package/rules/workspace-README.md +5 -0
- package/scripts/apply-update-hermes.sh +17 -0
- package/scripts/apply-update-workframe.sh +77 -0
- package/scripts/bootstrap-workspace-link.sh +8 -0
- package/scripts/bundle-workframe-ui.mjs +77 -0
- package/scripts/compose-docker-host.sh +37 -0
- package/scripts/create_workframe_scaffold.py +648 -0
- package/scripts/ensure-compose-host-paths.mjs +51 -0
- package/scripts/fix-zk-encryption-key.sh +35 -0
- package/scripts/lib/install-identity.mjs +212 -0
- package/scripts/lib/workframe-registry.mjs +290 -0
- package/scripts/new-project.mjs +68 -0
- package/scripts/restart-gateway-hermes.sh +12 -0
- package/scripts/security_audit.py +156 -0
- package/scripts/select_agent_pack.py +31 -0
- package/scripts/set-compose-public-url.mjs +92 -0
- package/scripts/setup-stack-secrets.sh +50 -0
- package/scripts/sync-canonical-to-package.mjs +146 -0
- package/scripts/test-scaffold.mjs +390 -0
- package/scripts/verify-public-deploy.sh +105 -0
- package/shared/WORKFRAME_AGENT_LIBRARY.md +31 -0
- package/shared/WORKFRAME_AGENT_OPERATIONS.md +29 -0
- package/shared/WORKFRAME_AGENT_PACKS.json +64 -0
- package/shared/WORKFRAME_AGENT_PACKS.yaml +20 -0
- package/shared/WORKFRAME_CHAT_PERMISSION_MODEL.md +20 -0
- package/shared/WORKFRAME_HANDOFF_SCHEMA.md +25 -0
- package/shared/WORKFRAME_SKILL_CURATION.md +27 -0
- package/shared/agent-avatars/ada.png +0 -0
- package/shared/agent-avatars/aibert.png +0 -0
- package/shared/agent-avatars/amelia.png +0 -0
- package/shared/agent-avatars/andy.png +0 -0
- package/shared/agent-avatars/arc.png +0 -0
- package/shared/agent-avatars/bob.png +0 -0
- package/shared/agent-avatars/buzz.png +0 -0
- package/shared/agent-avatars/carl.png +0 -0
- package/shared/agent-avatars/catalog.json +171 -0
- package/shared/agent-avatars/corbu.png +0 -0
- package/shared/agent-avatars/diana.png +0 -0
- package/shared/agent-avatars/ella.png +0 -0
- package/shared/agent-avatars/elvis.png +0 -0
- package/shared/agent-avatars/f1.png +0 -0
- package/shared/agent-avatars/f2.png +0 -0
- package/shared/agent-avatars/f3.png +0 -0
- package/shared/agent-avatars/f4.png +0 -0
- package/shared/agent-avatars/f5.png +0 -0
- package/shared/agent-avatars/f6.png +0 -0
- package/shared/agent-avatars/frida.png +0 -0
- package/shared/agent-avatars/george.png +0 -0
- package/shared/agent-avatars/grace.png +0 -0
- package/shared/agent-avatars/hedy.png +0 -0
- package/shared/agent-avatars/hermes.png +0 -0
- package/shared/agent-avatars/isaac.png +0 -0
- package/shared/agent-avatars/jes.png +0 -0
- package/shared/agent-avatars/john.png +0 -0
- package/shared/agent-avatars/joni.png +0 -0
- package/shared/agent-avatars/leo.png +0 -0
- package/shared/agent-avatars/louis.png +0 -0
- package/shared/agent-avatars/ludwig.png +0 -0
- package/shared/agent-avatars/m1.png +0 -0
- package/shared/agent-avatars/m2.png +0 -0
- package/shared/agent-avatars/m3.png +0 -0
- package/shared/agent-avatars/m4.png +0 -0
- package/shared/agent-avatars/m5.png +0 -0
- package/shared/agent-avatars/m6.png +0 -0
- package/shared/agent-avatars/marie.png +0 -0
- package/shared/agent-avatars/marilyn.png +0 -0
- package/shared/agent-avatars/neil.png +0 -0
- package/shared/agent-avatars/nikola.png +0 -0
- package/shared/agent-avatars/nina.png +0 -0
- package/shared/agent-avatars/paul.png +0 -0
- package/shared/agent-avatars/ringo.png +0 -0
- package/shared/agent-avatars/rosie.png +0 -0
- package/shared/agent-avatars/ste.png +0 -0
- package/shared/agent-avatars/steve.png +0 -0
- package/shared/agent-avatars/sun.png +0 -0
- package/shared/agent-avatars/tom.png +0 -0
- package/shared/agent-avatars/warren.png +0 -0
- package/shared/agent-avatars/woz.png +0 -0
- package/shared/agent-avatars/zaha.png +0 -0
- package/workframe-api/Dockerfile +14 -0
- package/workframe-api/README.md +28 -0
- package/workframe-api/action_proxy.py +131 -0
- package/workframe-api/auth_rate_limit.py +49 -0
- package/workframe-api/catalog/avatar-catalog.json +171 -0
- package/workframe-api/catalog/logo-catalog.json +86 -0
- package/workframe-api/catalog/user-avatar-catalog.json +171 -0
- package/workframe-api/credential_vault.py +445 -0
- package/workframe-api/data/.gitkeep +0 -0
- package/workframe-api/data/avatar-catalog.json +41 -0
- package/workframe-api/data/logo-catalog.json +14 -0
- package/workframe-api/data/user-avatar-catalog.json +18 -0
- package/workframe-api/email_sender.py +220 -0
- package/workframe-api/google_auth.py +90 -0
- package/workframe-api/install_api.py +359 -0
- package/workframe-api/internal_proxy_auth.py +150 -0
- package/workframe-api/llm_proxy.py +277 -0
- package/workframe-api/oidc_jwt.py +108 -0
- package/workframe-api/package.json +13 -0
- package/workframe-api/platform_auth.py +194 -0
- package/workframe-api/profile_secret_policy.py +86 -0
- package/workframe-api/public/assets/index-DPXu_lGn.css +1 -0
- package/workframe-api/public/assets/index-DYnLrCZZ.js +9 -0
- package/workframe-api/public/assets/index-DglUqFB_.js +9 -0
- package/workframe-api/public/index.html +12 -0
- package/workframe-api/requirements.txt +2 -0
- package/workframe-api/server.py +19646 -0
- package/workframe-api/site_meta.py +271 -0
- package/workframe-api/stack_config.py +427 -0
- package/workframe-api/tests/__init__.py +0 -0
- package/workframe-api/tests/db_setup.py +13 -0
- package/workframe-api/tests/test_admin_updates_gated.py +30 -0
- package/workframe-api/tests/test_agent_dm_bootstrap.py +196 -0
- package/workframe-api/tests/test_agent_profile_sync.py +76 -0
- package/workframe-api/tests/test_auth_email.py +222 -0
- package/workframe-api/tests/test_auth_hole_fix_selfcheck.py +99 -0
- package/workframe-api/tests/test_auth_rate_limit.py +19 -0
- package/workframe-api/tests/test_avatar_resolve.py +77 -0
- package/workframe-api/tests/test_child_soul_template.py +71 -0
- package/workframe-api/tests/test_credential_canary.py +135 -0
- package/workframe-api/tests/test_credential_isolation.py +448 -0
- package/workframe-api/tests/test_credential_resolution.py +206 -0
- package/workframe-api/tests/test_device_oauth.py +108 -0
- package/workframe-api/tests/test_doctor_repair.py +103 -0
- package/workframe-api/tests/test_ensure_profile_api.py +77 -0
- package/workframe-api/tests/test_gateway_compose_security.py +136 -0
- package/workframe-api/tests/test_install_secure_host.py +39 -0
- package/workframe-api/tests/test_internal_proxy_auth.py +125 -0
- package/workframe-api/tests/test_invite_runtime_bootstrap.py +72 -0
- package/workframe-api/tests/test_kanban_delegation.py +185 -0
- package/workframe-api/tests/test_llm_proxy.py +155 -0
- package/workframe-api/tests/test_login_access_policy.py +183 -0
- package/workframe-api/tests/test_mvp_model_bootstrap.py +75 -0
- package/workframe-api/tests/test_onboarding_bootstrap.py +248 -0
- package/workframe-api/tests/test_platform_auth.py +47 -0
- package/workframe-api/tests/test_profile_config_path.py +56 -0
- package/workframe-api/tests/test_profile_config_yaml_repair.py +63 -0
- package/workframe-api/tests/test_profile_create.py +72 -0
- package/workframe-api/tests/test_profile_identity_overlay.py +61 -0
- package/workframe-api/tests/test_profile_install_health.py +45 -0
- package/workframe-api/tests/test_profile_secret_policy.py +57 -0
- package/workframe-api/tests/test_profile_workspace_cwd.py +34 -0
- package/workframe-api/tests/test_provider_bootstrap.py +75 -0
- package/workframe-api/tests/test_provider_connect.py +54 -0
- package/workframe-api/tests/test_room_crud.py +192 -0
- package/workframe-api/tests/test_room_tenancy.py +701 -0
- package/workframe-api/tests/test_runtime_identity_backfill.py +34 -0
- package/workframe-api/tests/test_site_meta.py +81 -0
- package/workframe-api/tests/test_soul_stub.py +42 -0
- package/workframe-api/tests/test_space_member_sync.py +99 -0
- package/workframe-api/tests/test_stripe_stack_config.py +37 -0
- package/workframe-api/tests/test_supervisor_lifecycle.py +52 -0
- package/workframe-api/tests/test_turn_credential_vault.py +125 -0
- package/workframe-api/tests/test_updates.py +176 -0
- package/workframe-api/tests/test_user_cohort.py +113 -0
- package/workframe-api/tests/test_vault_envelope.py +110 -0
- package/workframe-api/tests/test_workspace_members.py +183 -0
- package/workframe-api/tests/test_workspace_messaging_sync.py +125 -0
- package/workframe-api/tests/test_workspace_provider_list.py +57 -0
- package/workframe-api/time-bind-chat.py +99 -0
- package/workframe-api/turn_credentials.py +226 -0
- package/workframe-api/updates.py +417 -0
- package/workframe-api/vault_kek.py +159 -0
- package/workframe-api/zk_auth.py +633 -0
- package/workframe-supervisor/Dockerfile +11 -0
- package/workframe-supervisor/profile_secret_policy.py +76 -0
- package/workframe-supervisor/server.py +787 -0
- package/workframe-supervisor/tests/test_exec_guard.py +42 -0
- package/workframe-supervisor/tests/test_server_import.py +21 -0
- package/workframe-ui/docker/nginx.conf +85 -0
- package/workframe-ui/public/assets/1-DLJbBkOb.png +0 -0
- package/workframe-ui/public/assets/10-uwRwj5ce.png +0 -0
- package/workframe-ui/public/assets/11-5OuV9F_e.png +0 -0
- package/workframe-ui/public/assets/12-u_axjxW-.png +0 -0
- package/workframe-ui/public/assets/13-ldSvcMsH.png +0 -0
- package/workframe-ui/public/assets/14-xdcALEYD.png +0 -0
- package/workframe-ui/public/assets/15-aZ4snEFB.png +0 -0
- package/workframe-ui/public/assets/16-L_5-DttY.png +0 -0
- package/workframe-ui/public/assets/2-zOPZTppD.png +0 -0
- package/workframe-ui/public/assets/3-Dc3WoVu5.png +0 -0
- package/workframe-ui/public/assets/4-C50hk7_m.png +0 -0
- package/workframe-ui/public/assets/5-Eweetkq4.png +0 -0
- package/workframe-ui/public/assets/6-5sOXgfkw.png +0 -0
- package/workframe-ui/public/assets/7-BqRBCbiC.png +0 -0
- package/workframe-ui/public/assets/8-DEDKS94h.png +0 -0
- package/workframe-ui/public/assets/9-DNj34GW-.png +0 -0
- package/workframe-ui/public/assets/ada-DsvuOc9n.png +0 -0
- package/workframe-ui/public/assets/aibert-BCz8Lo8H.png +0 -0
- package/workframe-ui/public/assets/amelia-DUf3EBGu.png +0 -0
- package/workframe-ui/public/assets/andy-Cpymuhhx.png +0 -0
- package/workframe-ui/public/assets/arc-CBDYvkAF.js +1 -0
- package/workframe-ui/public/assets/architecture-7EHR7CIX-CtbQKTuT.js +1 -0
- package/workframe-ui/public/assets/architectureDiagram-3BPJPVTR-XnBRKeW0.js +36 -0
- package/workframe-ui/public/assets/array-BifhSqXX.js +1 -0
- package/workframe-ui/public/assets/avatars/ada.png +0 -0
- package/workframe-ui/public/assets/avatars/aibert.png +0 -0
- package/workframe-ui/public/assets/avatars/amelia.png +0 -0
- package/workframe-ui/public/assets/avatars/andy.png +0 -0
- package/workframe-ui/public/assets/avatars/bob.png +0 -0
- package/workframe-ui/public/assets/avatars/buzz.png +0 -0
- package/workframe-ui/public/assets/avatars/carl.png +0 -0
- package/workframe-ui/public/assets/avatars/catalog.json +171 -0
- package/workframe-ui/public/assets/avatars/corbu.png +0 -0
- package/workframe-ui/public/assets/avatars/diana.png +0 -0
- package/workframe-ui/public/assets/avatars/elvis.png +0 -0
- package/workframe-ui/public/assets/avatars/frida.png +0 -0
- package/workframe-ui/public/assets/avatars/george.png +0 -0
- package/workframe-ui/public/assets/avatars/grace.png +0 -0
- package/workframe-ui/public/assets/avatars/hedy.png +0 -0
- package/workframe-ui/public/assets/avatars/hermes.png +0 -0
- package/workframe-ui/public/assets/avatars/isaac.png +0 -0
- package/workframe-ui/public/assets/avatars/john.png +0 -0
- package/workframe-ui/public/assets/avatars/joni.png +0 -0
- package/workframe-ui/public/assets/avatars/leo.png +0 -0
- package/workframe-ui/public/assets/avatars/louis.png +0 -0
- package/workframe-ui/public/assets/avatars/ludwig.png +0 -0
- package/workframe-ui/public/assets/avatars/marie.png +0 -0
- package/workframe-ui/public/assets/avatars/marilyn.png +0 -0
- package/workframe-ui/public/assets/avatars/nikola.png +0 -0
- package/workframe-ui/public/assets/avatars/nina.png +0 -0
- package/workframe-ui/public/assets/avatars/paul.png +0 -0
- package/workframe-ui/public/assets/avatars/ringo.png +0 -0
- package/workframe-ui/public/assets/avatars/rosie.png +0 -0
- package/workframe-ui/public/assets/avatars/steve.png +0 -0
- package/workframe-ui/public/assets/avatars/sun.png +0 -0
- package/workframe-ui/public/assets/avatars/warren.png +0 -0
- package/workframe-ui/public/assets/avatars/woz.png +0 -0
- package/workframe-ui/public/assets/avatars/zaha.png +0 -0
- package/workframe-ui/public/assets/blockDiagram-GPEHLZMM-VYHUfVhd.js +132 -0
- package/workframe-ui/public/assets/bob-DRz-48Id.png +0 -0
- package/workframe-ui/public/assets/branding/banner.png +0 -0
- package/workframe-ui/public/assets/branding/og-default.png +0 -0
- package/workframe-ui/public/assets/branding/workframe'white.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-1.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-2.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-3.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-4.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-5.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-banner.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-logo-horizontal-mini.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-logo-horizontal-nano.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-logo-horizontal.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-logo-vertical-alt.png +0 -0
- package/workframe-ui/public/assets/branding/workframe-logo-vertical.png +0 -0
- package/workframe-ui/public/assets/branding/workframe.png +0 -0
- package/workframe-ui/public/assets/buzz-mC4PtMvC.png +0 -0
- package/workframe-ui/public/assets/c4Diagram-AAUBKEIU-BTjUcJpm.js +10 -0
- package/workframe-ui/public/assets/carl-CtE74db_.png +0 -0
- package/workframe-ui/public/assets/channel-Dy4Z4-jn.js +1 -0
- package/workframe-ui/public/assets/chunk-2J33WTMH-w7uu7R-b.js +1 -0
- package/workframe-ui/public/assets/chunk-3OPIFGDE-Cb9LtnDX.js +62 -0
- package/workframe-ui/public/assets/chunk-4BX2VUAB-DiQ-qCwH.js +1 -0
- package/workframe-ui/public/assets/chunk-55IACEB6-C-mLFr7z.js +1 -0
- package/workframe-ui/public/assets/chunk-5ZQYHXKU-DOesfiCI.js +2 -0
- package/workframe-ui/public/assets/chunk-727SXJPM-BJ3oBZuz.js +206 -0
- package/workframe-ui/public/assets/chunk-AQP2D5EJ-CCA6xpGs.js +231 -0
- package/workframe-ui/public/assets/chunk-BSJP7CBP-a0cMNFb2.js +1 -0
- package/workframe-ui/public/assets/chunk-CSCIHK7Q-kuqN8EIY.js +122 -0
- package/workframe-ui/public/assets/chunk-FMBD7UC4-DyPgYHCg.js +15 -0
- package/workframe-ui/public/assets/chunk-KSCS5N6A-CdUuvR0V.js +10 -0
- package/workframe-ui/public/assets/chunk-L5ZTLDWV-Dq9NoWmK.js +1 -0
- package/workframe-ui/public/assets/chunk-LZXEDZCA-p74rddlO.js +2 -0
- package/workframe-ui/public/assets/chunk-ND2GUHAM-DBD2u1Gz.js +1 -0
- package/workframe-ui/public/assets/chunk-NNHCCRGN-DlpIbxXb.js +159 -0
- package/workframe-ui/public/assets/chunk-NZK2D7GU-BeIeYFnd.js +1 -0
- package/workframe-ui/public/assets/chunk-O5CBEL6O-ClHc56ib.js +70 -0
- package/workframe-ui/public/assets/chunk-QZHKN3VN-CtBEchFK.js +1 -0
- package/workframe-ui/public/assets/chunk-WU5MYG2G-B9pBtriN.js +1 -0
- package/workframe-ui/public/assets/chunk-XPW4576I-EFr8R_1p.js +32 -0
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BMAEA8jI.js +1 -0
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BMAEA8jI.js +1 -0
- package/workframe-ui/public/assets/corbu-KiaMXzXQ.png +0 -0
- package/workframe-ui/public/assets/cose-bilkent-S5V4N54A-C7aPBODd.js +1 -0
- package/workframe-ui/public/assets/cytoscape.esm-h6BdjjI9.js +321 -0
- package/workframe-ui/public/assets/dagre-BM42HDAG-BdU1Rv-H.js +4 -0
- package/workframe-ui/public/assets/dagre-Bx709z4p.js +1 -0
- package/workframe-ui/public/assets/defaultLocale-C8Fc0cco.js +1 -0
- package/workframe-ui/public/assets/diagram-2AECGRRQ-DWowSo85.js +43 -0
- package/workframe-ui/public/assets/diagram-5GNKFQAL-MnxBbceO.js +10 -0
- package/workframe-ui/public/assets/diagram-KO2AKTUF-DQaLRXFf.js +3 -0
- package/workframe-ui/public/assets/diagram-LMA3HP47-CQaBud9k.js +24 -0
- package/workframe-ui/public/assets/diagram-OG6HWLK6-D8bAXbY9.js +24 -0
- package/workframe-ui/public/assets/diana-DW0MsL38.png +0 -0
- package/workframe-ui/public/assets/dist-DGpTLHr_.js +1 -0
- package/workframe-ui/public/assets/elvis-LCFaZIcT.png +0 -0
- package/workframe-ui/public/assets/erDiagram-TEJ5UH35-1E-xSvBK.js +85 -0
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-D75cstNT.js +1 -0
- package/workframe-ui/public/assets/flowDiagram-I6XJVG4X-CgOVD5hu.js +162 -0
- package/workframe-ui/public/assets/frida-CXFA0w3F.png +0 -0
- package/workframe-ui/public/assets/ganttDiagram-6RSMTGT7-JFYAIauo.js +292 -0
- package/workframe-ui/public/assets/george-DBSH2Sm2.png +0 -0
- package/workframe-ui/public/assets/gitGraph-WXDBUCRP-B9REenIl.js +1 -0
- package/workframe-ui/public/assets/gitGraphDiagram-PVQCEYII-BQ7NcMSn.js +106 -0
- package/workframe-ui/public/assets/grace-BhV0UPc0.png +0 -0
- package/workframe-ui/public/assets/graphlib-B8gBHxth.js +1 -0
- package/workframe-ui/public/assets/hedy-BR2IHift.png +0 -0
- package/workframe-ui/public/assets/hermes-CqCzcE0y.png +0 -0
- package/workframe-ui/public/assets/index-Dnw6vjqb.js +133 -0
- package/workframe-ui/public/assets/index-DpAGxump.css +1 -0
- package/workframe-ui/public/assets/info-J43DQDTF-CL6-eTjH.js +1 -0
- package/workframe-ui/public/assets/infoDiagram-5YYISTIA-LJTODW4W.js +2 -0
- package/workframe-ui/public/assets/init-D6jRqBbL.js +1 -0
- package/workframe-ui/public/assets/isaac-D1nhJAuv.png +0 -0
- package/workframe-ui/public/assets/ishikawaDiagram-YF4QCWOH-bchrQVuo.js +70 -0
- package/workframe-ui/public/assets/john-zSPWwNi4.png +0 -0
- package/workframe-ui/public/assets/joni-BFLoyfJP.png +0 -0
- package/workframe-ui/public/assets/journeyDiagram-JHISSGLW-DkrvYuxP.js +139 -0
- package/workframe-ui/public/assets/kanban-definition-UN3LZRKU-DFRbj0IG.js +89 -0
- package/workframe-ui/public/assets/katex-Vhh-h91d.js +257 -0
- package/workframe-ui/public/assets/leo-C_3IOL11.png +0 -0
- package/workframe-ui/public/assets/line-Vd48P7-O.js +1 -0
- package/workframe-ui/public/assets/linear-Ckizh2G7.js +1 -0
- package/workframe-ui/public/assets/louis-DEEECFSX.png +0 -0
- package/workframe-ui/public/assets/ludwig-_hoKhhyK.png +0 -0
- package/workframe-ui/public/assets/marie-DET6MsfO.png +0 -0
- package/workframe-ui/public/assets/marilyn-DTqwt8Yh.png +0 -0
- package/workframe-ui/public/assets/mermaid-parser.core-Bkimsnqj.js +4 -0
- package/workframe-ui/public/assets/mermaid.core-x0TvVuPo.js +9 -0
- package/workframe-ui/public/assets/mindmap-definition-RKZ34NQL-6ykAFPEz.js +96 -0
- package/workframe-ui/public/assets/nikola-B4PtHrJv.png +0 -0
- package/workframe-ui/public/assets/nina-BYbrOn0d.png +0 -0
- package/workframe-ui/public/assets/ordinal-hYBb2elL.js +1 -0
- package/workframe-ui/public/assets/packet-YPE3B663-Dw3xgMDt.js +1 -0
- package/workframe-ui/public/assets/path-BWPyau1x.js +1 -0
- package/workframe-ui/public/assets/paul-CGURYQIn.png +0 -0
- package/workframe-ui/public/assets/pie-LRSECV5Y-DATysawG.js +1 -0
- package/workframe-ui/public/assets/pieDiagram-4H26LBE5-SJKD1S0S.js +30 -0
- package/workframe-ui/public/assets/project-logos/1.png +0 -0
- package/workframe-ui/public/assets/project-logos/10.png +0 -0
- package/workframe-ui/public/assets/project-logos/11.png +0 -0
- package/workframe-ui/public/assets/project-logos/12.png +0 -0
- package/workframe-ui/public/assets/project-logos/13.png +0 -0
- package/workframe-ui/public/assets/project-logos/14.png +0 -0
- package/workframe-ui/public/assets/project-logos/15.png +0 -0
- package/workframe-ui/public/assets/project-logos/16.png +0 -0
- package/workframe-ui/public/assets/project-logos/2.png +0 -0
- package/workframe-ui/public/assets/project-logos/3.png +0 -0
- package/workframe-ui/public/assets/project-logos/4.png +0 -0
- package/workframe-ui/public/assets/project-logos/5.png +0 -0
- package/workframe-ui/public/assets/project-logos/6.png +0 -0
- package/workframe-ui/public/assets/project-logos/7.png +0 -0
- package/workframe-ui/public/assets/project-logos/8.png +0 -0
- package/workframe-ui/public/assets/project-logos/9.png +0 -0
- package/workframe-ui/public/assets/project-logos/catalog.json +86 -0
- package/workframe-ui/public/assets/quadrantDiagram-W4KKPZXB-BrYDZX8q.js +7 -0
- package/workframe-ui/public/assets/radar-GUYGQ44K-BmWYPCds.js +1 -0
- package/workframe-ui/public/assets/requirementDiagram-4Y6WPE33-DwL9Mc8e.js +84 -0
- package/workframe-ui/public/assets/ringo-WhfUNOyY.png +0 -0
- package/workframe-ui/public/assets/rosie-CAtcIf87.png +0 -0
- package/workframe-ui/public/assets/rough.esm-CSKSodPl.js +1 -0
- package/workframe-ui/public/assets/sankeyDiagram-5OEKKPKP-DYIFsL8h.js +40 -0
- package/workframe-ui/public/assets/sequenceDiagram-3UESZ5HK-0-FPkFk8.js +162 -0
- package/workframe-ui/public/assets/src-B_od6b6h.js +1 -0
- package/workframe-ui/public/assets/stateDiagram-AJRCARHV-BQCiBk6u.js +1 -0
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-B89jAMFF.js +1 -0
- package/workframe-ui/public/assets/steve-CgXXJ9EZ.png +0 -0
- package/workframe-ui/public/assets/sun-BLNAhoZd.png +0 -0
- package/workframe-ui/public/assets/timeline-definition-PNZ67QCA-DS3tFcXj.js +120 -0
- package/workframe-ui/public/assets/treeView-BLDUP644-DSyUCKLY.js +1 -0
- package/workframe-ui/public/assets/treemap-LRROVOQU-CEZaNh5Y.js +1 -0
- package/workframe-ui/public/assets/vennDiagram-CIIHVFJN-CD-Vc9NF.js +34 -0
- package/workframe-ui/public/assets/wardley-L42UT6IY-Drq5w1Mc.js +1 -0
- package/workframe-ui/public/assets/wardleyDiagram-YWT4CUSO-DouXDJoF.js +78 -0
- package/workframe-ui/public/assets/warren-DIH7UKMY.png +0 -0
- package/workframe-ui/public/assets/woz-D2yleG-V.png +0 -0
- package/workframe-ui/public/assets/xychartDiagram-2RQKCTM6-DDf_Lol5.js +7 -0
- package/workframe-ui/public/assets/zaha-wersOEq9.png +0 -0
- package/workframe-ui/public/favicon.ico +0 -0
- package/workframe-ui/public/favicon.svg +7 -0
- package/workframe-ui/public/icons.svg +24 -0
- package/workframe-ui/public/index.html +50 -0
- package/workframe-ui/public/manifest.webmanifest +18 -0
- package/workframe-ui/public/workframe-config.json +4 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fix ZK_AUTH_ENCRYPTION_KEY when it was generated as hex instead of base64.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
ENV_FILE="${1:-/opt/workframe/MyBusiness/.env}"
|
|
5
|
+
FORCE="${2:-}"
|
|
6
|
+
python3 - "$ENV_FILE" "$FORCE" <<'PY'
|
|
7
|
+
import base64, os, re, sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
env, force = Path(sys.argv[1]), sys.argv[2] == "--force"
|
|
10
|
+
text = env.read_text(encoding="utf-8")
|
|
11
|
+
m = re.search(r"^ZK_AUTH_ENCRYPTION_KEY=(.*)$", text, re.M)
|
|
12
|
+
if not m:
|
|
13
|
+
print("ZK_AUTH_ENCRYPTION_KEY missing"); sys.exit(1)
|
|
14
|
+
val = m.group(1).strip()
|
|
15
|
+
try:
|
|
16
|
+
ok = len(base64.b64decode(val)) == 32
|
|
17
|
+
except Exception:
|
|
18
|
+
ok = False
|
|
19
|
+
if ok:
|
|
20
|
+
print("ZK_AUTH_ENCRYPTION_KEY already valid"); sys.exit(0)
|
|
21
|
+
|
|
22
|
+
zk_db = Path(os.environ.get("WORKFRAME_API_DATA_DIR", "/app/data")) / "zk_auth.db"
|
|
23
|
+
if zk_db.is_file() and not force:
|
|
24
|
+
import sqlite3
|
|
25
|
+
n = sqlite3.connect(str(zk_db)).execute("SELECT COUNT(*) FROM identities").fetchone()[0]
|
|
26
|
+
if n > 0:
|
|
27
|
+
print(f"REFUSING: {n} encrypted identities present. Regenerating the KEK will lock "
|
|
28
|
+
f"out every existing user. Re-run with --force to proceed.", file=sys.stderr)
|
|
29
|
+
sys.exit(1)
|
|
30
|
+
|
|
31
|
+
new_key = base64.b64encode(os.urandom(32)).decode()
|
|
32
|
+
text = re.sub(r"^ZK_AUTH_ENCRYPTION_KEY=.*$", f"ZK_AUTH_ENCRYPTION_KEY={new_key}", text, flags=re.M)
|
|
33
|
+
env.write_text(text, encoding="utf-8")
|
|
34
|
+
print("ZK_AUTH_ENCRYPTION_KEY regenerated (was invalid base64)")
|
|
35
|
+
PY
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-install identity: slot-based localhost ports + stable install id for cookies.
|
|
3
|
+
*
|
|
4
|
+
* Slot N (1–99) → host ports N*10000 + tail:
|
|
5
|
+
* gateway 8642, dashboard 9119, api 9120, ui 8644, supervisor 8090
|
|
6
|
+
* slot 1 → 18642, 19119, 19120, 18644, 18090
|
|
7
|
+
* slot 2 → 28642, 29119, 29120, 28644, 28090
|
|
8
|
+
*
|
|
9
|
+
* ponytail: cookies use WORKFRAME_INSTALL_ID (not display name); auth DB is per install so same email can sign into many.
|
|
10
|
+
*/
|
|
11
|
+
import crypto from 'node:crypto';
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import net from 'node:net';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
|
|
17
|
+
export const PORT_TAIL = Object.freeze({
|
|
18
|
+
gateway: 8642,
|
|
19
|
+
dashboard: 9119,
|
|
20
|
+
api: 9120,
|
|
21
|
+
ui: 8644,
|
|
22
|
+
supervisor: 8090,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export function portsForSlot(slot) {
|
|
26
|
+
const n = Number(slot);
|
|
27
|
+
if (!Number.isInteger(n) || n < 1 || n > 99) {
|
|
28
|
+
throw new Error(`WORKFRAME_SLOT must be 1–99, got ${slot}`);
|
|
29
|
+
}
|
|
30
|
+
const base = n * 10_000;
|
|
31
|
+
return {
|
|
32
|
+
slot: n,
|
|
33
|
+
gateway: base + PORT_TAIL.gateway,
|
|
34
|
+
dashboard: base + PORT_TAIL.dashboard,
|
|
35
|
+
api: base + PORT_TAIL.api,
|
|
36
|
+
ui: base + PORT_TAIL.ui,
|
|
37
|
+
supervisor: base + PORT_TAIL.supervisor,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function generateSupervisorToken() {
|
|
42
|
+
return crypto.randomBytes(32).toString('hex');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function generateProxyToken() {
|
|
46
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function generateApiToken() {
|
|
50
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function generateDashboardAuthPassword() {
|
|
54
|
+
return crypto.randomBytes(18).toString('base64url');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function generateDashboardAuthSecret() {
|
|
58
|
+
return crypto.randomBytes(24).toString('hex');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function generateVaultKekB64() {
|
|
62
|
+
return crypto.randomBytes(32).toString('base64');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function generateInstallId() {
|
|
66
|
+
return `wf_${crypto.randomBytes(6).toString('hex')}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function sessionCookieNameFromEnv(env = process.env) {
|
|
70
|
+
const installId = String(env.WORKFRAME_INSTALL_ID || '').trim();
|
|
71
|
+
if (installId) {
|
|
72
|
+
const safe = installId.replace(/[^a-zA-Z0-9_-]+/g, '_').replace(/^_+|_+$/g, '');
|
|
73
|
+
if (safe) return `${safe}_session`;
|
|
74
|
+
}
|
|
75
|
+
const slug = String(env.WORKFRAME_PROJECT || 'workframe')
|
|
76
|
+
.toLowerCase()
|
|
77
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
78
|
+
.replace(/^_+|_+$/g, '') || 'workframe';
|
|
79
|
+
return `wf_${slug}_session`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function portTaken(port, host = '127.0.0.1') {
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
const socket = net.createConnection({ port, host });
|
|
85
|
+
const done = (taken) => {
|
|
86
|
+
socket.removeAllListeners();
|
|
87
|
+
socket.destroy();
|
|
88
|
+
resolve(taken);
|
|
89
|
+
};
|
|
90
|
+
socket.setTimeout(400);
|
|
91
|
+
socket.once('connect', () => done(true));
|
|
92
|
+
socket.once('timeout', () => done(false));
|
|
93
|
+
socket.once('error', () => done(false));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function slotPortsFree(slot, host = '127.0.0.1') {
|
|
98
|
+
const ports = portsForSlot(slot);
|
|
99
|
+
for (const key of ['gateway', 'dashboard', 'api', 'ui']) {
|
|
100
|
+
if (await portTaken(ports[key], host)) return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Pick the first free slot, or use preferredSlot when its ports are free.
|
|
107
|
+
*/
|
|
108
|
+
export async function allocateInstall({
|
|
109
|
+
projectName,
|
|
110
|
+
preferredSlot = null,
|
|
111
|
+
maxSlot = 9,
|
|
112
|
+
host = '127.0.0.1',
|
|
113
|
+
installId = null,
|
|
114
|
+
} = {}) {
|
|
115
|
+
if (preferredSlot != null) {
|
|
116
|
+
const slot = Number(preferredSlot);
|
|
117
|
+
if (!await slotPortsFree(slot, host)) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`WORKFRAME_SLOT ${slot} ports are in use (${JSON.stringify(portsForSlot(slot))})`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
installId: installId || generateInstallId(),
|
|
124
|
+
projectName,
|
|
125
|
+
slot,
|
|
126
|
+
ports: portsForSlot(slot),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (let slot = 1; slot <= maxSlot; slot++) {
|
|
131
|
+
if (await slotPortsFree(slot, host)) {
|
|
132
|
+
return {
|
|
133
|
+
installId: installId || generateInstallId(),
|
|
134
|
+
projectName,
|
|
135
|
+
slot,
|
|
136
|
+
ports: portsForSlot(slot),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
throw new Error(`No free Workframe install slot (1–${maxSlot}) on ${host}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function envFileLines(install, { example = false, nativeProfile = '', deploy = 'docker', hermesHome = '' } = {}) {
|
|
144
|
+
const header = example
|
|
145
|
+
? '# Copy to .env — one install = one slot + one WORKFRAME_INSTALL_ID.\n# Same email may be used across installs; each has its own auth DB.\n'
|
|
146
|
+
: '# Local Workframe install identity (.env is gitignored).\n';
|
|
147
|
+
const { ports, installId, slot } = install;
|
|
148
|
+
const deployLine = deploy === 'native' ? 'WORKFRAME_DEPLOY=native\n' : 'WORKFRAME_DEPLOY=docker\n';
|
|
149
|
+
const hermesLine = hermesHome ? `HERMES_DATA=${hermesHome.replace(/\\/g, '/')}\n` : '';
|
|
150
|
+
return `${header}${deployLine}${hermesLine}WORKFRAME_INSTALL_ID=${installId}
|
|
151
|
+
WORKFRAME_SLOT=${slot}
|
|
152
|
+
WORKFRAME_PROJECT=${install.projectName}
|
|
153
|
+
WORKFRAME_GATEWAY_PORT=${ports.gateway}
|
|
154
|
+
WORKFRAME_DASHBOARD_PORT=${ports.dashboard}
|
|
155
|
+
WORKFRAME_UI_PORT=${ports.ui}
|
|
156
|
+
WORKFRAME_UI_STATIC_DIR=./workframe-ui/public
|
|
157
|
+
WORKFRAME_API_PORT=${ports.api}
|
|
158
|
+
WORKFRAME_SUPERVISOR_PORT=${ports.supervisor}
|
|
159
|
+
WORKFRAME_MISSION_PORT=${ports.api}
|
|
160
|
+
WORKFRAME_NATIVE_PROFILE=${nativeProfile}
|
|
161
|
+
SECURE_MODE=true
|
|
162
|
+
DEV_LOCAL_UNSAFE=false
|
|
163
|
+
WORKFRAME_DEPLOYMENT_MODE=trusted_team
|
|
164
|
+
WORKFRAME_API_TOKEN=${example ? '' : generateApiToken()}
|
|
165
|
+
WORKFRAME_SUPERVISOR_TOKEN=${example ? '' : generateSupervisorToken()}
|
|
166
|
+
WORKFRAME_PROXY_TOKEN=${example ? '' : generateProxyToken()}
|
|
167
|
+
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=workframe
|
|
168
|
+
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=${example ? '' : generateDashboardAuthPassword()}
|
|
169
|
+
HERMES_DASHBOARD_BASIC_AUTH_SECRET=${example ? '' : generateDashboardAuthSecret()}
|
|
170
|
+
# Credential vault KEK (32-byte base64). Required when WORKFRAME_DEPLOYMENT_MODE=public_multi_user.
|
|
171
|
+
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
172
|
+
WORKFRAME_VAULT_KEK=${example ? '' : ''}
|
|
173
|
+
APP_BASE_URL=http://127.0.0.1:${ports.ui}
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Existing host Hermes install (never overwrites). Windows: %LOCALAPPDATA%\\hermes */
|
|
178
|
+
export function detectHermesHome() {
|
|
179
|
+
const fromEnv = String(process.env.HERMES_HOME || '').trim();
|
|
180
|
+
if (fromEnv && fs.existsSync(path.join(fromEnv, 'config.yaml'))) return fromEnv;
|
|
181
|
+
const candidates = [];
|
|
182
|
+
if (process.platform === 'win32') {
|
|
183
|
+
const local = process.env.LOCALAPPDATA;
|
|
184
|
+
if (local) candidates.push(path.join(local, 'hermes'));
|
|
185
|
+
} else if (process.platform === 'darwin') {
|
|
186
|
+
candidates.push(path.join(os.homedir(), 'Library', 'Application Support', 'hermes'));
|
|
187
|
+
}
|
|
188
|
+
candidates.push(path.join(os.homedir(), '.hermes'));
|
|
189
|
+
for (const dir of candidates) {
|
|
190
|
+
if (fs.existsSync(path.join(dir, 'config.yaml'))) return dir;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function resolveDeployMode(requested = 'auto') {
|
|
196
|
+
const mode = String(requested || 'auto').trim().toLowerCase();
|
|
197
|
+
if (mode === 'native' || mode === 'docker') return mode;
|
|
198
|
+
return detectHermesHome() ? 'native' : 'docker';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ponytail: runnable self-check — node scripts/lib/install-identity.mjs
|
|
202
|
+
import { pathToFileURL } from 'node:url';
|
|
203
|
+
const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
204
|
+
if (isMain) {
|
|
205
|
+
const p1 = portsForSlot(1);
|
|
206
|
+
const p2 = portsForSlot(2);
|
|
207
|
+
console.assert(p1.ui === 18644 && p1.api === 19120 && p1.supervisor === 18090, 'slot 1 ports');
|
|
208
|
+
console.assert(p2.ui === 28644 && p2.gateway === 28642, 'slot 2 ports');
|
|
209
|
+
console.assert(sessionCookieNameFromEnv({ WORKFRAME_INSTALL_ID: 'wf_abc123' }) === 'wf_abc123_session');
|
|
210
|
+
allocateInstall({ projectName: 'Test', preferredSlot: 99 }).catch(() => {});
|
|
211
|
+
console.log('install-identity ok');
|
|
212
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
5
|
+
|
|
6
|
+
export function workframeDir(root) {
|
|
7
|
+
return path.join(root, 'Agents', 'workframe');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function agentsRegistryPath(root) {
|
|
11
|
+
return path.join(workframeDir(root), 'agents.json');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function avatarRegistryPath(root) {
|
|
15
|
+
return path.join(workframeDir(root), 'avatar-registry.json');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function ensureWorkframeDir(root) {
|
|
19
|
+
fs.mkdirSync(workframeDir(root), { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function loadAgentsRegistry(root) {
|
|
23
|
+
const file = agentsRegistryPath(root);
|
|
24
|
+
if (!fs.existsSync(file)) {
|
|
25
|
+
return { version: 1, owner_profile: '', agents: {} };
|
|
26
|
+
}
|
|
27
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function saveAgentsRegistry(root, data) {
|
|
31
|
+
ensureWorkframeDir(root);
|
|
32
|
+
fs.writeFileSync(agentsRegistryPath(root), `${JSON.stringify(data, null, 2)}\n`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function loadAvatarRegistry(root) {
|
|
36
|
+
const file = avatarRegistryPath(root);
|
|
37
|
+
if (!fs.existsSync(file)) {
|
|
38
|
+
return { version: 1, weights: {}, assignments: {} };
|
|
39
|
+
}
|
|
40
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function saveAvatarRegistry(root, data) {
|
|
44
|
+
ensureWorkframeDir(root);
|
|
45
|
+
fs.writeFileSync(avatarRegistryPath(root), `${JSON.stringify(data, null, 2)}\n`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function avatarCatalogPaths(root) {
|
|
49
|
+
return [
|
|
50
|
+
path.join(root, 'Files', 'packages', 'workframe-ui', 'public', 'assets', 'avatars', 'catalog.json'),
|
|
51
|
+
path.join(root, 'Files', 'packages', 'create-workframe', 'shared', 'agent-avatars', 'catalog.json'),
|
|
52
|
+
path.join(root, 'workframe-ui', 'public', 'assets', 'avatars', 'catalog.json'),
|
|
53
|
+
path.join(root, 'packages', 'workframe-ui', 'public', 'assets', 'avatars', 'catalog.json'),
|
|
54
|
+
path.join(root, 'packages', 'create-workframe', 'shared', 'agent-avatars', 'catalog.json'),
|
|
55
|
+
path.join(root, 'scripts', 'seed', 'assets', 'avatars', 'catalog.json'),
|
|
56
|
+
path.join(root, 'workframe-ui', 'public', 'assets', 'agents', 'catalog.json'),
|
|
57
|
+
path.join(root, 'packages', 'create-workframe', 'shared', 'agent-avatars', 'catalog.json'),
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function loadAvatarCatalog(root) {
|
|
62
|
+
for (const candidate of avatarCatalogPaths(root)) {
|
|
63
|
+
if (fs.existsSync(candidate)) {
|
|
64
|
+
const data = JSON.parse(fs.readFileSync(candidate, 'utf8'));
|
|
65
|
+
const base = String(data.public_base || '/assets/avatars').replace(/\/$/, '');
|
|
66
|
+
const avatars = (data.avatars || []).map((row) => ({
|
|
67
|
+
id: String(row.id),
|
|
68
|
+
file: String(row.file || `${row.id}.png`),
|
|
69
|
+
label: String(row.label || row.id),
|
|
70
|
+
url: `${base}/${row.file || `${row.id}.png`}`,
|
|
71
|
+
}));
|
|
72
|
+
if (avatars.length) return { base, avatars };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
throw new Error('Avatar catalog not found (expected workframe-ui/public/assets/avatars/catalog.json)');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function avatarUrlForId(catalog, avatarId) {
|
|
79
|
+
const row = catalog.avatars.find((a) => a.id === avatarId);
|
|
80
|
+
return row?.url ?? `${catalog.base}/${avatarId}.png`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Weighted pick: prefer unassigned avatars, then lowest weight; random tie-break. */
|
|
84
|
+
export function pickAvatarId(root, { avoidReuse = true } = {}) {
|
|
85
|
+
const catalog = loadAvatarCatalog(root);
|
|
86
|
+
const avatarRegistry = loadAvatarRegistry(root);
|
|
87
|
+
const agentsRegistry = loadAgentsRegistry(root);
|
|
88
|
+
const assigned = new Set(Object.values(avatarRegistry.assignments || {}));
|
|
89
|
+
for (const row of Object.values(agentsRegistry.agents || {})) {
|
|
90
|
+
if (row.avatar_id) assigned.add(row.avatar_id);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let pool = catalog.avatars;
|
|
94
|
+
if (avoidReuse) {
|
|
95
|
+
const unused = pool.filter((a) => !assigned.has(a.id));
|
|
96
|
+
if (unused.length) pool = unused;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const weights = avatarRegistry.weights || {};
|
|
100
|
+
let minWeight = Infinity;
|
|
101
|
+
for (const avatar of pool) {
|
|
102
|
+
const w = weights[avatar.id] ?? 0;
|
|
103
|
+
if (w < minWeight) minWeight = w;
|
|
104
|
+
}
|
|
105
|
+
const candidates = pool.filter((a) => (weights[a.id] ?? 0) <= minWeight);
|
|
106
|
+
const pick = candidates[Math.floor(Math.random() * candidates.length)];
|
|
107
|
+
return pick.id;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function assignAvatar(root, profile, avatarId) {
|
|
111
|
+
const catalog = loadAvatarCatalog(root);
|
|
112
|
+
if (!catalog.avatars.some((a) => a.id === avatarId)) {
|
|
113
|
+
throw new Error(`Unknown avatar id: ${avatarId}`);
|
|
114
|
+
}
|
|
115
|
+
const avatarRegistry = loadAvatarRegistry(root);
|
|
116
|
+
avatarRegistry.assignments = avatarRegistry.assignments || {};
|
|
117
|
+
avatarRegistry.weights = avatarRegistry.weights || {};
|
|
118
|
+
avatarRegistry.assignments[profile] = avatarId;
|
|
119
|
+
avatarRegistry.weights[avatarId] = (avatarRegistry.weights[avatarId] ?? 0) + 1;
|
|
120
|
+
saveAvatarRegistry(root, avatarRegistry);
|
|
121
|
+
return avatarUrlForId(catalog, avatarId);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function releaseAvatar(root, profile) {
|
|
125
|
+
const avatarRegistry = loadAvatarRegistry(root);
|
|
126
|
+
if (avatarRegistry.assignments?.[profile]) {
|
|
127
|
+
delete avatarRegistry.assignments[profile];
|
|
128
|
+
saveAvatarRegistry(root, avatarRegistry);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function upsertAgentRecord(root, profile, patch, ownerProfile) {
|
|
133
|
+
const registry = loadAgentsRegistry(root);
|
|
134
|
+
registry.owner_profile = registry.owner_profile || ownerProfile;
|
|
135
|
+
registry.agents = registry.agents || {};
|
|
136
|
+
const prev = registry.agents[profile] || {};
|
|
137
|
+
const now = new Date().toISOString();
|
|
138
|
+
registry.agents[profile] = {
|
|
139
|
+
...prev,
|
|
140
|
+
...patch,
|
|
141
|
+
profile,
|
|
142
|
+
owner: ownerProfile,
|
|
143
|
+
updated_at: now,
|
|
144
|
+
created_at: prev.created_at || now,
|
|
145
|
+
};
|
|
146
|
+
saveAgentsRegistry(root, registry);
|
|
147
|
+
return registry.agents[profile];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function removeAgentRecord(root, profile) {
|
|
151
|
+
const registry = loadAgentsRegistry(root);
|
|
152
|
+
if (registry.agents?.[profile]) {
|
|
153
|
+
delete registry.agents[profile];
|
|
154
|
+
saveAgentsRegistry(root, registry);
|
|
155
|
+
}
|
|
156
|
+
releaseAvatar(root, profile);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function getAgentRecord(root, profile) {
|
|
160
|
+
const registry = loadAgentsRegistry(root);
|
|
161
|
+
return registry.agents?.[profile] ?? null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function readModelFromConfig(root, profile) {
|
|
165
|
+
const cfgPath = path.join(root, 'Agents', 'profiles', profile, 'config.yaml');
|
|
166
|
+
if (!fs.existsSync(cfgPath)) return null;
|
|
167
|
+
const text = fs.readFileSync(cfgPath, 'utf8');
|
|
168
|
+
const match = text.match(/^model:\s*\n(?:\s+.+\n)*?\s+default:\s*(.+)$/m);
|
|
169
|
+
return match ? match[1].trim() : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const FORBIDDEN_CHILD_SKILLS = new Set(['botfather', 'crew-manager']);
|
|
173
|
+
|
|
174
|
+
export function profileSkillsSeedDir(root, sourceSlug) {
|
|
175
|
+
return path.join(root, 'scripts', 'seed', 'profiles', sourceSlug, 'skills');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function profileSkillsDir(root, profile) {
|
|
179
|
+
return path.join(root, 'Agents', 'profiles', profile, 'skills');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function copyDirRecursive(src, dest) {
|
|
183
|
+
if (!fs.existsSync(src)) return false;
|
|
184
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
185
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
186
|
+
const from = path.join(src, entry.name);
|
|
187
|
+
const to = path.join(dest, entry.name);
|
|
188
|
+
if (entry.isDirectory()) copyDirRecursive(from, to);
|
|
189
|
+
else fs.copyFileSync(from, to);
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function skillNameFromPath(skillsRoot, skillFile) {
|
|
195
|
+
const rel = path.relative(skillsRoot, skillFile).replace(/\\/g, '/');
|
|
196
|
+
const parts = rel.split('/');
|
|
197
|
+
if (parts.length >= 2 && parts[parts.length - 1] === 'SKILL.md') {
|
|
198
|
+
return parts[parts.length - 2];
|
|
199
|
+
}
|
|
200
|
+
return path.basename(path.dirname(skillFile));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function listInstalledSkillIds(root, profile) {
|
|
204
|
+
const skillsRoot = profileSkillsDir(root, profile);
|
|
205
|
+
if (!fs.existsSync(skillsRoot)) return [];
|
|
206
|
+
const ids = new Set();
|
|
207
|
+
const walk = (dir) => {
|
|
208
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
209
|
+
const full = path.join(dir, entry.name);
|
|
210
|
+
if (entry.isDirectory()) walk(full);
|
|
211
|
+
else if (entry.name === 'SKILL.md') ids.add(skillNameFromPath(skillsRoot, full));
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
walk(skillsRoot);
|
|
215
|
+
return [...ids].sort();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Copy skills from seed (or another seed slug). Never installs botfather on children. */
|
|
219
|
+
export function installProfileSkills(root, targetProfile, { sourceSlug, skillsDir } = {}) {
|
|
220
|
+
const dest = profileSkillsDir(root, targetProfile);
|
|
221
|
+
let src = skillsDir ? (path.isAbsolute(skillsDir) ? skillsDir : path.join(root, skillsDir)) : null;
|
|
222
|
+
if (!src) {
|
|
223
|
+
const seedSlug = sourceSlug || targetProfile;
|
|
224
|
+
src = profileSkillsSeedDir(root, seedSlug);
|
|
225
|
+
}
|
|
226
|
+
if (!fs.existsSync(src)) {
|
|
227
|
+
return { installed: [], skipped: [], source: src };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const staging = path.join(dest, '.staging');
|
|
231
|
+
fs.rmSync(staging, { recursive: true, force: true });
|
|
232
|
+
copyDirRecursive(src, staging);
|
|
233
|
+
|
|
234
|
+
const installed = [];
|
|
235
|
+
const skipped = [];
|
|
236
|
+
const walk = (dir) => {
|
|
237
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
238
|
+
const full = path.join(dir, entry.name);
|
|
239
|
+
if (entry.isDirectory()) walk(full);
|
|
240
|
+
else if (entry.name === 'SKILL.md') {
|
|
241
|
+
const id = skillNameFromPath(staging, full);
|
|
242
|
+
if (FORBIDDEN_CHILD_SKILLS.has(id)) {
|
|
243
|
+
skipped.push(id);
|
|
244
|
+
fs.rmSync(path.dirname(full), { recursive: true, force: true });
|
|
245
|
+
} else {
|
|
246
|
+
installed.push(id);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
walk(staging);
|
|
252
|
+
|
|
253
|
+
if (installed.length || skipped.length) {
|
|
254
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
255
|
+
for (const entry of fs.readdirSync(staging, { withFileTypes: true })) {
|
|
256
|
+
const from = path.join(staging, entry.name);
|
|
257
|
+
const to = path.join(dest, entry.name);
|
|
258
|
+
if (entry.isDirectory()) copyDirRecursive(from, to);
|
|
259
|
+
else if (fs.existsSync(from)) fs.copyFileSync(from, to);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
fs.rmSync(staging, { recursive: true, force: true });
|
|
263
|
+
return { installed: [...new Set(installed)].sort(), skipped: [...new Set(skipped)].sort(), source: src };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Remove botfather/crew-manager from a child profile after Hermes --clone. */
|
|
267
|
+
export function stripForbiddenChildSkills(root, profile) {
|
|
268
|
+
const skillsRoot = profileSkillsDir(root, profile);
|
|
269
|
+
if (!fs.existsSync(skillsRoot)) return { removed: [] };
|
|
270
|
+
const removed = [];
|
|
271
|
+
const walk = (dir) => {
|
|
272
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
273
|
+
const full = path.join(dir, entry.name);
|
|
274
|
+
if (entry.isDirectory()) {
|
|
275
|
+
if (entry.name === 'botfather' || entry.name === 'crew-manager') {
|
|
276
|
+
fs.rmSync(full, { recursive: true, force: true });
|
|
277
|
+
removed.push(entry.name);
|
|
278
|
+
} else walk(full);
|
|
279
|
+
} else if (entry.name === 'SKILL.md') {
|
|
280
|
+
const id = skillNameFromPath(skillsRoot, full);
|
|
281
|
+
if (FORBIDDEN_CHILD_SKILLS.has(id)) {
|
|
282
|
+
fs.rmSync(path.dirname(full), { recursive: true, force: true });
|
|
283
|
+
removed.push(id);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
walk(skillsRoot);
|
|
289
|
+
return { removed: [...new Set(removed)].sort() };
|
|
290
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Canonical dev/test bootstrap: bundle UI (meta layout only) → scaffold → auto-launch Phase B.
|
|
4
|
+
*
|
|
5
|
+
* Meta repo:
|
|
6
|
+
* node packages/create-workframe/scripts/new-project.mjs BrandAuthority --out D:/Workframe --force
|
|
7
|
+
*
|
|
8
|
+
* Published npm (UI already bundled in package):
|
|
9
|
+
* npx create-workframe BrandAuthority --out D:/Workframe --force
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { spawnSync } from 'child_process';
|
|
15
|
+
|
|
16
|
+
const PKG_ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
17
|
+
const CLI = path.join(PKG_ROOT, 'bin', 'create-workframe.js');
|
|
18
|
+
const BUNDLE = path.join(PKG_ROOT, 'scripts', 'bundle-workframe-ui.mjs');
|
|
19
|
+
const UI_SOURCE = path.join(PKG_ROOT, '..', 'workframe-ui', 'package.json');
|
|
20
|
+
|
|
21
|
+
function usage() {
|
|
22
|
+
console.log(`new-project — scaffold + Phase B installer (Workframe UI bundled when developing from meta)
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
node scripts/new-project.mjs <ProjectName> [--out DIR] [--force] [--no-launch] [--no-bundle]
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
node scripts/new-project.mjs BrandAuthority --out D:/Workframe --force
|
|
29
|
+
node scripts/new-project.mjs Demo --out /tmp/projects
|
|
30
|
+
|
|
31
|
+
Notes:
|
|
32
|
+
- Auto-bundles Workframe UI when run from the meta repo (workframe-ui source present).
|
|
33
|
+
- Skips bundle when installed from npm (pre-bundled UI in the package).
|
|
34
|
+
- Phase B installer opens in a new terminal; complete Hermes setup there.
|
|
35
|
+
- Equivalent published command: npx create-workframe <ProjectName> --out <DIR> --force
|
|
36
|
+
`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const argv = process.argv.slice(2);
|
|
40
|
+
if (!argv.length || argv.includes('-h') || argv.includes('--help')) {
|
|
41
|
+
usage();
|
|
42
|
+
process.exit(argv.length ? 0 : 1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const noBundle = argv.includes('--no-bundle');
|
|
46
|
+
const noLaunch = argv.includes('--no-launch');
|
|
47
|
+
const forwarded = argv.filter((a) => a !== '--no-bundle');
|
|
48
|
+
|
|
49
|
+
const isMetaDevLayout = fs.existsSync(UI_SOURCE);
|
|
50
|
+
if (isMetaDevLayout && !noBundle) {
|
|
51
|
+
console.log('Bundling Workframe UI into create-workframe…');
|
|
52
|
+
const bundle = spawnSync(process.execPath, [BUNDLE], {
|
|
53
|
+
stdio: 'inherit',
|
|
54
|
+
cwd: path.dirname(BUNDLE),
|
|
55
|
+
});
|
|
56
|
+
if (bundle.status !== 0) process.exit(bundle.status ?? 1);
|
|
57
|
+
} else if (!isMetaDevLayout && !noBundle) {
|
|
58
|
+
console.log('Using pre-bundled Workframe UI from create-workframe package.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const scaffold = spawnSync(process.execPath, [CLI, ...forwarded], { stdio: 'inherit' });
|
|
62
|
+
if (scaffold.status !== 0) process.exit(scaffold.status ?? 1);
|
|
63
|
+
|
|
64
|
+
if (!noLaunch) {
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log('Next: complete Hermes setup in the Phase B installer window.');
|
|
67
|
+
console.log('Re-open installer manually: .\\scripts\\start-install.ps1 or ./scripts/start-install.sh');
|
|
68
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Restart Hermes gateway container only — no image pull. Preserves runtime/Agents.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
# shellcheck source=compose-docker-host.sh
|
|
6
|
+
source "$SCRIPT_DIR/compose-docker-host.sh"
|
|
7
|
+
|
|
8
|
+
echo "=== Hermes gateway restart ==="
|
|
9
|
+
workframe_compose_prepare
|
|
10
|
+
echo "Compose dir: $compose_cd"
|
|
11
|
+
workframe_compose up -d --force-recreate --no-deps gateway
|
|
12
|
+
echo "=== Gateway restart complete ==="
|