create-workframe 0.1.1 → 0.1.3
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/README.md +14 -1
- package/SECURITY.md +4 -6
- package/bin/create-workframe.js +3 -3
- package/package.json +1 -1
- 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/compose-docker-host.sh +37 -37
- package/scripts/fix-zk-encryption-key.sh +35 -35
- package/scripts/restart-gateway-hermes.sh +12 -12
- package/scripts/setup-stack-secrets.sh +50 -50
- package/scripts/sync-canonical-to-package.mjs +19 -2
- package/scripts/verify-public-deploy.sh +105 -105
- package/workframe-api/README.md +10 -14
- package/workframe-api/concierge.py +173 -0
- package/workframe-api/llm_error_glossary.py +236 -0
- package/workframe-api/llm_proxy.py +3 -0
- package/workframe-api/openrouter_catalog.py +114 -0
- package/workframe-api/package.json +1 -1
- package/workframe-api/server.py +144 -48
- package/workframe-ui/public/assets/{arc-COAT3laO.js → arc-CRhulRPr.js} +1 -1
- package/workframe-ui/public/assets/architecture-7EHR7CIX-CwxC-UJz.js +1 -0
- package/workframe-ui/public/assets/{architectureDiagram-3BPJPVTR-BFjWV24l.js → architectureDiagram-3BPJPVTR-B1Gq5onc.js} +1 -1
- package/workframe-ui/public/assets/{blockDiagram-GPEHLZMM-DSQLPfrj.js → blockDiagram-GPEHLZMM-DtXEetq-.js} +1 -1
- package/workframe-ui/public/assets/{c4Diagram-AAUBKEIU-DKEHv1t2.js → c4Diagram-AAUBKEIU-Bw7NEN3_.js} +1 -1
- package/workframe-ui/public/assets/channel-BaZSQvmW.js +1 -0
- package/workframe-ui/public/assets/{chunk-2J33WTMH-DHZg-DUi.js → chunk-2J33WTMH-91lObBre.js} +1 -1
- package/workframe-ui/public/assets/{chunk-3OPIFGDE-BB-OYTfp.js → chunk-3OPIFGDE-D5IopWdW.js} +1 -1
- package/workframe-ui/public/assets/{chunk-4BX2VUAB-C93q0YIm.js → chunk-4BX2VUAB-DRHbh4Yd.js} +1 -1
- package/workframe-ui/public/assets/{chunk-55IACEB6-MAYniqik.js → chunk-55IACEB6-DcfKoh86.js} +1 -1
- package/workframe-ui/public/assets/{chunk-5ZQYHXKU-ChgN6YJs.js → chunk-5ZQYHXKU-BMtcWXOU.js} +1 -1
- package/workframe-ui/public/assets/{chunk-727SXJPM-B_FYwdAv.js → chunk-727SXJPM-BW7tTfej.js} +1 -1
- package/workframe-ui/public/assets/{chunk-AQP2D5EJ-1_Hw_h1A.js → chunk-AQP2D5EJ-CGoSQAgm.js} +1 -1
- package/workframe-ui/public/assets/{chunk-BSJP7CBP-CFiDQ1Rv.js → chunk-BSJP7CBP-RW1Jng9K.js} +1 -1
- package/workframe-ui/public/assets/{chunk-CSCIHK7Q-DZ9UMTlB.js → chunk-CSCIHK7Q-D8PwFfuF.js} +1 -1
- package/workframe-ui/public/assets/{chunk-FMBD7UC4-DlMlyFgw.js → chunk-FMBD7UC4-rUp_DSZ2.js} +1 -1
- package/workframe-ui/public/assets/{chunk-KSCS5N6A-DHXtQ_Hf.js → chunk-KSCS5N6A-C3eMeGne.js} +1 -1
- package/workframe-ui/public/assets/{chunk-L5ZTLDWV-CuQzg-QG.js → chunk-L5ZTLDWV-DaE5LwXn.js} +1 -1
- package/workframe-ui/public/assets/{chunk-LZXEDZCA-BHzjzCGg.js → chunk-LZXEDZCA-Cmn67nL3.js} +2 -2
- package/workframe-ui/public/assets/{chunk-ND2GUHAM-DHXx05n2.js → chunk-ND2GUHAM-Bg-FPAN5.js} +1 -1
- package/workframe-ui/public/assets/{chunk-NZK2D7GU-CV5pmDM_.js → chunk-NZK2D7GU-orlL5R9g.js} +1 -1
- package/workframe-ui/public/assets/{chunk-O5CBEL6O-6tkCHxsV.js → chunk-O5CBEL6O-BaPyh9SK.js} +1 -1
- package/workframe-ui/public/assets/chunk-QZHKN3VN-DXcsnu-x.js +1 -0
- package/workframe-ui/public/assets/chunk-WU5MYG2G-B2O0DZxu.js +1 -0
- package/workframe-ui/public/assets/{chunk-XPW4576I-BClwIiCp.js → chunk-XPW4576I-CXYFE8tT.js} +1 -1
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-C_TyUBYX.js +1 -0
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-C_TyUBYX.js +1 -0
- package/workframe-ui/public/assets/{cose-bilkent-S5V4N54A-DOrGV6DQ.js → cose-bilkent-S5V4N54A-DDzKMTbp.js} +1 -1
- package/workframe-ui/public/assets/{dagre-BM42HDAG-DXTPvJkX.js → dagre-BM42HDAG-DCa6EvS3.js} +1 -1
- package/workframe-ui/public/assets/{diagram-2AECGRRQ-xX_v-pbf.js → diagram-2AECGRRQ-C-WIQoE_.js} +1 -1
- package/workframe-ui/public/assets/{diagram-5GNKFQAL-Cd2pXbBe.js → diagram-5GNKFQAL-UDk1nRH6.js} +1 -1
- package/workframe-ui/public/assets/{diagram-KO2AKTUF-Df3XvUtk.js → diagram-KO2AKTUF-DjLxUTPx.js} +1 -1
- package/workframe-ui/public/assets/{diagram-LMA3HP47-CsijIPaD.js → diagram-LMA3HP47-DNA9GSwJ.js} +1 -1
- package/workframe-ui/public/assets/{diagram-OG6HWLK6-aq5fmfHd.js → diagram-OG6HWLK6-DO2nQu2f.js} +1 -1
- package/workframe-ui/public/assets/{dist-D1c0mkbB.js → dist-DyW8oFIW.js} +1 -1
- package/workframe-ui/public/assets/{erDiagram-TEJ5UH35-DnFysVRY.js → erDiagram-TEJ5UH35-DFtJLZx1.js} +1 -1
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-BTmiKS74.js +1 -0
- package/workframe-ui/public/assets/{flowDiagram-I6XJVG4X-C6Ebi3su.js → flowDiagram-I6XJVG4X-B6K6EnPA.js} +1 -1
- package/workframe-ui/public/assets/{ganttDiagram-6RSMTGT7-BQXQtUpa.js → ganttDiagram-6RSMTGT7-CbuWdupl.js} +1 -1
- package/workframe-ui/public/assets/{gitGraph-WXDBUCRP-Dt0zIs_M.js → gitGraph-WXDBUCRP-BW_jwUPU.js} +1 -1
- package/workframe-ui/public/assets/{gitGraphDiagram-PVQCEYII-BF8gHzRn.js → gitGraphDiagram-PVQCEYII-B4MYuP9j.js} +1 -1
- package/workframe-ui/public/assets/index-BYacg3Nj.js +133 -0
- package/workframe-ui/public/assets/{info-J43DQDTF-CSmszQJT.js → info-J43DQDTF-Xa61PMDn.js} +1 -1
- package/workframe-ui/public/assets/{infoDiagram-5YYISTIA-CVTKGW6p.js → infoDiagram-5YYISTIA-BNbQgdUG.js} +1 -1
- package/workframe-ui/public/assets/{ishikawaDiagram-YF4QCWOH-Z8pT09Lv.js → ishikawaDiagram-YF4QCWOH-BDfiCzHo.js} +1 -1
- package/workframe-ui/public/assets/{journeyDiagram-JHISSGLW-r3wD68_T.js → journeyDiagram-JHISSGLW-Dn52B8tT.js} +1 -1
- package/workframe-ui/public/assets/{kanban-definition-UN3LZRKU-Il8VglqN.js → kanban-definition-UN3LZRKU-rhwkS_g6.js} +1 -1
- package/workframe-ui/public/assets/{line-oyjpfz2A.js → line-BQJgrj-x.js} +1 -1
- package/workframe-ui/public/assets/{linear-Cf7p5tVp.js → linear-Bq2ZvWSJ.js} +1 -1
- package/workframe-ui/public/assets/{mermaid-parser.core-YmbZ-AfY.js → mermaid-parser.core-CwFK_i8p.js} +2 -2
- package/workframe-ui/public/assets/{mermaid.core-BFdCAqCo.js → mermaid.core-C9CAm83c.js} +3 -3
- package/workframe-ui/public/assets/{mindmap-definition-RKZ34NQL-Cy2iCtEl.js → mindmap-definition-RKZ34NQL-BXwEccNZ.js} +1 -1
- package/workframe-ui/public/assets/{packet-YPE3B663-DwOBZL6K.js → packet-YPE3B663-D754iSKT.js} +1 -1
- package/workframe-ui/public/assets/{pie-LRSECV5Y-04PPhnKK.js → pie-LRSECV5Y-BQ2V4UWk.js} +1 -1
- package/workframe-ui/public/assets/{pieDiagram-4H26LBE5-LxIpgHqi.js → pieDiagram-4H26LBE5-CNHecl4C.js} +1 -1
- package/workframe-ui/public/assets/{quadrantDiagram-W4KKPZXB-0nBYfYm4.js → quadrantDiagram-W4KKPZXB-Bu6ciL2O.js} +1 -1
- package/workframe-ui/public/assets/{radar-GUYGQ44K-D2-vBqps.js → radar-GUYGQ44K-Dr8b9Qak.js} +1 -1
- package/workframe-ui/public/assets/{requirementDiagram-4Y6WPE33-DbuU0nlu.js → requirementDiagram-4Y6WPE33-Beua6Xju.js} +1 -1
- package/workframe-ui/public/assets/{sankeyDiagram-5OEKKPKP-B2hQ6B2x.js → sankeyDiagram-5OEKKPKP-CSDFJ0P_.js} +1 -1
- package/workframe-ui/public/assets/{sequenceDiagram-3UESZ5HK-BBrU30e1.js → sequenceDiagram-3UESZ5HK-KKziodNI.js} +1 -1
- package/workframe-ui/public/assets/{src-BJEDmV70.js → src-PKTIkQR0.js} +1 -1
- package/workframe-ui/public/assets/{stateDiagram-AJRCARHV-7FGO4kkH.js → stateDiagram-AJRCARHV-Bu8FNMcj.js} +1 -1
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-E9A5RCa9.js +1 -0
- package/workframe-ui/public/assets/{timeline-definition-PNZ67QCA-ptDm4rCN.js → timeline-definition-PNZ67QCA-DMa-RPqy.js} +1 -1
- package/workframe-ui/public/assets/{treeView-BLDUP644-CS6Z-0q8.js → treeView-BLDUP644-CzQeUY7D.js} +1 -1
- package/workframe-ui/public/assets/{treemap-LRROVOQU-DqV4Y2VA.js → treemap-LRROVOQU-BT4RYAhY.js} +1 -1
- package/workframe-ui/public/assets/{vennDiagram-CIIHVFJN-C0UrZJYt.js → vennDiagram-CIIHVFJN-nKaUH6GG.js} +1 -1
- package/workframe-ui/public/assets/{wardley-L42UT6IY-bNDN3_Sa.js → wardley-L42UT6IY-eSpvH7au.js} +1 -1
- package/workframe-ui/public/assets/{wardleyDiagram-YWT4CUSO-jWiJsefM.js → wardleyDiagram-YWT4CUSO-B5u4s4a5.js} +1 -1
- package/workframe-ui/public/assets/{xychartDiagram-2RQKCTM6-Dsh_fLCy.js → xychartDiagram-2RQKCTM6-DcXo5hDi.js} +1 -1
- package/workframe-ui/public/index.html +1 -1
- package/workframe-ui/public/assets/architecture-7EHR7CIX-DUyH3hWG.js +0 -1
- package/workframe-ui/public/assets/channel-g7r_RGaY.js +0 -1
- package/workframe-ui/public/assets/chunk-QZHKN3VN-C5UQehWY.js +0 -1
- package/workframe-ui/public/assets/chunk-WU5MYG2G-DhWllrI8.js +0 -1
- package/workframe-ui/public/assets/classDiagram-4FO5ZUOK-BBM_8T8E.js +0 -1
- package/workframe-ui/public/assets/classDiagram-v2-Q7XG4LA2-BBM_8T8E.js +0 -1
- package/workframe-ui/public/assets/eventmodeling-FCH6USID-Ci8mdb44.js +0 -1
- package/workframe-ui/public/assets/index-lRpzpNPT.js +0 -133
- package/workframe-ui/public/assets/stateDiagram-v2-BHNVJYJU-DLTSizMg.js +0 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Published on npm as **create-workframe**.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npx create-workframe@0.1.
|
|
6
|
+
npx create-workframe@0.1.3 MyProject
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Scaffolds an isolated Workframe + Hermes project on Windows, macOS, and Linux.
|
|
@@ -14,14 +14,27 @@ Scaffolds an isolated Workframe + Hermes project on Windows, macOS, and Linux.
|
|
|
14
14
|
MyProject/
|
|
15
15
|
├── Agents/
|
|
16
16
|
├── Files/
|
|
17
|
+
├── scripts/ bootstrap, lifecycle, workframe.mjs CLI
|
|
17
18
|
├── docker-compose.yml
|
|
18
19
|
├── workframe-api/
|
|
19
20
|
├── workframe-ui/
|
|
21
|
+
├── workframe-supervisor/
|
|
20
22
|
└── workframe-manifest.json
|
|
21
23
|
```
|
|
22
24
|
|
|
25
|
+
## Project CLI
|
|
26
|
+
|
|
27
|
+
From the generated project root:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
node scripts/workframe.mjs doctor
|
|
31
|
+
node scripts/workframe.mjs setup
|
|
32
|
+
```
|
|
33
|
+
|
|
23
34
|
## Source
|
|
24
35
|
|
|
25
36
|
[github.com/npx-workframe/workframe](https://github.com/npx-workframe/workframe)
|
|
26
37
|
|
|
38
|
+
Documentation: [docs/README.md](https://github.com/npx-workframe/workframe/blob/main/docs/README.md)
|
|
39
|
+
|
|
27
40
|
Apache-2.0 — see `LICENSE`, `NOTICE`, `SECURITY.md`.
|
package/SECURITY.md
CHANGED
|
@@ -16,7 +16,7 @@ direct contact with the repository owner).
|
|
|
16
16
|
|
|
17
17
|
Include:
|
|
18
18
|
|
|
19
|
-
- Affected component (
|
|
19
|
+
- Affected component (workframe-api, supervisor, installer, UI, compose)
|
|
20
20
|
- Steps to reproduce
|
|
21
21
|
- Impact assessment (data exposure, auth bypass, RCE, etc.)
|
|
22
22
|
- Suggested fix if you have one
|
|
@@ -25,11 +25,9 @@ We aim to acknowledge reports within a few business days.
|
|
|
25
25
|
|
|
26
26
|
## Scope notes
|
|
27
27
|
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- Production deployments should run with `WORKFRAME_MODE=team`, invite-only access, and
|
|
32
|
-
without `DEV_LOCAL_UNSAFE`.
|
|
28
|
+
- Personal Hermes installs outside a Workframe compose stack are out of scope unless the issue is in shared Workframe source that affects all installs.
|
|
29
|
+
- Production deployments should use `WORKFRAME_DEPLOYMENT_MODE=trusted_team` or
|
|
30
|
+
`public_multi_user`, invite-only access, and without `DEV_LOCAL_UNSAFE`.
|
|
33
31
|
- BYOK (bring-your-own-key) is the default credential mode; workspace company-pays is an
|
|
34
32
|
explicit admin opt-in.
|
|
35
33
|
|
package/bin/create-workframe.js
CHANGED
|
@@ -2014,15 +2014,15 @@ function nativeConfigYaml(projectName, agentName, personality) {
|
|
|
2014
2014
|
const personalityLine = personality ? `# Personality: ${personality}
|
|
2015
2015
|
` : "";
|
|
2016
2016
|
return `${personalityLine}model:
|
|
2017
|
-
default:
|
|
2017
|
+
default: google/gemini-2.5-flash
|
|
2018
2018
|
provider: openrouter
|
|
2019
2019
|
base_url: https://openrouter.ai/api/v1
|
|
2020
2020
|
api_mode: chat_completions
|
|
2021
2021
|
fallback_providers:
|
|
2022
2022
|
- provider: openrouter
|
|
2023
|
-
model:
|
|
2023
|
+
model: anthropic/claude-sonnet-4.5
|
|
2024
2024
|
- provider: openrouter
|
|
2025
|
-
model:
|
|
2025
|
+
model: meta-llama/llama-3.3-70b-instruct:free
|
|
2026
2026
|
providers: {}
|
|
2027
2027
|
credential_pool_strategies: {}
|
|
2028
2028
|
toolsets:
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Safe Hermes update — pull gateway/dashboard images, recreate containers. 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 update (gateway + dashboard only) ==="
|
|
9
|
-
workframe_compose_prepare
|
|
10
|
-
echo "Compose dir: $compose_cd"
|
|
11
|
-
echo "Preserves: Agents/, Files/, workframe-api data volumes"
|
|
12
|
-
|
|
13
|
-
SERVICES=(gateway dashboard)
|
|
14
|
-
workframe_compose pull "${SERVICES[@]}"
|
|
15
|
-
workframe_compose up -d --force-recreate --no-deps "${SERVICES[@]}"
|
|
16
|
-
|
|
17
|
-
echo "=== Hermes update complete ==="
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Safe Hermes update — pull gateway/dashboard images, recreate containers. 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 update (gateway + dashboard only) ==="
|
|
9
|
+
workframe_compose_prepare
|
|
10
|
+
echo "Compose dir: $compose_cd"
|
|
11
|
+
echo "Preserves: Agents/, Files/, workframe-api data volumes"
|
|
12
|
+
|
|
13
|
+
SERVICES=(gateway dashboard)
|
|
14
|
+
workframe_compose pull "${SERVICES[@]}"
|
|
15
|
+
workframe_compose up -d --force-recreate --no-deps "${SERVICES[@]}"
|
|
16
|
+
|
|
17
|
+
echo "=== Hermes update complete ==="
|
|
@@ -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
|
|
@@ -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,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
|
|
@@ -1,12 +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 ==="
|
|
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 ==="
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Append production stack secrets to a Workframe .env (idempotent).
|
|
3
|
-
# Usage: bash scripts/workframe/setup-stack-secrets.sh path/to/.env
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
ENV_FILE="${1:-/workspace/.env}"
|
|
7
|
-
|
|
8
|
-
append_if_missing() {
|
|
9
|
-
local key="$1"
|
|
10
|
-
local value="$2"
|
|
11
|
-
local comment="${3:-}"
|
|
12
|
-
if [[ -f "$ENV_FILE" ]] && grep -q "^${key}=" "$ENV_FILE"; then
|
|
13
|
-
echo "${key} already exists in ${ENV_FILE}"
|
|
14
|
-
return 0
|
|
15
|
-
fi
|
|
16
|
-
mkdir -p "$(dirname "$ENV_FILE")"
|
|
17
|
-
{
|
|
18
|
-
[[ -n "$comment" ]] && printf '\n# %s\n' "$comment"
|
|
19
|
-
printf '%s=%s\n' "$key" "$value"
|
|
20
|
-
} >>"$ENV_FILE"
|
|
21
|
-
echo "${key} generated and appended to ${ENV_FILE}"
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
rand_hex() {
|
|
25
|
-
openssl rand -hex 32 2>/dev/null || python3 - <<'PY'
|
|
26
|
-
import secrets
|
|
27
|
-
print(secrets.token_hex(32))
|
|
28
|
-
PY
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
rand_b64() {
|
|
32
|
-
python3 - <<'PY'
|
|
33
|
-
import base64, os
|
|
34
|
-
print(base64.b64encode(os.urandom(32)).decode())
|
|
35
|
-
PY
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
rand_proxy() {
|
|
39
|
-
python3 - <<'PY'
|
|
40
|
-
import secrets
|
|
41
|
-
print(secrets.token_urlsafe(32))
|
|
42
|
-
PY
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
append_if_missing WORKFRAME_SUPERVISOR_TOKEN "${WORKFRAME_SUPERVISOR_TOKEN:-$(rand_hex)}" \
|
|
46
|
-
"Workframe supervisor token. Keep this secret."
|
|
47
|
-
append_if_missing WORKFRAME_PROXY_TOKEN "${WORKFRAME_PROXY_TOKEN:-$(rand_proxy)}" \
|
|
48
|
-
"Internal LLM/action proxy secret — gateway + API must match."
|
|
49
|
-
append_if_missing WORKFRAME_VAULT_KEK "${WORKFRAME_VAULT_KEK:-$(rand_b64)}" \
|
|
50
|
-
"Credential vault KEK (32-byte base64). Required for public_multi_user."
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Append production stack secrets to a Workframe .env (idempotent).
|
|
3
|
+
# Usage: bash scripts/workframe/setup-stack-secrets.sh path/to/.env
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
ENV_FILE="${1:-/workspace/.env}"
|
|
7
|
+
|
|
8
|
+
append_if_missing() {
|
|
9
|
+
local key="$1"
|
|
10
|
+
local value="$2"
|
|
11
|
+
local comment="${3:-}"
|
|
12
|
+
if [[ -f "$ENV_FILE" ]] && grep -q "^${key}=" "$ENV_FILE"; then
|
|
13
|
+
echo "${key} already exists in ${ENV_FILE}"
|
|
14
|
+
return 0
|
|
15
|
+
fi
|
|
16
|
+
mkdir -p "$(dirname "$ENV_FILE")"
|
|
17
|
+
{
|
|
18
|
+
[[ -n "$comment" ]] && printf '\n# %s\n' "$comment"
|
|
19
|
+
printf '%s=%s\n' "$key" "$value"
|
|
20
|
+
} >>"$ENV_FILE"
|
|
21
|
+
echo "${key} generated and appended to ${ENV_FILE}"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
rand_hex() {
|
|
25
|
+
openssl rand -hex 32 2>/dev/null || python3 - <<'PY'
|
|
26
|
+
import secrets
|
|
27
|
+
print(secrets.token_hex(32))
|
|
28
|
+
PY
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
rand_b64() {
|
|
32
|
+
python3 - <<'PY'
|
|
33
|
+
import base64, os
|
|
34
|
+
print(base64.b64encode(os.urandom(32)).decode())
|
|
35
|
+
PY
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
rand_proxy() {
|
|
39
|
+
python3 - <<'PY'
|
|
40
|
+
import secrets
|
|
41
|
+
print(secrets.token_urlsafe(32))
|
|
42
|
+
PY
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
append_if_missing WORKFRAME_SUPERVISOR_TOKEN "${WORKFRAME_SUPERVISOR_TOKEN:-$(rand_hex)}" \
|
|
46
|
+
"Workframe supervisor token. Keep this secret."
|
|
47
|
+
append_if_missing WORKFRAME_PROXY_TOKEN "${WORKFRAME_PROXY_TOKEN:-$(rand_proxy)}" \
|
|
48
|
+
"Internal LLM/action proxy secret — gateway + API must match."
|
|
49
|
+
append_if_missing WORKFRAME_VAULT_KEK "${WORKFRAME_VAULT_KEK:-$(rand_b64)}" \
|
|
50
|
+
"Credential vault KEK (32-byte base64). Required for public_multi_user."
|
|
@@ -57,6 +57,17 @@ function removeIfExists(p) {
|
|
|
57
57
|
if (fs.existsSync(p)) fs.rmSync(p, { recursive: true, force: true });
|
|
58
58
|
}
|
|
59
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
|
+
|
|
60
71
|
console.log(`Sync canonical BFF: ${CANONICAL_API} -> ${PKG_API}`);
|
|
61
72
|
copyTree(CANONICAL_API, PKG_API);
|
|
62
73
|
|
|
@@ -107,11 +118,17 @@ for (const name of applyScripts) {
|
|
|
107
118
|
const src = path.join(REPO_ROOT, 'scripts/workframe', name);
|
|
108
119
|
const dst = path.join(PKG_ROOT, 'scripts', name);
|
|
109
120
|
if (!fs.existsSync(src)) throw new Error(`Missing apply script: ${src}`);
|
|
110
|
-
|
|
111
|
-
fs.copyFileSync(src, dst);
|
|
121
|
+
copyIntoPackage(src, dst);
|
|
112
122
|
console.log(`Synced ${name} -> package/scripts/`);
|
|
113
123
|
}
|
|
114
124
|
|
|
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
|
+
|
|
115
132
|
const publicDeploySrc = path.join(REPO_ROOT, 'infra/compose/workframe/PUBLIC_DEPLOY.md');
|
|
116
133
|
const publicDeployDst = path.join(PKG_ROOT, 'docs/PUBLIC_DEPLOY.md');
|
|
117
134
|
if (fs.existsSync(publicDeploySrc)) {
|