sft0farid commited on
Commit
d15ff5e
·
verified ·
1 Parent(s): 02195c2

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +161 -140
Dockerfile CHANGED
@@ -1,158 +1,179 @@
1
  # syntax=docker/dockerfile:1.4
2
 
3
- FROM calciumion/new-api:latest AS newapi-source
 
4
  FROM justlikemaki/aiclient-2-api:latest AS aiclient-source
5
- FROM ghcr.io/quorinex/kiro-go:latest AS kirogo-source
6
 
7
- FROM python:3.11-slim
 
8
 
9
- # 1. Системные зависимости + Node.js
10
- RUN apt-get update && apt-get install -y curl gnupg supervisor \
 
 
 
 
 
11
  && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
12
- && apt-get install -y nodejs \
13
- && rm -rf /var/lib/apt/lists/*
14
 
15
- # 2. zrok2 latest
16
- RUN ARCH=$(dpkg --print-architecture) \
17
- && case "$ARCH" in \
18
- amd64) GOARCH=amd64 ;; \
19
- arm64) GOARCH=arm64 ;; \
20
- *) echo "Unsupported arch: $ARCH"; exit 1 ;; \
21
- esac \
22
- && VERSION=$(curl -sSf https://api.github.com/repos/openziti/zrok/releases/latest \
23
- | grep '"tag_name"' | cut -d'"' -f4) \
24
- && echo "Installing zrok ${VERSION} ${GOARCH}" \
25
- && curl -sSfL \
26
- "https://github.com/openziti/zrok/releases/download/${VERSION}/zrok_${VERSION#v}_linux_${GOARCH}.tar.gz" \
27
- | tar -xz -C /tmp \
28
- && (install /tmp/zrok2 /usr/local/bin/zrok2 2>/dev/null \
29
- || install /tmp/zrok /usr/local/bin/zrok2) \
30
- && chmod +x /usr/local/bin/zrok2
31
 
32
- # 3. Копируем из готовых образов
33
- COPY --from=newapi-source /new-api /usr/local/bin/new-api
34
- COPY --from=kirogo-source /app/kiro-go /usr/local/bin/kiro-go
35
- COPY --from=aiclient-source /app /app/aiclient2api
36
  RUN chmod +x /usr/local/bin/new-api /usr/local/bin/kiro-go
37
 
38
- # 4. Non-root пользователь
39
- RUN useradd -m -u 1000 user
40
 
41
- # 5. Директории
42
- RUN mkdir -p \
43
- /data/aiclient2api/configs \
44
- /data/kirogo \
45
- /data/newapi \
46
- /data/zrok-home \
47
- /data/supervisor/conf.d \
48
- /data/supervisor/run \
49
- && chown -R user:user /app /data
50
 
51
- # 6. supervisord.conf
52
- RUN echo '[supervisord]' > /data/supervisor/conf.d/supervisord.conf \
53
- && echo 'nodaemon=true' >> /data/supervisor/conf.d/supervisord.conf \
54
- && echo 'logfile=/dev/stdout' >> /data/supervisor/conf.d/supervisord.conf \
55
- && echo 'logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf \
56
- && echo 'pidfile=/data/supervisor/run/supervisord.pid' >> /data/supervisor/conf.d/supervisord.conf \
57
- && echo 'user=root' >> /data/supervisor/conf.d/supervisord.conf \
58
- && echo '' >> /data/supervisor/conf.d/supervisord.conf \
59
- && echo '[program:newapi]' >> /data/supervisor/conf.d/supervisord.conf \
60
- && echo 'command=new-api --port 7860 --log-dir /data/newapi/logs' >> /data/supervisor/conf.d/supervisord.conf \
61
- && echo 'environment=TZ="UTC"' >> /data/supervisor/conf.d/supervisord.conf \
62
- && echo 'directory=/data/newapi' >> /data/supervisor/conf.d/supervisord.conf \
63
- && echo 'autostart=true' >> /data/supervisor/conf.d/supervisord.conf \
64
- && echo 'autorestart=true' >> /data/supervisor/conf.d/supervisord.conf \
65
- && echo 'stdout_logfile=/dev/stdout' >> /data/supervisor/conf.d/supervisord.conf \
66
- && echo 'stdout_logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf \
67
- && echo 'stderr_logfile=/dev/stderr' >> /data/supervisor/conf.d/supervisord.conf \
68
- && echo 'stderr_logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf \
69
- && echo '' >> /data/supervisor/conf.d/supervisord.conf \
70
- && echo '[program:aiclient]' >> /data/supervisor/conf.d/supervisord.conf \
71
- && echo 'command=node /app/aiclient2api/src/core/master.js' >> /data/supervisor/conf.d/supervisord.conf \
72
- && echo 'environment=PORT=3000' >> /data/supervisor/conf.d/supervisord.conf \
73
- && echo 'directory=/app/aiclient2api' >> /data/supervisor/conf.d/supervisord.conf \
74
- && echo 'autostart=true' >> /data/supervisor/conf.d/supervisord.conf \
75
- && echo 'autorestart=true' >> /data/supervisor/conf.d/supervisord.conf \
76
- && echo 'stdout_logfile=/dev/stdout' >> /data/supervisor/conf.d/supervisord.conf \
77
- && echo 'stdout_logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf \
78
- && echo 'stderr_logfile=/dev/stderr' >> /data/supervisor/conf.d/supervisord.conf \
79
- && echo 'stderr_logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf \
80
- && echo '' >> /data/supervisor/conf.d/supervisord.conf \
81
- && echo '[program:kirogo]' >> /data/supervisor/conf.d/supervisord.conf \
82
- && echo 'command=kiro-go' >> /data/supervisor/conf.d/supervisord.conf \
83
- && echo 'directory=/data/kirogo' >> /data/supervisor/conf.d/supervisord.conf \
84
- && echo 'autostart=true' >> /data/supervisor/conf.d/supervisord.conf \
85
- && echo 'autorestart=true' >> /data/supervisor/conf.d/supervisord.conf \
86
- && echo 'stdout_logfile=/dev/stdout' >> /data/supervisor/conf.d/supervisord.conf \
87
- && echo 'stdout_logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf \
88
- && echo 'stderr_logfile=/dev/stderr' >> /data/supervisor/conf.d/supervisord.conf \
89
- && echo 'stderr_logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf \
90
- && echo '' >> /data/supervisor/conf.d/supervisord.conf \
91
- && echo '[program:zrok]' >> /data/supervisor/conf.d/supervisord.conf \
92
- && echo 'command=/app/start_zrok.sh' >> /data/supervisor/conf.d/supervisord.conf \
93
- && echo 'autostart=true' >> /data/supervisor/conf.d/supervisord.conf \
94
- && echo 'autorestart=false' >> /data/supervisor/conf.d/supervisord.conf \
95
- && echo 'stdout_logfile=/dev/stdout' >> /data/supervisor/conf.d/supervisord.conf \
96
- && echo 'stdout_logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf \
97
- && echo 'stderr_logfile=/dev/stderr' >> /data/supervisor/conf.d/supervisord.conf \
98
- && echo 'stderr_logfile_maxbytes=0' >> /data/supervisor/conf.d/supervisord.conf
 
 
 
 
 
 
 
 
 
 
99
 
100
- # 7. Скрипт zrok2
101
- # Нужные секреты: ZROK2_TOKEN (обязательный), ZROK2_SHARE_NAME (опциональный)
102
- # Для кастомного инстанса: ZROK2_API_ENDPOINT
103
- # URL будет: https://<ZROK2_SHARE_NAME>.share.zrok.io
104
- RUN echo '#!/bin/bash' > /app/start_zrok.sh \
105
- && echo 'if [ -z "$ZROK2_TOKEN" ]; then' >> /app/start_zrok.sh \
106
- && echo ' echo "[zrok] ZROK2_TOKEN not set, skipping"' >> /app/start_zrok.sh \
107
- && echo ' sleep infinity; exit 0' >> /app/start_zrok.sh \
108
- && echo 'fi' >> /app/start_zrok.sh \
109
- && echo 'export HOME=/data/zrok-home' >> /app/start_zrok.sh \
110
- && echo 'mkdir -p "$HOME"' >> /app/start_zrok.sh \
111
- && echo '# Включаем environment если ещё нет' >> /app/start_zrok.sh \
112
- && echo 'if [ ! -f "$HOME/.zrok2/environment.json" ]; then' >> /app/start_zrok.sh \
113
- && echo ' echo "[zrok] Enabling zrok2 environment..."' >> /app/start_zrok.sh \
114
- && echo ' zrok2 enable "$ZROK2_TOKEN" --headless' >> /app/start_zrok.sh \
115
- && echo ' if [ $? -ne 0 ]; then' >> /app/start_zrok.sh \
116
- && echo ' echo "[zrok] enable failed, sleeping"; sleep infinity; exit 1' >> /app/start_zrok.sh \
117
- && echo ' fi' >> /app/start_zrok.sh \
118
- && echo 'else' >> /app/start_zrok.sh \
119
- && echo ' echo "[zrok] Environment already enabled"' >> /app/start_zrok.sh \
120
- && echo 'fi' >> /app/start_zrok.sh \
121
- && echo '# Определяем имя шейра' >> /app/start_zrok.sh \
122
- && echo 'NAME_FILE="$HOME/.zrok2/share_name"' >> /app/start_zrok.sh \
123
- && echo 'if [ -n "$ZROK2_SHARE_NAME" ]; then' >> /app/start_zrok.sh \
124
- && echo ' SHARE_NAME="$ZROK2_SHARE_NAME"' >> /app/start_zrok.sh \
125
- && echo ' echo "[zrok] Using share name from env: $SHARE_NAME"' >> /app/start_zrok.sh \
126
- && echo 'elif [ -f "$NAME_FILE" ]; then' >> /app/start_zrok.sh \
127
- && echo ' SHARE_NAME=$(cat "$NAME_FILE")' >> /app/start_zrok.sh \
128
- && echo ' echo "[zrok] Using saved share name: $SHARE_NAME"' >> /app/start_zrok.sh \
129
- && echo 'else' >> /app/start_zrok.sh \
130
- && echo ' SHARE_NAME=$(cat /proc/sys/kernel/random/uuid | tr -d - | head -c 12)' >> /app/start_zrok.sh \
131
- && echo ' echo "[zrok] Generated share name: $SHARE_NAME"' >> /app/start_zrok.sh \
132
- && echo 'fi' >> /app/start_zrok.sh \
133
- && echo '# Создаём имя в namespace (идемпотентно)' >> /app/start_zrok.sh \
134
- && echo 'echo "[zrok] Reserving name public:${SHARE_NAME}..."' >> /app/start_zrok.sh \
135
- && echo 'zrok2 create name -n public "$SHARE_NAME" 2>&1 || true' >> /app/start_zrok.sh \
136
- && echo 'echo "$SHARE_NAME" > "$NAME_FILE"' >> /app/start_zrok.sh \
137
- && echo 'echo "[zrok] URL: https://${SHARE_NAME}.share.zrok.io"' >> /app/start_zrok.sh \
138
- && echo '# Шейр с retry loop' >> /app/start_zrok.sh \
139
- && echo 'while true; do' >> /app/start_zrok.sh \
140
- && echo ' echo "[zrok] Starting share..."' >> /app/start_zrok.sh \
141
- && echo ' zrok2 share public http://localhost:7860 -n "public:${SHARE_NAME}" --headless' >> /app/start_zrok.sh \
142
- && echo ' echo "[zrok] Share exited, retrying in 15s..."' >> /app/start_zrok.sh \
143
- && echo ' sleep 15' >> /app/start_zrok.sh \
144
- && echo 'done' >> /app/start_zrok.sh \
145
- && chmod +x /app/start_zrok.sh
 
 
 
 
 
146
 
147
  # 8. Entrypoint
148
- RUN echo '#!/bin/bash' > /app/start.sh \
149
- && echo '# Симлинк конфигов aiclient2api на персистентный том' >> /app/start.sh \
150
- && echo 'if [ ! -L /app/aiclient2api/configs ]; then' >> /app/start.sh \
151
- && echo ' rm -rf /app/aiclient2api/configs' >> /app/start.sh \
152
- && echo ' ln -s /data/aiclient2api/configs /app/aiclient2api/configs' >> /app/start.sh \
153
- && echo 'fi' >> /app/start.sh \
154
- && echo 'exec /usr/bin/supervisord -c /data/supervisor/conf.d/supervisord.conf' >> /app/start.sh \
155
- && chmod +x /app/start.sh
 
 
 
 
 
 
 
 
 
156
 
157
  EXPOSE 7860
158
 
 
1
  # syntax=docker/dockerfile:1.4
2
 
3
+ # ==================== STAGE 1: Source images ====================
4
+ FROM calciumion/new-api:latest AS newapi-source
5
  FROM justlikemaki/aiclient-2-api:latest AS aiclient-source
6
+ FROM ghcr.io/quorinex/kiro-go:latest AS kirogo-source
7
 
8
+ # ==================== STAGE 2: Final image ====================
9
+ FROM python:3.11-slim-bookworm
10
 
11
+ ENV DEBIAN_FRONTEND=noninteractive \
12
+ PYTHONUNBUFFERED=1 \
13
+ NODE_ENV=production
14
+
15
+ # 1. System deps: Node.js 20, supervisor, sqlite3, jq, curl
16
+ RUN apt-get update && apt-get install -y --no-install-recommends \
17
+ curl ca-certificates gnupg supervisor sqlite3 jq \
18
  && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
19
+ && apt-get install -y --no-install-recommends nodejs \
20
+ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
21
 
22
+ # 2. Install zrok2 (latest release)
23
+ RUN ARCH=$(dpkg --print-architecture) && \
24
+ case "$ARCH" in \
25
+ amd64) GOARCH=amd64 ;; \
26
+ arm64) GOARCH=arm64 ;; \
27
+ *) echo "Unsupported arch: $ARCH"; exit 1 ;; \
28
+ esac && \
29
+ VERSION=$(curl -sSf https://api.github.com/repos/openziti/zrok/releases/latest | grep '"tag_name"' | cut -d'"' -f4) && \
30
+ echo "Installing zrok ${VERSION} ${GOARCH}" && \
31
+ curl -sSfL "https://github.com/openziti/zrok/releases/download/${VERSION}/zrok_${VERSION#v}_linux_${GOARCH}.tar.gz" | tar -xz -C /tmp && \
32
+ install -m 755 /tmp/zrok2 /usr/local/bin/zrok2 && \
33
+ rm -rf /tmp/zrok*
 
 
 
 
34
 
35
+ # 3. Copy binaries from sources
36
+ COPY --from=newapi-source /new-api /usr/local/bin/new-api
37
+ COPY --from=kirogo-source /app/kiro-go /usr/local/bin/kiro-go
38
+ COPY --from=aiclient-source /app /app/aiclient2api
39
  RUN chmod +x /usr/local/bin/new-api /usr/local/bin/kiro-go
40
 
41
+ # 4. Create user (HF Spaces runs as uid 1000)
42
+ RUN useradd -m -u 1000 user 2>/dev/null || true
43
 
44
+ # 5. Prepare dirs
45
+ RUN mkdir -p /app /var/log/supervisor /tmp/supervisor /data && \
46
+ chown -R user:user /app /var/log/supervisor /tmp/supervisor /data
 
 
 
 
 
 
47
 
48
+ # 6. Supervisord config
49
+ RUN echo '[supervisord]' > /app/supervisord.conf \
50
+ && echo 'nodaemon=true' >> /app/supervisord.conf \
51
+ && echo 'logfile=/tmp/supervisor/supervisord.log' >> /app/supervisord.conf \
52
+ && echo 'logfile_maxbytes=0' >> /app/supervisord.conf \
53
+ && echo 'pidfile=/tmp/supervisor/supervisord.pid' >> /app/supervisord.conf \
54
+ && echo 'user=user' >> /app/supervisord.conf \
55
+ && echo '' >> /app/supervisord.conf \
56
+ && echo '[program:newapi]' >> /app/supervisord.conf \
57
+ && echo 'command=/usr/local/bin/new-api --port 7860' >> /app/supervisord.conf \
58
+ && echo 'directory=/data/newapi' >> /app/supervisord.conf \
59
+ && echo 'environment=HOME="/data/newapi",TZ="UTC"' >> /app/supervisord.conf \
60
+ && echo 'autostart=true' >> /app/supervisord.conf \
61
+ && echo 'autorestart=true' >> /app/supervisord.conf \
62
+ && echo 'stopasgroup=true' >> /app/supervisord.conf \
63
+ && echo 'killasgroup=true' >> /app/supervisord.conf \
64
+ && echo 'stdout_logfile=/dev/stdout' >> /app/supervisord.conf \
65
+ && echo 'stdout_logfile_maxbytes=0' >> /app/supervisord.conf \
66
+ && echo 'stderr_logfile=/dev/stderr' >> /app/supervisord.conf \
67
+ && echo 'stderr_logfile_maxbytes=0' >> /app/supervisord.conf \
68
+ && echo '' >> /app/supervisord.conf \
69
+ && echo '[program:aiclient]' >> /app/supervisord.conf \
70
+ && echo 'command=/usr/bin/node /app/aiclient2api/src/core/master.js' >> /app/supervisord.conf \
71
+ && echo 'environment=PORT=3000' >> /app/supervisord.conf \
72
+ && echo 'directory=/app/aiclient2api' >> /app/supervisord.conf \
73
+ && echo 'autostart=true' >> /app/supervisord.conf \
74
+ && echo 'autorestart=true' >> /app/supervisord.conf \
75
+ && echo 'stopasgroup=true' >> /app/supervisord.conf \
76
+ && echo 'killasgroup=true' >> /app/supervisord.conf \
77
+ && echo 'stdout_logfile=/dev/stdout' >> /app/supervisord.conf \
78
+ && echo 'stdout_logfile_maxbytes=0' >> /app/supervisord.conf \
79
+ && echo 'stderr_logfile=/dev/stderr' >> /app/supervisord.conf \
80
+ && echo 'stderr_logfile_maxbytes=0' >> /app/supervisord.conf \
81
+ && echo '' >> /app/supervisord.conf \
82
+ && echo '[program:kirogo]' >> /app/supervisord.conf \
83
+ && echo 'command=/usr/local/bin/kiro-go' >> /app/supervisord.conf \
84
+ && echo 'environment=PORT=8080' >> /app/supervisord.conf \
85
+ && echo 'directory=/app' >> /app/supervisord.conf \
86
+ && echo 'autostart=true' >> /app/supervisord.conf \
87
+ && echo 'autorestart=true' >> /app/supervisord.conf \
88
+ && echo 'stopasgroup=true' >> /app/supervisord.conf \
89
+ && echo 'killasgroup=true' >> /app/supervisord.conf \
90
+ && echo 'stdout_logfile=/dev/stdout' >> /app/supervisord.conf \
91
+ && echo 'stdout_logfile_maxbytes=0' >> /app/supervisord.conf \
92
+ && echo 'stderr_logfile=/dev/stderr' >> /app/supervisord.conf \
93
+ && echo 'stderr_logfile_maxbytes=0' >> /app/supervisord.conf \
94
+ && echo '' >> /app/supervisord.conf \
95
+ && echo '[program:zrok]' >> /app/supervisord.conf \
96
+ && echo 'command=/app/start_zrok.sh' >> /app/supervisord.conf \
97
+ && echo 'autostart=true' >> /app/supervisord.conf \
98
+ && echo 'autorestart=false' >> /app/supervisord.conf \
99
+ && echo 'startsecs=2' >> /app/supervisord.conf \
100
+ && echo 'stopasgroup=true' >> /app/supervisord.conf \
101
+ && echo 'killasgroup=true' >> /app/supervisord.conf \
102
+ && echo 'stdout_logfile=/dev/stdout' >> /app/supervisord.conf \
103
+ && echo 'stdout_logfile_maxbytes=0' >> /app/supervisord.conf \
104
+ && echo 'stderr_logfile=/dev/stderr' >> /app/supervisord.conf \
105
+ && echo 'stderr_logfile_maxbytes=0' >> /app/supervisord.conf
106
 
107
+ # 7. Zrok startup script
108
+ RUN echo '#!/bin/bash' > /app/start_zrok.sh \
109
+ && echo 'set -e' >> /app/start_zrok.sh \
110
+ && echo '' >> /app/start_zrok.sh \
111
+ && echo 'if [ -z "$ZROK_TOKEN" ]; then' >> /app/start_zrok.sh \
112
+ && echo ' echo "[zrok] ZROK_TOKEN not set, skipping tunnel."' >> /app/start_zrok.sh \
113
+ && echo ' sleep infinity' >> /app/start_zrok.sh \
114
+ && echo ' exit 0' >> /app/start_zrok.sh \
115
+ && echo 'fi' >> /app/start_zrok.sh \
116
+ && echo '' >> /app/start_zrok.sh \
117
+ && echo 'export HOME=/data/zrok-home' >> /app/start_zrok.sh \
118
+ && echo 'mkdir -p "$HOME"' >> /app/start_zrok.sh \
119
+ && echo '' >> /app/start_zrok.sh \
120
+ && echo 'export ZROK2_API_ENDPOINT=https://api-v2.zrok.io' >> /app/start_zrok.sh \
121
+ && echo '' >> /app/start_zrok.sh \
122
+ && echo 'if [ ! -f "$HOME/.zrok2/environment.json" ]; then' >> /app/start_zrok.sh \
123
+ && echo ' echo "[zrok] Enabling environment..."' >> /app/start_zrok.sh \
124
+ && echo ' if ! zrok2 enable "$ZROK_TOKEN" --headless; then' >> /app/start_zrok.sh \
125
+ && echo ' echo "[zrok] FATAL: enable failed. Check your token."' >> /app/start_zrok.sh \
126
+ && echo ' sleep infinity' >> /app/start_zrok.sh \
127
+ && echo ' exit 1' >> /app/start_zrok.sh \
128
+ && echo ' fi' >> /app/start_zrok.sh \
129
+ && echo ' echo "[zrok] Environment enabled."' >> /app/start_zrok.sh \
130
+ && echo 'else' >> /app/start_zrok.sh \
131
+ && echo ' echo "[zrok] Reusing persisted environment."' >> /app/start_zrok.sh \
132
+ && echo 'fi' >> /app/start_zrok.sh \
133
+ && echo '' >> /app/start_zrok.sh \
134
+ && echo 'NAME_FILE="$HOME/.zrok2/share_name"' >> /app/start_zrok.sh \
135
+ && echo 'if [ -n "$ZROK_SHARE_NAME" ]; then' >> /app/start_zrok.sh \
136
+ && echo ' SHARE_NAME="$ZROK_SHARE_NAME"' >> /app/start_zrok.sh \
137
+ && echo 'elif [ -f "$NAME_FILE" ]; then' >> /app/start_zrok.sh \
138
+ && echo ' SHARE_NAME=$(cat "$NAME_FILE")' >> /app/start_zrok.sh \
139
+ && echo 'else' >> /app/start_zrok.sh \
140
+ && echo ' SHARE_NAME=$(cat /proc/sys/kernel/random/uuid | tr -d - | head -c 12)' >> /app/start_zrok.sh \
141
+ && echo 'fi' >> /app/start_zrok.sh \
142
+ && echo 'echo "$SHARE_NAME" > "$NAME_FILE"' >> /app/start_zrok.sh \
143
+ && echo '' >> /app/start_zrok.sh \
144
+ && echo 'echo "[zrok] Reserving name: $SHARE_NAME ..."' >> /app/start_zrok.sh \
145
+ && echo 'zrok2 create name -n public "$SHARE_NAME" 2>/dev/null || echo "[zrok] Name already reserved."' >> /app/start_zrok.sh \
146
+ && echo '' >> /app/start_zrok.sh \
147
+ && echo 'echo "[zrok] ==========================================="' >> /app/start_zrok.sh \
148
+ && echo 'echo "[zrok] STATIC URL: https://${SHARE_NAME}.share.zrok.io"' >> /app/start_zrok.sh \
149
+ && echo 'echo "[zrok] ==========================================="' >> /app/start_zrok.sh \
150
+ && echo '' >> /app/start_zrok.sh \
151
+ && echo 'while true; do' >> /app/start_zrok.sh \
152
+ && echo ' echo "[zrok] Starting share on localhost:7860 ..."' >> /app/start_zrok.sh \
153
+ && echo ' zrok2 share public http://localhost:7860 -n "public:${SHARE_NAME}" --headless || true' >> /app/start_zrok.sh \
154
+ && echo ' echo "[zrok] Share crashed, retrying in 15s..."' >> /app/start_zrok.sh \
155
+ && echo ' sleep 15' >> /app/start_zrok.sh \
156
+ && echo 'done' >> /app/start_zrok.sh
157
+ RUN chmod +x /app/start_zrok.sh
158
 
159
  # 8. Entrypoint
160
+ RUN echo '#!/bin/bash' > /app/start.sh \
161
+ && echo 'set -e' >> /app/start.sh \
162
+ && echo '' >> /app/start.sh \
163
+ && echo 'mkdir -p /data/aiclient2api/configs /data/kirogo /data/newapi /data/zrok-home' >> /app/start.sh \
164
+ && echo 'chown -R user:user /data' >> /app/start.sh \
165
+ && echo '' >> /app/start.sh \
166
+ && echo 'if [ ! -L /app/aiclient2api/configs ] && [ ! -d /app/aiclient2api/configs ]; then' >> /app/start.sh \
167
+ && echo ' ln -s /data/aiclient2api/configs /app/aiclient2api/configs' >> /app/start.sh \
168
+ && echo 'fi' >> /app/start.sh \
169
+ && echo '' >> /app/start.sh \
170
+ && echo 'if [ ! -L /app/data ] && [ ! -d /app/data ]; then' >> /app/start.sh \
171
+ && echo ' ln -s /data/kirogo /app/data' >> /app/start.sh \
172
+ && echo 'fi' >> /app/start.sh \
173
+ && echo '' >> /app/start.sh \
174
+ && echo 'echo "[start] Starting supervisord..."' >> /app/start.sh \
175
+ && echo 'exec supervisord -c /app/supervisord.conf' >> /app/start.sh
176
+ RUN chmod +x /app/start.sh
177
 
178
  EXPOSE 7860
179