@agentsdock/server 1.0.7-beta.5 → 1.0.7

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/README.md +21 -7
  2. package/package.json +1 -1
  3. package/server/VERSION +1 -1
  4. package/server/agent_server.py +4913 -660
  5. package/server/agentsdock_chats.py +2 -1
  6. package/server/chat_mailbox.py +23 -6
  7. package/server/claude_history_repair.py +45 -4
  8. package/server/claude_model_catalog.py +200 -0
  9. package/server/claude_sdk_client.py +63 -24
  10. package/server/claude_side_question.py +1 -8
  11. package/server/codex_app_server.py +78 -30
  12. package/server/codex_auth.py +119 -0
  13. package/server/codex_provider.py +1 -1
  14. package/server/codex_side_question.py +154 -57
  15. package/server/cursor_agent_client.py +27 -1
  16. package/server/cursor_history.py +219 -0
  17. package/server/cursor_provider_mcp.py +290 -0
  18. package/server/execution_durability.py +10 -6
  19. package/server/execution_preparation.py +6 -2
  20. package/server/install.sh +281 -78
  21. package/server/instances.sh +15 -0
  22. package/server/interactive_chat_native.py +4 -3
  23. package/server/interactive_chat_projection.py +6 -19
  24. package/server/interactive_chat_share_routes.py +71 -45
  25. package/server/interactive_chat_share_web.py +2 -2
  26. package/server/interactive_chat_shares.py +6 -11
  27. package/server/local_session_ownership.py +232 -0
  28. package/server/opencode_agent_client.py +653 -0
  29. package/server/provider_commands.py +579 -0
  30. package/server/provider_usage.py +241 -0
  31. package/server/public_chat_shares.py +60 -62
  32. package/server/public_chat_transcript.py +9 -31
  33. package/server/server_instances.py +931 -0
  34. package/server/shared_chat_video_stream.py +21 -2
  35. package/server/shared_chat_videos.py +54 -24
  36. package/server/side_questions.py +493 -10
  37. package/server/uninstall.sh +95 -17
  38. package/server/update_preparation.py +4 -1
  39. package/server/update_runner.py +16 -0
package/server/install.sh CHANGED
@@ -84,6 +84,10 @@ else
84
84
  STATE_ROOT="$HOME/.agentsdock"
85
85
  fi
86
86
  SERVICE_NAME="agents-server"
87
+ INSTANCE_NAME="${AGENTS_SERVER_INSTANCE:-default}"
88
+ INSTANCE_EXPLICIT="false"
89
+ LABEL="com.agentsdock.server"
90
+ INSTANCE_LOG_DIR="$HOME/Library/Logs/AgentsServer"
87
91
  LEGACY_SERVICE_NAME="zenithbot-agent"
88
92
  # AgentsServer's cooperative shutdown has 18 independently bounded cleanup
89
93
  # phases in addition to uvicorn's graceful window. Five seconds was shorter
@@ -141,7 +145,7 @@ SYSTEMD_MANAGED_STOP_ATTEMPTS=50
141
145
  SYSTEMD_MANAGED_STOP_DELAY=0.1
142
146
  SYSTEMD_MANAGED_KILL_ATTEMPTS=20
143
147
 
144
- if [[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]] && [[ -z "${NO_COLOR:-}" ]]; then
148
+ if [[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]] && [[ -z "${NO_COLOR+x}" ]]; then
145
149
  COLOR_GREEN=$'\033[32m'
146
150
  COLOR_RED=$'\033[31m'
147
151
  COLOR_YELLOW=$'\033[33m'
@@ -162,13 +166,16 @@ usage() {
162
166
  cat <<'USAGE'
163
167
  Usage: ./install.sh [--port PORT] [--bind ADDRESS] [--release-version VERSION] [--team-hub-host|--reactivate-team-hub-host|--no-team-hub-host] [--team-hub-tailscale-serve-url URL] [--team-hub-direct-ip-url URL] [--non-interactive] [--allow-port-fallback|--no-port-fallback]
164
168
 
169
+ --instance NAME updates a named instance (use ./instances.sh new to create one
170
+ with an automatically selected name/port). No arguments retain default setup.
171
+
165
172
  Installs or updates AgentsServer for the current user. Releases and Python
166
173
  runtimes are versioned, the previous healthy release is retained for rollback,
167
174
  and existing chat state and generated tokens are preserved. No sudo privileges
168
175
  are required.
169
176
 
170
- --non-interactive skips the optional tmux install prompt on macOS instead of
171
- asking; use it for unattended/SSH-driven runs.
177
+ --non-interactive skips optional prompts (including clipboard copy); use it for
178
+ unattended/SSH-driven runs.
172
179
 
173
180
  --fresh-install-only refuses an existing installation, state, configuration or
174
181
  service. The npm fresh-install command uses it; existing servers update through
@@ -214,7 +221,8 @@ never silently reactivates preserved state. Without an explicit host option, an
214
221
  existing host/disabled setting is preserved; new installs default to disabled.
215
222
 
216
223
  --show-token prints the current access token for an already-installed
217
- AgentsServer and exits immediately; it makes no other changes.
224
+ AgentsServer without reinstalling or restarting. Interactive terminals offer
225
+ optional clipboard copy; redirected output remains just the raw token.
218
226
  USAGE
219
227
  }
220
228
 
@@ -222,6 +230,7 @@ SHOW_TOKEN="false"
222
230
 
223
231
  while (($#)); do
224
232
  case "$1" in
233
+ --instance) INSTANCE_NAME="${2:?--instance requires a name}"; INSTANCE_EXPLICIT="true"; shift 2 ;;
225
234
  --port) PORT="${2:-}"; PORT_EXPLICIT="true"; shift 2 ;;
226
235
  --bind) BIND_ADDRESS="${2:-}"; BIND_EXPLICIT="true"; shift 2 ;;
227
236
  --release-version) RELEASE_VERSION="${2:-}"; shift 2 ;;
