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.
- package/LICENSE +201 -201
- package/NOTICE +12 -12
- package/README.md +8 -92
- package/SECURITY.md +40 -40
- package/bin/workframe.js +329 -329
- package/docs/workspace-instructions/WORKFRAME_ONBOARDING.md +1 -1
- package/docs/workspace-instructions/WORKFRAME_ROUTING.md +8 -8
- package/package.json +3 -6
- package/profiles/architect/AGENTS.md +29 -29
- package/profiles/architect/SOUL.md +2 -2
- package/profiles/architect/skills/devops/kanban-worker/SKILL.md +27 -27
- package/profiles/designer/AGENTS.md +26 -26
- package/profiles/designer/skills/devops/kanban-worker/SKILL.md +27 -27
- package/profiles/dev/AGENTS.md +28 -28
- package/profiles/dev/skills/devops/kanban-worker/SKILL.md +27 -27
- package/profiles/docs/AGENTS.md +27 -27
- package/profiles/docs/skills/devops/kanban-worker/SKILL.md +27 -27
- package/profiles/research/AGENTS.md +26 -26
- package/profiles/research/skills/devops/kanban-worker/SKILL.md +27 -27
- package/profiles/visionary/AGENTS.md +25 -25
- package/profiles/visionary/skills/devops/kanban-worker/SKILL.md +27 -27
- package/profiles/workframe-agent/AGENTS.md +37 -37
- package/profiles/workframe-agent/skills/devops/botfather/SKILL.md +85 -85
- package/profiles/workframe-agent/skills/devops/kanban-handoff-pattern/SKILL.md +58 -58
- package/profiles/workframe-agent/skills/devops/workframe-cohort/SKILL.md +54 -54
- package/rules/workspace-README.md +5 -5
- package/scripts/apply-update-hermes.sh +17 -17
- package/scripts/apply-update-workframe.sh +77 -77
- package/scripts/bootstrap-workspace-link.sh +8 -8
- package/scripts/bundle-workframe-ui.mjs +3 -3
- package/scripts/compose-docker-host.sh +37 -37
- package/scripts/ensure-compose-host-paths.mjs +51 -51
- package/scripts/fix-zk-encryption-key.sh +35 -35
- package/scripts/lib/install-identity.mjs +212 -212
- package/scripts/restart-gateway-hermes.sh +12 -12
- package/scripts/set-compose-public-url.mjs +92 -92
- package/scripts/setup-stack-secrets.sh +50 -50
- package/scripts/sync-canonical-to-package.mjs +8 -7
- package/scripts/verify-public-deploy.sh +105 -105
- package/shared/WORKFRAME_AGENT_LIBRARY.md +17 -17
- package/shared/WORKFRAME_AGENT_OPERATIONS.md +15 -15
- package/shared/WORKFRAME_AGENT_PACKS.json +18 -18
- package/shared/WORKFRAME_AGENT_PACKS.yaml +8 -8
- package/shared/WORKFRAME_SKILL_CURATION.md +4 -4
- package/workframe-api/README.md +28 -28
- package/workframe-api/action_proxy.py +131 -131
- package/workframe-api/auth_rate_limit.py +49 -49
- package/workframe-api/credential_vault.py +445 -445
- package/workframe-api/data/avatar-catalog.json +41 -41
- package/workframe-api/email_sender.py +220 -220
- package/workframe-api/google_auth.py +90 -90
- package/workframe-api/install_api.py +359 -359
- package/workframe-api/internal_proxy_auth.py +150 -150
- package/workframe-api/llm_proxy.py +277 -277
- package/workframe-api/oidc_jwt.py +108 -108
- package/workframe-api/package.json +12 -13
- package/workframe-api/public/assets/index-DPXu_lGn.css +1 -1
- package/workframe-api/public/assets/index-DYnLrCZZ.js +8 -8
- package/workframe-api/requirements.txt +2 -2
- package/workframe-api/site_meta.py +271 -271
- package/workframe-api/stack_config.py +427 -427
- package/workframe-api/time-bind-chat.py +99 -99
- package/workframe-api/turn_credentials.py +226 -226
- package/workframe-api/updates.py +417 -417
- package/workframe-api/vault_kek.py +159 -159
- package/workframe-api/zk_auth.py +633 -633
- package/workframe-supervisor/Dockerfile +11 -11
- package/workframe-supervisor/server.py +787 -787
- package/workframe-ui/docker/nginx.conf +85 -85
- package/workframe-ui/public/assets/{arc-CBDYvkAF.js → arc-COAT3laO.js} +1 -1
- package/workframe-ui/public/assets/architecture-7EHR7CIX-DUyH3hWG.js +1 -0
- package/workframe-ui/public/assets/{architectureDiagram-3BPJPVTR-XnBRKeW0.js → architectureDiagram-3BPJPVTR-BFjWV24l.js} +1 -1
- package/workframe-ui/public/assets/{blockDiagram-GPEHLZMM-VYHUfVhd.js → blockDiagram-GPEHLZMM-DSQLPfrj.js} +1 -1
- package/workframe-ui/public/assets/{c4Diagram-AAUBKEIU-BTjUcJpm.js → c4Diagram-AAUBKEIU-DKEHv1t2.js} +1 -1
- package/workframe-ui/public/assets/channel-g7r_RGaY.js +1 -0
- package/workframe-ui/public/assets/{chunk-2J33WTMH-w7uu7R-b.js → chunk-2J33WTMH-DHZg-DUi.js} +1 -1
- package/workframe-ui/public/assets/{chunk-3OPIFGDE-Cb9LtnDX.js → chunk-3OPIFGDE-BB-OYTfp.js} +1 -1
- package/workframe-ui/public/assets/{chunk-4BX2VUAB-DiQ-qCwH.js → chunk-4BX2VUAB-C93q0YIm.js} +1 -1
- package/workframe-ui/public/assets/{chunk-55IACEB6-C-mLFr7z.js → chunk-55IACEB6-MAYniqik.js} +1 -1
- package/workframe-ui/public/assets/{chunk-5ZQYHXKU-DOesfiCI.js → chunk-5ZQYHXKU-ChgN6YJs.js} +1 -1
- package/workframe-ui/public/assets/{chunk-727SXJPM-BJ3oBZuz.js → chunk-727SXJPM-B_FYwdAv.js} +1 -1
- package/workframe-ui/public/assets/{chunk-AQP2D5EJ-CCA6xpGs.js → chunk-AQP2D5EJ-1_Hw_h1A.js} +1 -1
- package/workframe-ui/public/assets/{chunk-BSJP7CBP-a0cMNFb2.js → chunk-BSJP7CBP-CFiDQ1Rv.js} +1 -1
- package/workframe-ui/public/assets/{chunk-CSCIHK7Q-kuqN8EIY.js → chunk-CSCIHK7Q-DZ9UMTlB.js} +1 -1
- package/workframe-ui/public/assets/{chunk-FMBD7UC4-DyPgYHCg.js → chunk-FMBD7UC4-DlMlyFgw.js} +1 -1
- package/workframe-ui/public/assets/{chunk-KSCS5N6A-CdUuvR0V.js → chunk-KSCS5N6A-DHXtQ_Hf.js} +1 -1
- package/workframe-ui/public/assets/{chunk-L5ZTLDWV-Dq9NoWmK.js → chunk-L5ZTLDWV-CuQzg-QG.js} +1 -1
- package/workframe-ui/public/assets/{chunk-LZXEDZCA-p74rddlO.js → chunk-LZXEDZCA-BHzjzCGg.js} +2 -2
- package/workframe-ui/public/assets/{chunk-ND2GUHAM-DBD2u1Gz.js → chunk-ND2GUHAM-DHXx05n2.js} +1 -1
- package/workframe-ui/public/assets/{chunk-NZK2D7GU-BeIeYFnd.js → chunk-NZK2D7GU-CV5pmDM_.js} +1 -1
- package/workframe-ui/public/assets/{chunk-O5CBEL6O-ClHc56ib.js → chunk-O5CBEL6O-6tkCHxsV.js} +1 -1
- package/workframe-ui/public/assets/chunk-QZHKN3VN-C5UQehWY.js +1 -0
- package/workframe-ui/public/assets/chunk-WU5MYG2G-DhWllrI8.js +1 -0
- package/workframe-ui/public/assets/{chunk-XPW4576I-EFr8R_1p.js → chunk-XPW4576I-BClwIiCp.js} +1 -1
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BBM_8T8E.js +1 -0
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BBM_8T8E.js +1 -0
- package/workframe-ui/public/assets/{cose-bilkent-S5V4N54A-C7aPBODd.js → cose-bilkent-S5V4N54A-DOrGV6DQ.js} +1 -1
- package/workframe-ui/public/assets/{dagre-BM42HDAG-BdU1Rv-H.js → dagre-BM42HDAG-DXTPvJkX.js} +1 -1
- package/workframe-ui/public/assets/{diagram-2AECGRRQ-DWowSo85.js → diagram-2AECGRRQ-xX_v-pbf.js} +1 -1
- package/workframe-ui/public/assets/{diagram-5GNKFQAL-MnxBbceO.js → diagram-5GNKFQAL-Cd2pXbBe.js} +1 -1
- package/workframe-ui/public/assets/{diagram-KO2AKTUF-DQaLRXFf.js → diagram-KO2AKTUF-Df3XvUtk.js} +1 -1
- package/workframe-ui/public/assets/{diagram-LMA3HP47-CQaBud9k.js → diagram-LMA3HP47-CsijIPaD.js} +1 -1
- package/workframe-ui/public/assets/{diagram-OG6HWLK6-D8bAXbY9.js → diagram-OG6HWLK6-aq5fmfHd.js} +1 -1
- package/workframe-ui/public/assets/{dist-DGpTLHr_.js → dist-D1c0mkbB.js} +1 -1
- package/workframe-ui/public/assets/{erDiagram-TEJ5UH35-1E-xSvBK.js → erDiagram-TEJ5UH35-DnFysVRY.js} +1 -1
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-Ci8mdb44.js +1 -0
- package/workframe-ui/public/assets/{flowDiagram-I6XJVG4X-CgOVD5hu.js → flowDiagram-I6XJVG4X-C6Ebi3su.js} +1 -1
- package/workframe-ui/public/assets/{ganttDiagram-6RSMTGT7-JFYAIauo.js → ganttDiagram-6RSMTGT7-BQXQtUpa.js} +1 -1
- package/workframe-ui/public/assets/{gitGraph-WXDBUCRP-B9REenIl.js → gitGraph-WXDBUCRP-Dt0zIs_M.js} +1 -1
- package/workframe-ui/public/assets/{gitGraphDiagram-PVQCEYII-BQ7NcMSn.js → gitGraphDiagram-PVQCEYII-BF8gHzRn.js} +1 -1
- package/workframe-ui/public/assets/index-DpoUZAxh.css +1 -0
- package/workframe-ui/public/assets/{index-Dnw6vjqb.js → index-lRpzpNPT.js} +2 -2
- package/workframe-ui/public/assets/{info-J43DQDTF-CL6-eTjH.js → info-J43DQDTF-CSmszQJT.js} +1 -1
- package/workframe-ui/public/assets/{infoDiagram-5YYISTIA-LJTODW4W.js → infoDiagram-5YYISTIA-CVTKGW6p.js} +1 -1
- package/workframe-ui/public/assets/{ishikawaDiagram-YF4QCWOH-bchrQVuo.js → ishikawaDiagram-YF4QCWOH-Z8pT09Lv.js} +1 -1
- package/workframe-ui/public/assets/{journeyDiagram-JHISSGLW-DkrvYuxP.js → journeyDiagram-JHISSGLW-r3wD68_T.js} +1 -1
- package/workframe-ui/public/assets/{kanban-definition-UN3LZRKU-DFRbj0IG.js → kanban-definition-UN3LZRKU-Il8VglqN.js} +1 -1
- package/workframe-ui/public/assets/{line-Vd48P7-O.js → line-oyjpfz2A.js} +1 -1
- package/workframe-ui/public/assets/{linear-Ckizh2G7.js → linear-Cf7p5tVp.js} +1 -1
- package/workframe-ui/public/assets/{mermaid-parser.core-Bkimsnqj.js → mermaid-parser.core-YmbZ-AfY.js} +2 -2
- package/workframe-ui/public/assets/{mermaid.core-x0TvVuPo.js → mermaid.core-BFdCAqCo.js} +3 -3
- package/workframe-ui/public/assets/{mindmap-definition-RKZ34NQL-6ykAFPEz.js → mindmap-definition-RKZ34NQL-Cy2iCtEl.js} +1 -1
- package/workframe-ui/public/assets/{packet-YPE3B663-Dw3xgMDt.js → packet-YPE3B663-DwOBZL6K.js} +1 -1
- package/workframe-ui/public/assets/{pie-LRSECV5Y-DATysawG.js → pie-LRSECV5Y-04PPhnKK.js} +1 -1
- package/workframe-ui/public/assets/{pieDiagram-4H26LBE5-SJKD1S0S.js → pieDiagram-4H26LBE5-LxIpgHqi.js} +1 -1
- package/workframe-ui/public/assets/{quadrantDiagram-W4KKPZXB-BrYDZX8q.js → quadrantDiagram-W4KKPZXB-0nBYfYm4.js} +1 -1
- package/workframe-ui/public/assets/{radar-GUYGQ44K-BmWYPCds.js → radar-GUYGQ44K-D2-vBqps.js} +1 -1
- package/workframe-ui/public/assets/{requirementDiagram-4Y6WPE33-DwL9Mc8e.js → requirementDiagram-4Y6WPE33-DbuU0nlu.js} +1 -1
- package/workframe-ui/public/assets/{sankeyDiagram-5OEKKPKP-DYIFsL8h.js → sankeyDiagram-5OEKKPKP-B2hQ6B2x.js} +1 -1
- package/workframe-ui/public/assets/{sequenceDiagram-3UESZ5HK-0-FPkFk8.js → sequenceDiagram-3UESZ5HK-BBrU30e1.js} +1 -1
- package/workframe-ui/public/assets/{src-B_od6b6h.js → src-BJEDmV70.js} +1 -1
- package/workframe-ui/public/assets/{stateDiagram-AJRCARHV-BQCiBk6u.js → stateDiagram-AJRCARHV-7FGO4kkH.js} +1 -1
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-DLTSizMg.js +1 -0
- package/workframe-ui/public/assets/{timeline-definition-PNZ67QCA-DS3tFcXj.js → timeline-definition-PNZ67QCA-ptDm4rCN.js} +1 -1
- package/workframe-ui/public/assets/{treeView-BLDUP644-DSyUCKLY.js → treeView-BLDUP644-CS6Z-0q8.js} +1 -1
- package/workframe-ui/public/assets/{treemap-LRROVOQU-CEZaNh5Y.js → treemap-LRROVOQU-DqV4Y2VA.js} +1 -1
- package/workframe-ui/public/assets/{vennDiagram-CIIHVFJN-CD-Vc9NF.js → vennDiagram-CIIHVFJN-C0UrZJYt.js} +1 -1
- package/workframe-ui/public/assets/{wardley-L42UT6IY-Drq5w1Mc.js → wardley-L42UT6IY-bNDN3_Sa.js} +1 -1
- package/workframe-ui/public/assets/{wardleyDiagram-YWT4CUSO-DouXDJoF.js → wardleyDiagram-YWT4CUSO-jWiJsefM.js} +1 -1
- package/workframe-ui/public/assets/{xychartDiagram-2RQKCTM6-DDf_Lol5.js → xychartDiagram-2RQKCTM6-Dsh_fLCy.js} +1 -1
- package/workframe-ui/public/favicon.svg +7 -7
- package/workframe-ui/public/index.html +50 -50
- package/workframe-ui/public/workframe-config.json +3 -3
- package/scripts/security_audit.py +0 -156
- package/scripts/test-scaffold.mjs +0 -390
- package/workframe-api/tests/__init__.py +0 -0
- package/workframe-api/tests/db_setup.py +0 -13
- package/workframe-api/tests/test_admin_updates_gated.py +0 -30
- package/workframe-api/tests/test_agent_dm_bootstrap.py +0 -196
- package/workframe-api/tests/test_agent_profile_sync.py +0 -76
- package/workframe-api/tests/test_auth_email.py +0 -222
- package/workframe-api/tests/test_auth_hole_fix_selfcheck.py +0 -99
- package/workframe-api/tests/test_auth_rate_limit.py +0 -19
- package/workframe-api/tests/test_avatar_resolve.py +0 -77
- package/workframe-api/tests/test_child_soul_template.py +0 -71
- package/workframe-api/tests/test_credential_canary.py +0 -135
- package/workframe-api/tests/test_credential_isolation.py +0 -448
- package/workframe-api/tests/test_credential_resolution.py +0 -206
- package/workframe-api/tests/test_device_oauth.py +0 -108
- package/workframe-api/tests/test_doctor_repair.py +0 -103
- package/workframe-api/tests/test_ensure_profile_api.py +0 -77
- package/workframe-api/tests/test_gateway_compose_security.py +0 -136
- package/workframe-api/tests/test_install_secure_host.py +0 -39
- package/workframe-api/tests/test_internal_proxy_auth.py +0 -125
- package/workframe-api/tests/test_invite_runtime_bootstrap.py +0 -72
- package/workframe-api/tests/test_kanban_delegation.py +0 -185
- package/workframe-api/tests/test_llm_proxy.py +0 -155
- package/workframe-api/tests/test_login_access_policy.py +0 -183
- package/workframe-api/tests/test_mvp_model_bootstrap.py +0 -75
- package/workframe-api/tests/test_onboarding_bootstrap.py +0 -248
- package/workframe-api/tests/test_platform_auth.py +0 -47
- package/workframe-api/tests/test_profile_config_path.py +0 -56
- package/workframe-api/tests/test_profile_config_yaml_repair.py +0 -63
- package/workframe-api/tests/test_profile_create.py +0 -72
- package/workframe-api/tests/test_profile_identity_overlay.py +0 -61
- package/workframe-api/tests/test_profile_install_health.py +0 -45
- package/workframe-api/tests/test_profile_secret_policy.py +0 -57
- package/workframe-api/tests/test_profile_workspace_cwd.py +0 -34
- package/workframe-api/tests/test_provider_bootstrap.py +0 -75
- package/workframe-api/tests/test_provider_connect.py +0 -54
- package/workframe-api/tests/test_room_crud.py +0 -192
- package/workframe-api/tests/test_room_tenancy.py +0 -701
- package/workframe-api/tests/test_runtime_identity_backfill.py +0 -34
- package/workframe-api/tests/test_site_meta.py +0 -81
- package/workframe-api/tests/test_soul_stub.py +0 -42
- package/workframe-api/tests/test_space_member_sync.py +0 -99
- package/workframe-api/tests/test_stripe_stack_config.py +0 -37
- package/workframe-api/tests/test_supervisor_lifecycle.py +0 -52
- package/workframe-api/tests/test_turn_credential_vault.py +0 -125
- package/workframe-api/tests/test_updates.py +0 -176
- package/workframe-api/tests/test_user_cohort.py +0 -113
- package/workframe-api/tests/test_vault_envelope.py +0 -110
- package/workframe-api/tests/test_workspace_members.py +0 -183
- package/workframe-api/tests/test_workspace_messaging_sync.py +0 -125
- package/workframe-api/tests/test_workspace_provider_list.py +0 -57
- package/workframe-supervisor/tests/test_exec_guard.py +0 -42
- package/workframe-supervisor/tests/test_server_import.py +0 -21
- package/workframe-ui/public/assets/architecture-7EHR7CIX-CtbQKTuT.js +0 -1
- package/workframe-ui/public/assets/channel-Dy4Z4-jn.js +0 -1
- package/workframe-ui/public/assets/chunk-QZHKN3VN-CtBEchFK.js +0 -1
- package/workframe-ui/public/assets/chunk-WU5MYG2G-B9pBtriN.js +0 -1
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BMAEA8jI.js +0 -1
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BMAEA8jI.js +0 -1
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-D75cstNT.js +0 -1
- package/workframe-ui/public/assets/index-DpAGxump.css +0 -1
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-B89jAMFF.js +0 -1
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Safe Workframe update — sync npm template (optional), rebuild API/supervisor/UI containers. Never wipes runtime/DB.
|
|
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
|
-
workframe_compose_prepare
|
|
9
|
-
PROJECT_ROOT="${WORKFRAME_HOST_PROJECT_ROOT:-${WORKFRAME_PROJECT_ROOT:-$compose_cd}}"
|
|
10
|
-
|
|
11
|
-
echo "=== Workframe update (API + supervisor + UI) ==="
|
|
12
|
-
echo "Project root: $PROJECT_ROOT"
|
|
13
|
-
echo "Preserves: Agents/, Files/, .env, workframe-api/data, gateway/Hermes profiles"
|
|
14
|
-
|
|
15
|
-
TARGET_VERSION="${WORKFRAME_UPDATE_VERSION:-}"
|
|
16
|
-
NPM_PACKAGE="${WORKFRAME_NPM_PACKAGE:-create-workframe}"
|
|
17
|
-
|
|
18
|
-
if [[ "${WORKFRAME_UPDATE_SKIP_NPM:-1}" == "1" ]] && [[ "${WORKFRAME_UPDATE_ALLOW_NPM:-}" != "1" ]]; then
|
|
19
|
-
echo "Skipping npm template sync (WORKFRAME_UPDATE_SKIP_NPM=1; set WORKFRAME_UPDATE_ALLOW_NPM=1 to fetch)"
|
|
20
|
-
elif command -v npm >/dev/null 2>&1; then
|
|
21
|
-
if [[ "${WORKFRAME_UPDATE_ALLOW_NPM:-}" == "1" ]] && [[ -z "$TARGET_VERSION" ]]; then
|
|
22
|
-
echo "WORKFRAME_UPDATE_VERSION is required when WORKFRAME_UPDATE_ALLOW_NPM=1" >&2
|
|
23
|
-
exit 1
|
|
24
|
-
fi
|
|
25
|
-
TMP="$(mktemp -d)"
|
|
26
|
-
trap 'rm -rf "$TMP"' EXIT
|
|
27
|
-
echo "Fetching ${NPM_PACKAGE}@${TARGET_VERSION:-latest} from npm..."
|
|
28
|
-
(cd "$TMP" && npm pack "${NPM_PACKAGE}@${TARGET_VERSION:-latest}" --silent)
|
|
29
|
-
TARBALL="$(ls -1 "$TMP"/${NPM_PACKAGE}-*.tgz 2>/dev/null | head -n1 || true)"
|
|
30
|
-
if [[ -n "$TARBALL" ]]; then
|
|
31
|
-
EXPECTED="$(npm view "${NPM_PACKAGE}@${TARGET_VERSION:-latest}" dist.integrity --silent 2>/dev/null || true)"
|
|
32
|
-
if [[ -n "$EXPECTED" ]]; then
|
|
33
|
-
ACTUAL="sha512-$(sha512sum "$TARBALL" | awk '{print $1}' | xxd -r -p | base64 | tr -d '\n')"
|
|
34
|
-
if [[ "$ACTUAL" != "$EXPECTED" ]]; then
|
|
35
|
-
echo "integrity mismatch for ${NPM_PACKAGE}@${TARGET_VERSION:-latest}" >&2
|
|
36
|
-
exit 1
|
|
37
|
-
fi
|
|
38
|
-
echo "integrity ok: ${NPM_PACKAGE}@${TARGET_VERSION:-latest}"
|
|
39
|
-
fi
|
|
40
|
-
tar -xf "$TARBALL" -C "$TMP"
|
|
41
|
-
PKG="$TMP/package"
|
|
42
|
-
for dir in workframe-api workframe-supervisor; do
|
|
43
|
-
if [[ -d "$PKG/$dir" ]]; then
|
|
44
|
-
echo "Syncing $dir/"
|
|
45
|
-
mkdir -p "$PROJECT_ROOT/$dir"
|
|
46
|
-
cp -a "$PKG/$dir/." "$PROJECT_ROOT/$dir/"
|
|
47
|
-
fi
|
|
48
|
-
done
|
|
49
|
-
if [[ -d "$PKG/workframe-ui/public" ]]; then
|
|
50
|
-
echo "Syncing workframe-ui/public/"
|
|
51
|
-
mkdir -p "$PROJECT_ROOT/workframe-ui/public"
|
|
52
|
-
cp -a "$PKG/workframe-ui/public/." "$PROJECT_ROOT/workframe-ui/public/"
|
|
53
|
-
fi
|
|
54
|
-
for script in apply-update-hermes.sh apply-update-workframe.sh restart-gateway-hermes.sh compose-docker-host.sh update-hermes.sh; do
|
|
55
|
-
if [[ -f "$PKG/scripts/$script" ]]; then
|
|
56
|
-
cp -a "$PKG/scripts/$script" "$PROJECT_ROOT/scripts/workframe/$script"
|
|
57
|
-
chmod +x "$PROJECT_ROOT/scripts/workframe/$script" 2>/dev/null || true
|
|
58
|
-
fi
|
|
59
|
-
done
|
|
60
|
-
else
|
|
61
|
-
echo "npm pack produced no tarball — skipping template sync"
|
|
62
|
-
fi
|
|
63
|
-
else
|
|
64
|
-
echo "Skipping npm template sync (npm missing or WORKFRAME_UPDATE_SKIP_NPM=1)"
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
echo "Rebuilding workframe-api and workframe-supervisor..."
|
|
68
|
-
workframe_compose build workframe-api workframe-supervisor
|
|
69
|
-
workframe_compose up -d --build --no-deps workframe-api workframe-supervisor
|
|
70
|
-
|
|
71
|
-
if workframe_compose config --services 2>/dev/null | grep -qx workframe-ui; then
|
|
72
|
-
workframe_compose up -d --no-deps workframe-ui || workframe_compose restart workframe-ui || true
|
|
73
|
-
elif workframe_compose config --services 2>/dev/null | grep -qx workframe; then
|
|
74
|
-
workframe_compose up -d --no-deps workframe || workframe_compose restart workframe || true
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
echo "=== Workframe update complete ==="
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Safe Workframe update — sync npm template (optional), rebuild API/supervisor/UI containers. Never wipes runtime/DB.
|
|
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
|
+
workframe_compose_prepare
|
|
9
|
+
PROJECT_ROOT="${WORKFRAME_HOST_PROJECT_ROOT:-${WORKFRAME_PROJECT_ROOT:-$compose_cd}}"
|
|
10
|
+
|
|
11
|
+
echo "=== Workframe update (API + supervisor + UI) ==="
|
|
12
|
+
echo "Project root: $PROJECT_ROOT"
|
|
13
|
+
echo "Preserves: Agents/, Files/, .env, workframe-api/data, gateway/Hermes profiles"
|
|
14
|
+
|
|
15
|
+
TARGET_VERSION="${WORKFRAME_UPDATE_VERSION:-}"
|
|
16
|
+
NPM_PACKAGE="${WORKFRAME_NPM_PACKAGE:-create-workframe}"
|
|
17
|
+
|
|
18
|
+
if [[ "${WORKFRAME_UPDATE_SKIP_NPM:-1}" == "1" ]] && [[ "${WORKFRAME_UPDATE_ALLOW_NPM:-}" != "1" ]]; then
|
|
19
|
+
echo "Skipping npm template sync (WORKFRAME_UPDATE_SKIP_NPM=1; set WORKFRAME_UPDATE_ALLOW_NPM=1 to fetch)"
|
|
20
|
+
elif command -v npm >/dev/null 2>&1; then
|
|
21
|
+
if [[ "${WORKFRAME_UPDATE_ALLOW_NPM:-}" == "1" ]] && [[ -z "$TARGET_VERSION" ]]; then
|
|
22
|
+
echo "WORKFRAME_UPDATE_VERSION is required when WORKFRAME_UPDATE_ALLOW_NPM=1" >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
TMP="$(mktemp -d)"
|
|
26
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
27
|
+
echo "Fetching ${NPM_PACKAGE}@${TARGET_VERSION:-latest} from npm..."
|
|
28
|
+
(cd "$TMP" && npm pack "${NPM_PACKAGE}@${TARGET_VERSION:-latest}" --silent)
|
|
29
|
+
TARBALL="$(ls -1 "$TMP"/${NPM_PACKAGE}-*.tgz 2>/dev/null | head -n1 || true)"
|
|
30
|
+
if [[ -n "$TARBALL" ]]; then
|
|
31
|
+
EXPECTED="$(npm view "${NPM_PACKAGE}@${TARGET_VERSION:-latest}" dist.integrity --silent 2>/dev/null || true)"
|
|
32
|
+
if [[ -n "$EXPECTED" ]]; then
|
|
33
|
+
ACTUAL="sha512-$(sha512sum "$TARBALL" | awk '{print $1}' | xxd -r -p | base64 | tr -d '\n')"
|
|
34
|
+
if [[ "$ACTUAL" != "$EXPECTED" ]]; then
|
|
35
|
+
echo "integrity mismatch for ${NPM_PACKAGE}@${TARGET_VERSION:-latest}" >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
echo "integrity ok: ${NPM_PACKAGE}@${TARGET_VERSION:-latest}"
|
|
39
|
+
fi
|
|
40
|
+
tar -xf "$TARBALL" -C "$TMP"
|
|
41
|
+
PKG="$TMP/package"
|
|
42
|
+
for dir in workframe-api workframe-supervisor; do
|
|
43
|
+
if [[ -d "$PKG/$dir" ]]; then
|
|
44
|
+
echo "Syncing $dir/"
|
|
45
|
+
mkdir -p "$PROJECT_ROOT/$dir"
|
|
46
|
+
cp -a "$PKG/$dir/." "$PROJECT_ROOT/$dir/"
|
|
47
|
+
fi
|
|
48
|
+
done
|
|
49
|
+
if [[ -d "$PKG/workframe-ui/public" ]]; then
|
|
50
|
+
echo "Syncing workframe-ui/public/"
|
|
51
|
+
mkdir -p "$PROJECT_ROOT/workframe-ui/public"
|
|
52
|
+
cp -a "$PKG/workframe-ui/public/." "$PROJECT_ROOT/workframe-ui/public/"
|
|
53
|
+
fi
|
|
54
|
+
for script in apply-update-hermes.sh apply-update-workframe.sh restart-gateway-hermes.sh compose-docker-host.sh update-hermes.sh; do
|
|
55
|
+
if [[ -f "$PKG/scripts/$script" ]]; then
|
|
56
|
+
cp -a "$PKG/scripts/$script" "$PROJECT_ROOT/scripts/workframe/$script"
|
|
57
|
+
chmod +x "$PROJECT_ROOT/scripts/workframe/$script" 2>/dev/null || true
|
|
58
|
+
fi
|
|
59
|
+
done
|
|
60
|
+
else
|
|
61
|
+
echo "npm pack produced no tarball — skipping template sync"
|
|
62
|
+
fi
|
|
63
|
+
else
|
|
64
|
+
echo "Skipping npm template sync (npm missing or WORKFRAME_UPDATE_SKIP_NPM=1)"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
echo "Rebuilding workframe-api and workframe-supervisor..."
|
|
68
|
+
workframe_compose build workframe-api workframe-supervisor
|
|
69
|
+
workframe_compose up -d --build --no-deps workframe-api workframe-supervisor
|
|
70
|
+
|
|
71
|
+
if workframe_compose config --services 2>/dev/null | grep -qx workframe-ui; then
|
|
72
|
+
workframe_compose up -d --no-deps workframe-ui || workframe_compose restart workframe-ui || true
|
|
73
|
+
elif workframe_compose config --services 2>/dev/null | grep -qx workframe; then
|
|
74
|
+
workframe_compose up -d --no-deps workframe || workframe_compose restart workframe || true
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
echo "=== Workframe update complete ==="
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# Hermes sets HERMES_WRITE_SAFE_ROOT=/opt/data — user artifacts must resolve under it.
|
|
3
|
-
# Bind host Files at /opt/data/workspace; keep /workspace as a symlink for agents and API.
|
|
4
|
-
mkdir -p /opt/data/workspace
|
|
5
|
-
if [ -L /workspace ] || [ ! -e /workspace ]; then
|
|
6
|
-
rm -rf /workspace 2>/dev/null || true
|
|
7
|
-
ln -sfn /opt/data/workspace /workspace
|
|
8
|
-
fi
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Hermes sets HERMES_WRITE_SAFE_ROOT=/opt/data — user artifacts must resolve under it.
|
|
3
|
+
# Bind host Files at /opt/data/workspace; keep /workspace as a symlink for agents and API.
|
|
4
|
+
mkdir -p /opt/data/workspace
|
|
5
|
+
if [ -L /workspace ] || [ ! -e /workspace ]; then
|
|
6
|
+
rm -rf /workspace 2>/dev/null || true
|
|
7
|
+
ln -sfn /opt/data/workspace /workspace
|
|
8
|
+
fi
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Build apps/web and copy dist into create-workframe/workframe-ui/public for npm publish.
|
|
4
4
|
* Canonical UI pipeline: apps/web `npm run build` → dist (same as monorepo `pnpm build:web`).
|
|
5
|
-
* Run from
|
|
5
|
+
* Run from repository root: node packages/create-workframe/scripts/bundle-workframe-ui.mjs
|
|
6
6
|
*/
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
@@ -11,8 +11,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
11
11
|
|
|
12
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
13
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
14
|
-
const
|
|
15
|
-
const UI_SRC = path.join(
|
|
14
|
+
const REPO_ROOT = path.resolve(PKG_ROOT, '../..');
|
|
15
|
+
const UI_SRC = path.join(REPO_ROOT, 'apps/web');
|
|
16
16
|
const UI_DEST = path.join(PKG_ROOT, 'workframe-ui', 'public');
|
|
17
17
|
|
|
18
18
|
function npmCmd() {
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Shared docker compose invocation for in-container apply (docker.sock on host).
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
workframe_compose_prepare() {
|
|
6
|
-
compose_cd=""
|
|
7
|
-
compose_files=()
|
|
8
|
-
|
|
9
|
-
# Host-bindings overlay: docker.sock apply from supervisor uses WORKFRAME_HOST_* paths.
|
|
10
|
-
if [[ -n "${WORKFRAME_HOST_COMPOSE_DIR:-}" && -n "${WORKFRAME_COMPOSE_DIR:-}" \
|
|
11
|
-
&& -f "${WORKFRAME_COMPOSE_DIR}/docker-compose.yml" \
|
|
12
|
-
&& -f "${WORKFRAME_COMPOSE_DIR}/docker-compose.host-bindings.yml" ]]; then
|
|
13
|
-
compose_cd="${WORKFRAME_COMPOSE_DIR}"
|
|
14
|
-
compose_files=(-f docker-compose.yml -f docker-compose.host-bindings.yml)
|
|
15
|
-
return 0
|
|
16
|
-
fi
|
|
17
|
-
|
|
18
|
-
# ponytail: docker Desktop resolves WORKFRAME_HOST_* via socket even when path is not visible in this container.
|
|
19
|
-
if [[ -n "${WORKFRAME_HOST_COMPOSE_DIR:-}" ]]; then
|
|
20
|
-
compose_cd="${WORKFRAME_HOST_COMPOSE_DIR}"
|
|
21
|
-
compose_files=(-f docker-compose.yml)
|
|
22
|
-
return 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
compose_cd="${WORKFRAME_COMPOSE_DIR:-${WORKFRAME_PROJECT_ROOT:-.}}"
|
|
26
|
-
compose_files=(-f docker-compose.yml)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
workframe_compose() {
|
|
30
|
-
workframe_compose_prepare
|
|
31
|
-
cd "$compose_cd"
|
|
32
|
-
if [[ ! -f docker-compose.yml ]]; then
|
|
33
|
-
echo "docker-compose.yml not found in $compose_cd" >&2
|
|
34
|
-
exit 1
|
|
35
|
-
fi
|
|
36
|
-
docker compose "${compose_files[@]}" "$@"
|
|
37
|
-
}
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared docker compose invocation for in-container apply (docker.sock on host).
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
workframe_compose_prepare() {
|
|
6
|
+
compose_cd=""
|
|
7
|
+
compose_files=()
|
|
8
|
+
|
|
9
|
+
# Host-bindings overlay: docker.sock apply from supervisor uses WORKFRAME_HOST_* paths.
|
|
10
|
+
if [[ -n "${WORKFRAME_HOST_COMPOSE_DIR:-}" && -n "${WORKFRAME_COMPOSE_DIR:-}" \
|
|
11
|
+
&& -f "${WORKFRAME_COMPOSE_DIR}/docker-compose.yml" \
|
|
12
|
+
&& -f "${WORKFRAME_COMPOSE_DIR}/docker-compose.host-bindings.yml" ]]; then
|
|
13
|
+
compose_cd="${WORKFRAME_COMPOSE_DIR}"
|
|
14
|
+
compose_files=(-f docker-compose.yml -f docker-compose.host-bindings.yml)
|
|
15
|
+
return 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# ponytail: docker Desktop resolves WORKFRAME_HOST_* via socket even when path is not visible in this container.
|
|
19
|
+
if [[ -n "${WORKFRAME_HOST_COMPOSE_DIR:-}" ]]; then
|
|
20
|
+
compose_cd="${WORKFRAME_HOST_COMPOSE_DIR}"
|
|
21
|
+
compose_files=(-f docker-compose.yml)
|
|
22
|
+
return 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
compose_cd="${WORKFRAME_COMPOSE_DIR:-${WORKFRAME_PROJECT_ROOT:-.}}"
|
|
26
|
+
compose_files=(-f docker-compose.yml)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
workframe_compose() {
|
|
30
|
+
workframe_compose_prepare
|
|
31
|
+
cd "$compose_cd"
|
|
32
|
+
if [[ ! -f docker-compose.yml ]]; then
|
|
33
|
+
echo "docker-compose.yml not found in $compose_cd" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
docker compose "${compose_files[@]}" "$@"
|
|
37
|
+
}
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Ensure WORKFRAME_HOST_* paths exist in compose .env (VPS in-app publish/sync).
|
|
4
|
-
* Usage: node ensure-compose-host-paths.mjs --project-root /opt/workframe/
|
|
5
|
-
*/
|
|
6
|
-
import fs from 'node:fs';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
|
|
9
|
-
const args = process.argv.slice(2);
|
|
10
|
-
const rootFlag = args.indexOf('--project-root');
|
|
11
|
-
const envFlag = args.indexOf('--env');
|
|
12
|
-
const projectRoot =
|
|
13
|
-
rootFlag >= 0 ? args[rootFlag + 1] : '/opt/workframe/
|
|
14
|
-
const envPath =
|
|
15
|
-
envFlag >= 0
|
|
16
|
-
? args[envFlag + 1]
|
|
17
|
-
: path.join(projectRoot, 'infra/compose/workframe/.env');
|
|
18
|
-
const composeDir = path.join(projectRoot, 'infra/compose/workframe');
|
|
19
|
-
|
|
20
|
-
function setIfEmpty(text, key, val) {
|
|
21
|
-
const esc = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22
|
-
const re = new RegExp(`^${esc}=(.*)$`, 'm');
|
|
23
|
-
const m = text.match(re);
|
|
24
|
-
if (m && String(m[1] || '').trim()) return text;
|
|
25
|
-
const line = `${key}=${val}`;
|
|
26
|
-
if (m) return text.replace(re, line);
|
|
27
|
-
return `${text}${text.endsWith('\n') || !text ? '' : '\n'}${line}\n`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!fs.existsSync(envPath)) {
|
|
31
|
-
console.error(`Missing env file: ${envPath}`);
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let text = fs.readFileSync(envPath, 'utf8');
|
|
36
|
-
text = setIfEmpty(text, 'WORKFRAME_HOST_PROJECT_ROOT', projectRoot.replace(/\\/g, '/'));
|
|
37
|
-
text = setIfEmpty(text, 'WORKFRAME_HOST_COMPOSE_DIR', composeDir.replace(/\\/g, '/'));
|
|
38
|
-
fs.writeFileSync(envPath, text);
|
|
39
|
-
|
|
40
|
-
console.log(
|
|
41
|
-
JSON.stringify(
|
|
42
|
-
{
|
|
43
|
-
ok: true,
|
|
44
|
-
env: envPath,
|
|
45
|
-
project_root: projectRoot.replace(/\\/g, '/'),
|
|
46
|
-
compose_dir: composeDir.replace(/\\/g, '/'),
|
|
47
|
-
},
|
|
48
|
-
null,
|
|
49
|
-
2,
|
|
50
|
-
),
|
|
51
|
-
);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Ensure WORKFRAME_HOST_* paths exist in compose .env (VPS in-app publish/sync).
|
|
4
|
+
* Usage: node ensure-compose-host-paths.mjs --project-root /opt/workframe/repo [--env path]
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
const rootFlag = args.indexOf('--project-root');
|
|
11
|
+
const envFlag = args.indexOf('--env');
|
|
12
|
+
const projectRoot =
|
|
13
|
+
rootFlag >= 0 ? args[rootFlag + 1] : '/opt/workframe/repo';
|
|
14
|
+
const envPath =
|
|
15
|
+
envFlag >= 0
|
|
16
|
+
? args[envFlag + 1]
|
|
17
|
+
: path.join(projectRoot, 'infra/compose/workframe/.env');
|
|
18
|
+
const composeDir = path.join(projectRoot, 'infra/compose/workframe');
|
|
19
|
+
|
|
20
|
+
function setIfEmpty(text, key, val) {
|
|
21
|
+
const esc = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22
|
+
const re = new RegExp(`^${esc}=(.*)$`, 'm');
|
|
23
|
+
const m = text.match(re);
|
|
24
|
+
if (m && String(m[1] || '').trim()) return text;
|
|
25
|
+
const line = `${key}=${val}`;
|
|
26
|
+
if (m) return text.replace(re, line);
|
|
27
|
+
return `${text}${text.endsWith('\n') || !text ? '' : '\n'}${line}\n`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(envPath)) {
|
|
31
|
+
console.error(`Missing env file: ${envPath}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let text = fs.readFileSync(envPath, 'utf8');
|
|
36
|
+
text = setIfEmpty(text, 'WORKFRAME_HOST_PROJECT_ROOT', projectRoot.replace(/\\/g, '/'));
|
|
37
|
+
text = setIfEmpty(text, 'WORKFRAME_HOST_COMPOSE_DIR', composeDir.replace(/\\/g, '/'));
|
|
38
|
+
fs.writeFileSync(envPath, text);
|
|
39
|
+
|
|
40
|
+
console.log(
|
|
41
|
+
JSON.stringify(
|
|
42
|
+
{
|
|
43
|
+
ok: true,
|
|
44
|
+
env: envPath,
|
|
45
|
+
project_root: projectRoot.replace(/\\/g, '/'),
|
|
46
|
+
compose_dir: composeDir.replace(/\\/g, '/'),
|
|
47
|
+
},
|
|
48
|
+
null,
|
|
49
|
+
2,
|
|
50
|
+
),
|
|
51
|
+
);
|
|
@@ -1,35 +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
|
|
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
|