@dotdrelle/wiki-manager 0.7.3 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.env.example +20 -0
  2. package/README.md +50 -1
  3. package/docker-compose.yml +1 -23
  4. package/mcp.endpoints.example.json +13 -0
  5. package/package.json +2 -2
  6. package/src/agent/graph.js +101 -15
  7. package/src/agent/graph.test.js +145 -0
  8. package/src/cli/wiki-manager.js +306 -53
  9. package/src/commands/slash.js +4 -24
  10. package/src/core/agentEvents.js +169 -4
  11. package/src/core/agentEvents.test.js +176 -4
  12. package/src/core/agentLoop.js +3 -0
  13. package/src/core/compose.js +1 -2
  14. package/src/core/dockerCompose.test.js +5 -5
  15. package/src/core/jobQueue.js +29 -12
  16. package/src/core/mcp.js +120 -10
  17. package/src/core/mcp.test.js +121 -1
  18. package/src/core/plan.js +33 -0
  19. package/src/core/queueStore.test.js +1 -0
  20. package/src/core/sessionConfig.js +24 -0
  21. package/src/core/wikiWorkspace.test.js +24 -0
  22. package/src/runtime/approvals.js +113 -0
  23. package/src/runtime/auth.test.js +8 -0
  24. package/src/runtime/client.js +52 -6
  25. package/src/runtime/lifecycle.js +27 -3
  26. package/src/runtime/queueStore.js +3 -3
  27. package/src/runtime/runner.js +340 -0
  28. package/src/runtime/runner.test.js +270 -0
  29. package/src/runtime/server.js +252 -33
  30. package/src/runtime/server.test.js +577 -0
  31. package/src/runtime/store.js +181 -39
  32. package/src/runtime/store.test.js +363 -4
  33. package/src/runtime/supervisor.js +6 -0
  34. package/src/runtime/supervisor.test.js +141 -0
  35. package/src/shell/RightPane.tsx +1 -1
  36. package/src/shell/repl.js +22 -6
  37. package/src/shell/useAgent.ts +1 -1
  38. package/src/shell/useSession.ts +10 -5
  39. package/wiki-workspace +198 -4
@@ -5,7 +5,7 @@ import { formatMcpToolResult, callMcpTool } from '../core/mcp.js';
5
5
  import { extractActivity, parseJsonText, sessionActivities } from '../core/activity.js';
6
6
  import { formatPlanStatus, formatCompletedActivities } from '../core/plan.js';
7
7
  import { createAgentEvent, dispatchAgentEvent } from '../core/agentEvents.js';
8
- import { queueCounts, startNextQueuedJob, syncQueueWithActivity } from '../core/jobQueue.js';
8
+ import { projectQueue, queueCounts, startNextQueuedJob, syncQueueWithActivity } from '../core/jobQueue.js';
9
9
  import { queueStoreFor } from '../core/queueStore.js';
10
10
  import { fetchRuntimeState, streamRuntimeEvents } from '../runtime/client.js';
11
11
  import type { ActiveFileEditor } from './FileEditorDialog';
@@ -186,14 +186,15 @@ export function useSession(props: { agent: unknown; packageJson: Record<string,
186
186
  version();
187
187
  const runtimeQueue = nonEmptyRuntimeArray(runtimeState()?.queue);
188
188
  if (runtimeQueue) return runtimeQueue.map((item: any) => ({ ...item, _runtime: true }));
189
- return ((session as any).jobQueue ?? []).map((item: any) => ({ ...item }));
189
+ return projectQueue((session as any).headlessPlan, (session as any).jobQueue ?? [], { workspace: (session as any).workspace ?? null })
190
+ .map((item: any) => ({ ...item }));
190
191
  });
