@dotdrelle/wiki-manager 0.15.34 → 0.15.38

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 (41) hide show
  1. package/.env.example +8 -5
  2. package/README.md +42 -0
  3. package/agents.docker-compose.yml +11 -16
  4. package/docker-compose.yml +9 -8
  5. package/package.json +2 -2
  6. package/src/agent/graph.js +20 -0
  7. package/src/cli/wiki-manager.js +20 -0
  8. package/src/commands/slash.js +85 -13
  9. package/src/commands/slash.test.js +85 -3
  10. package/src/core/agentsCompose.js +86 -1
  11. package/src/core/buildInfo.json +2 -2
  12. package/src/core/compose.js +24 -2
  13. package/src/core/dockerCompose.test.js +18 -0
  14. package/src/core/env.js +2 -1
  15. package/src/core/env.test.js +14 -0
  16. package/src/core/googleGrants.js +38 -0
  17. package/src/core/googleGrants.test.js +59 -0
  18. package/src/core/mcp.js +1 -1
  19. package/src/core/mcp.test.js +1 -0
  20. package/src/core/mcpEndpoints.js +96 -0
  21. package/src/core/mcpEndpoints.test.js +126 -0
  22. package/src/core/modelFetch.js +241 -27
  23. package/src/core/modelFetch.test.js +213 -0
  24. package/src/core/profileServiceStatus.test.js +146 -0
  25. package/src/core/wikiSetup.js +11 -2
  26. package/src/core/wikiWorkspace.test.js +16 -3
  27. package/src/orchestrator/agentRegistry.js +13 -1
  28. package/src/orchestrator/agentRegistry.test.js +20 -0
  29. package/src/runtime/lifecycle.js +105 -3
  30. package/src/runtime/lifecycle.test.js +68 -0
  31. package/src/runtime/server.js +28 -0
  32. package/src/shell/LeftPane.tsx +55 -41
  33. package/src/shell/SetupWizard.tsx +416 -116
  34. package/src/shell/repl.js +61 -12
  35. package/src/shell/repl.test.js +48 -19
  36. package/src/shell/setupWizardDiscovery.test.js +48 -0
  37. package/src/shell/setupWizardPlaceholders.test.js +15 -2
  38. package/src/shell/setupWizardSuggestions.test.js +36 -2
  39. package/src/shell/wrapText.js +57 -0
  40. package/src/shell/wrapText.test.js +48 -0
  41. package/wiki-workspace +122 -32
package/wiki-workspace CHANGED
@@ -262,30 +262,10 @@ agents_compose() {
262
262
  local compose_env_args=()
263
263
  [[ -f "$MANAGER_ENV_FILE" ]] && compose_env_args+=(--env-file "$MANAGER_ENV_FILE")
264
264
 
265
- # Local-iteration build only: the packaged connectors service carries a
266
- # `build:` block pointing at the sibling agent-connectors repo, whose OAuth
267
- # credentials live in a gitignored .env.build.local. Compose resolves the
268
- # value-less build args from *this* process environment, not from
269
- # --env-file, so the variables must be exported here or the image ships with
270
- # empty WIKILLM_GOOGLE_OAUTH_* and every Google connector stays unusable.
271
- local connectors_build_env="$ROOT_DIR/../agent-external/agent-connectors/.env.build.local"
272
- if [[ -f "$connectors_build_env" ]]; then
273
- local build_key build_value
274
- while IFS='=' read -r build_key build_value; do
275
- case "$build_key" in
276
- WIKILLM_GOOGLE_OAUTH_CLIENT_ID|WIKILLM_GOOGLE_OAUTH_CLIENT_SECRET) ;;
277
- *) continue ;;
278
- esac
279
- [[ -n "${!build_key:-}" ]] || export "$build_key=$build_value"
280
- done < <(grep -v '^[[:space:]]*#' "$connectors_build_env" || true)
281
- fi
282
-
283
- require_connectors_build_credentials() {
284
- local build_key
285
- for build_key in WIKILLM_GOOGLE_OAUTH_CLIENT_ID WIKILLM_GOOGLE_OAUTH_CLIENT_SECRET; do
286
- [[ -n "${!build_key:-}" ]] || die "cannot build connectors: $build_key is missing from $connectors_build_env"
287
- done
288
- }
265
+ # Ce script ne construit aucune image. L'application OAuth Google est cuite
266
+ # dans l'image publiée par build-and-push.sh ; pour en utiliser une autre,
267
+ # l'opérateur pose GOOGLE_OAUTH_CLIENT_ID / _SECRET dans le .env du manager,
268
+ # que Compose transmet au conteneur et qui l'emporte sur la valeur embarquée.
289
269
 