@@ -340,6 +349,41 @@ while (($#)); do
340
349
  esac
341
350
  done
342
351
 
352
+ if [[ ! "$INSTANCE_NAME" =~ ^[a-z][a-z0-9-]{0,31}$ ]]; then
353
+ echo "Invalid instance name." >&2
354
+ exit 2
355
+ fi
356
+ if [[ "$INSTANCE_NAME" != "default" || ( "$INSTANCE_EXPLICIT" == "true" && "${AGENTS_SERVER_INSTANCE:-default}" != "default" ) ]]; then
357
+ INSTANCE_SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
358
+ instance_bindings="$(bash "$INSTANCE_SCRIPT_DIR/instances.sh" _bindings "$INSTANCE_NAME")" || exit 2
359
+ eval "$instance_bindings"
360
+ # Never inherit the default instance's token, Hub designation or history.
361
+ unset AGENTSDOCK_AGENT_TOKEN ZENITHDOCK_AGENT_TOKEN ZENITHBOT_AGENT_TOKEN
362
+ export AGENTS_SERVER_INSTANCE="$INSTANCE_NAME"
363
+ export AGENTS_SERVER_INSTALL_DIR="$INSTALL_ROOT" AGENTS_SERVER_CONFIG_DIR="$CONFIG_ROOT" AGENTSDOCK_STATE_DIR="$STATE_ROOT"
364
+ [[ -n "${AGENTSDOCK_SERVER_NAME:-}" ]] || export AGENTSDOCK_SERVER_NAME="$INSTANCE_NAME"
365
+ PORT_FALLBACK="false"
366
+ fi
367
+ # Named services keep their independently named legacy unit. The split service
368
+ # protocol currently reserves the default gateway labels; never take them over.
369
+ if [[ "$INSTANCE_NAME" != "default" ]]; then
370
+ if [[ "$EXECUTION_MODE_EXPLICIT" == "true" && "$EXECUTION_MODE" != "legacy" ]]; then
371
+ echo "Named instances currently require --execution-mode legacy; no services were changed." >&2
372
+ exit 2
373
+ fi
374
+ if [[ -e "$INSTALL_ROOT/execution-layout.json" || -L "$INSTALL_ROOT/execution-layout.json" ]]; then
375
+ echo "A named instance cannot use the default split service layout." >&2
376
+ exit 2
377
+ fi
378
+ EXECUTION_MODE="legacy"
379
+ EXECUTION_MODE_EXPLICIT="true"
380
+ fi
381
+ export AGENTS_SERVER_INSTANCE="$INSTANCE_NAME"
382
+ if [[ "$INSTANCE_NAME" != "default" && ! -f "$CONFIG_ROOT/env" && "$PORT_EXPLICIT" != "true" && "$SHOW_TOKEN" != "true" ]]; then
383
+ echo "A new named instance needs --port; use ./instances.sh new for automatic allocation." >&2
384
+ exit 2
385
+ fi
386
+
343
387
  if [[ "$PORT_FALLBACK" == "auto" ]]; then
344
388
  if [[ "$PORT_EXPLICIT" == "true" ]]; then
345
389
  PORT_FALLBACK="false"
@@ -1278,7 +1322,7 @@ canonical_team_hub_direct_ipv4_url() {
1278
1322
 
1279
1323
  LEGACY_ENV_FILE=""
1280
1324
  LEGACY_SERVICE_CONTENTS=""
1281
- if [[ -e "$LEGACY_SERVICE_FILE" || -L "$LEGACY_SERVICE_FILE" ]]; then
1325
+ if [[ "$INSTANCE_NAME" == "default" && ( -e "$LEGACY_SERVICE_FILE" || -L "$LEGACY_SERVICE_FILE" ) ]]; then
1282
1326
  if ! LEGACY_SERVICE_CONTENTS="$(read_owned_config_file "$LEGACY_SERVICE_FILE")"; then
1283
1327
  echo "$LEGACY_SERVICE_FILE is not a safe regular legacy service file." >&2
1284
1328
  exit 1
@@ -1295,7 +1339,7 @@ if [[ -e "$LEGACY_SERVICE_FILE" || -L "$LEGACY_SERVICE_FILE" ]]; then
1295
1339
  exit 1
1296
1340
  fi
1297
1341
  fi
1298
- if [[ -z "$LEGACY_ENV_FILE" \
1342
+ if [[ "$INSTANCE_NAME" == "default" && -z "$LEGACY_ENV_FILE" \
1299
1343
  && ( -e "$HOME/Zenithbot/.env" || -L "$HOME/Zenithbot/.env" ) ]]; then
1300
1344
  LEGACY_ENV_FILE="$HOME/Zenithbot/.env"
1301
1345
  fi
@@ -1334,9 +1378,55 @@ find_existing_token() {
1334
1378
  return 1
1335
1379
  }
1336
1380
 
1381
+ interactive_token_output() {
1382
+ [[ "$NON_INTERACTIVE" != "true" && -t 0 && -t 1 && -z "$EXPECTED_SERVER_IDENTITY" ]]
1383
+ }
1384
+
1385
+ print_token_for_copy() {
1386
+ local access_token="$1"
1387
+ local copy_reply=""
1388
+ local clipboard_command=()
1389
+ printf '\nAccess token (%s):\n%s\n\n' "$INSTANCE_NAME" "$access_token"
1390
+ # A remote clipboard is not the user's local clipboard. Do not emit OSC 52
1391
+ # sequences or attempt to modify a remote desktop's clipboard over SSH.
1392
+ if [[ -n "${SSH_CONNECTION:-}${SSH_CLIENT:-}${SSH_TTY:-}" ]]; then
1393
+ printf '%s\n' 'Clipboard copy is unavailable over SSH; copy the token line above manually.'
1394
+ return 0
1395
+ fi
1396
+ if [[ "$(uname -s)" == "Darwin" ]] && command -v pbcopy >/dev/null 2>&1; then
1397
+ clipboard_command=(pbcopy)
1398
+ elif [[ -n "${WAYLAND_DISPLAY:-}" ]] && command -v wl-copy >/dev/null 2>&1; then
1399
+ clipboard_command=(wl-copy)
1400
+ elif [[ -n "${DISPLAY:-}" ]] && command -v xclip >/dev/null 2>&1; then
1401
+ clipboard_command=(xclip -selection clipboard)
1402
+ elif [[ -n "${DISPLAY:-}" ]] && command -v xsel >/dev/null 2>&1; then
1403
+ clipboard_command=(xsel --clipboard --input)
1404
+ fi
1405
+ if ((${#clipboard_command[@]} == 0)); then
1406
+ printf '%s\n' 'Clipboard copy is unavailable here; copy the token line above manually.'
1407
+ return 0
1408
+ fi
1409
+ read -r -p "Copy token to this machine's clipboard? [y/N] " copy_reply || return 0
1410
+ case "$copy_reply" in
1411
+ y|Y|[yY][eE][sS]) ;;
1412
+ *) return 0 ;;
1413
+ esac
1414
+ # Pass the credential via stdin, never an external process's argv. Optional
1415
+ # clipboard failures must not turn a successful installation into a failure.
1416
+ if printf '%s' "$access_token" | "${clipboard_command[@]}" >/dev/null 2>&1; then
1417
+ printf '%s\n' 'Copied to your clipboard.'
1418
+ else
1419
+ printf '%s\n' 'Could not copy to the clipboard; copy the token line above manually.'
1420
+ fi
1421
+ }
1422
+
1337
1423
  if [[ "$SHOW_TOKEN" == "true" ]]; then
1338
1424
  if TOKEN_TO_SHOW="$(find_existing_token)"; then
1339
- printf '%s\n' "$TOKEN_TO_SHOW"
1425
+ if interactive_token_output; then
1426
+ print_token_for_copy "$TOKEN_TO_SHOW"
1427
+ else
1428
+ printf '%s\n' "$TOKEN_TO_SHOW"
1429
+ fi
1340
1430
  exit 0
1341
1431
  fi
1342
1432
  echo "No AgentsServer access token found at $ENV_FILE. Run install.sh first." >&2
@@ -1345,7 +1435,7 @@ fi
1345
1435
 
1346
1436
  OS_NAME="$(uname -s)"
1347
1437
  SYSTEMD_SERVICE_FILE="$HOME/.config/systemd/user/$SERVICE_NAME.service"
1348
- LABEL="com.agentsdock.server"
1438
+ LABEL="${LABEL:-com.agentsdock.server}"
1349
1439
  PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
1350
1440
 
1351
1441
  validate_legacy_macos_update_roots() {
@@ -1749,7 +1839,7 @@ if [[ "$TEAM_HUB_MODE" == "host" && "$TEAM_HUB_OPERATION_PENDING" != "true" \
1749
1839
  fi
1750
1840
  fi
1751
1841
 
1752
- RELEASE_FILES=(activation_transaction.py execution_activation.py execution_legacy_runner.py execution_preparation.py update_preparation.py update_handoff.py update_recovery.py execution_update_status.py execution_uninstall.py execution_recovery.py execution_recovery_status.py execution_http.py execution_durability.py execution_control.py execution_install.py execution_maintenance.py execution_manage.py execution_ownership.py execution_service.py execution_transport.py agent_server.py workspace_git.py team_hub_host.py secure_peer_runtime.py team_mail_runtime.py team_mail_websocket.py team_mail_grants.py secure_peer_delivery.py agentsdock_jobs.py agentsdock_chats.py chat_mailbox.py agentsdock_emergency.py agentsdock_publish.py agentsdock_mail.py agentsdock_team.py provider_commands.py claude_sdk_client.py claude_goals.py claude_background_reconciliation.py codex_app_server.py codex_auth.py codex_provider.py side_questions.py title_generation.py codex_side_question.py claude_side_question.py cursor_agent_client.py cursor_process_guard.py claude_history_repair.py claude_history_provenance.py codex_history_repair.py public_chat_shares.py public_chat_transcript.py public_chat_share_routes.py interactive_chat_shares.py interactive_chat_share_routes.py interactive_chat_share_web.py interactive_chat_projection.py interactive_chat_runtime.py interactive_chat_native.py shared_chat_videos.py shared_chat_video_stream.py interactive_chat_controls.py install.sh uninstall.sh update_runner.py pyproject.toml uv.lock VERSION release-public-key.pem LICENSE NOTICE)
1842
+ RELEASE_FILES=(activation_transaction.py execution_activation.py execution_legacy_runner.py execution_preparation.py update_preparation.py update_handoff.py update_recovery.py execution_update_status.py execution_uninstall.py execution_recovery.py execution_recovery_status.py execution_http.py execution_durability.py execution_control.py execution_install.py execution_maintenance.py execution_manage.py execution_ownership.py execution_service.py execution_transport.py agent_server.py local_session_ownership.py cursor_history.py workspace_git.py team_hub_host.py secure_peer_runtime.py team_mail_runtime.py team_mail_websocket.py team_mail_grants.py secure_peer_delivery.py agentsdock_jobs.py agentsdock_chats.py chat_mailbox.py agentsdock_emergency.py agentsdock_publish.py agentsdock_mail.py agentsdock_team.py provider_commands.py provider_usage.py claude_sdk_client.py claude_model_catalog.py claude_goals.py claude_background_reconciliation.py codex_app_server.py codex_auth.py codex_provider.py side_questions.py title_generation.py codex_side_question.py claude_side_question.py cursor_agent_client.py cursor_provider_mcp.py opencode_agent_client.py cursor_process_guard.py claude_history_repair.py claude_history_provenance.py codex_history_repair.py public_chat_shares.py public_chat_transcript.py public_chat_share_routes.py interactive_chat_shares.py interactive_chat_share_routes.py interactive_chat_share_web.py interactive_chat_projection.py interactive_chat_runtime.py interactive_chat_native.py shared_chat_videos.py shared_chat_video_stream.py interactive_chat_controls.py install.sh uninstall.sh instances.sh server_instances.py update_runner.py pyproject.toml uv.lock VERSION release-public-key.pem LICENSE NOTICE)
1753
1843
  RELEASE_DIRECTORIES=(agentsdock_team_hub)
1754
1844
  TEAM_HUB_RELEASE_FILES=(
1755
1845
  __init__.py
@@ -1801,12 +1891,11 @@ for name in "${RELEASE_DIRECTORIES[@]}"; do
1801
1891
  echo "$name is missing beside install.sh or is not a real directory." >&2
1802
1892
  exit 1
1803
1893
  fi
1894
+ # Local Python runs create bytecode caches. Never copy those (staging uses
1895
+ # the explicit manifest below), but still reject links/special files anywhere,
1896
+ # including inside a cache directory.
1804
1897
  if find "$SOURCE_DIR/$name" \( -type l -o \( ! -type d ! -type f \) \) -print -quit | grep -q .; then
1805
- echo "$name contains unsafe linked or special entries and cannot be installed." >&2
1806
- exit 1
1807
- fi
1808
- if [[ -z "$ACTIVATE_PREPARED" && "$RECOVER_ONLY" != "true" ]] && find "$SOURCE_DIR/$name" \( -type f \( -name '*.pyc' -o -name '*.pyo' \) -o -type d -name '__pycache__' \) -print -quit | grep -q .; then
1809
- echo "$name contains linked or generated entries and cannot be installed." >&2
1898
+ echo "$name contains linked or special entries and cannot be installed." >&2
1810
1899
  exit 1
1811
1900
  fi
1812
1901
  done
@@ -1820,13 +1909,13 @@ done
1820
1909
  # bound by the receipt or owned recovery journal before activation; raw source
1821
1910
  # archives continue to require the exact clean allowlist.
1822
1911
  if [[ -z "$ACTIVATE_PREPARED" && "$RECOVER_ONLY" != "true" ]]; then
1823
- TEAM_HUB_RELEASE_FILE_COUNT="$(find "$SOURCE_DIR/agentsdock_team_hub" -type f | wc -l)"
1912
+ TEAM_HUB_RELEASE_FILE_COUNT="$(find "$SOURCE_DIR/agentsdock_team_hub" -type d -name '__pycache__' -prune -o -type f ! -name '*.pyc' ! -name '*.pyo' -print | wc -l)"
1824
1913
  TEAM_HUB_RELEASE_FILE_COUNT="${TEAM_HUB_RELEASE_FILE_COUNT//[[:space:]]/}"
1825
1914
  if [[ "$TEAM_HUB_RELEASE_FILE_COUNT" != "${#TEAM_HUB_RELEASE_FILES[@]}" ]]; then
1826
1915
  echo "agentsdock_team_hub contains unexpected release files." >&2
1827
1916
  exit 1
1828
1917
  fi
1829
- TEAM_HUB_RELEASE_DIRECTORY_COUNT="$(find "$SOURCE_DIR/agentsdock_team_hub" -type d | wc -l)"
1918
+ TEAM_HUB_RELEASE_DIRECTORY_COUNT="$(find "$SOURCE_DIR/agentsdock_team_hub" -type d -name '__pycache__' -prune -o -type d -print | wc -l)"
1830
1919
  TEAM_HUB_RELEASE_DIRECTORY_COUNT="${TEAM_HUB_RELEASE_DIRECTORY_COUNT//[[:space:]]/}"
1831
1920
  if [[ "$TEAM_HUB_RELEASE_DIRECTORY_COUNT" != "2" ]]; then
1832
1921
  echo "agentsdock_team_hub contains unexpected release directories." >&2
@@ -2459,7 +2548,7 @@ backup_runtime_configuration() {
2459
2548
  current_snapshot="${current_snapshot#*|}"
2460
2549
  PRIOR_SERVICE_ENABLED="${current_snapshot%%|*}"
2461
2550
  fi
2462
- if [[ "$OS_NAME" == "Linux" ]]; then
2551
+ if [[ "$OS_NAME" == "Linux" && "${INSTANCE_NAME:-default}" == "default" ]]; then
2463
2552
  local legacy_exists="false"
2464
2553
  local legacy_snapshot=""
2465
2554
  if [[ -e "$LEGACY_SERVICE_FILE" || -L "$LEGACY_SERVICE_FILE" ]]; then
@@ -3496,6 +3585,7 @@ sync_release_dependencies() (
3496
3585
  --project "$STAGE_DIR" \
3497
3586
  --python '>=3.10' \
3498
3587
  --no-dev \
3588
+ --quiet \
3499
3589
  --frozen
3500
3590
  )
3501
3591
 
@@ -3526,6 +3616,8 @@ validate_staged_release_runtime() (
3526
3616
  "$STAGE_DIR/execution_service.py" \
3527
3617
  "$STAGE_DIR/execution_transport.py" \
3528
3618
  "$STAGE_DIR/agent_server.py" \
3619
+ "$STAGE_DIR/local_session_ownership.py" \
3620
+ "$STAGE_DIR/cursor_history.py" \
3529
3621
  "$STAGE_DIR/team_hub_host.py" \
3530
3622
  "$STAGE_DIR/secure_peer_runtime.py" \
3531
3623
  "$STAGE_DIR/team_mail_runtime.py" \
@@ -3540,7 +3632,9 @@ validate_staged_release_runtime() (
3540
3632
  "$STAGE_DIR/agentsdock_mail.py" \
3541
3633
  "$STAGE_DIR/agentsdock_team.py" \
3542
3634
  "$STAGE_DIR/provider_commands.py" \
3635
+ "$STAGE_DIR/provider_usage.py" \
3543
3636
  "$STAGE_DIR/claude_sdk_client.py" \
3637
+ "$STAGE_DIR/claude_model_catalog.py" \
3544
3638
  "$STAGE_DIR/claude_goals.py" \
3545
3639
  "$STAGE_DIR/claude_background_reconciliation.py" \
3546
3640
  "$STAGE_DIR/codex_app_server.py" \
@@ -3552,6 +3646,8 @@ validate_staged_release_runtime() (
3552
3646
  "$STAGE_DIR/title_generation.py" \
3553
3647
  "$STAGE_DIR/claude_side_question.py" \
3554
3648
  "$STAGE_DIR/cursor_agent_client.py" \
3649
+ "$STAGE_DIR/cursor_provider_mcp.py" \
3650
+ "$STAGE_DIR/opencode_agent_client.py" \
3555
3651
  "$STAGE_DIR/cursor_process_guard.py" \
3556
3652
  "$STAGE_DIR/claude_history_repair.py" \
3557
3653
  "$STAGE_DIR/claude_history_provenance.py" \
@@ -3569,8 +3665,9 @@ validate_staged_release_runtime() (
3569
3665
  "$STAGE_DIR/shared_chat_video_stream.py" \
3570
3666
  "$STAGE_DIR/interactive_chat_controls.py" \
3571
3667
  "$STAGE_DIR/update_runner.py"
3668
+ "$STAGE_DIR/.venv/bin/python" -m py_compile "$STAGE_DIR/server_instances.py"
3572
3669
  "$STAGE_DIR/.venv/bin/python" -m compileall -q "$STAGE_DIR/agentsdock_team_hub"
3573
- PYTHONPATH="$STAGE_DIR" "$STAGE_DIR/.venv/bin/python" -c 'import execution_activation, execution_preparation, update_preparation, update_handoff, update_recovery, execution_update_status, execution_uninstall, execution_recovery, execution_recovery_status, execution_http, execution_durability, execution_control, execution_install, execution_maintenance, execution_manage, execution_ownership, execution_service, execution_transport; import workspace_git; import codex_auth, codex_provider, side_questions, title_generation, codex_side_question, claude_side_question, agentsdock_team_hub, cursor_agent_client, cursor_process_guard, secure_peer_delivery, secure_peer_runtime, team_mail_runtime, team_mail_websocket, team_mail_grants, team_hub_host, agentsdock_mail, agentsdock_team, claude_history_repair, claude_history_provenance, claude_goals, claude_background_reconciliation, codex_history_repair, public_chat_shares, public_chat_transcript, public_chat_share_routes, interactive_chat_shares, interactive_chat_share_routes, interactive_chat_share_web, interactive_chat_projection, interactive_chat_runtime, interactive_chat_native, shared_chat_videos, shared_chat_video_stream, interactive_chat_controls, chat_mailbox, provider_commands; from agentsdock_team_hub import secure_peer, secure_peer_hub' >/dev/null
3670
+ PYTHONPATH="$STAGE_DIR" "$STAGE_DIR/.venv/bin/python" -c 'import execution_activation, execution_preparation, update_preparation, update_handoff, update_recovery, execution_update_status, execution_uninstall, execution_recovery, execution_recovery_status, execution_http, execution_durability, execution_control, execution_install, execution_maintenance, execution_manage, execution_ownership, execution_service, execution_transport; import workspace_git, claude_model_catalog, server_instances, local_session_ownership, cursor_history; import codex_auth, codex_provider, side_questions, title_generation, codex_side_question, claude_side_question, agentsdock_team_hub, cursor_agent_client, cursor_provider_mcp, opencode_agent_client, cursor_process_guard, secure_peer_delivery, secure_peer_runtime, team_mail_runtime, team_mail_websocket, team_mail_grants, team_hub_host, agentsdock_mail, agentsdock_team, claude_history_repair, claude_history_provenance, claude_goals, claude_background_reconciliation, codex_history_repair, public_chat_shares, public_chat_transcript, public_chat_share_routes, interactive_chat_shares, interactive_chat_share_routes, interactive_chat_share_web, interactive_chat_projection, interactive_chat_runtime, interactive_chat_native, shared_chat_videos, shared_chat_video_stream, interactive_chat_controls, chat_mailbox, provider_commands, provider_usage; from agentsdock_team_hub import secure_peer, secure_peer_hub' >/dev/null
3574
3671
  )
3575
3672
 
3576
3673
  abort_unclaimed_team_hub_reactivation() {
@@ -3898,6 +3995,7 @@ for name in "${TEAM_HUB_RELEASE_FILES[@]}"; do
3898
3995
  "$STAGE_DIR/agentsdock_team_hub/$name"
3899
3996
  done
3900
3997
  chmod 755 "$STAGE_DIR/agent_server.py" "$STAGE_DIR/agentsdock_jobs.py" "$STAGE_DIR/agentsdock_chats.py" "$STAGE_DIR/agentsdock_emergency.py" "$STAGE_DIR/agentsdock_publish.py" "$STAGE_DIR/agentsdock_mail.py" "$STAGE_DIR/agentsdock_team.py" "$STAGE_DIR/install.sh" "$STAGE_DIR/uninstall.sh" "$STAGE_DIR/update_runner.py"
3998
+ chmod 755 "$STAGE_DIR/instances.sh"
3901
3999
 
3902
4000
  if [[ "$PREPARE_ONLY" == "true" ]]; then
3903
4001
  source_inventory="$(mktemp "$INSTALL_ROOT/.prepared-inventory.XXXXXXXX")" || exit 1
@@ -3922,7 +4020,9 @@ with output.open("w") as stream:
3922
4020
  PYINVENTORY
3923
4021
  fi
3924
4022
 
3925
- echo "[2/7] Resolving the release dependencies with uv"
4023
+ echo "[2/7] Preparing private Python dependencies for instance $INSTANCE_NAME"
4024
+ echo " Reusing uv's package cache where available; errors will still be shown."
4025
+ echo " This does not reinstall Claude Code, Codex, or tmux."
3926
4026
  if run_timed_stage \
3927
4027
  "dependency resolution" \
3928
4028
  "$DEPENDENCY_SYNC_TIMEOUT_SECONDS" \
@@ -3998,7 +4098,7 @@ write_runtime_env() {
3998
4098
  local filter_status=0
3999
4099
  preserved_contents="$(read_owned_config_file "$PRESERVE_SOURCE")" || return 1
4000
4100
  if printf '%s\n' "$preserved_contents" \
4001
- | grep -Ev '^(AGENTSDOCK_(STATE_DIR|AGENT_CWD|AGENT_BIND|AGENT_PORT|AGENT_TOKEN|SERVER_NAME|TEAM_HUB_MODE|TEAM_HUB_TRANSPORT|TEAM_HUB_URL|TEAM_HUB_DIRECT_IP_URL|TEAM_HUB_REACTIVATION_HUB_ID|TEAM_HUB_REACTIVATION_OPERATION_ID|TEAM_HUB_REACTIVATION_SNAPSHOT|TEAM_HUB_UPDATE_HUB_ID|TEAM_HUB_UPDATE_OPERATION_ID|TEAM_HUB_UPDATE_SNAPSHOT)|AGENTS_SERVER_(STATE_DIR|INSTALL_DIR)|ZENITHBOT_AGENT_(DIR|CWD|BIND|PORT|TOKEN)|ZENITHDOCK_AGENT_TOKEN|PATH)=' \
4101
+ | grep -Ev '^(AGENTSDOCK_(STATE_DIR|AGENT_CWD|AGENT_BIND|AGENT_PORT|AGENT_TOKEN|SERVER_NAME|TEAM_HUB_MODE|TEAM_HUB_TRANSPORT|TEAM_HUB_URL|TEAM_HUB_DIRECT_IP_URL|TEAM_HUB_REACTIVATION_HUB_ID|TEAM_HUB_REACTIVATION_OPERATION_ID|TEAM_HUB_REACTIVATION_SNAPSHOT|TEAM_HUB_UPDATE_HUB_ID|TEAM_HUB_UPDATE_OPERATION_ID|TEAM_HUB_UPDATE_SNAPSHOT)|AGENTS_SERVER_(STATE_DIR|INSTALL_DIR|CONFIG_DIR|INSTANCE)|ZENITHBOT_AGENT_(DIR|CWD|BIND|PORT|TOKEN)|ZENITHDOCK_AGENT_TOKEN|PATH)=' \
4002
4102
  > "$env_temp"; then
4003
4103
  :
4004
4104
  else
@@ -4024,6 +4124,8 @@ AGENTSDOCK_TEAM_HUB_TRANSPORT=$TEAM_HUB_TRANSPORT
4024
4124
  AGENTSDOCK_TEAM_HUB_URL=$TEAM_HUB_URL
4025
4125
  AGENTSDOCK_TEAM_HUB_DIRECT_IP_URL=$TEAM_HUB_DIRECT_IP_URL
4026
4126
  AGENTS_SERVER_INSTALL_DIR=$INSTALL_ROOT
4127
+ AGENTS_SERVER_CONFIG_DIR=$CONFIG_ROOT
4128
+ AGENTS_SERVER_INSTANCE=${INSTANCE_NAME:-default}
4027
4129
  PATH=$SERVER_PATH
4028
4130
  EOF
4029
4131
  if ! replace_activation_config env "$env_temp" 600; then
@@ -4181,21 +4283,23 @@ restart_service() {
4181
4283
  return
4182
4284
  fi
4183
4285
  if [[ "$OS_NAME" == "Linux" ]]; then
4184
- local legacy_exists="false"
4185
- local legacy_snapshot=""
4186
- [[ -f "$LEGACY_SERVICE_FILE" && ! -L "$LEGACY_SERVICE_FILE" ]] \
4187
- && legacy_exists="true"
4188
- if [[ "$PRIOR_LEGACY_SERVICE_STATE" == "absent" ]]; then
4189
- legacy_snapshot="$(systemd_unit_snapshot \
4190
- "$LEGACY_SERVICE_NAME.service" "$legacy_exists")" || return 1
4191
- [[ "${legacy_snapshot%%|*}" == "absent" ]] || return 1
4192
- else
4193
- systemctl --user disable --now "$LEGACY_SERVICE_NAME.service" \
4194
- >/dev/null || return 1
4195
- legacy_snapshot="$(systemd_unit_snapshot \
4196
- "$LEGACY_SERVICE_NAME.service" "$legacy_exists")" || return 1
4197
- [[ "${legacy_snapshot%%|*}" == "stopped" \
4198
- && "${legacy_snapshot#*|}" == "false|"* ]] || return 1
4286
+ if [[ "${INSTANCE_NAME:-default}" == "default" ]]; then
4287
+ local legacy_exists="false"
4288
+ local legacy_snapshot=""
4289
+ [[ -f "$LEGACY_SERVICE_FILE" && ! -L "$LEGACY_SERVICE_FILE" ]] \
4290
+ && legacy_exists="true"
4291
+ if [[ "$PRIOR_LEGACY_SERVICE_STATE" == "absent" ]]; then
4292
+ legacy_snapshot="$(systemd_unit_snapshot \
4293
+ "$LEGACY_SERVICE_NAME.service" "$legacy_exists")" || return 1
4294
+ [[ "${legacy_snapshot%%|*}" == "absent" ]] || return 1
4295
+ else
4296
+ systemctl --user disable --now "$LEGACY_SERVICE_NAME.service" \
4297
+ >/dev/null || return 1
4298
+ legacy_snapshot="$(systemd_unit_snapshot \
4299
+ "$LEGACY_SERVICE_NAME.service" "$legacy_exists")" || return 1
4300
+ [[ "${legacy_snapshot%%|*}" == "stopped" \
4301
+ && "${legacy_snapshot#*|}" == "false|"* ]] || return 1
4302
+ fi
4199
4303
  fi
4200
4304
  systemctl --user daemon-reload || return
4201
4305
  systemctl --user enable "$SERVICE_NAME.service" >/dev/null || return
@@ -4527,19 +4631,21 @@ restore_prior_service_state() {
4527
4631
  [[ "$PRIOR_SERVICE_STATE" == "absent" ]] || return 1
4528
4632
  }
4529
4633
  fi
4530
- if [[ "$PRIOR_LEGACY_SERVICE_ENABLED" == "true" ]]; then
4531
- systemctl --user enable "$LEGACY_SERVICE_NAME.service" >/dev/null || return 1
4532
- elif [[ "$PRIOR_LEGACY_SERVICE_STATE" == "absent" ]]; then
4533
- systemctl --user disable "$LEGACY_SERVICE_NAME.service" >/dev/null 2>&1 || true
4534
- else
4535
- systemctl --user disable "$LEGACY_SERVICE_NAME.service" >/dev/null || return 1
4536
- fi
4537
- if [[ "$PRIOR_LEGACY_SERVICE_STATE" == "running" ]]; then
4538
- systemctl --user start "$LEGACY_SERVICE_NAME.service" || return 1
4539
- else
4540
- systemctl --user stop "$LEGACY_SERVICE_NAME.service" >/dev/null 2>&1 || {
4541
- [[ "$PRIOR_LEGACY_SERVICE_STATE" == "absent" ]] || return 1
4542
- }
4634
+ if [[ "${INSTANCE_NAME:-default}" == "default" ]]; then
4635
+ if [[ "$PRIOR_LEGACY_SERVICE_ENABLED" == "true" ]]; then
4636
+ systemctl --user enable "$LEGACY_SERVICE_NAME.service" >/dev/null || return 1
4637
+ elif [[ "$PRIOR_LEGACY_SERVICE_STATE" == "absent" ]]; then
4638
+ systemctl --user disable "$LEGACY_SERVICE_NAME.service" >/dev/null 2>&1 || true
4639
+ else
4640
+ systemctl --user disable "$LEGACY_SERVICE_NAME.service" >/dev/null || return 1
4641
+ fi
4642
+ if [[ "$PRIOR_LEGACY_SERVICE_STATE" == "running" ]]; then
4643
+ systemctl --user start "$LEGACY_SERVICE_NAME.service" || return 1
4644
+ else
4645
+ systemctl --user stop "$LEGACY_SERVICE_NAME.service" >/dev/null 2>&1 || {
4646
+ [[ "$PRIOR_LEGACY_SERVICE_STATE" == "absent" ]] || return 1
4647
+ }
4648
+ fi
4543
4649
  fi
4544
4650
  local current_exists="false"
4545
4651
  local legacy_exists="false"
@@ -4555,7 +4661,7 @@ restore_prior_service_state() {
4555
4661
  && "${observed_snapshot#*|}" == "$PRIOR_SERVICE_ENABLED|"* ]] \
4556
4662
  || return 1
4557
4663
  fi
4558
- if [[ "$PRIOR_LEGACY_SERVICE_STATE" != "running" ]]; then
4664
+ if [[ "${INSTANCE_NAME:-default}" == "default" && "$PRIOR_LEGACY_SERVICE_STATE" != "running" ]]; then
4559
4665
  observed_snapshot="$(systemd_unit_snapshot \
4560
4666
  "$LEGACY_SERVICE_NAME.service" "$legacy_exists")" || return 1
4561
4667
  [[ "${observed_snapshot%%|*}" == "$PRIOR_LEGACY_SERVICE_STATE" \
@@ -4722,7 +4828,7 @@ write_service_files() {
4722
4828
  if [[ "$OS_NAME" == "Linux" ]]; then
4723
4829
  USER_SERVICE_DIR="$HOME/.config/systemd/user"
4724
4830
  mkdir -p "$USER_SERVICE_DIR"
4725
- service_temp="$USER_SERVICE_DIR/.agents-server.service.activation-$ACTIVATION_TRANSACTION_ID-service.source"
4831
+ service_temp="$USER_SERVICE_DIR/.$SERVICE_NAME.service.activation-$ACTIVATION_TRANSACTION_ID-service.source"
4726
4832
  (umask 077; set -o noclobber; : > "$service_temp") 2>/dev/null || return 1
4727
4833
  chmod 600 "$service_temp" || return 1
4728
4834
  cat > "$service_temp" <<EOF
@@ -4756,8 +4862,9 @@ EOF
4756
4862
  SERVICE_KIND="systemd-user"
4757
4863
  elif [[ "$OS_NAME" == "Darwin" ]]; then
4758
4864
  LAUNCH_AGENTS="$HOME/Library/LaunchAgents"
4759
- mkdir -p "$LAUNCH_AGENTS" "$HOME/Library/Logs/AgentsServer"
4760
- service_temp="$LAUNCH_AGENTS/.com.agentsdock.server.plist.activation-$ACTIVATION_TRANSACTION_ID-service.source"
4865
+ local instance_log_dir="${INSTANCE_LOG_DIR:-$HOME/Library/Logs/AgentsServer}"
4866
+ mkdir -p "$LAUNCH_AGENTS" "$instance_log_dir"
4867
+ service_temp="$LAUNCH_AGENTS/.$LABEL.plist.activation-$ACTIVATION_TRANSACTION_ID-service.source"
4761
4868
  (umask 077; set -o noclobber; : > "$service_temp") 2>/dev/null || return 1
4762
4869
  chmod 600 "$service_temp" || return 1
4763
4870
  local launchd_server_name_entry=""
@@ -4790,11 +4897,13 @@ $launchd_server_name_entry
4790
4897
  <key>AGENTSDOCK_TEAM_HUB_URL</key><string>$TEAM_HUB_URL</string>
4791
4898
  <key>AGENTSDOCK_TEAM_HUB_DIRECT_IP_URL</key><string>$TEAM_HUB_DIRECT_IP_URL</string>
4792
4899
  <key>AGENTS_SERVER_INSTALL_DIR</key><string>$INSTALL_ROOT</string>
4900
+ <key>AGENTS_SERVER_CONFIG_DIR</key><string>$CONFIG_ROOT</string>
4901
+ <key>AGENTS_SERVER_INSTANCE</key><string>${INSTANCE_NAME:-default}</string>
4793
4902
  <key>PATH</key><string>$SERVER_PATH</string>
4794
4903
  </dict>
4795
4904
  <key>RunAtLoad</key><true/><key>KeepAlive</key><true/>
4796
- <key>StandardOutPath</key><string>$HOME/Library/Logs/AgentsServer/server.log</string>
4797
- <key>StandardErrorPath</key><string>$HOME/Library/Logs/AgentsServer/server-error.log</string>
4905
+ <key>StandardOutPath</key><string>$instance_log_dir/server.log</string>
4906
+ <key>StandardErrorPath</key><string>$instance_log_dir/server-error.log</string>
4798
4907
  </dict></plist>
4799
4908
  EOF
4800
4909
  if ! replace_activation_config service "$service_temp" 600; then
@@ -4928,7 +5037,8 @@ service_manager_owns_listener() {
4928
5037
  services=("$LABEL")
4929
5038
  [[ "$EXECUTION_MODE" != "split" ]] || services+=("com.agentsdock.gateway")
4930
5039
  else
4931
- services=("$SERVICE_NAME" "${LEGACY_SERVICE_NAME:-}")
5040
+ services=("$SERVICE_NAME")
5041
+ [[ "${INSTANCE_NAME:-default}" != "default" ]] || services+=("${LEGACY_SERVICE_NAME:-}")
4932
5042
  [[ "$EXECUTION_MODE" != "split" ]] || services+=("agents-server-gateway")
4933
5043
  fi
4934
5044
  for service in "${services[@]}"; do
@@ -4968,7 +5078,8 @@ pinned_managed_http_get() {
4968
5078
  services=("$LABEL")
4969
5079
  [[ "$EXECUTION_MODE" != "split" ]] || services+=("com.agentsdock.gateway")
4970
5080
  else
4971
- services=("$SERVICE_NAME" "${LEGACY_SERVICE_NAME:-}")
5081
+ services=("$SERVICE_NAME")
5082
+ [[ "${INSTANCE_NAME:-default}" != "default" ]] || services+=("${LEGACY_SERVICE_NAME:-}")
4972
5083
  [[ "$EXECUTION_MODE" != "split" ]] || services+=("agents-server-gateway")
4973
5084
  fi
4974
5085
  for service in "${services[@]}"; do
@@ -7013,36 +7124,125 @@ if [[ "$CLAUDE_READY" == "false" && "$CODEX_READY" == "false" ]]; then
7013
7124
  echo " Sign in to at least one before starting a chat."
7014
7125
  fi
7015
7126
 
7016
- TAILSCALE_IP=""
7017
- if command -v tailscale >/dev/null 2>&1; then
7018
- TAILSCALE_IP="$(tailscale ip -4 2>/dev/null | head -n 1 || true)"
7019
- fi
7020
- SERVER_URL="$(health_origin "$PORT")"
7021
- if [[ "$BIND_ADDRESS" == "0.0.0.0" && -n "$TAILSCALE_IP" ]]; then
7022
- SERVER_URL="http://$TAILSCALE_IP:$PORT"
7023
- fi
7127
+ setup_network_summary() {
7128
+ TAILSCALE_IP=""
7129
+ TAILSCALE_STATUS="unknown"
7130
+ TAILSCALE_BIND_MATCH="false"
7131
+ SERVER_LOCAL_ONLY="false"
7132
+ NETWORK_URLS=""
7133
+ SERVER_URL="$(health_origin "$PORT")"
7134
+ local network_bindings=""
7135
+ # The shipped helper emits only fixed keys and shell-quoted validated values.
7136
+ # Optional network diagnostics must not fail an already healthy installation.
7137
+ if network_bindings="$("$CURRENT_LINK/.venv/bin/python" \
7138
+ "$CURRENT_LINK/server_instances.py" _setup-network \
7139
+ --bind "$BIND_ADDRESS" --port "$PORT" 2>/dev/null)" \
7140
+ && [[ -n "$network_bindings" ]]; then
7141
+ eval "$network_bindings"
7142
+ fi
7143
+ }
7144
+
7145
+ print_tailscale_summary() {
7146
+ case "$TAILSCALE_STATUS" in
7147
+ connected)
7148
+ if [[ "$TAILSCALE_BIND_MATCH" == "true" ]]; then
7149
+ echo " $CHECK_MARK Tailscale already connected: http://$TAILSCALE_IP:$PORT (tailnet access rules still apply)"
7150
+ elif [[ "$SERVER_LOCAL_ONLY" == "true" ]]; then
7151
+ echo " $DOT_MARK Tailscale already connected; this server is bound to localhost only, so phones cannot connect directly."
7152
+ echo " For phone access, choose a reachable server bind: https://github.com/ZhengyiLuo/AgentsServer"
7153
+ else
7154
+ echo " $DOT_MARK Tailscale already connected; its IPv4 address is not available on this server's configured bind."
7155
+ echo " For tailnet access, review the server bind: https://github.com/ZhengyiLuo/AgentsServer"
7156
+ fi
7157
+ ;;
7158
+ disconnected)
7159
+ echo " $DOT_MARK Tailscale already installed but not connected; no installation or configuration changes made."
7160
+ echo " To connect from other networks, open Tailscale and sign in/connect."
7161
+ ;;
7162
+ not-installed)
7163
+ echo " $DOT_MARK optional: Tailscale was not found; use your LAN address on the same network, or see https://tailscale.com/download"
7164
+ ;;
7165
+ unavailable)
7166
+ echo " $DOT_MARK Tailscale already installed; connection status could not be checked. No setup changes made."
7167
+ echo " Open Tailscale to check the connection, or try: tailscale status"
7168
+ ;;
7169
+ *)
7170
+ echo " $DOT_MARK Tailscale connection status could not be checked; no installation or configuration changes made."
7171
+ echo " If you use Tailscale, check its app or try: tailscale status"
7172
+ ;;
7173
+ esac
7174
+ }
7175
+
7176
+ print_setup_checklist() {
7177
+ local network_ready="false"
7178
+ [[ "$TAILSCALE_STATUS" != "connected" || "$TAILSCALE_BIND_MATCH" != "true" ]] || network_ready="true"
7179
+ if [[ -z "$TMUX_WARNING" && "$network_ready" == "true" \
7180
+ && ( "$CLAUDE_READY" == "true" || "$CODEX_READY" == "true" ) ]]; then
7181
+ echo " ${COLOR_GREEN}${COLOR_BOLD}You are all set${COLOR_RESET}"
7182
+ echo " $CHECK_MARK tmux available"
7183
+ print_tailscale_summary
7184
+ return
7185
+ fi
7186
+
7187
+ echo " ${COLOR_BOLD}You already have${COLOR_RESET}"
7188
+ echo " $CHECK_MARK AgentsServer running"
7189
+ if [[ -z "$TMUX_WARNING" ]]; then
7190
+ echo " $CHECK_MARK tmux available"
7191
+ fi
7192
+ if [[ "$network_ready" == "true" ]]; then
7193
+ print_tailscale_summary
7194
+ elif [[ "$TAILSCALE_STATUS" == "connected" || "$TAILSCALE_STATUS" == "disconnected" || "$TAILSCALE_STATUS" == "unavailable" ]]; then
7195
+ echo " $CHECK_MARK Tailscale installed"
7196
+ fi
7197
+
7198
+ if [[ "$CLAUDE_READY" == "false" && "$CODEX_READY" == "false" ]]; then
7199
+ echo
7200
+ echo " ${COLOR_BOLD}To start chats${COLOR_RESET}"
7201
+ echo " $DOT_MARK Choose one agent CLI and sign in:"
7202
+ echo " Claude Code: npm install -g @anthropic-ai/claude-code, then run: claude"
7203
+ echo " Codex: npm install -g @openai/codex, then run: codex login"
7204
+ fi
7205
+ if [[ -n "$TMUX_WARNING" || "$network_ready" != "true" ]]; then
7206
+ echo
7207
+ echo " ${COLOR_BOLD}Optional next steps${COLOR_RESET}"
7208
+ echo " You can skip these unless you need the features below."
7209
+ if [[ -n "$TMUX_WARNING" ]]; then
7210
+ echo " $DOT_MARK For persistent terminals, pane inspection and in-app updates, install tmux:"
7211
+ if [[ "$OS_NAME" == "Darwin" ]]; then
7212
+ echo " With Homebrew: brew install tmux"
7213
+ else
7214
+ echo " Use your package manager, for example: sudo apt install tmux"
7215
+ fi
7216
+ fi
7217
+ if [[ "$network_ready" != "true" ]]; then
7218
+ print_tailscale_summary
7219
+ fi
7220
+ fi
7221
+ }
7222
+
7223
+ setup_network_summary
7024
7224
 
7025
7225
  echo "[7/7] AgentsServer $RELEASE_VERSION is ready"
7226
+ if [[ "$PRIOR_SERVICE_STATE" == "absent" && "$PRIOR_LEGACY_SERVICE_STATE" == "absent" && -z "$EXPECTED_SERVER_IDENTITY" ]]; then
7227
+ printf '\n%s%s\n%s\n%s%s\n' \
7228
+ "$COLOR_GREEN" '================================' \
7229
+ 'Your new service is up!' \
7230
+ '================================' "$COLOR_RESET"
7231
+ fi
7026
7232
  echo
7027
7233
  echo " ${COLOR_BOLD}Server URL${COLOR_RESET} $SERVER_URL"
7234
+ if [[ -n "$NETWORK_URLS" ]]; then
7235
+ echo " Other local/network addresses (reachability depends on network/firewall):"
7236
+ while IFS= read -r network_url; do
7237
+ [[ -z "$network_url" ]] || echo " $network_url"
7238
+ done <<< "$NETWORK_URLS"
7239
+ fi
7028
7240
  echo
7029
- echo " ${COLOR_BOLD}Next steps${COLOR_RESET}"
7241
+ print_setup_checklist
7030
7242
  if [[ "$PORT_AUTO_SELECTED" == "true" ]]; then
7243
+ echo
7031
7244
  echo " $DOT_MARK port $ORIGINAL_PORT was already in use, installed on $PORT instead (--port pins an exact port unless --allow-port-fallback is supplied)"
7032
7245
  fi
7033
- if [[ "$CLAUDE_READY" == "false" && "$CODEX_READY" == "false" ]]; then
7034
- echo " $CROSS_MARK install and sign in to Claude Code or Codex (see [6/7] above) before starting a chat"
7035
- fi
7036
- if [[ -n "$TMUX_WARNING" ]]; then
7037
- echo " $CROSS_MARK tmux unavailable: persistent terminal, pane inspection, and in-app updates won't work - $TMUX_WARNING"
7038
- else
7039
- echo " $CHECK_MARK tmux available"
7040
- fi
7041
- if [[ -n "$TAILSCALE_IP" ]]; then
7042
- echo " $CHECK_MARK reachable via Tailscale at $TAILSCALE_IP"
7043
- else
7044
- echo " $DOT_MARK optional: install and connect Tailscale to reach this server from another device or WiFi network: https://tailscale.com/download"
7045
- fi
7046
7246
  if [[ "$TEAM_HUB_MODE" == "host" ]]; then
7047
7247
  echo
7048
7248
  if [[ "$TEAM_HUB_TRANSPORT" == "tailscale_serve" ]]; then
@@ -7071,4 +7271,7 @@ echo
7071
7271
  if [[ -z "$EXPECTED_SERVER_IDENTITY" ]]; then
7072
7272
  printf 'AGENTSDOCK_SETUP_RESULT={"server_url":"%s","access_token":"%s","service":"%s","tailscale_ip":"%s","server_version":"%s"}\n' \
7073
7273
  "$SERVER_URL" "$TOKEN" "$SERVICE_KIND" "$TAILSCALE_IP" "$RELEASE_VERSION"
7274
+ if interactive_token_output; then
7275
+ print_token_for_copy "$TOKEN"
7276
+ fi
7074
7277
  fi
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # No downloads or package installation just to list/manage existing instances.
5
+ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
6
+ for instance_python in "$SCRIPT_DIR/.venv/bin/python" \
7
+ "$HOME/.local/share/agents-server/current/.venv/bin/python" \
8
+ "$(command -v python3 || true)"; do
9
+ if [[ -n "$instance_python" && -x "$instance_python" ]] \
10
+ && "$instance_python" -c 'import sys; sys.exit(sys.version_info < (3, 10))'; then
11
+ exec "$instance_python" "$SCRIPT_DIR/server_instances.py" "$@"
12
+ fi
13
+ done
14
+ echo "Instance management needs Python 3.10+; run ./install.sh first or install Python." >&2
15
+ exit 1