191
192
  const queueInfo = createMemo(() => {
192
193
  version();
193
194
  const runtimeQueue = runtimeState()?.queue;
194
195
  if (Array.isArray(runtimeQueue)) {
195
196
  return {
196
- active: runtimeQueue.filter((item: any) => ['waiting', 'starting', 'running', 'queued', 'pending'].includes(String(item.status ?? '').toLowerCase())).length,
197
+ active: runtimeQueue.filter((item: any) => ['waiting', 'starting', 'running', 'queued', 'pending', 'pending_approval'].includes(String(item.status ?? '').toLowerCase())).length,
197
198
  current: runtimeQueue.filter((item: any) => ['starting', 'running'].includes(String(item.status ?? '').toLowerCase())).length,
198
199
  frozen: 0,
199
200
  };
@@ -229,7 +230,7 @@ export function useSession(props: { agent: unknown; packageJson: Record<string,
229
230
  });
230
231
 
231
232
  function syncRuntimeState() {
232
- void fetchRuntimeState({ url: props.runtime.url })
233
+ void fetchRuntimeState({ url: props.runtime.url, workspace: (session as any).workspace ?? null })
233
234
  .then((state) => {
234
235
  setRuntimeState(state);
235
236
  setRuntimeStatus('connected');
@@ -254,7 +255,11 @@ export function useSession(props: { agent: unknown; packageJson: Record<string,
254
255
  if (!props.runtime?.url || runtimeStreamStopped) return;
255
256
  runtimeStreamAbort = new AbortController();
256
257
  try {
257
- for await (const _event of streamRuntimeEvents({ url: props.runtime.url, signal: runtimeStreamAbort.signal })) {
258
+ for await (const _event of streamRuntimeEvents({
259
+ url: props.runtime.url,
260
+ signal: runtimeStreamAbort.signal,
261
+ workspace: (session as any).workspace ?? null,
262
+ })) {
258
263
  setRuntimeStatus('connected');
259
264
  debouncedSyncRuntimeState();
260
265
  }
package/wiki-workspace CHANGED
@@ -92,6 +92,11 @@ Commands:
92
92
  agents pull Pull latest external agent images
93
93
  agents status Check reachability of configured MCP endpoints
94
94
 
95
+ runtime up Start the single host agent-runtime on Node.js 22
96
+ runtime down Stop the host agent-runtime
97
+ runtime status Check host agent-runtime health
98
+ runtime logs [args...] Show host agent-runtime logs
99
+
95
100
  wiki <workspace> init Initialize the workspace with wiki init
96
101
  wiki <workspace> up Start llm-wiki serve + mcp-http + production-mcp
97
102
  wiki <workspace> down Stop all services for this workspace
@@ -106,6 +111,7 @@ Commands:
106
111
  Configuration:
107
112
  workspaces/<workspace>/.env
108
113
  Override directory: WIKI_WORKSPACES_DIR=/path/to/dir
114
+ Runtime node: WIKI_MANAGER_NODE_BIN=/path/to/node
109
115
  Local CA: --cacert /absolute/path/to/ca.pem (Docker must be able to read this host path)
110
116
  EOF
111
117
  }
@@ -362,6 +368,179 @@ NODE
362
368
  esac
363
369
  }
364
370
 
371
+ runtime_node_bin() {
372
+ printf '%s\n' "${WIKI_MANAGER_NODE_BIN:-node}"
373
+ }
374
+
375
+ ensure_runtime_node() {
376
+ local node_bin
377
+ node_bin="$(runtime_node_bin)"
378
+ command -v "$node_bin" >/dev/null 2>&1 || die "runtime requires Node.js 22+: command not found: $node_bin (set WIKI_MANAGER_NODE_BIN)"
379
+ local version major
380
+ version="$("$node_bin" -p 'process.versions.node' 2>/dev/null)" || die "could not execute Node runtime: $node_bin"
381
+ major="${version%%.*}"
382
+ [[ "$major" =~ ^[0-9]+$ && "$major" -ge 22 ]] || die "runtime requires Node.js 22+ for node:sqlite; $node_bin is Node $version"
383
+ }
384
+
385
+ ensure_manager_runtime_token() {
386
+ local token
387
+ token="$(env_value "$MANAGER_ENV_FILE" WIKI_MANAGER_RUNTIME_TOKEN "")"
388
+ if [[ -z "$token" ]]; then
389
+ token="$(generate_token)"
390
+ mkdir -p "$(dirname "$MANAGER_ENV_FILE")"
391
+ set_env_value "$MANAGER_ENV_FILE" WIKI_MANAGER_RUNTIME_TOKEN "$token"
392
+ fi
393
+ printf '%s\n' "$token"
394
+ }
395
+
396
+ runtime_pid_file() {
397
+ printf '%s/runtime.pid\n' "$MANAGER_RUNTIME_DIR"
398
+ }
399
+
400
+ runtime_log_file() {
401
+ printf '%s/runtime.log\n' "$MANAGER_RUNTIME_DIR"
402
+ }
403
+
404
+ runtime_pid_running() {
405
+ local pid_file pid
406
+ pid_file="$(runtime_pid_file)"
407
+ [[ -f "$pid_file" ]] || return 1
408
+ pid="$(cat "$pid_file" 2>/dev/null || true)"
409
+ [[ "$pid" =~ ^[0-9]+$ ]] || return 1
410
+ kill -0 "$pid" >/dev/null 2>&1
411
+ }
412
+
413
+ runtime_pid_command() {
414
+ local pid="$1"
415
+ ps -p "$pid" -o command= 2>/dev/null || ps -p "$pid" -o args= 2>/dev/null || true
416
+ }
417
+
418
+ runtime_pid_matches() {
419
+ local pid command
420
+ pid="$(cat "$(runtime_pid_file)" 2>/dev/null || true)"
421
+ [[ "$pid" =~ ^[0-9]+$ ]] || return 1
422
+ command="$(runtime_pid_command "$pid")"
423
+ [[ "$command" == *"/bin/wiki-manager.js runtime"* || "$command" == *" bin/wiki-manager.js runtime"* ]]
424
+ }
425
+
426
+ runtime_health_url() {
427
+ printf 'http://127.0.0.1:%s/health\n' "${WIKI_MANAGER_RUNTIME_PORT:-7788}"
428
+ }
429
+
430
+ runtime_health_check() {
431
+ local token url
432
+ token="$(env_value "$MANAGER_ENV_FILE" WIKI_MANAGER_RUNTIME_TOKEN "")"
433
+ url="$(runtime_health_url)"
434
+ if command -v curl >/dev/null 2>&1; then
435
+ if [[ -n "$token" ]]; then
436
+ curl -fsS -H "Authorization: Bearer $token" "$url" >/dev/null 2>&1
437
+ else
438
+ curl -fsS "$url" >/dev/null 2>&1
439
+ fi
440
+ return
441
+ fi
442
+ local node_bin
443
+ node_bin="$(runtime_node_bin)"
444
+ RUNTIME_URL="$url" RUNTIME_TOKEN="$token" "$node_bin" --input-type=module -e "const h=process.env.RUNTIME_TOKEN?{Authorization:'Bearer '+process.env.RUNTIME_TOKEN}:{}; const r=await fetch(process.env.RUNTIME_URL,{headers:h}); process.exit(r.ok?0:1);"
445
+ }
446
+
447
+ runtime_command() {
448
+ local subcommand="$1"
449
+ shift
450
+ local host="${WIKI_MANAGER_RUNTIME_HOST:-0.0.0.0}"
451
+ local port="${WIKI_MANAGER_RUNTIME_PORT:-7788}"
452
+ local pid_file log_file node_bin token
453
+ pid_file="$(runtime_pid_file)"
454
+ log_file="$(runtime_log_file)"
455
+ node_bin="$(runtime_node_bin)"
456
+
457
+ case "$subcommand" in
458
+ up)
459
+ [[ $# -eq 0 ]] || die "runtime up does not take arguments"
460
+ mkdir -p "$MANAGER_RUNTIME_DIR"
461
+ ensure_runtime_node
462
+ token="$(ensure_manager_runtime_token)"
463
+ export WIKI_MANAGER_RUNTIME_TOKEN="$token"
464
+ if runtime_health_check; then
465
+ printf 'agent-runtime already healthy at %s\n' "$(runtime_health_url)"
466
+ return
467
+ fi
468
+ if runtime_pid_running; then
469
+ printf 'agent-runtime process is already running (pid %s), but health is not ready yet.\n' "$(cat "$pid_file")"
470
+ return
471
+ fi
472
+ WIKI_WORKSPACES_DIR="$WORKSPACES_DIR" \
473
+ WIKI_MANAGER_ENV_FILE="$MANAGER_ENV_FILE" \
474
+ WIKI_MANAGER_STATE_DIR="$MANAGER_RUNTIME_DIR" \
475
+ WIKI_MANAGER_RUNTIME_TOKEN="$token" \
476
+ nohup "$node_bin" "$ROOT_DIR/bin/wiki-manager.js" runtime --host "$host" --port "$port" --state-dir "$MANAGER_RUNTIME_DIR" > "$log_file" 2>&1 &
477
+ printf '%s\n' "$!" > "$pid_file"
478
+ sleep 0.5
479
+ printf 'agent-runtime started on http://127.0.0.1:%s (pid %s)\n' "$port" "$(cat "$pid_file")"
480
+ printf 'state: %s\nlog: %s\n' "$MANAGER_RUNTIME_DIR" "$log_file"
481
+ ;;
482
+ down|stop)
483
+ [[ $# -eq 0 ]] || die "runtime down does not take arguments"
484
+ if ! runtime_pid_running; then
485
+ printf 'agent-runtime is not running from %s\n' "$pid_file"
486
+ rm -f "$pid_file"
487
+ return
488
+ fi
489
+ if ! runtime_pid_matches; then
490
+ printf 'refusing to stop pid %s: command does not look like wiki-manager runtime\n' "$(cat "$pid_file")" >&2
491
+ printf 'command: %s\n' "$(runtime_pid_command "$(cat "$pid_file")")" >&2
492
+ rm -f "$pid_file"
493
+ exit 1
494
+ fi
495
+ kill "$(cat "$pid_file")"
496
+ rm -f "$pid_file"
497
+ printf 'agent-runtime stopped\n'
498
+ ;;
499
+ status)
500
+ [[ $# -eq 0 ]] || die "runtime status does not take arguments"
501
+ if runtime_health_check; then
502
+ printf 'agent-runtime healthy at %s\n' "$(runtime_health_url)"
503
+ elif runtime_pid_running; then
504
+ printf 'agent-runtime process running (pid %s), health check failed\n' "$(cat "$pid_file")"
505
+ exit 1
506
+ else
507
+ printf 'agent-runtime is not running\n'
508
+ exit 1
509
+ fi
510
+ ;;
511
+ logs)
512
+ if [[ ! -f "$log_file" ]]; then
513
+ printf 'No runtime log found at %s\n' "$log_file" >&2
514
+ exit 1
515
+ fi
516
+ if [[ $# -eq 0 ]]; then
517
+ tail -100 -f "$log_file"
518
+ else
519
+ tail "$@" "$log_file"
520
+ fi
521
+ ;;
522
+ *)
523
+ die "unknown runtime command: $subcommand (expected: up, down, status, logs)"
524
+ ;;
525
+ esac
526
+ }
527
+
528
+ ensure_runtime_up() {
529
+ if [[ "${WIKI_MANAGER_RUNTIME_AUTOSTART:-1}" == "0" ]]; then
530
+ return
531
+ fi
532
+ if runtime_health_check; then
533
+ return
534
+ fi
535
+ printf 'Starting host agent-runtime...\n'
536
+ runtime_command up
537
+ }
538
+
539
+ start_workspace_services() {
540
+ ensure_runtime_up
541
+ compose_for_workspace "$1" up -d serve mcp-http production-mcp
542
+ }
543
+
365
544
  ensure_manager_endpoints_file() {
366
545
  if [[ ! -f "$MANAGER_ENDPOINTS_FILE" && -f "$ROOT_DIR/mcp.endpoints.example.json" ]]; then
367
546
  mkdir -p "$(dirname "$MANAGER_ENDPOINTS_FILE")"
@@ -743,8 +922,7 @@ up_workspace() {
743
922
  local ws_path
744
923
  ws_path="$(normalize_path "$(workspace_value "$workspace" WIKI_WORKSPACE_PATH)")"
745
924
  ensure_workspace_dirs "$ws_path"
746
-
747
- compose_for_workspace "$workspace" up -d serve mcp-http production-mcp
925
+ start_workspace_services "$workspace"
748
926
 
749
927
  local serve_port prod_port
750
928
  serve_port="$(workspace_value "$workspace" WIKI_SERVE_PORT 3100)"
@@ -752,6 +930,7 @@ up_workspace() {
752
930
  printf 'Workspace UI: http://localhost:%s\n' "$serve_port"
753
931
  printf 'Chat: http://localhost:%s/chat\n' "$serve_port"
754
932
  printf 'Production: http://localhost:%s/mcp/\n' "$prod_port"
933
+ printf 'Runtime: %s\n' "$(runtime_health_check && printf 'healthy at %s' "$(runtime_health_url)" || printf 'start with: wiki-workspace runtime up')"
755
934
  printf 'External MCPs: see %s\n' "$MANAGER_ENDPOINTS_FILE"
756
935
 
757
936
  if [[ "$open_browser" == "1" ]]; then
@@ -802,10 +981,22 @@ main() {
802
981
  exit 0
803
982
  fi
804
983
 
984
+ # runtime <subcommand> [args...]
985
+ if [[ $# -ge 1 && "$1" == "runtime" ]]; then
986
+ local runtime_sub="${2:-status}"
987
+ if [[ $# -ge 2 ]]; then
988
+ shift 2
989
+ else
990
+ shift 1
991
+ fi
992
+ runtime_command "$runtime_sub" "$@"
993
+ exit 0
994
+ fi
995
+
805
996
  [[ $# -ge 2 ]] || { usage; exit 2; }
806
997
 
807
998
  local scope="$1"
808
- [[ "$scope" == "wiki" ]] || die "unknown scope: $scope (expected: wiki, agents, up, config, or list)"
999
+ [[ "$scope" == "wiki" ]] || die "unknown scope: $scope (expected: wiki, agents, runtime, up, config, or list)"
809
1000
  shift
810
1001
  [[ $# -ge 2 ]] || die "wiki requires a workspace and a command"
811
1002
 
@@ -818,13 +1009,14 @@ main() {
818
1009
  run_wiki "$workspace" init "$@"
819
1010
  ;;
820
1011
  up)
821
- compose_for_workspace "$workspace" up -d serve mcp-http production-mcp
1012
+ start_workspace_services "$workspace"
822
1013
  local serve_port production_port
823
1014
  serve_port="$(workspace_value "$workspace" WIKI_SERVE_PORT 3100)"
824
1015
  production_port="$(workspace_value "$workspace" PRODUCTION_MCP_PORT 3102)"
825
1016
  printf 'Workspace UI: http://localhost:%s\n' "$serve_port"
826
1017
  printf 'Chat MCP: http://localhost:%s/chat\n' "$serve_port"
827
1018
  printf 'Production: http://localhost:%s/mcp/\n' "$production_port"
1019
+ printf 'Runtime: %s\n' "$(runtime_health_check && printf 'healthy at %s' "$(runtime_health_url)" || printf 'start with: wiki-workspace runtime up')"
828
1020
  ;;
829
1021
  down)
830
1022
  compose_for_workspace "$workspace" down
@@ -842,6 +1034,7 @@ main() {
842
1034
  die "serve accepts only --open as optional flag; port is configured in the workspace .env"
843
1035
  fi
844
1036
 
1037
+ ensure_runtime_up
845
1038
  printf 'Starting mcp-http...\n'
846
1039
  compose_for_workspace "$workspace" up -d mcp-http
847
1040
  printf 'Starting production-mcp...\n'
@@ -852,6 +1045,7 @@ main() {
852
1045
  printf 'Workspace UI: http://localhost:%s\n' "$serve_port"
853
1046
  printf 'Chat MCP: http://localhost:%s/chat\n' "$serve_port"
854
1047
  printf 'Production: http://localhost:%s/mcp/\n' "$production_port"
1048
+ printf 'Runtime: %s\n' "$(runtime_health_check && printf 'healthy at %s' "$(runtime_health_url)" || printf 'start with: wiki-workspace runtime up')"
855
1049
  printf 'Note: compose logs may show http://localhost:3000, which is the container port.\n'
856
1050
  if [[ $open_browser -eq 1 ]]; then
857
1051
  wait_and_open "http://localhost:${serve_port}" &