create-workframe 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/NOTICE +12 -12
- package/README.md +8 -92
- package/SECURITY.md +38 -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/bundle-workframe-ui.mjs +3 -3
- package/scripts/ensure-compose-host-paths.mjs +51 -51
- package/scripts/lib/install-identity.mjs +212 -212
- package/scripts/set-compose-public-url.mjs +92 -92
- package/scripts/sync-canonical-to-package.mjs +27 -9
- 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 +26 -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
package/bin/workframe.js
CHANGED
|
@@ -1,329 +1,329 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* workframe — lifecycle CLI for generated Workframe projects.
|
|
4
|
-
*
|
|
5
|
-
* Commands:
|
|
6
|
-
* workframe doctor [--repair] Diagnose stack; --repair provisions missing agent DM runtimes
|
|
7
|
-
* workframe setup Open Hermes setup (credentials)
|
|
8
|
-
* workframe stop Stop all stack containers
|
|
9
|
-
* workframe start Start the full stack (docker compose up -d)
|
|
10
|
-
* workframe restart Restart the full stack
|
|
11
|
-
* workframe status Show running containers
|
|
12
|
-
* workframe logs Tail gateway logs
|
|
13
|
-
* workframe ui Open Workframe UI in browser
|
|
14
|
-
*/
|
|
15
|
-
import fs from 'node:fs';
|
|
16
|
-
import path from 'node:path';
|
|
17
|
-
import { spawn, spawnSync } from 'node:child_process';
|
|
18
|
-
import { fileURLToPath } from 'node:url';
|
|
19
|
-
|
|
20
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
|
|
22
|
-
function findProjectRoot() {
|
|
23
|
-
let dir = process.cwd();
|
|
24
|
-
while (true) {
|
|
25
|
-
if (fs.existsSync(path.join(dir, 'workframe-manifest.json'))) return dir;
|
|
26
|
-
if (fs.existsSync(path.join(dir, 'docker-compose.yml')) && fs.existsSync(path.join(dir, '.env'))) return dir;
|
|
27
|
-
const parent = path.dirname(dir);
|
|
28
|
-
if (parent === dir) return null;
|
|
29
|
-
dir = parent;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function readManifest(root) {
|
|
34
|
-
const p = path.join(root, 'workframe-manifest.json');
|
|
35
|
-
if (!fs.existsSync(p)) return null;
|
|
36
|
-
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function readEnvPort(root, key) {
|
|
40
|
-
const envFile = path.join(root, '.env');
|
|
41
|
-
if (!fs.existsSync(envFile)) return null;
|
|
42
|
-
const line = fs.readFileSync(envFile, 'utf8').split('\n').find((l) => l.startsWith(`${key}=`));
|
|
43
|
-
return line ? line.split('=', 2)[1].trim() : null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function dockerCompose(cwd, args, { stdio = 'inherit' } = {}) {
|
|
47
|
-
return spawnSync('docker', ['compose', ...args], { cwd, stdio, encoding: 'utf8' });
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function doctorAgentDmRuntimes(root, { repair = false } = {}) {
|
|
51
|
-
const fn = repair ? 'doctor_repair_agent_dm_runtimes(repair=True)' : 'doctor_audit_agent_dm_runtimes()';
|
|
52
|
-
const py = `import json, server; print(json.dumps(server.${fn}))`;
|
|
53
|
-
return dockerCompose(root, ['exec', '-T', 'workframe-api', 'python3', '-c', py], { stdio: 'pipe' });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function cmdDoctor(root, extraArgs = []) {
|
|
57
|
-
const repair = extraArgs.includes('--repair');
|
|
58
|
-
const manifest = readManifest(root);
|
|
59
|
-
const issues = [];
|
|
60
|
-
|
|
61
|
-
console.log(repair ? 'workframe doctor --repair' : 'workframe doctor');
|
|
62
|
-
console.log('================\n');
|
|
63
|
-
|
|
64
|
-
// Check Docker
|
|
65
|
-
const dockerCheck = spawnSync('docker', ['info'], { encoding: 'utf8' });
|
|
66
|
-
if (dockerCheck.status !== 0) {
|
|
67
|
-
issues.push('Docker is not running or not installed.');
|
|
68
|
-
console.log('[FAIL] Docker: not running');
|
|
69
|
-
} else {
|
|
70
|
-
console.log('[ OK] Docker: running');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Check manifest
|
|
74
|
-
if (!manifest) {
|
|
75
|
-
issues.push('No workframe-manifest.json found. Are you in a Workframe project?');
|
|
76
|
-
console.log('[FAIL] Manifest: not found');
|
|
77
|
-
} else {
|
|
78
|
-
console.log(`[ OK] Manifest: ${manifest.project_name} (${manifest.docker?.stack})`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Check .env
|
|
82
|
-
const envFile = path.join(root, '.env');
|
|
83
|
-
if (!fs.existsSync(envFile)) {
|
|
84
|
-
issues.push('.env file missing.');
|
|
85
|
-
console.log('[FAIL] .env: missing');
|
|
86
|
-
} else {
|
|
87
|
-
console.log('[ OK] .env: present');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Check docker-compose.yml
|
|
91
|
-
const composeFile = path.join(root, 'docker-compose.yml');
|
|
92
|
-
if (!fs.existsSync(composeFile)) {
|
|
93
|
-
issues.push('docker-compose.yml missing.');
|
|
94
|
-
console.log('[FAIL] docker-compose.yml: missing');
|
|
95
|
-
} else {
|
|
96
|
-
console.log('[ OK] docker-compose.yml: present');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Check required directories
|
|
100
|
-
for (const dir of ['Agents', 'Files']) {
|
|
101
|
-
if (!fs.existsSync(path.join(root, dir))) {
|
|
102
|
-
issues.push(`${dir}/ directory missing.`);
|
|
103
|
-
console.log(`[FAIL] ${dir}/: missing`);
|
|
104
|
-
} else {
|
|
105
|
-
console.log(`[ OK] ${dir}/: present`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Check containers
|
|
110
|
-
if (manifest && dockerCheck.status === 0) {
|
|
111
|
-
const ps = dockerCompose(root, ['ps', '--format', 'json']);
|
|
112
|
-
if (ps.status === 0 && ps.stdout) {
|
|
113
|
-
try {
|
|
114
|
-
const containers = JSON.parse(ps.stdout);
|
|
115
|
-
const expected = ['gateway', 'dashboard', 'workframe-api', 'workframe'];
|
|
116
|
-
for (const name of expected) {
|
|
117
|
-
const container = containers.find((c) => c.Name?.includes(name) || c.Service === name);
|
|
118
|
-
if (!container) {
|
|
119
|
-
issues.push(`${name} container not found.`);
|
|
120
|
-
console.log(`[FAIL] ${name}: not found`);
|
|
121
|
-
} else if (container.State !== 'running') {
|
|
122
|
-
issues.push(`${name} container is ${container.State}, not running.`);
|
|
123
|
-
console.log(`[FAIL] ${name}: ${container.State}`);
|
|
124
|
-
} else {
|
|
125
|
-
console.log(`[ OK] ${name}: running`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
} catch {
|
|
129
|
-
// Fallback: plain text parse
|
|
130
|
-
const psPlain = dockerCompose(root, ['ps']);
|
|
131
|
-
console.log('\nContainer status:\n' + psPlain.stdout);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Check bootstrap
|
|
137
|
-
if (manifest) {
|
|
138
|
-
const nativeSlug = manifest.native_agent?.profile_slug;
|
|
139
|
-
const soulFile = path.join(root, 'Agents', 'profiles', nativeSlug, 'SOUL.md');
|
|
140
|
-
if (!fs.existsSync(soulFile)) {
|
|
141
|
-
issues.push('Native agent not bootstrapped. Run: ./scripts/bootstrap-native.sh');
|
|
142
|
-
console.log('[FAIL] Bootstrap: native SOUL missing');
|
|
143
|
-
} else {
|
|
144
|
-
console.log('[ OK] Bootstrap: native SOUL present');
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Check ports
|
|
149
|
-
if (manifest) {
|
|
150
|
-
const ports = manifest.ports;
|
|
151
|
-
if (ports) {
|
|
152
|
-
console.log(`\nPorts:`);
|
|
153
|
-
console.log(` Gateway: ${ports.gateway}, Dashboard: ${ports.dashboard}, UI: ${ports.ui}, API: ${ports.api}`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Agent DM runtime slots (explicit repair only with --repair)
|
|
158
|
-
if (manifest && dockerCheck.status === 0) {
|
|
159
|
-
const api = dockerCompose(root, ['ps', '--format', 'json'], { stdio: 'pipe' });
|
|
160
|
-
const apiUp = api.status === 0 && (api.stdout || '').includes('workframe-api');
|
|
161
|
-
if (apiUp) {
|
|
162
|
-
const audit = doctorAgentDmRuntimes(root, { repair: false });
|
|
163
|
-
if (audit.status === 0 && audit.stdout) {
|
|
164
|
-
try {
|
|
165
|
-
const data = JSON.parse(audit.stdout.trim().split('\n').pop());
|
|
166
|
-
const missing = data.missing?.length ?? 0;
|
|
167
|
-
if (missing > 0) {
|
|
168
|
-
const msg = `${missing} agent DM runtime profile(s) missing`;
|
|
169
|
-
if (repair) {
|
|
170
|
-
const fixed = doctorAgentDmRuntimes(root, { repair: true });
|
|
171
|
-
if (fixed.status === 0 && fixed.stdout) {
|
|
172
|
-
const result = JSON.parse(fixed.stdout.trim().split('\n').pop());
|
|
173
|
-
const repaired = result.repaired?.length ?? 0;
|
|
174
|
-
const failed = result.failed?.length ?? 0;
|
|
175
|
-
console.log(`[REPAIR] Agent DM runtimes: ${repaired} provisioned, ${failed} failed`);
|
|
176
|
-
if (failed || (result.still_missing?.length ?? 0) > 0) {
|
|
177
|
-
issues.push(msg);
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
issues.push(`${msg} (repair failed)`);
|
|
181
|
-
console.log('[FAIL] Agent DM runtime repair failed');
|
|
182
|
-
}
|
|
183
|
-
} else {
|
|
184
|
-
issues.push(`${msg} — run: workframe doctor --repair`);
|
|
185
|
-
console.log(`[WARN] Agent DM runtimes: ${msg}`);
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
console.log('[ OK] Agent DM runtimes: all provisioned');
|
|
189
|
-
}
|
|
190
|
-
} catch {
|
|
191
|
-
console.log('[skip] Agent DM runtime audit: could not parse API response');
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
console.log('[skip] Agent DM runtime audit: workframe-api not reachable');
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (issues.length > 0) {
|
|
200
|
-
console.log(`\n${issues.length} issue(s) found:\n`);
|
|
201
|
-
issues.forEach((i) => console.log(` - ${i}`));
|
|
202
|
-
process.exit(1);
|
|
203
|
-
} else {
|
|
204
|
-
console.log('\nAll checks passed. Workframe is healthy.');
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function cmdSetup(root) {
|
|
209
|
-
const manifest = readManifest(root);
|
|
210
|
-
const image = manifest?.docker?.image || 'nousresearch/hermes-agent:latest';
|
|
211
|
-
const name = manifest?.docker?.stack || 'workframe';
|
|
212
|
-
console.log('Opening Hermes setup (interactive)...');
|
|
213
|
-
console.log('Credentials never belong in chat.\n');
|
|
214
|
-
dockerCompose(root, ['pull'], { stdio: 'inherit' });
|
|
215
|
-
const res = spawnSync('docker', [
|
|
216
|
-
'run', '--rm', '-it',
|
|
217
|
-
'--name', `${name}-setup`,
|
|
218
|
-
'--entrypoint', 'hermes',
|
|
219
|
-
'-v', `${path.join(root, 'Agents')}:/opt/data`,
|
|
220
|
-
'-v', `${path.join(root, 'Files')}:/workspace`,
|
|
221
|
-
image, 'setup',
|
|
222
|
-
], { stdio: 'inherit' });
|
|
223
|
-
if (res.status !== 0) {
|
|
224
|
-
console.error('Setup failed or was cancelled.');
|
|
225
|
-
process.exit(res.status || 1);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function cmdStop(root) {
|
|
230
|
-
console.log('Stopping Workframe stack...');
|
|
231
|
-
const res = dockerCompose(root, ['down']);
|
|
232
|
-
if (res.status !== 0) {
|
|
233
|
-
console.error('Failed to stop stack.');
|
|
234
|
-
process.exit(1);
|
|
235
|
-
}
|
|
236
|
-
console.log('Stack stopped.');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function cmdStart(root) {
|
|
240
|
-
console.log('Starting Workframe stack...');
|
|
241
|
-
const res = dockerCompose(root, ['up', '-d']);
|
|
242
|
-
if (res.status !== 0) {
|
|
243
|
-
console.error('Failed to start stack.');
|
|
244
|
-
process.exit(1);
|
|
245
|
-
}
|
|
246
|
-
const manifest = readManifest(root);
|
|
247
|
-
if (manifest?.ports) {
|
|
248
|
-
console.log(`\nWorkframe UI: http://127.0.0.1:${manifest.ports.ui}/`);
|
|
249
|
-
console.log(`Hermes chat: http://127.0.0.1:${manifest.ports.dashboard}/chat`);
|
|
250
|
-
}
|
|
251
|
-
console.log('Stack started.');
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function cmdRestart(root) {
|
|
255
|
-
console.log('Restarting Workframe stack...');
|
|
256
|
-
const res = dockerCompose(root, ['restart']);
|
|
257
|
-
if (res.status !== 0) {
|
|
258
|
-
console.error('Failed to restart stack.');
|
|
259
|
-
process.exit(1);
|
|
260
|
-
}
|
|
261
|
-
console.log('Stack restarted.');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function cmdStatus(root) {
|
|
265
|
-
dockerCompose(root, ['ps'], { stdio: 'inherit' });
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function cmdLogs(root, args) {
|
|
269
|
-
const follow = args.includes('--follow') || args.includes('-f');
|
|
270
|
-
const composeArgs = ['logs'];
|
|
271
|
-
if (follow) composeArgs.push('--follow');
|
|
272
|
-
composeArgs.push('gateway');
|
|
273
|
-
dockerCompose(root, composeArgs, { stdio: 'inherit' });
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function cmdUi(root) {
|
|
277
|
-
const manifest = readManifest(root);
|
|
278
|
-
const port = readEnvPort(root, 'WORKFRAME_UI_PORT') || manifest?.ports?.ui || '18644';
|
|
279
|
-
const url = `http://127.0.0.1:${port}/`;
|
|
280
|
-
console.log(`Opening Workframe UI: ${url}`);
|
|
281
|
-
const openCmd = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
282
|
-
const child = spawn(openCmd, [url], { detached: true, stdio: 'ignore' });
|
|
283
|
-
child.unref();
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function usage() {
|
|
287
|
-
console.log(`workframe — lifecycle CLI for Workframe projects
|
|
288
|
-
|
|
289
|
-
Usage:
|
|
290
|
-
workframe doctor [--repair] Diagnose stack; --repair provisions missing agent DM runtimes
|
|
291
|
-
workframe setup Open Hermes setup (credentials)
|
|
292
|
-
workframe start Start the full stack (docker compose up -d)
|
|
293
|
-
workframe stop Stop all stack containers
|
|
294
|
-
workframe restart Restart the full stack
|
|
295
|
-
workframe status Show running containers
|
|
296
|
-
workframe logs [--follow] Tail gateway logs
|
|
297
|
-
workframe ui Open Workframe UI in browser
|
|
298
|
-
|
|
299
|
-
Run from a Workframe project directory (where workframe-manifest.json lives).
|
|
300
|
-
`);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const args = process.argv.slice(2);
|
|
304
|
-
const command = args[0];
|
|
305
|
-
const extraArgs = args.slice(1);
|
|
306
|
-
|
|
307
|
-
const root = findProjectRoot();
|
|
308
|
-
if (!root) {
|
|
309
|
-
console.error('ERROR: Not in a Workframe project. No workframe-manifest.json found.');
|
|
310
|
-
process.exit(1);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
switch (command) {
|
|
314
|
-
case 'doctor': cmdDoctor(root, extraArgs); break;
|
|
315
|
-
case 'setup': cmdSetup(root); break;
|
|
316
|
-
case 'stop': cmdStop(root); break;
|
|
317
|
-
case 'start': cmdStart(root); break;
|
|
318
|
-
case 'restart': cmdRestart(root); break;
|
|
319
|
-
case 'status': cmdStatus(root); break;
|
|
320
|
-
case 'logs': cmdLogs(root, extraArgs); break;
|
|
321
|
-
case 'ui': cmdUi(root); break;
|
|
322
|
-
case '--help':
|
|
323
|
-
case '-h':
|
|
324
|
-
case 'help': usage(); break;
|
|
325
|
-
default:
|
|
326
|
-
if (command) console.error(`Unknown command: ${command}`);
|
|
327
|
-
usage();
|
|
328
|
-
process.exit(command ? 1 : 0);
|
|
329
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* workframe — lifecycle CLI for generated Workframe projects.
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* workframe doctor [--repair] Diagnose stack; --repair provisions missing agent DM runtimes
|
|
7
|
+
* workframe setup Open Hermes setup (credentials)
|
|
8
|
+
* workframe stop Stop all stack containers
|
|
9
|
+
* workframe start Start the full stack (docker compose up -d)
|
|
10
|
+
* workframe restart Restart the full stack
|
|
11
|
+
* workframe status Show running containers
|
|
12
|
+
* workframe logs Tail gateway logs
|
|
13
|
+
* workframe ui Open Workframe UI in browser
|
|
14
|
+
*/
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
|
|
20
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
|
|
22
|
+
function findProjectRoot() {
|
|
23
|
+
let dir = process.cwd();
|
|
24
|
+
while (true) {
|
|
25
|
+
if (fs.existsSync(path.join(dir, 'workframe-manifest.json'))) return dir;
|
|
26
|
+
if (fs.existsSync(path.join(dir, 'docker-compose.yml')) && fs.existsSync(path.join(dir, '.env'))) return dir;
|
|
27
|
+
const parent = path.dirname(dir);
|
|
28
|
+
if (parent === dir) return null;
|
|
29
|
+
dir = parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readManifest(root) {
|
|
34
|
+
const p = path.join(root, 'workframe-manifest.json');
|
|
35
|
+
if (!fs.existsSync(p)) return null;
|
|
36
|
+
return JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readEnvPort(root, key) {
|
|
40
|
+
const envFile = path.join(root, '.env');
|
|
41
|
+
if (!fs.existsSync(envFile)) return null;
|
|
42
|
+
const line = fs.readFileSync(envFile, 'utf8').split('\n').find((l) => l.startsWith(`${key}=`));
|
|
43
|
+
return line ? line.split('=', 2)[1].trim() : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function dockerCompose(cwd, args, { stdio = 'inherit' } = {}) {
|
|
47
|
+
return spawnSync('docker', ['compose', ...args], { cwd, stdio, encoding: 'utf8' });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function doctorAgentDmRuntimes(root, { repair = false } = {}) {
|
|
51
|
+
const fn = repair ? 'doctor_repair_agent_dm_runtimes(repair=True)' : 'doctor_audit_agent_dm_runtimes()';
|
|
52
|
+
const py = `import json, server; print(json.dumps(server.${fn}))`;
|
|
53
|
+
return dockerCompose(root, ['exec', '-T', 'workframe-api', 'python3', '-c', py], { stdio: 'pipe' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function cmdDoctor(root, extraArgs = []) {
|
|
57
|
+
const repair = extraArgs.includes('--repair');
|
|
58
|
+
const manifest = readManifest(root);
|
|
59
|
+
const issues = [];
|
|
60
|
+
|
|
61
|
+
console.log(repair ? 'workframe doctor --repair' : 'workframe doctor');
|
|
62
|
+
console.log('================\n');
|
|
63
|
+
|
|
64
|
+
// Check Docker
|
|
65
|
+
const dockerCheck = spawnSync('docker', ['info'], { encoding: 'utf8' });
|
|
66
|
+
if (dockerCheck.status !== 0) {
|
|
67
|
+
issues.push('Docker is not running or not installed.');
|
|
68
|
+
console.log('[FAIL] Docker: not running');
|
|
69
|
+
} else {
|
|
70
|
+
console.log('[ OK] Docker: running');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check manifest
|
|
74
|
+
if (!manifest) {
|
|
75
|
+
issues.push('No workframe-manifest.json found. Are you in a Workframe project?');
|
|
76
|
+
console.log('[FAIL] Manifest: not found');
|
|
77
|
+
} else {
|
|
78
|
+
console.log(`[ OK] Manifest: ${manifest.project_name} (${manifest.docker?.stack})`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Check .env
|
|
82
|
+
const envFile = path.join(root, '.env');
|
|
83
|
+
if (!fs.existsSync(envFile)) {
|
|
84
|
+
issues.push('.env file missing.');
|
|
85
|
+
console.log('[FAIL] .env: missing');
|
|
86
|
+
} else {
|
|
87
|
+
console.log('[ OK] .env: present');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check docker-compose.yml
|
|
91
|
+
const composeFile = path.join(root, 'docker-compose.yml');
|
|
92
|
+
if (!fs.existsSync(composeFile)) {
|
|
93
|
+
issues.push('docker-compose.yml missing.');
|
|
94
|
+
console.log('[FAIL] docker-compose.yml: missing');
|
|
95
|
+
} else {
|
|
96
|
+
console.log('[ OK] docker-compose.yml: present');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Check required directories
|
|
100
|
+
for (const dir of ['Agents', 'Files']) {
|
|
101
|
+
if (!fs.existsSync(path.join(root, dir))) {
|
|
102
|
+
issues.push(`${dir}/ directory missing.`);
|
|
103
|
+
console.log(`[FAIL] ${dir}/: missing`);
|
|
104
|
+
} else {
|
|
105
|
+
console.log(`[ OK] ${dir}/: present`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check containers
|
|
110
|
+
if (manifest && dockerCheck.status === 0) {
|
|
111
|
+
const ps = dockerCompose(root, ['ps', '--format', 'json']);
|
|
112
|
+
if (ps.status === 0 && ps.stdout) {
|
|
113
|
+
try {
|
|
114
|
+
const containers = JSON.parse(ps.stdout);
|
|
115
|
+
const expected = ['gateway', 'dashboard', 'workframe-api', 'workframe'];
|
|
116
|
+
for (const name of expected) {
|
|
117
|
+
const container = containers.find((c) => c.Name?.includes(name) || c.Service === name);
|
|
118
|
+
if (!container) {
|
|
119
|
+
issues.push(`${name} container not found.`);
|
|
120
|
+
console.log(`[FAIL] ${name}: not found`);
|
|
121
|
+
} else if (container.State !== 'running') {
|
|
122
|
+
issues.push(`${name} container is ${container.State}, not running.`);
|
|
123
|
+
console.log(`[FAIL] ${name}: ${container.State}`);
|
|
124
|
+
} else {
|
|
125
|
+
console.log(`[ OK] ${name}: running`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
// Fallback: plain text parse
|
|
130
|
+
const psPlain = dockerCompose(root, ['ps']);
|
|
131
|
+
console.log('\nContainer status:\n' + psPlain.stdout);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check bootstrap
|
|
137
|
+
if (manifest) {
|
|
138
|
+
const nativeSlug = manifest.native_agent?.profile_slug;
|
|
139
|
+
const soulFile = path.join(root, 'Agents', 'profiles', nativeSlug, 'SOUL.md');
|
|
140
|
+
if (!fs.existsSync(soulFile)) {
|
|
141
|
+
issues.push('Native agent not bootstrapped. Run: ./scripts/bootstrap-native.sh');
|
|
142
|
+
console.log('[FAIL] Bootstrap: native SOUL missing');
|
|
143
|
+
} else {
|
|
144
|
+
console.log('[ OK] Bootstrap: native SOUL present');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check ports
|
|
149
|
+
if (manifest) {
|
|
150
|
+
const ports = manifest.ports;
|
|
151
|
+
if (ports) {
|
|
152
|
+
console.log(`\nPorts:`);
|
|
153
|
+
console.log(` Gateway: ${ports.gateway}, Dashboard: ${ports.dashboard}, UI: ${ports.ui}, API: ${ports.api}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Agent DM runtime slots (explicit repair only with --repair)
|
|
158
|
+
if (manifest && dockerCheck.status === 0) {
|
|
159
|
+
const api = dockerCompose(root, ['ps', '--format', 'json'], { stdio: 'pipe' });
|
|
160
|
+
const apiUp = api.status === 0 && (api.stdout || '').includes('workframe-api');
|
|
161
|
+
if (apiUp) {
|
|
162
|
+
const audit = doctorAgentDmRuntimes(root, { repair: false });
|
|
163
|
+
if (audit.status === 0 && audit.stdout) {
|
|
164
|
+
try {
|
|
165
|
+
const data = JSON.parse(audit.stdout.trim().split('\n').pop());
|
|
166
|
+
const missing = data.missing?.length ?? 0;
|
|
167
|
+
if (missing > 0) {
|
|
168
|
+
const msg = `${missing} agent DM runtime profile(s) missing`;
|
|
169
|
+
if (repair) {
|
|
170
|
+
const fixed = doctorAgentDmRuntimes(root, { repair: true });
|
|
171
|
+
if (fixed.status === 0 && fixed.stdout) {
|
|
172
|
+
const result = JSON.parse(fixed.stdout.trim().split('\n').pop());
|
|
173
|
+
const repaired = result.repaired?.length ?? 0;
|
|
174
|
+
const failed = result.failed?.length ?? 0;
|
|
175
|
+
console.log(`[REPAIR] Agent DM runtimes: ${repaired} provisioned, ${failed} failed`);
|
|
176
|
+
if (failed || (result.still_missing?.length ?? 0) > 0) {
|
|
177
|
+
issues.push(msg);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
issues.push(`${msg} (repair failed)`);
|
|
181
|
+
console.log('[FAIL] Agent DM runtime repair failed');
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
issues.push(`${msg} — run: workframe doctor --repair`);
|
|
185
|
+
console.log(`[WARN] Agent DM runtimes: ${msg}`);
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
console.log('[ OK] Agent DM runtimes: all provisioned');
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
console.log('[skip] Agent DM runtime audit: could not parse API response');
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
console.log('[skip] Agent DM runtime audit: workframe-api not reachable');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (issues.length > 0) {
|
|
200
|
+
console.log(`\n${issues.length} issue(s) found:\n`);
|
|
201
|
+
issues.forEach((i) => console.log(` - ${i}`));
|
|
202
|
+
process.exit(1);
|
|
203
|
+
} else {
|
|
204
|
+
console.log('\nAll checks passed. Workframe is healthy.');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function cmdSetup(root) {
|
|
209
|
+
const manifest = readManifest(root);
|
|
210
|
+
const image = manifest?.docker?.image || 'nousresearch/hermes-agent:latest';
|
|
211
|
+
const name = manifest?.docker?.stack || 'workframe';
|
|
212
|
+
console.log('Opening Hermes setup (interactive)...');
|
|
213
|
+
console.log('Credentials never belong in chat.\n');
|
|
214
|
+
dockerCompose(root, ['pull'], { stdio: 'inherit' });
|
|
215
|
+
const res = spawnSync('docker', [
|
|
216
|
+
'run', '--rm', '-it',
|
|
217
|
+
'--name', `${name}-setup`,
|
|
218
|
+
'--entrypoint', 'hermes',
|
|
219
|
+
'-v', `${path.join(root, 'Agents')}:/opt/data`,
|
|
220
|
+
'-v', `${path.join(root, 'Files')}:/workspace`,
|
|
221
|
+
image, 'setup',
|
|
222
|
+
], { stdio: 'inherit' });
|
|
223
|
+
if (res.status !== 0) {
|
|
224
|
+
console.error('Setup failed or was cancelled.');
|
|
225
|
+
process.exit(res.status || 1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function cmdStop(root) {
|
|
230
|
+
console.log('Stopping Workframe stack...');
|
|
231
|
+
const res = dockerCompose(root, ['down']);
|
|
232
|
+
if (res.status !== 0) {
|
|
233
|
+
console.error('Failed to stop stack.');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
console.log('Stack stopped.');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function cmdStart(root) {
|
|
240
|
+
console.log('Starting Workframe stack...');
|
|
241
|
+
const res = dockerCompose(root, ['up', '-d']);
|
|
242
|
+
if (res.status !== 0) {
|
|
243
|
+
console.error('Failed to start stack.');
|
|
244
|
+
process.exit(1);
|
|
245
|
+
}
|
|
246
|
+
const manifest = readManifest(root);
|
|
247
|
+
if (manifest?.ports) {
|
|
248
|
+
console.log(`\nWorkframe UI: http://127.0.0.1:${manifest.ports.ui}/`);
|
|
249
|
+
console.log(`Hermes chat: http://127.0.0.1:${manifest.ports.dashboard}/chat`);
|
|
250
|
+
}
|
|
251
|
+
console.log('Stack started.');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function cmdRestart(root) {
|
|
255
|
+
console.log('Restarting Workframe stack...');
|
|
256
|
+
const res = dockerCompose(root, ['restart']);
|
|
257
|
+
if (res.status !== 0) {
|
|
258
|
+
console.error('Failed to restart stack.');
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
console.log('Stack restarted.');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function cmdStatus(root) {
|
|
265
|
+
dockerCompose(root, ['ps'], { stdio: 'inherit' });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function cmdLogs(root, args) {
|
|
269
|
+
const follow = args.includes('--follow') || args.includes('-f');
|
|
270
|
+
const composeArgs = ['logs'];
|
|
271
|
+
if (follow) composeArgs.push('--follow');
|
|
272
|
+
composeArgs.push('gateway');
|
|
273
|
+
dockerCompose(root, composeArgs, { stdio: 'inherit' });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function cmdUi(root) {
|
|
277
|
+
const manifest = readManifest(root);
|
|
278
|
+
const port = readEnvPort(root, 'WORKFRAME_UI_PORT') || manifest?.ports?.ui || '18644';
|
|
279
|
+
const url = `http://127.0.0.1:${port}/`;
|
|
280
|
+
console.log(`Opening Workframe UI: ${url}`);
|
|
281
|
+
const openCmd = process.platform === 'win32' ? 'start' : process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
282
|
+
const child = spawn(openCmd, [url], { detached: true, stdio: 'ignore' });
|
|
283
|
+
child.unref();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function usage() {
|
|
287
|
+
console.log(`workframe — lifecycle CLI for Workframe projects
|
|
288
|
+
|
|
289
|
+
Usage:
|
|
290
|
+
workframe doctor [--repair] Diagnose stack; --repair provisions missing agent DM runtimes
|
|
291
|
+
workframe setup Open Hermes setup (credentials)
|
|
292
|
+
workframe start Start the full stack (docker compose up -d)
|
|
293
|
+
workframe stop Stop all stack containers
|
|
294
|
+
workframe restart Restart the full stack
|
|
295
|
+
workframe status Show running containers
|
|
296
|
+
workframe logs [--follow] Tail gateway logs
|
|
297
|
+
workframe ui Open Workframe UI in browser
|
|
298
|
+
|
|
299
|
+
Run from a Workframe project directory (where workframe-manifest.json lives).
|
|
300
|
+
`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const args = process.argv.slice(2);
|
|
304
|
+
const command = args[0];
|
|
305
|
+
const extraArgs = args.slice(1);
|
|
306
|
+
|
|
307
|
+
const root = findProjectRoot();
|
|
308
|
+
if (!root) {
|
|
309
|
+
console.error('ERROR: Not in a Workframe project. No workframe-manifest.json found.');
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
switch (command) {
|
|
314
|
+
case 'doctor': cmdDoctor(root, extraArgs); break;
|
|
315
|
+
case 'setup': cmdSetup(root); break;
|
|
316
|
+
case 'stop': cmdStop(root); break;
|
|
317
|
+
case 'start': cmdStart(root); break;
|
|
318
|
+
case 'restart': cmdRestart(root); break;
|
|
319
|
+
case 'status': cmdStatus(root); break;
|
|
320
|
+
case 'logs': cmdLogs(root, extraArgs); break;
|
|
321
|
+
case 'ui': cmdUi(root); break;
|
|
322
|
+
case '--help':
|
|
323
|
+
case '-h':
|
|
324
|
+
case 'help': usage(); break;
|
|
325
|
+
default:
|
|
326
|
+
if (command) console.error(`Unknown command: ${command}`);
|
|
327
|
+
usage();
|
|
328
|
+
process.exit(command ? 1 : 0);
|
|
329
|
+
}
|
|
@@ -5,7 +5,7 @@ Goal
|
|
|
5
5
|
|
|
6
6
|
Concierge onboarding loop
|
|
7
7
|
1) clarify mission, scope, constraints, success criteria
|
|
8
|
-
2) default to native-only starter, then expand with specialist packs or individual profiles on demand
|
|
8
|
+
2) default to native-only starter, then expand with specialist packs or individual profiles on demand
|
|
9
9
|
3) establish file-based source of truth
|
|
10
10
|
4) route first tasks through Kanban
|
|
11
11
|
5) persist outcomes in project files
|