@arkhera30/cli 0.2.2 → 0.2.4
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/compose/docker-compose.yml +3 -57
- package/dist/index.js +118 -55
- package/package.json +3 -2
|
@@ -16,40 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
services:
|
|
18
18
|
|
|
19
|
-
# ── QMD Daemon ─────────────────────────────────────────────────────────────
|
|
20
|
-
# Shared QMD MCP HTTP server. Keeps GGUF models warm in memory so Anvil and
|
|
21
|
-
# Vault pay the model-load cost only once.
|
|
22
|
-
#
|
|
23
|
-
# The qmd-daemon-data volume is also mounted into the Anvil and Vault
|
|
24
|
-
# containers at their respective ~/.cache/qmd paths. This lets both services
|
|
25
|
-
# run `qmd collection add` / `qmd update` subprocess calls that write to the
|
|
26
|
-
# same SQLite database the daemon reads from — keeping the index current.
|
|
27
|
-
#
|
|
28
|
-
# start_period covers first-boot GGUF model download (~1-2 GB) + initial embed.
|
|
29
|
-
qmd-daemon:
|
|
30
|
-
image: ghcr.io/arjunkhera/horus/qmd-daemon:latest
|
|
31
|
-
environment:
|
|
32
|
-
- QMD_DAEMON_PORT=8181
|
|
33
|
-
- HORUS_RUNTIME=${HORUS_RUNTIME:-docker}
|
|
34
|
-
volumes:
|
|
35
|
-
- qmd-daemon-data:/home/qmd/.cache/qmd
|
|
36
|
-
networks:
|
|
37
|
-
- horus-net
|
|
38
|
-
restart: unless-stopped
|
|
39
|
-
stop_grace_period: 15s
|
|
40
|
-
deploy:
|
|
41
|
-
resources:
|
|
42
|
-
limits:
|
|
43
|
-
memory: 4g
|
|
44
|
-
reservations:
|
|
45
|
-
memory: 512m
|
|
46
|
-
healthcheck:
|
|
47
|
-
test: ["CMD", "curl", "-f", "http://localhost:8181/health"]
|
|
48
|
-
interval: 30s
|
|
49
|
-
timeout: 10s
|
|
50
|
-
start_period: 600s
|
|
51
|
-
retries: 3
|
|
52
|
-
|
|
53
19
|
# ── Anvil ──────────────────────────────────────────────────────────────────
|
|
54
20
|
# Notes system and MCP server. Indexes markdown files from the Notes repo.
|
|
55
21
|
anvil:
|
|
@@ -59,8 +25,6 @@ services:
|
|
|
59
25
|
volumes:
|
|
60
26
|
# Notes repo — read/write so Anvil can git-sync or clone on first boot
|
|
61
27
|
- ${HORUS_DATA_PATH}/notes:/data/notes:rw
|
|
62
|
-
# Shared QMD database + model cache (same volume as qmd-daemon).
|
|
63
|
-
- qmd-daemon-data:/home/anvil/.cache/qmd
|
|
64
28
|
environment:
|
|
65
29
|
- HORUS_RUNTIME=${HORUS_RUNTIME:-docker}
|
|
66
30
|
- ANVIL_TRANSPORT=http
|
|
@@ -68,15 +32,9 @@ services:
|
|
|
68
32
|
- ANVIL_HOST=0.0.0.0
|
|
69
33
|
- ANVIL_NOTES_PATH=/data/notes
|
|
70
34
|
- ANVIL_REPO_URL=${ANVIL_REPO_URL:-}
|
|
71
|
-
- ANVIL_QMD_COLLECTION=${ANVIL_QMD_COLLECTION:-anvil}
|
|
72
35
|
- ANVIL_SYNC_INTERVAL=${ANVIL_SYNC_INTERVAL:-300}
|
|
73
36
|
- ANVIL_DEBOUNCE_SECONDS=${ANVIL_DEBOUNCE_SECONDS:-5}
|
|
74
37
|
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
|
75
|
-
# Route search calls to the shared daemon; fall back to subprocess if unset.
|
|
76
|
-
- QMD_DAEMON_URL=http://qmd-daemon:8181
|
|
77
|
-
depends_on:
|
|
78
|
-
qmd-daemon:
|
|
79
|
-
condition: service_healthy
|
|
80
38
|
networks:
|
|
81
39
|
- horus-net
|
|
82
40
|
restart: unless-stopped
|
|
@@ -91,11 +49,11 @@ services:
|
|
|
91
49
|
test: ["CMD", "curl", "-f", "http://localhost:8100/health"]
|
|
92
50
|
interval: 30s
|
|
93
51
|
timeout: 5s
|
|
94
|
-
start_period:
|
|
52
|
+
start_period: 60s
|
|
95
53
|
retries: 3
|
|
96
54
|
|
|
97
55
|
# ── Vault ──────────────────────────────────────────────────────────────────
|
|
98
|
-
# Knowledge service.
|
|
56
|
+
# Knowledge service. Search over the knowledge-base repo.
|
|
99
57
|
vault:
|
|
100
58
|
image: ghcr.io/arjunkhera/horus/vault:latest
|
|
101
59
|
ports:
|
|
@@ -105,25 +63,17 @@ services:
|
|
|
105
63
|
- ${HORUS_DATA_PATH}/knowledge-base:/data/knowledge-repo:rw
|
|
106
64
|
# Write-path workspace: staging area for draft pages before PR
|
|
107
65
|
- vault-workspace:/data/workspace
|
|
108
|
-
# Shared QMD database + model cache (same volume as qmd-daemon).
|
|
109
|
-
- qmd-daemon-data:/home/appuser/.cache/qmd
|
|
110
66
|
environment:
|
|
111
67
|
- HORUS_RUNTIME=${HORUS_RUNTIME:-docker}
|
|
112
68
|
- KNOWLEDGE_REPO_PATH=/data/knowledge-repo
|
|
113
69
|
- WORKSPACE_PATH=/data/workspace
|
|
114
70
|
- VAULT_KNOWLEDGE_REPO_URL=${VAULT_KNOWLEDGE_REPO_URL:-}
|
|
115
|
-
- QMD_INDEX_NAME=${QMD_INDEX_NAME:-knowledge}
|
|
116
71
|
- SYNC_INTERVAL=${VAULT_SYNC_INTERVAL:-300}
|
|
117
72
|
- VAULT_SYNC_INTERVAL=${VAULT_SYNC_INTERVAL:-300}
|
|
118
73
|
- LOG_LEVEL=${LOG_LEVEL:-info}
|
|
119
74
|
- HOST=0.0.0.0
|
|
120
75
|
- PORT=8000
|
|
121
76
|
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
|
122
|
-
# Route search calls to the shared daemon; fall back to subprocess if unset.
|
|
123
|
-
- QMD_DAEMON_URL=http://qmd-daemon:8181
|
|
124
|
-
depends_on:
|
|
125
|
-
qmd-daemon:
|
|
126
|
-
condition: service_healthy
|
|
127
77
|
networks:
|
|
128
78
|
- horus-net
|
|
129
79
|
restart: unless-stopped
|
|
@@ -138,7 +88,7 @@ services:
|
|
|
138
88
|
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
|
139
89
|
interval: 30s
|
|
140
90
|
timeout: 10s
|
|
141
|
-
start_period:
|
|
91
|
+
start_period: 60s
|
|
142
92
|
retries: 3
|
|
143
93
|
|
|
144
94
|
# ── Vault MCP ──────────────────────────────────────────────────────────────
|
|
@@ -236,9 +186,5 @@ networks:
|
|
|
236
186
|
|
|
237
187
|
# ── Volumes ───────────────────────────────────────────────────────────────────
|
|
238
188
|
volumes:
|
|
239
|
-
# Shared QMD daemon database + GGUF model cache.
|
|
240
|
-
# Mounted into qmd-daemon, Anvil, and Vault so all three share one SQLite index.
|
|
241
|
-
# Persists model downloads (~1-2 GB) and index across container rebuilds.
|
|
242
|
-
qmd-daemon-data:
|
|
243
189
|
# Vault write-path staging workspace
|
|
244
190
|
vault-workspace:
|
package/dist/index.js
CHANGED
|
@@ -48,18 +48,23 @@ var DEFAULT_PORTS = {
|
|
|
48
48
|
vault_rest: 8e3,
|
|
49
49
|
// keep for individual vault instances
|
|
50
50
|
vault_mcp: 8300,
|
|
51
|
-
vault_router:
|
|
52
|
-
//
|
|
53
|
-
|
|
51
|
+
vault_router: 8050,
|
|
52
|
+
// internal routing layer
|
|
53
|
+
ui: 8400,
|
|
54
|
+
// horus-ui — user-facing web interface
|
|
55
|
+
forge: 8200,
|
|
56
|
+
typesense: 8108
|
|
57
|
+
// Typesense search engine
|
|
54
58
|
};
|
|
55
59
|
var DEFAULT_DATA_DIR = join(homedir(), "Horus", "data");
|
|
56
60
|
var SERVICES = [
|
|
57
|
-
"qmd-daemon",
|
|
58
61
|
"anvil",
|
|
59
62
|
"vault-router",
|
|
60
63
|
// replaces 'vault'
|
|
61
64
|
"vault-mcp",
|
|
62
|
-
"forge"
|
|
65
|
+
"forge",
|
|
66
|
+
"horus-ui",
|
|
67
|
+
"typesense"
|
|
63
68
|
];
|
|
64
69
|
var CONFIG_VERSION = "1.0";
|
|
65
70
|
|
|
@@ -74,10 +79,14 @@ function defaultConfig() {
|
|
|
74
79
|
anvil_notes: "",
|
|
75
80
|
forge_registry: ""
|
|
76
81
|
},
|
|
82
|
+
search: {
|
|
83
|
+
api_key: "horus-local-key"
|
|
84
|
+
},
|
|
77
85
|
vaults: {},
|
|
78
86
|
github_hosts: {},
|
|
79
87
|
host_repos_path: "",
|
|
80
|
-
host_repos_extra_scan_dirs: []
|
|
88
|
+
host_repos_extra_scan_dirs: [],
|
|
89
|
+
enable_ui: true
|
|
81
90
|
};
|
|
82
91
|
}
|
|
83
92
|
function ensureHorusDir() {
|
|
@@ -124,16 +133,21 @@ function buildConfigFromParsed(parsed) {
|
|
|
124
133
|
vault_rest: parsedPorts?.vault_rest ?? defaults.ports.vault_rest,
|
|
125
134
|
vault_mcp: parsedPorts?.vault_mcp ?? defaults.ports.vault_mcp,
|
|
126
135
|
vault_router: parsedPorts?.vault_router ?? defaults.ports.vault_router,
|
|
127
|
-
forge: parsedPorts?.forge ?? defaults.ports.forge
|
|
136
|
+
forge: parsedPorts?.forge ?? defaults.ports.forge,
|
|
137
|
+
typesense: parsedPorts?.typesense ?? defaults.ports.typesense
|
|
128
138
|
},
|
|
129
139
|
repos: {
|
|
130
140
|
anvil_notes: repos?.anvil_notes ?? defaults.repos.anvil_notes,
|
|
131
141
|
forge_registry: repos?.forge_registry ?? defaults.repos.forge_registry
|
|
132
142
|
},
|
|
143
|
+
search: {
|
|
144
|
+
api_key: parsed.search?.api_key ?? defaults.search.api_key
|
|
145
|
+
},
|
|
133
146
|
vaults: parsed.vaults ?? defaults.vaults,
|
|
134
147
|
github_hosts: parsed.github_hosts ?? defaults.github_hosts,
|
|
135
148
|
host_repos_path: parsed.host_repos_path ?? defaults.host_repos_path,
|
|
136
|
-
host_repos_extra_scan_dirs: parsed.host_repos_extra_scan_dirs ?? defaults.host_repos_extra_scan_dirs
|
|
149
|
+
host_repos_extra_scan_dirs: parsed.host_repos_extra_scan_dirs ?? defaults.host_repos_extra_scan_dirs,
|
|
150
|
+
enable_ui: parsed.enable_ui ?? defaults.enable_ui
|
|
137
151
|
};
|
|
138
152
|
}
|
|
139
153
|
function saveConfig(config) {
|
|
@@ -220,6 +234,10 @@ function generateEnv(config) {
|
|
|
220
234
|
`VAULT_MCP_PORT=${config.ports.vault_mcp}`,
|
|
221
235
|
`VAULT_ROUTER_PORT=${config.ports.vault_router}`,
|
|
222
236
|
`FORGE_PORT=${config.ports.forge}`,
|
|
237
|
+
`TYPESENSE_PORT=${config.ports.typesense}`,
|
|
238
|
+
"",
|
|
239
|
+
"# Search",
|
|
240
|
+
`TYPESENSE_API_KEY=${config.search.api_key}`,
|
|
223
241
|
"",
|
|
224
242
|
"# Repository URLs (must be HTTPS \u2014 container services do not have SSH keys)",
|
|
225
243
|
`ANVIL_REPO_URL=${config.repos.anvil_notes}`,
|
|
@@ -243,8 +261,11 @@ var CONFIG_KEYS = [
|
|
|
243
261
|
"port.vault-mcp",
|
|
244
262
|
"port.vault-router",
|
|
245
263
|
"port.forge",
|
|
264
|
+
"port.typesense",
|
|
246
265
|
"repo.anvil-notes",
|
|
247
|
-
"repo.forge-registry"
|
|
266
|
+
"repo.forge-registry",
|
|
267
|
+
"search.api-key",
|
|
268
|
+
"enable-ui"
|
|
248
269
|
];
|
|
249
270
|
function getConfigValue(config, key) {
|
|
250
271
|
switch (key) {
|
|
@@ -266,10 +287,16 @@ function getConfigValue(config, key) {
|
|
|
266
287
|
return String(config.ports.vault_router);
|
|
267
288
|
case "port.forge":
|
|
268
289
|
return String(config.ports.forge);
|
|
290
|
+
case "port.typesense":
|
|
291
|
+
return String(config.ports.typesense);
|
|
269
292
|
case "repo.anvil-notes":
|
|
270
293
|
return config.repos.anvil_notes;
|
|
271
294
|
case "repo.forge-registry":
|
|
272
295
|
return config.repos.forge_registry;
|
|
296
|
+
case "search.api-key":
|
|
297
|
+
return config.search.api_key;
|
|
298
|
+
case "enable-ui":
|
|
299
|
+
return String(config.enable_ui);
|
|
273
300
|
}
|
|
274
301
|
}
|
|
275
302
|
function setConfigValue(config, key, value) {
|
|
@@ -305,12 +332,24 @@ function setConfigValue(config, key, value) {
|
|
|
305
332
|
case "port.forge":
|
|
306
333
|
updated.ports = { ...updated.ports, forge: parseInt(value, 10) };
|
|
307
334
|
break;
|
|
335
|
+
case "port.typesense":
|
|
336
|
+
updated.ports = { ...updated.ports, typesense: parseInt(value, 10) };
|
|
337
|
+
break;
|
|
308
338
|
case "repo.anvil-notes":
|
|
309
339
|
updated.repos = { ...updated.repos, anvil_notes: value };
|
|
310
340
|
break;
|
|
311
341
|
case "repo.forge-registry":
|
|
312
342
|
updated.repos = { ...updated.repos, forge_registry: value };
|
|
313
343
|
break;
|
|
344
|
+
case "search.api-key":
|
|
345
|
+
updated.search = { ...updated.search, api_key: value };
|
|
346
|
+
break;
|
|
347
|
+
case "enable-ui":
|
|
348
|
+
if (value !== "true" && value !== "false") {
|
|
349
|
+
throw new Error(`Invalid value for enable-ui: ${value}. Must be "true" or "false".`);
|
|
350
|
+
}
|
|
351
|
+
updated.enable_ui = value === "true";
|
|
352
|
+
break;
|
|
314
353
|
}
|
|
315
354
|
return updated;
|
|
316
355
|
}
|
|
@@ -543,32 +582,6 @@ function applyPodmanUserOverride(compose) {
|
|
|
543
582
|
'$1\n user: "0:0"'
|
|
544
583
|
);
|
|
545
584
|
}
|
|
546
|
-
var QMD_DAEMON_SERVICE = ` # \u2500\u2500 QMD Daemon \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
547
|
-
# Shared QMD MCP HTTP server. Keeps GGUF models warm in memory so Anvil and
|
|
548
|
-
# Vault pay the model-load cost only once.
|
|
549
|
-
qmd-daemon:
|
|
550
|
-
image: ghcr.io/arjunkhera/horus/qmd-daemon:latest
|
|
551
|
-
environment:
|
|
552
|
-
- QMD_DAEMON_PORT=8181
|
|
553
|
-
- HORUS_RUNTIME=\${HORUS_RUNTIME:-docker}
|
|
554
|
-
volumes:
|
|
555
|
-
- qmd-daemon-data:/home/qmd/.cache/qmd
|
|
556
|
-
networks:
|
|
557
|
-
- horus-net
|
|
558
|
-
restart: unless-stopped
|
|
559
|
-
stop_grace_period: 15s
|
|
560
|
-
deploy:
|
|
561
|
-
resources:
|
|
562
|
-
limits:
|
|
563
|
-
memory: 4g
|
|
564
|
-
reservations:
|
|
565
|
-
memory: 512m
|
|
566
|
-
healthcheck:
|
|
567
|
-
test: ["CMD", "curl", "-f", "http://localhost:8181/health"]
|
|
568
|
-
interval: 30s
|
|
569
|
-
timeout: 10s
|
|
570
|
-
start_period: 600s
|
|
571
|
-
retries: 3`;
|
|
572
585
|
var ANVIL_SERVICE = ` # \u2500\u2500 Anvil \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
573
586
|
# Notes system and MCP server. Indexes markdown files from the Notes repo.
|
|
574
587
|
anvil:
|
|
@@ -577,7 +590,6 @@ var ANVIL_SERVICE = ` # \u2500\u2500 Anvil \u2500\u2500\u2500\u2500\u2500\u2500
|
|
|
577
590
|
- "\${ANVIL_PORT:-8100}:8100"
|
|
578
591
|
volumes:
|
|
579
592
|
- \${HORUS_DATA_PATH}/notes:/data/notes:rw
|
|
580
|
-
- qmd-daemon-data:/home/anvil/.cache/qmd
|
|
581
593
|
environment:
|
|
582
594
|
- HORUS_RUNTIME=\${HORUS_RUNTIME:-docker}
|
|
583
595
|
- ANVIL_TRANSPORT=http
|
|
@@ -585,14 +597,9 @@ var ANVIL_SERVICE = ` # \u2500\u2500 Anvil \u2500\u2500\u2500\u2500\u2500\u2500
|
|
|
585
597
|
- ANVIL_HOST=0.0.0.0
|
|
586
598
|
- ANVIL_NOTES_PATH=/data/notes
|
|
587
599
|
- ANVIL_REPO_URL=\${ANVIL_REPO_URL:-}
|
|
588
|
-
- ANVIL_QMD_COLLECTION=\${ANVIL_QMD_COLLECTION:-anvil}
|
|
589
600
|
- ANVIL_SYNC_INTERVAL=\${ANVIL_SYNC_INTERVAL:-300}
|
|
590
601
|
- ANVIL_DEBOUNCE_SECONDS=\${ANVIL_DEBOUNCE_SECONDS:-5}
|
|
591
602
|
- GITHUB_TOKEN=\${GITHUB_TOKEN:-}
|
|
592
|
-
- QMD_DAEMON_URL=http://qmd-daemon:8181
|
|
593
|
-
depends_on:
|
|
594
|
-
qmd-daemon:
|
|
595
|
-
condition: service_healthy
|
|
596
603
|
networks:
|
|
597
604
|
- horus-net
|
|
598
605
|
restart: unless-stopped
|
|
@@ -607,7 +614,7 @@ var ANVIL_SERVICE = ` # \u2500\u2500 Anvil \u2500\u2500\u2500\u2500\u2500\u2500
|
|
|
607
614
|
test: ["CMD", "curl", "-f", "http://localhost:8100/health"]
|
|
608
615
|
interval: 30s
|
|
609
616
|
timeout: 5s
|
|
610
|
-
start_period:
|
|
617
|
+
start_period: 60s
|
|
611
618
|
retries: 3`;
|
|
612
619
|
var FORGE_SERVICE = ` # \u2500\u2500 Forge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
613
620
|
# Workspace manager and package registry MCP server.
|
|
@@ -657,6 +664,67 @@ var FORGE_SERVICE = ` # \u2500\u2500 Forge \u2500\u2500\u2500\u2500\u2500\u2500
|
|
|
657
664
|
timeout: 5s
|
|
658
665
|
start_period: 60s
|
|
659
666
|
retries: 3`;
|
|
667
|
+
var TYPESENSE_SERVICE = ` # \u2500\u2500 Typesense \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
668
|
+
# Full-text and vector search engine for unified Horus Search.
|
|
669
|
+
typesense:
|
|
670
|
+
image: typesense/typesense:27.1
|
|
671
|
+
ports:
|
|
672
|
+
- "\${TYPESENSE_PORT:-8108}:8108"
|
|
673
|
+
volumes:
|
|
674
|
+
- \${HORUS_DATA_PATH}/typesense-data:/data
|
|
675
|
+
command: >
|
|
676
|
+
--data-dir=/data
|
|
677
|
+
--api-key=\${TYPESENSE_API_KEY:-horus-local-key}
|
|
678
|
+
--enable-cors
|
|
679
|
+
networks:
|
|
680
|
+
- horus-net
|
|
681
|
+
healthcheck:
|
|
682
|
+
test: ["CMD-SHELL", "bash -c 'echo > /dev/tcp/localhost/8108'"]
|
|
683
|
+
interval: 10s
|
|
684
|
+
timeout: 5s
|
|
685
|
+
retries: 3
|
|
686
|
+
start_period: 5s
|
|
687
|
+
restart: unless-stopped`;
|
|
688
|
+
var HORUS_UI_SERVICE = ` # \u2500\u2500 Horus UI \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
689
|
+
# Web interface \u2014 React SPA served by Express proxy on port 8400.
|
|
690
|
+
# Proxies /api/anvil, /api/vault, /api/forge to the respective services.
|
|
691
|
+
# Stores dashboard configs and preferences in _system/ui/ (not indexed by Anvil).
|
|
692
|
+
horus-ui:
|
|
693
|
+
image: ghcr.io/arjunkhera/horus/horus-ui:latest
|
|
694
|
+
ports:
|
|
695
|
+
- "\${UI_PORT:-8400}:8400"
|
|
696
|
+
volumes:
|
|
697
|
+
- \${HORUS_DATA_PATH}/notes:/data/notes:rw
|
|
698
|
+
environment:
|
|
699
|
+
- PORT=8400
|
|
700
|
+
- HORUS_DATA_PATH=/data/notes
|
|
701
|
+
- ANVIL_URL=http://anvil:8100
|
|
702
|
+
- VAULT_URL=http://vault-mcp:8300
|
|
703
|
+
- FORGE_URL=http://forge:8200
|
|
704
|
+
- NODE_ENV=production
|
|
705
|
+
depends_on:
|
|
706
|
+
anvil:
|
|
707
|
+
condition: service_healthy
|
|
708
|
+
vault-mcp:
|
|
709
|
+
condition: service_healthy
|
|
710
|
+
forge:
|
|
711
|
+
condition: service_healthy
|
|
712
|
+
networks:
|
|
713
|
+
- horus-net
|
|
714
|
+
restart: unless-stopped
|
|
715
|
+
stop_grace_period: 10s
|
|
716
|
+
deploy:
|
|
717
|
+
resources:
|
|
718
|
+
limits:
|
|
719
|
+
memory: 256m
|
|
720
|
+
reservations:
|
|
721
|
+
memory: 64m
|
|
722
|
+
healthcheck:
|
|
723
|
+
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8400/api/health"]
|
|
724
|
+
interval: 30s
|
|
725
|
+
timeout: 5s
|
|
726
|
+
start_period: 30s
|
|
727
|
+
retries: 3`;
|
|
660
728
|
function generateComposeFile(config, runtime) {
|
|
661
729
|
const vaultEntries = Object.entries(config.vaults).sort(([a], [b]) => a.localeCompare(b));
|
|
662
730
|
const vaultServices = vaultEntries.map(([name, vault], index) => {
|
|
@@ -673,13 +741,11 @@ function generateComposeFile(config, runtime) {
|
|
|
673
741
|
volumes:
|
|
674
742
|
- \${HORUS_DATA_PATH}/vaults/${name}:/data/knowledge-repo:rw
|
|
675
743
|
- vault-${name}-workspace:/data/workspace
|
|
676
|
-
- qmd-daemon-data:/home/appuser/.cache/qmd
|
|
677
744
|
environment:
|
|
678
745
|
- HORUS_RUNTIME=\${HORUS_RUNTIME:-docker}
|
|
679
746
|
- KNOWLEDGE_REPO_PATH=/data/knowledge-repo
|
|
680
747
|
- WORKSPACE_PATH=/data/workspace
|
|
681
748
|
- VAULT_KNOWLEDGE_REPO_URL=${vault.repo}
|
|
682
|
-
- QMD_INDEX_NAME=vault-${name}
|
|
683
749
|
- SYNC_INTERVAL=\${VAULT_SYNC_INTERVAL:-300}
|
|
684
750
|
- VAULT_SYNC_INTERVAL=\${VAULT_SYNC_INTERVAL:-300}
|
|
685
751
|
- LOG_LEVEL=\${LOG_LEVEL:-info}
|
|
@@ -687,10 +753,6 @@ function generateComposeFile(config, runtime) {
|
|
|
687
753
|
- PORT=8000
|
|
688
754
|
- GITHUB_TOKEN=${token}
|
|
689
755
|
- GITHUB_API_HOST=${apiHost}
|
|
690
|
-
- QMD_DAEMON_URL=http://qmd-daemon:8181
|
|
691
|
-
depends_on:
|
|
692
|
-
qmd-daemon:
|
|
693
|
-
condition: service_healthy
|
|
694
756
|
networks:
|
|
695
757
|
- horus-net
|
|
696
758
|
restart: unless-stopped
|
|
@@ -704,7 +766,7 @@ function generateComposeFile(config, runtime) {
|
|
|
704
766
|
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
|
705
767
|
interval: 30s
|
|
706
768
|
timeout: 10s
|
|
707
|
-
start_period:
|
|
769
|
+
start_period: 60s
|
|
708
770
|
retries: 3`;
|
|
709
771
|
});
|
|
710
772
|
const defaultVaultEntry = vaultEntries.find(([, v]) => v.default);
|
|
@@ -717,7 +779,7 @@ function generateComposeFile(config, runtime) {
|
|
|
717
779
|
vault-router:
|
|
718
780
|
image: ghcr.io/arjunkhera/horus/vault-router:latest
|
|
719
781
|
ports:
|
|
720
|
-
- "\${VAULT_ROUTER_PORT:-
|
|
782
|
+
- "\${VAULT_ROUTER_PORT:-8050}:8400"
|
|
721
783
|
environment:
|
|
722
784
|
- VAULT_ENDPOINTS=${vaultEndpoints}
|
|
723
785
|
- VAULT_DEFAULT=${defaultVaultName}
|
|
@@ -778,8 +840,6 @@ ${vaultRouterDependsOn}
|
|
|
778
840
|
"",
|
|
779
841
|
"services:",
|
|
780
842
|
"",
|
|
781
|
-
QMD_DAEMON_SERVICE,
|
|
782
|
-
"",
|
|
783
843
|
ANVIL_SERVICE,
|
|
784
844
|
"",
|
|
785
845
|
...vaultServices.map((s) => s + "\n"),
|
|
@@ -789,6 +849,9 @@ ${vaultRouterDependsOn}
|
|
|
789
849
|
"",
|
|
790
850
|
FORGE_SERVICE,
|
|
791
851
|
"",
|
|
852
|
+
TYPESENSE_SERVICE,
|
|
853
|
+
"",
|
|
854
|
+
...config.enable_ui !== false ? [HORUS_UI_SERVICE, ""] : [],
|
|
792
855
|
"# \u2500\u2500 Networks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
793
856
|
"networks:",
|
|
794
857
|
" horus-net:",
|
|
@@ -796,7 +859,6 @@ ${vaultRouterDependsOn}
|
|
|
796
859
|
"",
|
|
797
860
|
"# \u2500\u2500 Volumes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
798
861
|
"volumes:",
|
|
799
|
-
" qmd-daemon-data:",
|
|
800
862
|
vaultVolumeEntries
|
|
801
863
|
];
|
|
802
864
|
let content = sections.join("\n");
|
|
@@ -1282,7 +1344,8 @@ var setupCommand = new Command2("setup").description("Interactive first-run setu
|
|
|
1282
1344
|
vault_rest: vault_rest ?? DEFAULT_PORTS.vault_rest,
|
|
1283
1345
|
vault_mcp: vault_mcp ?? DEFAULT_PORTS.vault_mcp,
|
|
1284
1346
|
vault_router: vault_router ?? DEFAULT_PORTS.vault_router,
|
|
1285
|
-
forge: forge ?? DEFAULT_PORTS.forge
|
|
1347
|
+
forge: forge ?? DEFAULT_PORTS.forge,
|
|
1348
|
+
typesense: DEFAULT_PORTS.typesense
|
|
1286
1349
|
};
|
|
1287
1350
|
}
|
|
1288
1351
|
console.log("");
|
|
@@ -2271,7 +2334,7 @@ function checkDiskSpace(dataDir) {
|
|
|
2271
2334
|
return {
|
|
2272
2335
|
status: "warn",
|
|
2273
2336
|
label: "Disk space",
|
|
2274
|
-
message: `Disk space low: only ${freeGBStr}GB available (5GB recommended
|
|
2337
|
+
message: `Disk space low: only ${freeGBStr}GB available (5GB recommended)`,
|
|
2275
2338
|
hint: "Free up disk space before running Horus"
|
|
2276
2339
|
};
|
|
2277
2340
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkhera30/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "CLI for managing the Horus AI development stack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@types/node": "^20.0.0",
|
|
25
25
|
"tsup": "^8.0.0",
|
|
26
26
|
"typescript": "^5.4.0",
|
|
27
|
+
"vite": "^6.0.0",
|
|
27
28
|
"vitest": "^4.0.18"
|
|
28
29
|
},
|
|
29
30
|
"engines": {
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"repository": {
|
|
37
38
|
"type": "git",
|
|
38
39
|
"url": "git+https://github.com/Arjunkhera/Horus.git",
|
|
39
|
-
"directory": "cli"
|
|
40
|
+
"directory": "packages/cli"
|
|
40
41
|
},
|
|
41
42
|
"homepage": "https://github.com/Arjunkhera/Horus#readme",
|
|
42
43
|
"bugs": {
|