@dotdrelle/wiki-manager 0.15.26 → 0.15.27
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/.env.example +18 -5
- package/README.md +35 -13
- package/agents.docker-compose.override.example.yml +82 -0
- package/agents.docker-compose.yml +8 -2
- package/docker-compose.override.example.yml +81 -0
- package/docker-compose.yml +23 -17
- package/package.json +4 -2
- package/src/cli/wiki-manager.js +3 -1
- package/src/commands/slash.js +53 -39
- package/src/core/agentEvents.js +20 -0
- package/src/core/agentEvents.test.js +38 -1
- package/src/core/buildInfo.json +2 -2
- package/src/core/commandFailure.js +81 -0
- package/src/core/commandFailure.test.js +67 -0
- package/src/core/compose.js +20 -3
- package/src/core/composeOverrides.test.js +74 -0
- package/src/core/dockerCompose.test.js +69 -2
- package/src/core/env.js +59 -0
- package/src/core/env.test.js +33 -1
- package/src/core/mcp.js +1 -1
- package/src/core/setEnvValue.test.js +96 -0
- package/src/runtime/client.js +18 -0
- package/src/runtime/lifecycle.js +5 -1
- package/src/runtime/server.js +43 -1
- package/src/runtime/server.test.js +117 -0
- package/src/runtime/store.js +47 -0
- package/src/runtime/store.test.js +89 -1
- package/src/shell/LeftPane.tsx +46 -16
- package/src/shell/RightPane.tsx +2 -4
- package/src/shell/SetupWizard.tsx +19 -7
- package/src/shell/externalLinks.js +24 -2
- package/src/shell/openExternal.js +53 -0
- package/src/shell/repl.js +33 -10
- package/src/shell/repl.test.js +91 -9
- package/src/shell/setupWizardPlaceholders.test.js +66 -0
- package/src/shell/tui.tsx +54 -25
- package/src/shell/useSession.ts +42 -6
- package/wiki-workspace +84 -6
package/.env.example
CHANGED
|
@@ -78,10 +78,15 @@ CONNECTORS_ENABLED=false
|
|
|
78
78
|
|
|
79
79
|
# ── Documents LLM OCR / Mermaid (optional) ─────────────────────────────────────
|
|
80
80
|
|
|
81
|
-
# All image OCR is delegated to an OpenAI-compatible vision LLM
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
81
|
+
# All image OCR is delegated to an OpenAI-compatible vision LLM, and it is OFF
|
|
82
|
+
# until you configure it here. The compose file passes empty defaults on
|
|
83
|
+
# purpose, so no deployment ever calls a provider it was not pointed at.
|
|
84
|
+
#
|
|
85
|
+
# Uncomment the three keys together — the agent disables LLM OCR when any of
|
|
86
|
+
# the endpoint, the model or the key is missing.
|
|
87
|
+
#
|
|
88
|
+
# DOCUMENT_LLM_BASE_URL=
|
|
89
|
+
# DOCUMENT_LLM_MODEL=
|
|
85
90
|
# DOCUMENT_LLM_API_KEY=
|
|
86
91
|
# DOCUMENT_LLM_TIMEOUT_SECONDS=120
|
|
87
92
|
|
|
@@ -99,7 +104,15 @@ CONNECTORS_ENABLED=false
|
|
|
99
104
|
# WIKI_MANAGER_RUNTIME_PORT=7788
|
|
100
105
|
# Set to 0 to skip pulling and renewing already-running containers at startup.
|
|
101
106
|
# WIKI_MANAGER_AUTO_UPDATE=1
|
|
102
|
-
#
|
|
107
|
+
#
|
|
108
|
+
# Active by default, and required: `serve` runs in Docker and reaches the host
|
|
109
|
+
# runtime through host.docker.internal. A runtime bound to 127.0.0.1 only
|
|
110
|
+
# accepts connections from the host itself, so the container gets ECONNREFUSED
|
|
111
|
+
# and the workspace UI shows no runtime at all. Binding all interfaces always
|
|
112
|
+
# generates WIKI_MANAGER_RUNTIME_TOKEN (0600 in .wiki/runtime/runtime.token),
|
|
113
|
+
# so the exposed port is never unauthenticated.
|
|
114
|
+
# Set to 127.0.0.1 only when nothing runs in a container.
|
|
115
|
+
WIKI_MANAGER_RUNTIME_HOST=0.0.0.0
|
|
103
116
|
|
|
104
117
|
|
|
105
118
|
# ── Parallelism & throughput ───────────────────────────────────────────────────
|
package/README.md
CHANGED
|
@@ -729,17 +729,33 @@ capabilityRouting:
|
|
|
729
729
|
allowedAgents: [connectors]
|
|
730
730
|
```
|
|
731
731
|
|
|
732
|
-
####
|
|
732
|
+
#### Compose overrides — optional agents, proxies, local fixes
|
|
733
733
|
|
|
734
|
-
|
|
735
|
-
configured independently by the user. To run one alongside the packaged
|
|
736
|
-
agents, create a file named
|
|
737
|
-
`agents.docker-compose.override.yml` **next to your `.env`**:
|
|
734
|
+
Two override files sit **next to your `.env`**, one per stack:
|
|
738
735
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
736
|
+
| File | Applies to |
|
|
737
|
+
| --- | --- |
|
|
738
|
+
| `docker-compose.override.yml` | workspace stack (`serve`, `mcp-http`, `production-mcp`, `wiki`) |
|
|
739
|
+
| `agents.docker-compose.override.yml` | agents stack (`cme`, `documents`, `connectors`) |
|
|
740
|
+
|
|
741
|
+
Both are created for you on first use, from packaged templates full of
|
|
742
|
+
ready-to-uncomment examples, and are **never rewritten afterwards** — your edits
|
|
743
|
+
survive package updates. Do not confuse them with `.wiki/runtime/*.compose.yml`,
|
|
744
|
+
which the manager regenerates on every Compose command; editing those is always
|
|
745
|
+
lost.
|
|
746
|
+
|
|
747
|
+
Compose merge is standard: new services are added, same-name keys override the
|
|
748
|
+
defaults, `environment` merges per variable name. Only extend services the
|
|
749
|
+
packaged file declares — an invented service name becomes a phantom service
|
|
750
|
+
Compose keeps trying to start.
|
|
751
|
+
|
|
752
|
+
The most common use behind a VPN is proxy passthrough: containers do not inherit
|
|
753
|
+
the host environment, and only `connectors` ships proxy variables by default. See
|
|
754
|
+
[`docs/configuration.md`](docs/configuration.md) § "Compose overrides" for a
|
|
755
|
+
copy-paste block and the `host.docker.internal` / `NO_PROXY` pitfalls.
|
|
756
|
+
|
|
757
|
+
The second use is running an external connector alongside the packaged agents.
|
|
758
|
+
Complete the setup by
|
|
743
759
|
adding the connector's variables to your `.env` and its endpoint block to
|
|
744
760
|
your `mcp.endpoints.json` — every variable an external MCP endpoint needs
|
|
745
761
|
lives in the `.env` and is referenced as `${VAR_NAME}` from
|
|
@@ -872,11 +888,17 @@ The TUI uses a two-pane layout:
|
|
|
872
888
|
- **Left** — scrollable conversation thread with a chat input at the bottom.
|
|
873
889
|
Typing `/` opens a slash-command completion overlay just above the input.
|
|
874
890
|
Mouse wheel scrolls the conversation, and selecting text copies it through the
|
|
875
|
-
TUI clipboard bridge. Message headers
|
|
876
|
-
|
|
891
|
+
TUI clipboard bridge. Message headers expose a `[ copy ]` target for copying
|
|
892
|
+
one message, and user questions also carry `[ redo ]`: it discards every
|
|
893
|
+
answer, plan step and activity recorded after that question — in the runtime
|
|
894
|
+
as well as on screen — then re-asks it. Redo is refused while a run is still
|
|
895
|
+
active; cancel it first. PageUp/PageDown remain available for keyboard
|
|
896
|
+
scrolling.
|
|
877
897
|
- **Right** — Plan/Queue tabs, active MCP jobs, plus a live log/trace panel.
|
|
878
|
-
`
|
|
879
|
-
|
|
898
|
+
Click `Plan` or `Queue (N)` to select a tab. `Queue (N)` counts run requests
|
|
899
|
+
sent while the runtime is already busy, so it reads `(0)` whenever you submit
|
|
900
|
+
one request at a time. MCP connection details remain available through
|
|
901
|
+
`/mcp status`.
|
|
880
902
|
|
|
881
903
|
In the served browser Activity panel, `Clear` is a per-tab display cleanup and
|
|
882
904
|
`Clear all` applies it to Plan, Local activity, Runtime activity, and Logs. It
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# agents.docker-compose.override.yml — user-owned overrides for the agents stack
|
|
2
|
+
#
|
|
3
|
+
# Copied here once by wiki-manager when absent, then NEVER touched again:
|
|
4
|
+
# your edits survive every package update. Do not confuse it with
|
|
5
|
+
# .wiki/runtime/agents.cacert.compose.yml, which is generated state and is
|
|
6
|
+
# rewritten on every compose command — editing that one is always lost.
|
|
7
|
+
#
|
|
8
|
+
# Merge order: agents.docker-compose.yml → this file → generated CA override.
|
|
9
|
+
# `environment` entries merge per variable name; `volumes`, `extra_hosts` and
|
|
10
|
+
# other sequences are appended. Only services declared in the packaged file
|
|
11
|
+
# (cme, documents, connectors) should be extended — inventing a service name
|
|
12
|
+
# here creates a phantom service that compose will try to start.
|
|
13
|
+
#
|
|
14
|
+
# To activate anything below: replace the `services: {}` line at the bottom
|
|
15
|
+
# with your own `services:` block, then `wiki-workspace agents up`.
|
|
16
|
+
# Check the result with `wiki-workspace agents config` before restarting.
|
|
17
|
+
#
|
|
18
|
+
# ── Corporate proxy / VPN ─────────────────────────────────────────────────────
|
|
19
|
+
#
|
|
20
|
+
# Docker never inherits the host environment: exporting HTTP_PROXY in your shell
|
|
21
|
+
# does NOT reach a container. Only `connectors` declares proxy variables in the
|
|
22
|
+
# packaged file, so cme and documents try to reach Confluence and the LLM APIs
|
|
23
|
+
# directly. Declare the variables here, and set their values in the manager .env:
|
|
24
|
+
#
|
|
25
|
+
# HTTP_PROXY=http://host.docker.internal:3128
|
|
26
|
+
# HTTPS_PROXY=http://host.docker.internal:3128
|
|
27
|
+
# NO_PROXY=localhost,127.0.0.1,host.docker.internal
|
|
28
|
+
#
|
|
29
|
+
# Use `host.docker.internal` (never `localhost`, which is the container itself)
|
|
30
|
+
# when the proxy listens on the host — this requires the extra_hosts mapping
|
|
31
|
+
# below on Linux. Set both the upper- and lower-case spellings: Node reads the
|
|
32
|
+
# upper-case ones, Python/requests accepts either but some libraries only look
|
|
33
|
+
# at the lower-case ones.
|
|
34
|
+
#
|
|
35
|
+
# services:
|
|
36
|
+
# cme:
|
|
37
|
+
# environment:
|
|
38
|
+
# - HTTP_PROXY=${HTTP_PROXY:-}
|
|
39
|
+
# - HTTPS_PROXY=${HTTPS_PROXY:-}
|
|
40
|
+
# - http_proxy=${HTTP_PROXY:-}
|
|
41
|
+
# - https_proxy=${HTTPS_PROXY:-}
|
|
42
|
+
# - NO_PROXY=${NO_PROXY:-localhost,127.0.0.1,host.docker.internal}
|
|
43
|
+
# - no_proxy=${NO_PROXY:-localhost,127.0.0.1,host.docker.internal}
|
|
44
|
+
# extra_hosts:
|
|
45
|
+
# - host.docker.internal:host-gateway
|
|
46
|
+
# documents:
|
|
47
|
+
# environment:
|
|
48
|
+
# - HTTP_PROXY=${HTTP_PROXY:-}
|
|
49
|
+
# - HTTPS_PROXY=${HTTPS_PROXY:-}
|
|
50
|
+
# - http_proxy=${HTTP_PROXY:-}
|
|
51
|
+
# - https_proxy=${HTTPS_PROXY:-}
|
|
52
|
+
# - NO_PROXY=${NO_PROXY:-localhost,127.0.0.1,host.docker.internal}
|
|
53
|
+
# - no_proxy=${NO_PROXY:-localhost,127.0.0.1,host.docker.internal}
|
|
54
|
+
# extra_hosts:
|
|
55
|
+
# - host.docker.internal:host-gateway
|
|
56
|
+
#
|
|
57
|
+
# A TLS-intercepting proxy also needs its CA inside the containers. Do not mount
|
|
58
|
+
# it here: pass it once with `wiki-workspace --cacert /absolute/path/to/ca.pem`
|
|
59
|
+
# and the manager mounts it into every service with the right env variables
|
|
60
|
+
# (NODE_EXTRA_CA_CERTS, SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE).
|
|
61
|
+
#
|
|
62
|
+
# ── Optional external agents ──────────────────────────────────────────────────
|
|
63
|
+
#
|
|
64
|
+
# The MailerSend agent is built and version-checked but not part of the default
|
|
65
|
+
# delivery. Enable it by declaring it here (credentials stay in the container
|
|
66
|
+
# environment; set MAILERSEND_API_KEY in the manager .env):
|
|
67
|
+
#
|
|
68
|
+
# services:
|
|
69
|
+
# mailer:
|
|
70
|
+
# image: dotdrelle/agent-mailer-api:latest
|
|
71
|
+
# user: "${UID:-1000}:${GID:-1000}"
|
|
72
|
+
# ports:
|
|
73
|
+
# - "${MAILER_MCP_PORT:-3339}:8080"
|
|
74
|
+
# environment:
|
|
75
|
+
# - MCP_AUTH_TOKEN=${MAILER_MCP_AUTH_TOKEN:-}
|
|
76
|
+
# - MAILERSEND_API_KEY=${MAILERSEND_API_KEY:-}
|
|
77
|
+
# restart: unless-stopped
|
|
78
|
+
#
|
|
79
|
+
# Remember to declare the matching endpoint in mcp.endpoints.json, otherwise
|
|
80
|
+
# Donna never discovers the agent.
|
|
81
|
+
|
|
82
|
+
services: {}
|
|
@@ -72,8 +72,14 @@ services:
|
|
|
72
72
|
- DOCUMENT_OUTPUT_DIR=/documents/output
|
|
73
73
|
- WORKSPACES_ROOT=/workspaces
|
|
74
74
|
- DOCUMENT_MAX_UPLOAD_BYTES=${DOCUMENT_MAX_UPLOAD_BYTES:-52428800}
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
# Deliberately empty defaults: the .env is the only source. A hardcoded
|
|
76
|
+
# default here sent every deployment to a provider the operator never
|
|
77
|
+
# chose and could not see, since the keys ship commented. An empty value
|
|
78
|
+
# also overrides the image's own ENV fallback, so nothing is called until
|
|
79
|
+
# the operator opts in. The agent already disables LLM OCR when the key or
|
|
80
|
+
# the model is unset, so this degrades cleanly rather than failing.
|
|
81
|
+
- DOCUMENT_LLM_BASE_URL=${DOCUMENT_LLM_BASE_URL:-}
|
|
82
|
+
- DOCUMENT_LLM_MODEL=${DOCUMENT_LLM_MODEL:-}
|
|
77
83
|
- DOCUMENT_LLM_API_KEY=${DOCUMENT_LLM_API_KEY:-}
|
|
78
84
|
- DOCUMENT_LLM_TIMEOUT_SECONDS=${DOCUMENT_LLM_TIMEOUT_SECONDS:-120}
|
|
79
85
|
# HTTPS — uncomment and place cert files in AGENTS_DATA_DIR/certs/
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# docker-compose.override.yml — user-owned overrides for the workspace stack
|
|
2
|
+
#
|
|
3
|
+
# Copied here once by wiki-manager when absent, then NEVER touched again:
|
|
4
|
+
# your edits survive every package update. Do not confuse it with
|
|
5
|
+
# .wiki/runtime/cacert.compose.yml, which is generated state and is rewritten
|
|
6
|
+
# on every compose command — editing that one is always lost.
|
|
7
|
+
#
|
|
8
|
+
# This file is GLOBAL: it lives next to the manager .env and applies to every
|
|
9
|
+
# workspace stack (`wiki-workspace up <workspace>`, `/start` in the shell). A
|
|
10
|
+
# proxy or a private registry is a property of the machine, not of a workspace,
|
|
11
|
+
# so there is deliberately no per-workspace variant. For values that really do
|
|
12
|
+
# differ per workspace, use workspaces/<name>/.env and reference them here with
|
|
13
|
+
# ${VARIABLE}.
|
|
14
|
+
#
|
|
15
|
+
# Merge order: docker-compose.yml → this file → generated CA override.
|
|
16
|
+
# `environment` entries merge per variable name; `volumes`, `extra_hosts` and
|
|
17
|
+
# other sequences are appended. Only services declared in the packaged file
|
|
18
|
+
# (wiki, serve, mcp-http, production-mcp) should be extended — inventing a
|
|
19
|
+
# service name here creates a phantom service that compose will try to start.
|
|
20
|
+
#
|
|
21
|
+
# To activate anything below: replace the `services: {}` line at the bottom
|
|
22
|
+
# with your own `services:` block, then restart the workspace.
|
|
23
|
+
# Check the result with `wiki-workspace compose <workspace> config`.
|
|
24
|
+
#
|
|
25
|
+
# ── Corporate proxy / VPN ─────────────────────────────────────────────────────
|
|
26
|
+
#
|
|
27
|
+
# Docker never inherits the host environment: exporting HTTP_PROXY in your shell
|
|
28
|
+
# does NOT reach a container. None of the workspace services declares proxy
|
|
29
|
+
# variables, so serve and production-mcp reach LLM endpoints directly. Declare
|
|
30
|
+
# them here and set the values in the manager .env:
|
|
31
|
+
#
|
|
32
|
+
# HTTP_PROXY=http://host.docker.internal:3128
|
|
33
|
+
# HTTPS_PROXY=http://host.docker.internal:3128
|
|
34
|
+
# NO_PROXY=localhost,127.0.0.1,host.docker.internal
|
|
35
|
+
#
|
|
36
|
+
# NO_PROXY must keep host.docker.internal: serve, the runtime and the MCP
|
|
37
|
+
# servers talk to each other through it, and routing that traffic through the
|
|
38
|
+
# proxy breaks the workspace even when the outside world works.
|
|
39
|
+
#
|
|
40
|
+
# services:
|
|
41
|
+
# serve:
|
|
42
|
+
# environment:
|
|
43
|
+
# - HTTP_PROXY=${HTTP_PROXY:-}
|
|
44
|
+
# - HTTPS_PROXY=${HTTPS_PROXY:-}
|
|
45
|
+
# - http_proxy=${HTTP_PROXY:-}
|
|
46
|
+
# - https_proxy=${HTTPS_PROXY:-}
|
|
47
|
+
# - NO_PROXY=${NO_PROXY:-localhost,127.0.0.1,host.docker.internal}
|
|
48
|
+
# - no_proxy=${NO_PROXY:-localhost,127.0.0.1,host.docker.internal}
|
|
49
|
+
# production-mcp:
|
|
50
|
+
# environment:
|
|
51
|
+
# - HTTP_PROXY=${HTTP_PROXY:-}
|
|
52
|
+
# - HTTPS_PROXY=${HTTPS_PROXY:-}
|
|
53
|
+
# - http_proxy=${HTTP_PROXY:-}
|
|
54
|
+
# - https_proxy=${HTTPS_PROXY:-}
|
|
55
|
+
# - NO_PROXY=${NO_PROXY:-localhost,127.0.0.1,host.docker.internal}
|
|
56
|
+
# - no_proxy=${NO_PROXY:-localhost,127.0.0.1,host.docker.internal}
|
|
57
|
+
# # production-mcp has no host mapping in the packaged file; add it when
|
|
58
|
+
# # the proxy (or an LLM endpoint) listens on the host.
|
|
59
|
+
# extra_hosts:
|
|
60
|
+
# - host.docker.internal:host-gateway
|
|
61
|
+
#
|
|
62
|
+
# A TLS-intercepting proxy also needs its CA inside the containers. Do not mount
|
|
63
|
+
# it here: pass it once with `wiki-workspace --cacert /absolute/path/to/ca.pem`
|
|
64
|
+
# and the manager mounts it into every service with the right env variables
|
|
65
|
+
# (NODE_EXTRA_CA_CERTS, SSL_CERT_FILE, REQUESTS_CA_BUNDLE, CURL_CA_BUNDLE).
|
|
66
|
+
#
|
|
67
|
+
# ── Other common uses ─────────────────────────────────────────────────────────
|
|
68
|
+
#
|
|
69
|
+
# services:
|
|
70
|
+
# serve:
|
|
71
|
+
# # Extra read-only mount, e.g. a shared templates directory.
|
|
72
|
+
# volumes:
|
|
73
|
+
# - /srv/shared/templates:/templates:ro
|
|
74
|
+
# production-mcp:
|
|
75
|
+
# # Pin resources on a loaded host.
|
|
76
|
+
# deploy:
|
|
77
|
+
# resources:
|
|
78
|
+
# limits:
|
|
79
|
+
# cpus: '2.0'
|
|
80
|
+
|
|
81
|
+
services: {}
|
package/docker-compose.yml
CHANGED
|
@@ -25,7 +25,7 @@ services:
|
|
|
25
25
|
- ${WIKI_WORKSPACE_PATH:-/tmp/llm-wiki-workspace-not-set}:/workspace
|
|
26
26
|
environment:
|
|
27
27
|
- WIKI_WORKSPACE_PATH=/workspace
|
|
28
|
-
- WIKI_CONFIG_PATH
|
|
28
|
+
- WIKI_CONFIG_PATH=${WIKI_CONFIG_PATH:-}
|
|
29
29
|
extra_hosts:
|
|
30
30
|
- host.docker.internal:host-gateway
|
|
31
31
|
|
|
@@ -47,22 +47,28 @@ services:
|
|
|
47
47
|
#- ${WIKI_SERVE_CERTS_DIR:-./certs}:/certs:ro
|
|
48
48
|
environment:
|
|
49
49
|
- WIKI_WORKSPACE_PATH=/workspace
|
|
50
|
-
|
|
50
|
+
# Every variable below is written as ${VAR:-default}, never as a bare
|
|
51
|
+
# `- VAR` passthrough. The bare form makes Compose emit
|
|
52
|
+
# `The "VAR" variable is not set. Defaulting to a blank string.` on stderr
|
|
53
|
+
# for any key the operator has not written in a .env — the ports and
|
|
54
|
+
# tokens shipped as commented placeholders in .env.example. The warning is
|
|
55
|
+
# noise that surfaces in the ShellUI as if it were a failure.
|
|
56
|
+
- WIKI_CONFIG_PATH=${WIKI_CONFIG_PATH:-}
|
|
51
57
|
- WIKI_MCP_HTTP_PORT=${WIKI_MCP_PORT:-3101}
|
|
52
|
-
- WIKI_MCP_AUTH_TOKEN
|
|
53
|
-
- PRODUCTION_MCP_AUTH_TOKEN
|
|
54
|
-
- PRODUCTION_MCP_PORT
|
|
55
|
-
- CME_MCP_PORT
|
|
56
|
-
- DOCUMENTS_MCP_PORT
|
|
57
|
-
- CME_MCP_AUTH_TOKEN
|
|
58
|
-
- DOCUMENTS_MCP_AUTH_TOKEN
|
|
58
|
+
- WIKI_MCP_AUTH_TOKEN=${WIKI_MCP_AUTH_TOKEN:-}
|
|
59
|
+
- PRODUCTION_MCP_AUTH_TOKEN=${PRODUCTION_MCP_AUTH_TOKEN:-}
|
|
60
|
+
- PRODUCTION_MCP_PORT=${PRODUCTION_MCP_PORT:-3102}
|
|
61
|
+
- CME_MCP_PORT=${CME_MCP_PORT:-3336}
|
|
62
|
+
- DOCUMENTS_MCP_PORT=${DOCUMENTS_MCP_PORT:-3337}
|
|
63
|
+
- CME_MCP_AUTH_TOKEN=${CME_MCP_AUTH_TOKEN:-}
|
|
64
|
+
- DOCUMENTS_MCP_AUTH_TOKEN=${DOCUMENTS_MCP_AUTH_TOKEN:-}
|
|
59
65
|
# Connectors MCP: the serve panel resolves ${CONNECTORS_MCP_AUTH_TOKEN} in
|
|
60
66
|
# /mcp.endpoints.json from process.env only, so it must be forwarded here
|
|
61
67
|
# (symmetric with cme/documents) or the panel sends an empty Bearer → 401.
|
|
62
|
-
- CONNECTORS_MCP_PORT
|
|
63
|
-
- CONNECTORS_MCP_AUTH_TOKEN
|
|
68
|
+
- CONNECTORS_MCP_PORT=${CONNECTORS_MCP_PORT:-3338}
|
|
69
|
+
- CONNECTORS_MCP_AUTH_TOKEN=${CONNECTORS_MCP_AUTH_TOKEN:-}
|
|
64
70
|
- CONNECTORS_OAUTH_START_TOKEN=${OAUTH_START_TOKEN:-}
|
|
65
|
-
- WORKSPACE_NAME
|
|
71
|
+
- WORKSPACE_NAME=${WORKSPACE_NAME:-}
|
|
66
72
|
- DOCUMENT_INPUT_DIR=/documents/input
|
|
67
73
|
- DOCUMENT_UPLOADS_DIR=/documents/uploads
|
|
68
74
|
- DOCUMENT_MAX_UPLOAD_BYTES=${DOCUMENT_MAX_UPLOAD_BYTES:-52428800}
|
|
@@ -92,12 +98,12 @@ services:
|
|
|
92
98
|
#- ${WIKI_MCP_CERTS_DIR:-./certs}:/certs:ro
|
|
93
99
|
environment:
|
|
94
100
|
- WIKI_WORKSPACE_PATH=/workspace
|
|
95
|
-
- WIKI_CONFIG_PATH
|
|
96
|
-
- WIKI_MCP_AUTH_TOKEN
|
|
101
|
+
- WIKI_CONFIG_PATH=${WIKI_CONFIG_PATH:-}
|
|
102
|
+
- WIKI_MCP_AUTH_TOKEN=${WIKI_MCP_AUTH_TOKEN:-}
|
|
97
103
|
# HTTPS — set paths inside the container (e.g. /certs/server.crt) and uncomment the volume above
|
|
98
|
-
- WIKI_MCP_TLS_CERT_PATH
|
|
99
|
-
- WIKI_MCP_TLS_KEY_PATH
|
|
100
|
-
- WIKI_MCP_TLS_CA_PATH
|
|
104
|
+
- WIKI_MCP_TLS_CERT_PATH=${WIKI_MCP_TLS_CERT_PATH:-}
|
|
105
|
+
- WIKI_MCP_TLS_KEY_PATH=${WIKI_MCP_TLS_KEY_PATH:-}
|
|
106
|
+
- WIKI_MCP_TLS_CA_PATH=${WIKI_MCP_TLS_CA_PATH:-}
|
|
101
107
|
ports:
|
|
102
108
|
- '${WIKI_MCP_PORT:-3101}:3333'
|
|
103
109
|
extra_hosts:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotdrelle/wiki-manager",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.27",
|
|
4
4
|
"description": "Agentic shell and orchestration cockpit for llm-wiki workspaces.",
|
|
5
5
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
6
6
|
"author": "dotrelle",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"start": "bun ./bin/wiki-manager.js",
|
|
14
|
-
"test": "node --test src/cli/runtimeStartup.test.js src/cli/wiki-manager.test.js src/agent/graph.test.js src/contracts/schemas.test.js src/core/activity.test.js src/core/env.test.js src/core/buildInfo.test.js src/core/agentEvents.test.js src/core/runtimeLog.test.js src/activity/activityAggregator.test.js src/graph/runGraphProjector.test.js src/core/workflow.test.js src/core/planPatch.test.js src/core/agentLoop.test.js src/core/plan.test.js src/core/mcp.test.js src/core/toolLoop.test.js src/core/documentIntake.test.js src/core/dockerCompose.test.js src/core/wikiWorkspace.test.js src/core/wikirc.test.js src/core/cacert.test.js src/core/modelFetch.test.js src/core/startupCheck.test.js src/core/queueStore.test.js src/orchestrator/agentRegistry.test.js src/orchestrator/capabilityRegistry.test.js src/orchestrator/capabilityResolver.test.js src/orchestrator/planValidator.test.js src/orchestrator/planIntegrator.test.js src/orchestrator/scheduler.test.js src/orchestrator/attemptManager.test.js src/orchestrator/resultAggregator.test.js src/orchestrator/approvalPolicy.test.js src/orchestrator/dispatcher.test.js src/orchestrator/objectiveResolver.test.js src/commands/slash.test.js src/shell/repl.test.js src/runtime/store.test.js src/runtime/controlMessages.test.js src/runtime/recoveryManager.test.js src/runtime/server.test.js src/runtime/supervisor.test.js src/runtime/runner.test.js src/runtime/runner.e2e.test.js src/runtime/donna-contract.test.js src/runtime/auth.test.js",
|
|
14
|
+
"test": "node --test src/cli/runtimeStartup.test.js src/cli/wiki-manager.test.js src/agent/graph.test.js src/contracts/schemas.test.js src/core/activity.test.js src/core/env.test.js src/core/buildInfo.test.js src/core/agentEvents.test.js src/core/runtimeLog.test.js src/activity/activityAggregator.test.js src/graph/runGraphProjector.test.js src/core/workflow.test.js src/core/planPatch.test.js src/core/agentLoop.test.js src/core/plan.test.js src/core/mcp.test.js src/core/toolLoop.test.js src/core/documentIntake.test.js src/core/dockerCompose.test.js src/core/wikiWorkspace.test.js src/core/wikirc.test.js src/core/cacert.test.js src/core/composeOverrides.test.js src/core/setEnvValue.test.js src/core/commandFailure.test.js src/core/modelFetch.test.js src/core/startupCheck.test.js src/core/queueStore.test.js src/orchestrator/agentRegistry.test.js src/orchestrator/capabilityRegistry.test.js src/orchestrator/capabilityResolver.test.js src/orchestrator/planValidator.test.js src/orchestrator/planIntegrator.test.js src/orchestrator/scheduler.test.js src/orchestrator/attemptManager.test.js src/orchestrator/resultAggregator.test.js src/orchestrator/approvalPolicy.test.js src/orchestrator/dispatcher.test.js src/orchestrator/objectiveResolver.test.js src/commands/slash.test.js src/shell/repl.test.js src/shell/setupWizardPlaceholders.test.js src/runtime/store.test.js src/runtime/controlMessages.test.js src/runtime/recoveryManager.test.js src/runtime/server.test.js src/runtime/supervisor.test.js src/runtime/runner.test.js src/runtime/runner.e2e.test.js src/runtime/donna-contract.test.js src/runtime/auth.test.js",
|
|
15
15
|
"check-versions": "node scripts/check-versions.js",
|
|
16
16
|
"prepack": "node scripts/check-versions.js",
|
|
17
17
|
"prepublishOnly": "node scripts/check-versions.js",
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
"wiki-workspace",
|
|
28
28
|
"docker-compose.yml",
|
|
29
29
|
"agents.docker-compose.yml",
|
|
30
|
+
"docker-compose.override.example.yml",
|
|
31
|
+
"agents.docker-compose.override.example.yml",
|
|
30
32
|
"mcp.endpoints.example.json",
|
|
31
33
|
".env.example",
|
|
32
34
|
"tsconfig.json",
|
package/src/cli/wiki-manager.js
CHANGED
|
@@ -660,7 +660,9 @@ async function runRuntime(argv, agent) {
|
|
|
660
660
|
const { createApprovalManager } = await import('../runtime/approvals.js');
|
|
661
661
|
const { conversationSeed, runRuntimeAgenticWorkflow } = await import('../runtime/runner.js');
|
|
662
662
|
|
|
663
|
-
|
|
663
|
+
// Same default as wiki-workspace runtime up and ensureRuntime: a loopback
|
|
664
|
+
// bind is invisible to the serve container.
|
|
665
|
+
const host = valueAfter(argv, '--host') ?? process.env.WIKI_MANAGER_RUNTIME_HOST ?? '0.0.0.0';
|
|
664
666
|
const port = Number(valueAfter(argv, '--port') ?? process.env.WIKI_MANAGER_RUNTIME_PORT ?? 7788);
|
|
665
667
|
const stateDir = valueAfter(argv, '--state-dir') ?? defaultRuntimeStateDir();
|
|
666
668
|
const auth = resolveRuntimeAuthToken({ host, stateDir });
|
package/src/commands/slash.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
1
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { openExternalUrl } from '../shell/openExternal.js';
|
|
3
|
+
import { classifyCommandFailure, failureHint, rawFailureText } from '../core/commandFailure.js';
|
|
3
4
|
import { join, relative } from 'node:path';
|
|
4
|
-
import { composeServices, listServices, runWikiCli, serviceLogs, serviceStates, startService, stopService } from '../core/compose.js';
|
|
5
|
+
import { composeServices, listServices, runWikiCli, serviceLogs, serviceNames, serviceStates, startService, stopService } from '../core/compose.js';
|
|
5
6
|
import {
|
|
6
7
|
applyMcpRuntimeStatus,
|
|
7
8
|
buildMcpStatus,
|
|
@@ -187,17 +188,7 @@ function countIndexLinks(workspacePath) {
|
|
|
187
188
|
function folderStats(files) {
|
|
188
189
|
const totalBytes = files.reduce((sum, file) => sum + file.size, 0);
|
|
189
190
|
const latest = files.reduce((best, file) => (file.mtimeMs > (best?.mtimeMs ?? 0) ? file : best), null);
|
|
190
|
-
|
|
191
|
-
return { count: files.length, totalBytes, latest, largest };
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function formatRecentFiles(files, limit = 3) {
|
|
195
|
-
const recent = [...files].sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, limit);
|
|
196
|
-
if (recent.length === 0) return ' recent: -';
|
|
197
|
-
return [
|
|
198
|
-
' recent:',
|
|
199
|
-
...recent.map((file) => ` - ${file.relativePath} (${formatBytes(file.size)})`),
|
|
200
|
-
].join('\n');
|
|
191
|
+
return { count: files.length, totalBytes, latest };
|
|
201
192
|
}
|
|
202
193
|
|
|
203
194
|
function collectWorkspaceStats(session) {
|
|
@@ -224,7 +215,6 @@ function collectWorkspaceStats(session) {
|
|
|
224
215
|
deliverables: folderStats(deliverables),
|
|
225
216
|
logs: folderStats(logs),
|
|
226
217
|
index,
|
|
227
|
-
untrackedFiles: untracked,
|
|
228
218
|
};
|
|
229
219
|
}
|
|
230
220
|
|
|
@@ -265,11 +255,12 @@ function workspaceStatsColumns(stats) {
|
|
|
265
255
|
statLine('answers', stats.answers),
|
|
266
256
|
`index: ${stats.index.exists ? 'ok' : 'missing'} (${stats.index.links} links)`,
|
|
267
257
|
]);
|
|
258
|
+
// Counts only. `largest:` and the `recent:` list printed full relative paths
|
|
259
|
+
// in a half-width column, so every line wrapped or was clipped and the block
|
|
260
|
+
// read as noise. The file listing belongs to a command with room for it.
|
|
268
261
|
const rawColumn = sectionBlock('Raw sources', [
|
|
269
262
|
statLine('untracked', stats.untracked),
|
|
270
263
|
statLine('ingested', stats.ingested),
|
|
271
|
-
stats.untracked.largest ? `largest: ${stats.untracked.largest.relativePath} (${formatBytes(stats.untracked.largest.size)})` : 'largest: -',
|
|
272
|
-
...formatRecentFiles(stats.untrackedFiles).split('\n'),
|
|
273
264
|
]);
|
|
274
265
|
const deliveryColumn = sectionBlock(`Deliverables: ${deliverablesLatest}`, [
|
|
275
266
|
statLine('templates', stats.templates),
|
|
@@ -723,6 +714,32 @@ export function localizedOperationResult({ operation, target, status = 'succeede
|
|
|
723
714
|
};
|
|
724
715
|
}
|
|
725
716
|
|
|
717
|
+
// Failure counterpart of localizedOperationResult. The raw docker output never
|
|
718
|
+
// reaches the conversation: it goes to the runtime log lane through `step()`,
|
|
719
|
+
// while Donna gets a stable reason code she can phrase — and act on, since
|
|
720
|
+
// every reason maps to something the operator can actually do.
|
|
721
|
+
export function localizedOperationFailure({ operation, target, error }) {
|
|
722
|
+
const reason = classifyCommandFailure(error);
|
|
723
|
+
const hint = reason === 'unknown' ? failureHint(error) : '';
|
|
724
|
+
const facts = JSON.stringify({
|
|
725
|
+
operation,
|
|
726
|
+
target,
|
|
727
|
+
status: 'failed',
|
|
728
|
+
reason,
|
|
729
|
+
...(hint ? { detail: hint } : {}),
|
|
730
|
+
});
|
|
731
|
+
return {
|
|
732
|
+
output: facts,
|
|
733
|
+
rawOutput: true,
|
|
734
|
+
agentTrigger: [
|
|
735
|
+
"Formule l'échec structuré suivant dans la langue et le ton demandés par le profil du workspace.",
|
|
736
|
+
"Réponds en une ou deux phrases humaines: ce qui a échoué, et l'action concrète que la personne peut faire.",
|
|
737
|
+
'Ne cite aucune commande, aucun chemin de fichier, aucun drapeau shell ni sortie docker.',
|
|
738
|
+
`Résultat: ${facts}`,
|
|
739
|
+
].join('\n'),
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
726
743
|
function formatRuntimeRunStatus(state) {
|
|
727
744
|
const status = state?.status ?? 'unknown';
|
|
728
745
|
const runId = state?.runId ? ` run=${state.runId}` : '';
|
|
@@ -770,7 +787,8 @@ export async function handleSlashCommand(line, context) {
|
|
|
770
787
|
});
|
|
771
788
|
} catch (err) {
|
|
772
789
|
step(formatActivityError('agents', verb, err));
|
|
773
|
-
|
|
790
|
+
step(`Agents: docker output — ${rawFailureText(err)}`);
|
|
791
|
+
return localizedOperationFailure({ operation: verb, target: 'agents', error: err });
|
|
774
792
|
}
|
|
775
793
|
};
|
|
776
794
|
|
|
@@ -915,9 +933,9 @@ export async function handleSlashCommand(line, context) {
|
|
|
915
933
|
const output = await listServices(context.session);
|
|
916
934
|
return rawCommandResult('/services', output);
|
|
917
935
|
} catch (err) {
|
|
918
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
919
936
|
step(formatActivityError('services', 'list', err));
|
|
920
|
-
|
|
937
|
+
step(`Services: docker output — ${rawFailureText(err)}`);
|
|
938
|
+
return localizedOperationFailure({ operation: 'list', target: 'workspace-services', error: err });
|
|
921
939
|
}
|
|
922
940
|
}
|
|
923
941
|
case 'start': {
|
|
@@ -939,9 +957,9 @@ export async function handleSlashCommand(line, context) {
|
|
|
939
957
|
images: missingImages,
|
|
940
958
|
});
|
|
941
959
|
} catch (err) {
|
|
942
|
-
|
|
943
|
-
step(
|
|
944
|
-
return {
|
|
960
|
+
step(formatActivityError('services', 'start', err));
|
|
961
|
+
step(`Services: docker output — ${rawFailureText(err)}`);
|
|
962
|
+
return localizedOperationFailure({ operation: 'start', target: service || 'workspace-services', error: err });
|
|
945
963
|
}
|
|
946
964
|
}
|
|
947
965
|
case 'stop': {
|
|
@@ -957,21 +975,25 @@ export async function handleSlashCommand(line, context) {
|
|
|
957
975
|
target: service || 'workspace-services',
|
|
958
976
|
});
|
|
959
977
|
} catch (err) {
|
|
960
|
-
|
|
961
|
-
step(
|
|
962
|
-
return {
|
|
978
|
+
step(formatActivityError('services', 'stop', err));
|
|
979
|
+
step(`Services: docker output — ${rawFailureText(err)}`);
|
|
980
|
+
return localizedOperationFailure({ operation: 'stop', target: service || 'workspace-services', error: err });
|
|
963
981
|
}
|
|
964
982
|
}
|
|
965
983
|
case 'logs': {
|
|
966
984
|
const service = args[1];
|
|
985
|
+
// A usage mistake is not a runtime failure: keep it a plain instruction
|
|
986
|
+
// rather than sending it to Donna to be rephrased.
|
|
987
|
+
if (!service) return { output: `Usage: /logs <service> [tail] — services: ${serviceNames().join(', ')}` };
|
|
967
988
|
const tail = args[2] ? Number(args[2]) : 120;
|
|
968
989
|
try {
|
|
969
990
|
step(`Services: reading logs for ${service ?? 'service'}…`);
|
|
970
991
|
const output = await serviceLogs(context.session, service, { tail });
|
|
971
992
|
return rawCommandResult(`/logs ${[service, args[2]].filter(Boolean).join(' ')}`.trim(), output);
|
|
972
993
|
} catch (err) {
|
|
973
|
-
|
|
974
|
-
|
|
994
|
+
step(formatActivityError('services', 'logs', err));
|
|
995
|
+
step(`Services: docker output — ${rawFailureText(err)}`);
|
|
996
|
+
return localizedOperationFailure({ operation: 'logs', target: service || 'workspace-services', error: err });
|
|
975
997
|
}
|
|
976
998
|
}
|
|
977
999
|
case 'mcp': {
|
|
@@ -1070,13 +1092,10 @@ export async function handleSlashCommand(line, context) {
|
|
|
1070
1092
|
if (payload?.ok !== true || typeof authorizationUrl !== 'string') {
|
|
1071
1093
|
return connectorResult(`Google authorization could not start (${payload?.error ?? 'missing authorization URL'}).`);
|
|
1072
1094
|
}
|
|
1073
|
-
|
|
1074
|
-
try {
|
|
1075
|
-
execFileSync(opener, [authorizationUrl], { stdio: 'ignore' });
|
|
1095
|
+
if (openExternalUrl(authorizationUrl)) {
|
|
1076
1096
|
return connectorResult('Google authorization opened successfully in the user browser.');
|
|
1077
|
-
} catch {
|
|
1078
|
-
return connectorResult(`Google authorization requires the user to open this URL: ${authorizationUrl}`);
|
|
1079
1097
|
}
|
|
1098
|
+
return connectorResult(`Google authorization requires the user to open this URL: ${authorizationUrl}`);
|
|
1080
1099
|
} catch (err) {
|
|
1081
1100
|
return connectorResult(`Google authorization could not start (${err instanceof Error ? err.message : String(err)}).`);
|
|
1082
1101
|
}
|
|
@@ -1362,13 +1381,8 @@ export async function handleSlashCommand(line, context) {
|
|
|
1362
1381
|
const port = context.session.workspaceEnv?.WIKI_SERVE_PORT ?? '3100';
|
|
1363
1382
|
const url = `http://localhost:${port}`;
|
|
1364
1383
|
const note = context.session.workspaceEnv ? '' : ' (no workspace loaded — using default port)';
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
execFileSync(opener, [url], { stdio: 'ignore' });
|
|
1368
|
-
return { output: `Opening web UI: ${url}${note}` };
|
|
1369
|
-
} catch {
|
|
1370
|
-
return { output: `Web UI: ${url}${note}` };
|
|
1371
|
-
}
|
|
1384
|
+
if (openExternalUrl(url)) return { output: `Opening web UI: ${url}${note}` };
|
|
1385
|
+
return { output: `Web UI: ${url}${note}` };
|
|
1372
1386
|
}
|
|
1373
1387
|
case 'clear': {
|
|
1374
1388
|
const key = context.session.workspace || '__global__';
|
package/src/core/agentEvents.js
CHANGED
|
@@ -151,6 +151,26 @@ export function reduceAgentEvents(events = []) {
|
|
|
151
151
|
return publicProjection(state);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
// Maps every projected conversation entry back to the sequence of the event
|
|
155
|
+
// that produced it. The conversation is derived from the event log, never
|
|
156
|
+
// stored, so "delete everything below this question" can only be expressed as
|
|
157
|
+
// "delete every event after the one that appended it". Replays the same
|
|
158
|
+
// applyEvent used by reduceAgentEvents, so the mapping cannot drift from the
|
|
159
|
+
// projection it describes.
|
|
160
|
+
export function conversationEventSequences(events = []) {
|
|
161
|
+
const state = createProjectionState();
|
|
162
|
+
const sequences = [];
|
|
163
|
+
for (const event of events) {
|
|
164
|
+
applyEvent(state, event);
|
|
165
|
+
const sequence = Number.isFinite(Number(event?.sequence)) ? Number(event.sequence) : null;
|
|
166
|
+
// Streaming deltas mutate the last entry in place instead of appending, so
|
|
167
|
+
// an entry keeps the sequence of the event that first created it.
|
|
168
|
+
while (sequences.length < state.conversation.length) sequences.push(sequence);
|
|
169
|
+
if (sequences.length > state.conversation.length) sequences.length = state.conversation.length;
|
|
170
|
+
}
|
|
171
|
+
return sequences;
|
|
172
|
+
}
|
|
173
|
+
|
|
154
174
|
function createProjectionState() {
|
|
155
175
|
return {
|
|
156
176
|
conversation: [],
|