290
270
  _agents_dc() {
291
271
  local cacert_args=()
@@ -312,12 +292,11 @@ agents_compose() {
312
292
  do_pull=true
313
293
  shift
314
294
  fi
315
- [[ $# -eq 0 ]] || die "agents up accepts only --pull"
295
+ # Services nommés : démarrer `connectors` seul une fois le profil actif,
296
+ # sans relancer toute la pile. Sans argument, tout monte comme avant.
297
+ local only_services=("$@")
316
298
  ensure_agent_tokens
317
299
  ensure_endpoints_file
318
- if connectors_enabled && [[ -f "$ROOT_DIR/../agent-external/agent-connectors/Dockerfile" ]]; then
319
- require_connectors_build_credentials
320
- fi
321
300
  # The .env may have just been created above — recompute the env-file
322
301
  # flag so freshly generated tokens reach the containers on first run.
323
302
  compose_env_args=()
@@ -330,6 +309,7 @@ agents_compose() {
330
309
  if [[ -n "$CACERT_PATH" ]]; then
331
310
  up_args+=(--force-recreate)
332
311
  fi
312
+ up_args+=(${only_services[@]+"${only_services[@]}"})
333
313
  _agents_dc "${up_args[@]}"
334
314
  printf 'Workspaces root: %s\n' "$workspaces_root"
335
315
  printf 'Agents data: %s\n' "$agents_data_dir"
@@ -341,8 +321,15 @@ agents_compose() {
341
321
  [[ ! -f "$MANAGER_COMPOSE_DIR/agents.docker-compose.override.yml" ]] || printf 'User override: %s\n' "$MANAGER_COMPOSE_DIR/agents.docker-compose.override.yml"
342
322
  ;;
343
323
  down)
344
- [[ $# -eq 0 ]] || die "agents down does not take arguments"
345
- _agents_dc down
324
+ # `down` sans argument démonte la pile ; avec des services nommés, Compose
325
+ # n'accepte que `stop` + `rm` — `down <service>` supprime le réseau commun
326
+ # et couperait les autres agents.
327
+ if [[ $# -eq 0 ]]; then
328
+ _agents_dc down
329
+ else
330
+ _agents_dc stop "$@"
331
+ _agents_dc rm -f "$@"
332
+ fi
346
333
  ;;
347
334
  purge)
348
335
  [[ $# -eq 0 ]] || die "agents purge does not take arguments"
@@ -547,7 +534,11 @@ ensure_endpoints_file() {
547
534
  # Without mcp.endpoints.json the shell never connects to the agents it
548
535
  # just started (and `agents status` refuses to run). Seed it from the
549
536
  # packaged example; the \${VAR} placeholders resolve against the .env.
537
+ if [[ -e "$MANAGER_ENDPOINTS_FILE" && ! -f "$MANAGER_ENDPOINTS_FILE" ]]; then
538
+ die "MCP endpoints path is not a file: $MANAGER_ENDPOINTS_FILE"
539
+ fi
550
540
  if [[ ! -f "$MANAGER_ENDPOINTS_FILE" && -f "$ROOT_DIR/mcp.endpoints.example.json" ]]; then
541
+ mkdir -p "$(dirname "$MANAGER_ENDPOINTS_FILE")"
551
542
  cp "$ROOT_DIR/mcp.endpoints.example.json" "$MANAGER_ENDPOINTS_FILE"
552
543
  printf 'Created %s from packaged example\n' "$MANAGER_ENDPOINTS_FILE"
553
544
  fi
@@ -634,12 +625,65 @@ runtime_pid_command() {
634
625
  ps -p "$pid" -o command= 2>/dev/null || ps -p "$pid" -o args= 2>/dev/null || true
635
626
  }
636
627
 
628
+ runtime_command_looks_like_runtime() {
629
+ local command="$1"
630
+ [[ "$command" == *"/bin/wiki-manager.js runtime"* || "$command" == *" bin/wiki-manager.js runtime"* ]]
631
+ }
632
+
637
633
  runtime_pid_matches() {
638
634
  local pid command
639
635
  pid="$(cat "$(runtime_pid_file)" 2>/dev/null || true)"
640
636
  [[ "$pid" =~ ^[0-9]+$ ]] || return 1
641
637
  command="$(runtime_pid_command "$pid")"
642
- [[ "$command" == *"/bin/wiki-manager.js runtime"* || "$command" == *" bin/wiki-manager.js runtime"* ]]
638
+ runtime_command_looks_like_runtime "$command"
639
+ }
640
+
641
+ # Pids qui écoutent sur le port du runtime, quel que soit leur parent.
642
+ #
643
+ # Un runtime démarré par une session de shell précédente survit à la mort de
644
+ # cette session : il ne répond plus à notre jeton (401, donc « injoignable »)
645
+ # et son fichier pid a disparu avec l'ancien répertoire d'état. Sans ce repli,
646
+ # `runtime down` répond « not running » pendant que le port reste occupé, et il
647
+ # ne reste que le `kill` manuel — inacceptable pour qui utilise l'application.
648
+ runtime_port_pids() {
649
+ local port="$1"
650
+ if command -v lsof >/dev/null 2>&1; then
651
+ lsof -nP -iTCP:"$port" -sTCP:LISTEN -t 2>/dev/null | sort -u
652
+ elif command -v ss >/dev/null 2>&1; then
653
+ ss -lptnH "sport = :${port}" 2>/dev/null |
654
+ grep -o 'pid=[0-9]*' | cut -d= -f2 | sort -u
655
+ fi
656
+ }
657
+
658
+ # Arrête les runtimes orphelins tenant le port. Ne tue QUE des processus dont
659
+ # la ligne de commande est celle d'un `wiki-manager runtime` : un port occupé
660
+ # par autre chose est signalé, jamais tué.
661
+ runtime_kill_orphans() {
662
+ local port="$1"
663
+ local pid command stopped=0 waited
664
+ while read -r pid; do
665
+ [[ "$pid" =~ ^[0-9]+$ ]] || continue
666
+ [[ "$pid" == "$$" ]] && continue
667
+ command="$(runtime_pid_command "$pid")"
668
+ if ! runtime_command_looks_like_runtime "$command"; then
669
+ printf 'port %s is held by pid %s, which is not a wiki-manager runtime — leaving it alone\n' "$port" "$pid" >&2
670
+ printf 'command: %s\n' "$command" >&2
671
+ continue
672
+ fi
673
+ printf 'Stopping orphan agent-runtime (pid %s)\n' "$pid"
674
+ kill "$pid" 2>/dev/null || true
675
+ waited=0
676
+ while kill -0 "$pid" 2>/dev/null && (( waited < 50 )); do
677
+ sleep 0.1
678
+ (( waited++ )) || true
679
+ done
680
+ if kill -0 "$pid" 2>/dev/null; then
681
+ printf 'pid %s did not stop, sending SIGKILL\n' "$pid" >&2
682
+ kill -9 "$pid" 2>/dev/null || true
683
+ fi
684
+ stopped=1
685
+ done < <(runtime_port_pids "$port")
686
+ (( stopped == 1 ))
643
687
  }
644
688
 
645
689
  runtime_port() {
@@ -704,6 +748,14 @@ runtime_command() {
704
748
  if runtime_pid_running; then
705
749
  printf 'Waiting for agent-runtime process (pid %s)...\n' "$(cat "$pid_file")"
706
750
  else
751
+ # Le port peut être tenu par un runtime d'une session précédente, qui
752
+ # répond 401 à notre jeton : la sonde de santé le croit absent, et le
753
+ # démarrage se solderait par un EADDRINUSE incompréhensible. On le
754
+ # remplace, puisque `runtime up` veut précisément un runtime ici.
755
+ if [[ -n "$(runtime_port_pids "$port")" ]]; then
756
+ printf 'Port %s is already held by an unreachable agent-runtime.\n' "$port"
757
+ runtime_kill_orphans "$port" || true
758
+ fi
707
759
  WIKI_WORKSPACES_DIR="$WORKSPACES_DIR" \
708
760
  WIKI_MANAGER_ENV_FILE="$MANAGER_ENV_FILE" \
709
761
  WIKI_MANAGER_STATE_DIR="$MANAGER_RUNTIME_DIR" \
@@ -730,12 +782,24 @@ runtime_command() {
730
782
  sleep 0.1
731
783
  (( wait_count++ )) || true
732
784
  done
785
+ # Un arrêt propre refusé laissait le port occupé sans le dire.
786
+ if runtime_health_check; then
787
+ printf 'agent-runtime ignored the shutdown request; stopping the process\n' >&2
788
+ runtime_kill_orphans "$port" || true
789
+ fi
733
790
  rm -f "$pid_file"
734
791
  printf 'agent-runtime stopped\n'
735
792
  return
736
793
  fi
737
794
  if ! runtime_pid_running; then
738
- printf 'agent-runtime is not running from %s\n' "$pid_file"
795
+ # Ni sain, ni référencé par un pid : reste le port. C'est le cas d'un
796
+ # orphelin, qu'on ne peut pas demander à l'utilisateur de tuer.
797
+ if runtime_kill_orphans "$port"; then
798
+ rm -f "$pid_file"
799
+ printf 'agent-runtime stopped\n'
800
+ return
801
+ fi
802
+ printf 'agent-runtime is not running (no healthy endpoint, no pid in %s, nothing listening on port %s)\n' "$pid_file" "$port"
739
803
  rm -f "$pid_file"
740
804
  return
741
805
  fi
@@ -757,6 +821,16 @@ runtime_command() {
757
821
  printf 'agent-runtime process running (pid %s), health check failed\n' "$(cat "$pid_file")"
758
822
  exit 1
759
823
  else
824
+ local orphan_pids
825
+ orphan_pids="$(runtime_port_pids "$port")"
826
+ if [[ -n "$orphan_pids" ]]; then
827
+ # Le symptôme perçu comme « ça ne démarre pas » : quelqu'un tient le
828
+ # port et répond 401 à notre jeton, donc les sondes le voient absent.
829
+ printf 'agent-runtime is not reachable with this token, but pid(s) %s hold port %s\n' \
830
+ "$(printf '%s' "$orphan_pids" | tr '\n' ' ')" "$port"
831
+ printf 'run `%s runtime down` to stop it\n' "$(basename "$0")"
832
+ exit 1
833
+ fi
760
834
  printf 'agent-runtime is not running\n'
761
835
  exit 1
762
836
  fi
@@ -792,6 +866,19 @@ refresh_project() {
792
866
 
793
867
  printf 'Removing external-agent stack and images...\n'
794
868
  agents_compose purge
869
+
870
+ # `refresh` promet un état propre : le vérifier plutôt que l'affirmer. Un
871
+ # port encore tenu ici signifie qu'il l'est par un processus étranger, que
872
+ # `runtime down` refuse de tuer à raison — mais le taire ferait échouer le
873
+ # prochain démarrage sans que personne ne fasse le lien.
874
+ local leftover
875
+ leftover="$(runtime_port_pids "$(runtime_port)")"
876
+ if [[ -n "$leftover" ]]; then
877
+ printf 'Refresh incomplete: port %s is still held by pid(s) %s.\n' \
878
+ "$(runtime_port)" "$(printf '%s' "$leftover" | tr '\n' ' ')" >&2
879
+ printf 'It is not a wiki-manager runtime, so it was left running. Free the port, or set WIKI_MANAGER_RUNTIME_PORT to another one.\n' >&2
880
+ return 1
881
+ fi
795
882
  printf 'Refresh complete. Persistent workspace and agent data were preserved.\n'
796
883
  }
797
884
 
@@ -835,6 +922,9 @@ ensure_compose_override() {
835
922
  }
836
923
 
837
924
  ensure_manager_endpoints_file() {
925
+ if [[ -e "$MANAGER_ENDPOINTS_FILE" && ! -f "$MANAGER_ENDPOINTS_FILE" ]]; then
926
+ die "MCP endpoints path is not a file: $MANAGER_ENDPOINTS_FILE"
927
+ fi
838
928
  if [[ ! -f "$MANAGER_ENDPOINTS_FILE" && -f "$ROOT_DIR/mcp.endpoints.example.json" ]]; then
839
929
  mkdir -p "$(dirname "$MANAGER_ENDPOINTS_FILE")"
840
930
  cp "$ROOT_DIR/mcp.endpoints.example.json" "$MANAGER_ENDPOINTS_FILE"