docker-entrypoint.sh 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. PILOT_HOME="${PILOT_HOME:-/root/.pilotdeck}"
  4. CONFIG_FILE="$PILOT_HOME/pilotdeck.yaml"
  5. mkdir -p \
  6. "$PILOT_HOME/projects" \
  7. "$PILOT_HOME/router" \
  8. "$PILOT_HOME/skills" \
  9. "$PILOT_HOME/plugins" \
  10. "$PILOT_HOME/memory"
  11. if [ -d "$CONFIG_FILE" ]; then
  12. echo "[pilotdeck-docker] ERROR: $CONFIG_FILE is a directory, not a config file." >&2
  13. echo "[pilotdeck-docker] If you intended to mount a YAML config, create the host file first or remove the bind mount and use PILOTDECK_* env vars." >&2
  14. exit 1
  15. fi
  16. # ── Generate config from env vars if no config file is mounted ────────
  17. if [ ! -f "$CONFIG_FILE" ]; then
  18. MODEL="${PILOTDECK_MODEL:-openrouter/deepseek/deepseek-v4-flash}"
  19. LIGHT_MODEL="${PILOTDECK_LIGHT_MODEL:-openrouter/qwen/qwen3-8b}"
  20. API_KEY="${PILOTDECK_API_KEY:-PLACEHOLDER_RUN_ONBOARDING_TO_REPLACE}"
  21. API_URL="${PILOTDECK_API_URL:-https://openrouter.ai/api/v1}"
  22. # Derive provider name from model string (e.g. "openrouter/deepseek/deepseek-v4-flash" -> "openrouter")
  23. PROVIDER="${MODEL%%/*}"
  24. LIGHT_PROVIDER="${LIGHT_MODEL%%/*}"
  25. # Model ID is everything after the first slash
  26. MODEL_ID="${MODEL#*/}"
  27. LIGHT_MODEL_ID="${LIGHT_MODEL#*/}"
  28. # Router section shared by both same-provider and cross-provider branches
  29. ROUTER_SECTION="router:
  30. scenarios:
  31. default: ${MODEL}
  32. fallback:
  33. default:
  34. - ${MODEL}
  35. zeroUsageRetry:
  36. enabled: true
  37. maxAttempts: 2
  38. tokenSaver:
  39. enabled: true
  40. judge: ${LIGHT_MODEL}
  41. defaultTier: medium
  42. judgeTimeoutMs: 15000
  43. tiers:
  44. simple:
  45. model: ${LIGHT_MODEL}
  46. description: \"Simple greetings, confirmations, single-step Q&A, trivial file writes, remembering rules\"
  47. medium:
  48. model: ${LIGHT_MODEL}
  49. description: \"Single tool call, short text generation, 1-2 file read/write, code generation\"
  50. complex:
  51. model: ${MODEL}
  52. description: \"Needs sub-agent orchestration: parallel workstreams, delegation to specialized agents\"
  53. reasoning:
  54. model: ${MODEL}
  55. description: \"Deep single-agent work: multi-file operations, data analysis, multi-step workflows, web research, structured reports from many sources\"
  56. rules:
  57. - \"complex is ONLY for tasks that need sub-agent orchestration or parallel delegation — do NOT use it for single-agent multi-step work\"
  58. - \"Multi-file operations, data analysis, and multi-step workflows without orchestration should be reasoning\"
  59. - \"Simple file creation (1-2 files) or single code generation is medium\"
  60. - \"Trivial greetings, confirmations, remembering rules, or reading one file and answering a short question is simple\"
  61. autoOrchestrate:
  62. enabled: true
  63. triggerTiers:
  64. - complex
  65. slimSystemPrompt: true
  66. allowedTools:
  67. - agent
  68. - read_file
  69. - grep
  70. - glob
  71. - read_skill
  72. subagentMaxTokens: 48000
  73. stats:
  74. enabled: true"
  75. if [ "$PROVIDER" = "$LIGHT_PROVIDER" ]; then
  76. # Same provider for both models
  77. cat > "$CONFIG_FILE" <<YAML
  78. schemaVersion: 1
  79. agent:
  80. model: ${MODEL}
  81. model:
  82. providers:
  83. ${PROVIDER}:
  84. protocol: openai
  85. url: ${API_URL}
  86. apiKey: ${API_KEY}
  87. models:
  88. ${MODEL_ID}:
  89. capabilities:
  90. maxOutputTokens: 32768
  91. ${LIGHT_MODEL_ID}:
  92. capabilities:
  93. maxOutputTokens: 16384
  94. cron:
  95. enabled: true
  96. ${ROUTER_SECTION}
  97. YAML
  98. else
  99. # Different providers — declare both
  100. LIGHT_API_URL="${PILOTDECK_LIGHT_API_URL:-${API_URL}}"
  101. LIGHT_API_KEY="${PILOTDECK_LIGHT_API_KEY:-${API_KEY}}"
  102. cat > "$CONFIG_FILE" <<YAML
  103. schemaVersion: 1
  104. agent:
  105. model: ${MODEL}
  106. model:
  107. providers:
  108. ${PROVIDER}:
  109. protocol: openai
  110. url: ${API_URL}
  111. apiKey: ${API_KEY}
  112. models:
  113. ${MODEL_ID}:
  114. capabilities:
  115. maxOutputTokens: 32768
  116. ${LIGHT_PROVIDER}:
  117. protocol: openai
  118. url: ${LIGHT_API_URL}
  119. apiKey: ${LIGHT_API_KEY}
  120. models:
  121. ${LIGHT_MODEL_ID}:
  122. capabilities:
  123. maxOutputTokens: 16384
  124. cron:
  125. enabled: true
  126. ${ROUTER_SECTION}
  127. YAML
  128. fi
  129. echo "[pilotdeck-docker] Generated config at $CONFIG_FILE (provider=$PROVIDER, model=$MODEL, light=$LIGHT_MODEL)"
  130. fi
  131. # ── Forward proxy env vars ────────────────────────────────────────────
  132. if [ -n "${PILOTDECK_PROXY:-}" ]; then
  133. export http_proxy="$PILOTDECK_PROXY"
  134. export https_proxy="$PILOTDECK_PROXY"
  135. export HTTP_PROXY="$PILOTDECK_PROXY"
  136. export HTTPS_PROXY="$PILOTDECK_PROXY"
  137. echo "[pilotdeck-docker] Proxy set to $PILOTDECK_PROXY"
  138. fi
  139. echo "[pilotdeck-docker] Starting PilotDeck (gateway + UI server)..."
  140. echo "[pilotdeck-docker] Config: $CONFIG_FILE"
  141. echo "[pilotdeck-docker] UI will be available at http://0.0.0.0:${SERVER_PORT:-3001}"
  142. # ── Start gateway + UI server via concurrently ────────────────────────
  143. cd /app
  144. exec npx concurrently --kill-others --names gateway,server \
  145. "node dist/src/cli/pilotdeck.js server" \
  146. "node --import tsx ui/server/index.js"