liutaio 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +279 -0
- package/agents-template.md +152 -0
- package/bin/liutaio +9 -0
- package/docker/Dockerfile +27 -0
- package/docker/entrypoint.sh +567 -0
- package/docker/run.sh +366 -0
- package/package.json +32 -0
package/docker/run.sh
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# ─── Liutaio — run Claude Code loops with auto-detecting auth ───
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# liutaio <agents-file> <iterations> <base-branch> [options]
|
|
8
|
+
#
|
|
9
|
+
# Authentication (checked in this order):
|
|
10
|
+
# 1. Cached OAuth credentials (Docker volume from a previous --oauth run)
|
|
11
|
+
# 2. Host credentials (macOS Keychain or ~/.claude/.credentials.json)
|
|
12
|
+
# 3. ANTHROPIC_API_KEY env var
|
|
13
|
+
# 4. Interactive OAuth login (prompts user to authorise in browser)
|
|
14
|
+
#
|
|
15
|
+
# Options:
|
|
16
|
+
# --interactive Run in foreground (default: detached with log tailing)
|
|
17
|
+
# --oauth Force interactive OAuth login (skip host credentials)
|
|
18
|
+
# --fresh-login Clear cached OAuth and re-authenticate
|
|
19
|
+
# --rebuild Force rebuild the Docker image
|
|
20
|
+
# --dry-run Print the docker run command without executing
|
|
21
|
+
# --name NAME Container name (default: liutaio-<base-branch>)
|
|
22
|
+
# --node-version V Node.js version for the Docker image (default: 22)
|
|
23
|
+
# --repo PATH Path to the git repository (default: auto-detect)
|
|
24
|
+
# --env KEY=VALUE Pass env var into the container (repeatable)
|
|
25
|
+
# ─────────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
28
|
+
|
|
29
|
+
IMAGE_NAME="liutaio"
|
|
30
|
+
CREDS_VOLUME="liutaio-creds"
|
|
31
|
+
|
|
32
|
+
# ─── Parse arguments ─────────────────────────────────────────────────
|
|
33
|
+
AGENTS_FILE=""
|
|
34
|
+
ITERATIONS=""
|
|
35
|
+
BASE_BRANCH=""
|
|
36
|
+
INTERACTIVE=false
|
|
37
|
+
REBUILD=false
|
|
38
|
+
DRY_RUN=false
|
|
39
|
+
CONTAINER_NAME=""
|
|
40
|
+
NODE_VERSION="${LIUTAIO_NODE_VERSION:-22}"
|
|
41
|
+
REPO_ROOT=""
|
|
42
|
+
EXTRA_ENVS=()
|
|
43
|
+
FORCE_OAUTH=false
|
|
44
|
+
FRESH_LOGIN=false
|
|
45
|
+
|
|
46
|
+
while [[ $# -gt 0 ]]; do
|
|
47
|
+
case $1 in
|
|
48
|
+
--interactive) INTERACTIVE=true; shift ;;
|
|
49
|
+
--oauth) FORCE_OAUTH=true; shift ;;
|
|
50
|
+
--fresh-login) FRESH_LOGIN=true; FORCE_OAUTH=true; shift ;;
|
|
51
|
+
--rebuild) REBUILD=true; shift ;;
|
|
52
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
53
|
+
--name) CONTAINER_NAME="$2"; shift 2 ;;
|
|
54
|
+
--node-version) NODE_VERSION="$2"; shift 2 ;;
|
|
55
|
+
--repo) REPO_ROOT="$2"; shift 2 ;;
|
|
56
|
+
--env) EXTRA_ENVS+=("$2"); shift 2 ;;
|
|
57
|
+
-*) echo "Unknown option: $1"; exit 1 ;;
|
|
58
|
+
*)
|
|
59
|
+
if [ -z "$AGENTS_FILE" ]; then AGENTS_FILE="$1"
|
|
60
|
+
elif [ -z "$ITERATIONS" ]; then ITERATIONS="$1"
|
|
61
|
+
elif [ -z "$BASE_BRANCH" ]; then BASE_BRANCH="$1"
|
|
62
|
+
fi
|
|
63
|
+
shift ;;
|
|
64
|
+
esac
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
if [ -z "$AGENTS_FILE" ] || [ -z "$ITERATIONS" ] || [ -z "$BASE_BRANCH" ]; then
|
|
68
|
+
echo "Liutaio — run Claude Code loops with auto-detecting auth"
|
|
69
|
+
echo ""
|
|
70
|
+
echo "Usage:"
|
|
71
|
+
echo " liutaio <agents-file> <iterations> <base-branch> [options]"
|
|
72
|
+
echo ""
|
|
73
|
+
echo "Arguments:"
|
|
74
|
+
echo " agents-file Path to agents.md relative to repo root"
|
|
75
|
+
echo " iterations Number of loop iterations"
|
|
76
|
+
echo " base-branch Name of the base branch to create from main"
|
|
77
|
+
echo ""
|
|
78
|
+
echo "Options:"
|
|
79
|
+
echo " --interactive Run in foreground with attached TTY"
|
|
80
|
+
echo " --oauth Force interactive OAuth login (skip host credentials)"
|
|
81
|
+
echo " --fresh-login Clear cached OAuth and re-authenticate"
|
|
82
|
+
echo " --rebuild Force rebuild the Docker image"
|
|
83
|
+
echo " --dry-run Print docker command without executing"
|
|
84
|
+
echo " --name NAME Container name (default: liutaio-<base-branch>)"
|
|
85
|
+
echo " --node-version V Node.js version (default: 22, or LIUTAIO_NODE_VERSION)"
|
|
86
|
+
echo " --repo PATH Path to git repo (default: auto-detect from cwd)"
|
|
87
|
+
echo " --env KEY=VALUE Pass env var into the container (repeatable)"
|
|
88
|
+
echo ""
|
|
89
|
+
echo "Authentication (checked in this order):"
|
|
90
|
+
echo " 1. Cached OAuth credentials (Docker volume from a previous --oauth run)"
|
|
91
|
+
echo " 2. Host credentials (macOS Keychain or ~/.claude/.credentials.json)"
|
|
92
|
+
echo " 3. ANTHROPIC_API_KEY env var"
|
|
93
|
+
echo " 4. Interactive OAuth login (prompts in the terminal)"
|
|
94
|
+
echo ""
|
|
95
|
+
echo "Examples:"
|
|
96
|
+
echo " liutaio agents.md 10 my-branch # auto-detect auth"
|
|
97
|
+
echo " liutaio agents.md 10 my-branch --oauth # force OAuth login"
|
|
98
|
+
echo " liutaio agents.md 10 my-branch --fresh-login # re-authenticate"
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# ─── Detect repo root ───────────────────────────────────────────────
|
|
103
|
+
if [ -z "$REPO_ROOT" ]; then
|
|
104
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
|
|
105
|
+
if [ -z "$REPO_ROOT" ]; then
|
|
106
|
+
echo "Error: not inside a git repository. Use --repo to specify the path."
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
CONTAINER_NAME="${CONTAINER_NAME:-liutaio-${BASE_BRANCH}}"
|
|
112
|
+
|
|
113
|
+
# ─── Validate agents file exists ─────────────────────────────────────
|
|
114
|
+
if [ ! -f "$REPO_ROOT/$AGENTS_FILE" ]; then
|
|
115
|
+
echo "Error: agents file not found: $REPO_ROOT/$AGENTS_FILE"
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# ─── Build image ─────────────────────────────────────────────────────
|
|
120
|
+
if $REBUILD || ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
|
|
121
|
+
echo "Building Liutaio image (Node $NODE_VERSION)..."
|
|
122
|
+
docker build --build-arg NODE_VERSION="$NODE_VERSION" -t "$IMAGE_NAME" "$SCRIPT_DIR"
|
|
123
|
+
echo ""
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# ─── Ensure credentials volume exists ───────────────────────────────
|
|
127
|
+
docker volume create "$CREDS_VOLUME" &>/dev/null || true
|
|
128
|
+
|
|
129
|
+
if $FRESH_LOGIN; then
|
|
130
|
+
echo "Clearing cached credentials (--fresh-login)..."
|
|
131
|
+
docker run --rm -v "$CREDS_VOLUME:/credentials" alpine sh -c "rm -f /credentials/credentials.json"
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# ─── Resolve authentication method ──────────────────────────────────
|
|
135
|
+
# Determines: AUTH_METHOD, CREDS_FILE, USE_API_KEY, NEEDS_TTY
|
|
136
|
+
|
|
137
|
+
AUTH_METHOD=""
|
|
138
|
+
CREDS_FILE=""
|
|
139
|
+
USE_API_KEY=false
|
|
140
|
+
NEEDS_TTY=false
|
|
141
|
+
|
|
142
|
+
cleanup_creds() {
|
|
143
|
+
if [ -n "$CREDS_FILE" ] && [ -f "$CREDS_FILE" ]; then
|
|
144
|
+
rm -f "$CREDS_FILE"
|
|
145
|
+
fi
|
|
146
|
+
}
|
|
147
|
+
trap cleanup_creds EXIT
|
|
148
|
+
|
|
149
|
+
resolve_auth() {
|
|
150
|
+
# --oauth skips host credentials, goes straight to cached OAuth or interactive
|
|
151
|
+
if ! $FORCE_OAUTH; then
|
|
152
|
+
|
|
153
|
+
# 1. Host credentials: macOS Keychain
|
|
154
|
+
if [ "$(uname)" = "Darwin" ]; then
|
|
155
|
+
local keychain_data
|
|
156
|
+
keychain_data=$(security find-generic-password -s "Claude Code-credentials" -a "$(whoami)" -w 2>/dev/null || true)
|
|
157
|
+
if [ -n "$keychain_data" ]; then
|
|
158
|
+
CREDS_FILE=$(mktemp "${TMPDIR:-/tmp}/liutaio-creds-XXXXXX")
|
|
159
|
+
chmod 600 "$CREDS_FILE"
|
|
160
|
+
echo "$keychain_data" > "$CREDS_FILE"
|
|
161
|
+
if jq -e '.claudeAiOauth.accessToken and .claudeAiOauth.refreshToken' "$CREDS_FILE" >/dev/null 2>&1; then
|
|
162
|
+
AUTH_METHOD="keychain"
|
|
163
|
+
return 0
|
|
164
|
+
fi
|
|
165
|
+
rm -f "$CREDS_FILE"
|
|
166
|
+
CREDS_FILE=""
|
|
167
|
+
fi
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# 2. Host credentials: ~/.claude/.credentials.json
|
|
171
|
+
if [ -f "$HOME/.claude/.credentials.json" ]; then
|
|
172
|
+
CREDS_FILE=$(mktemp "${TMPDIR:-/tmp}/liutaio-creds-XXXXXX")
|
|
173
|
+
chmod 600 "$CREDS_FILE"
|
|
174
|
+
cp "$HOME/.claude/.credentials.json" "$CREDS_FILE"
|
|
175
|
+
if jq -e '.claudeAiOauth.accessToken' "$CREDS_FILE" >/dev/null 2>&1; then
|
|
176
|
+
AUTH_METHOD="credentials-file"
|
|
177
|
+
return 0
|
|
178
|
+
fi
|
|
179
|
+
rm -f "$CREDS_FILE"
|
|
180
|
+
CREDS_FILE=""
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# 3. API key
|
|
184
|
+
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
|
185
|
+
AUTH_METHOD="api-key"
|
|
186
|
+
USE_API_KEY=true
|
|
187
|
+
return 0
|
|
188
|
+
fi
|
|
189
|
+
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
# 4. Cached OAuth (Docker volume)
|
|
193
|
+
local has_cached
|
|
194
|
+
has_cached=$(docker run --rm -v "$CREDS_VOLUME:/credentials" alpine sh -c \
|
|
195
|
+
"test -f /credentials/credentials.json && echo yes || echo no")
|
|
196
|
+
if [ "$has_cached" = "yes" ]; then
|
|
197
|
+
AUTH_METHOD="cached-oauth"
|
|
198
|
+
return 0
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# 5. Interactive OAuth (fallback) — needs TTY
|
|
202
|
+
AUTH_METHOD="interactive-oauth"
|
|
203
|
+
NEEDS_TTY=true
|
|
204
|
+
return 0
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
resolve_auth
|
|
208
|
+
|
|
209
|
+
# ─── Read git user from host ─────────────────────────────────────────
|
|
210
|
+
GIT_USER_NAME=$(git config user.name 2>/dev/null || echo "Liutaio Agent")
|
|
211
|
+
GIT_USER_EMAIL=$(git config user.email 2>/dev/null || echo "liutaio@users.noreply.github.com")
|
|
212
|
+
|
|
213
|
+
# ─── Resolve SSH key path ────────────────────────────────────────────
|
|
214
|
+
SSH_DIR="$HOME/.ssh"
|
|
215
|
+
if [ ! -d "$SSH_DIR" ]; then
|
|
216
|
+
echo "Warning: ~/.ssh not found — git push from container will not work"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# ─── Remove existing container with same name ────────────────────────
|
|
220
|
+
if docker container inspect "$CONTAINER_NAME" &>/dev/null; then
|
|
221
|
+
echo "Removing existing container '$CONTAINER_NAME'..."
|
|
222
|
+
docker rm -f "$CONTAINER_NAME" &>/dev/null
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
# ─── Resolve output directory ────────────────────────────────────────
|
|
226
|
+
AGENTS_DIR=$(dirname "$AGENTS_FILE")
|
|
227
|
+
OUTPUT_DIR="$REPO_ROOT/$AGENTS_DIR"
|
|
228
|
+
mkdir -p "$OUTPUT_DIR"
|
|
229
|
+
|
|
230
|
+
# ─── Assemble docker run command ─────────────────────────────────────
|
|
231
|
+
DOCKER_CMD=(
|
|
232
|
+
docker run
|
|
233
|
+
--name "$CONTAINER_NAME"
|
|
234
|
+
--hostname liutaio
|
|
235
|
+
--init
|
|
236
|
+
|
|
237
|
+
# Repo mounted read-only as clone source
|
|
238
|
+
-v "$REPO_ROOT:/repo:ro"
|
|
239
|
+
|
|
240
|
+
# SSH keys
|
|
241
|
+
-v "$SSH_DIR:/ssh-keys:ro"
|
|
242
|
+
|
|
243
|
+
# Output directory
|
|
244
|
+
-v "$OUTPUT_DIR:/output"
|
|
245
|
+
|
|
246
|
+
# Git identity
|
|
247
|
+
-e "GIT_USER_NAME=$GIT_USER_NAME"
|
|
248
|
+
-e "GIT_USER_EMAIL=$GIT_USER_EMAIL"
|
|
249
|
+
|
|
250
|
+
# Memory limit
|
|
251
|
+
--memory=16g
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# Tell the entrypoint which auth method is in use (controls token refresh behaviour)
|
|
255
|
+
DOCKER_CMD+=(-e "LIUTAIO_AUTH_METHOD=$AUTH_METHOD")
|
|
256
|
+
|
|
257
|
+
# Credentials: mount host file (read-only) OR Docker volume (read-write for OAuth caching)
|
|
258
|
+
if [ -n "$CREDS_FILE" ]; then
|
|
259
|
+
# Host credentials — mount as read-only file, no volume needed
|
|
260
|
+
DOCKER_CMD+=(-v "$CREDS_FILE:/credentials/credentials.json:ro")
|
|
261
|
+
else
|
|
262
|
+
# OAuth or no-creds — mount the volume so the entrypoint can cache credentials
|
|
263
|
+
DOCKER_CMD+=(-v "$CREDS_VOLUME:/credentials")
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
# Pass API key if using that method
|
|
267
|
+
if $USE_API_KEY; then
|
|
268
|
+
DOCKER_CMD+=(-e "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY")
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
# SSH hosts override
|
|
272
|
+
if [ -n "${LIUTAIO_SSH_HOSTS:-}" ]; then
|
|
273
|
+
DOCKER_CMD+=(-e "LIUTAIO_SSH_HOSTS=$LIUTAIO_SSH_HOSTS")
|
|
274
|
+
fi
|
|
275
|
+
|
|
276
|
+
# Pass ANTHROPIC_API_KEY as fallback even for OAuth modes
|
|
277
|
+
if ! $USE_API_KEY && [ -n "${ANTHROPIC_API_KEY:-}" ]; then
|
|
278
|
+
DOCKER_CMD+=(-e "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY")
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
# User-provided env vars
|
|
282
|
+
if [ ${#EXTRA_ENVS[@]} -gt 0 ]; then
|
|
283
|
+
for env_pair in "${EXTRA_ENVS[@]}"; do
|
|
284
|
+
DOCKER_CMD+=(-e "$env_pair")
|
|
285
|
+
done
|
|
286
|
+
fi
|
|
287
|
+
|
|
288
|
+
# Image and arguments
|
|
289
|
+
DOCKER_CMD+=(
|
|
290
|
+
"$IMAGE_NAME"
|
|
291
|
+
"$AGENTS_FILE" "$ITERATIONS" "$BASE_BRANCH"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Determine run mode: interactive OAuth needs -it, otherwise respect --interactive
|
|
295
|
+
if $NEEDS_TTY || $INTERACTIVE; then
|
|
296
|
+
DOCKER_CMD=("${DOCKER_CMD[@]:0:2}" -it "${DOCKER_CMD[@]:2}")
|
|
297
|
+
else
|
|
298
|
+
DOCKER_CMD=("${DOCKER_CMD[@]:0:2}" -d "${DOCKER_CMD[@]:2}")
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
# ─── Dry run ─────────────────────────────────────────────────────────
|
|
302
|
+
if $DRY_RUN; then
|
|
303
|
+
echo "Dry run — would execute:"
|
|
304
|
+
echo ""
|
|
305
|
+
PREV_WAS_ENV=false
|
|
306
|
+
for arg in "${DOCKER_CMD[@]}"; do
|
|
307
|
+
if $PREV_WAS_ENV; then
|
|
308
|
+
KEY="${arg%%=*}"
|
|
309
|
+
if echo "$KEY" | grep -qiE '(TOKEN|CREDENTIAL|SECRET|KEY|PASSWORD)'; then
|
|
310
|
+
printf ' %s=***MASKED*** \\\n' "$KEY"
|
|
311
|
+
else
|
|
312
|
+
printf ' %s \\\n' "$arg"
|
|
313
|
+
fi
|
|
314
|
+
PREV_WAS_ENV=false
|
|
315
|
+
elif [ "$arg" = "-e" ]; then
|
|
316
|
+
printf ' %s' "$arg "
|
|
317
|
+
PREV_WAS_ENV=true
|
|
318
|
+
elif [[ "$arg" == *credentials* ]]; then
|
|
319
|
+
printf ' %s \\\n' "***CREDENTIALS_FILE***"
|
|
320
|
+
else
|
|
321
|
+
printf ' %s \\\n' "$arg"
|
|
322
|
+
fi
|
|
323
|
+
done
|
|
324
|
+
exit 0
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
# ─── Display auth method ────────────────────────────────────────────
|
|
328
|
+
AUTH_DISPLAY=""
|
|
329
|
+
case "$AUTH_METHOD" in
|
|
330
|
+
keychain) AUTH_DISPLAY="macOS Keychain" ;;
|
|
331
|
+
credentials-file) AUTH_DISPLAY="~/.claude/.credentials.json" ;;
|
|
332
|
+
api-key) AUTH_DISPLAY="ANTHROPIC_API_KEY" ;;
|
|
333
|
+
cached-oauth) AUTH_DISPLAY="cached OAuth (use --fresh-login to re-auth)" ;;
|
|
334
|
+
interactive-oauth) AUTH_DISPLAY="interactive OAuth (will prompt)" ;;
|
|
335
|
+
esac
|
|
336
|
+
|
|
337
|
+
# ─── Run ─────────────────────────────────────────────────────────────
|
|
338
|
+
echo "============================================"
|
|
339
|
+
echo " Liutaio"
|
|
340
|
+
echo "============================================"
|
|
341
|
+
echo " Container : $CONTAINER_NAME"
|
|
342
|
+
echo " Agents : $AGENTS_FILE"
|
|
343
|
+
echo " Iterations : $ITERATIONS"
|
|
344
|
+
echo " Base branch: $BASE_BRANCH"
|
|
345
|
+
echo " Node : $NODE_VERSION"
|
|
346
|
+
echo " Output : $OUTPUT_DIR"
|
|
347
|
+
echo " Auth : $AUTH_DISPLAY"
|
|
348
|
+
echo " Mode : $(if $NEEDS_TTY || $INTERACTIVE; then echo 'interactive'; else echo 'detached'; fi)"
|
|
349
|
+
echo "============================================"
|
|
350
|
+
echo ""
|
|
351
|
+
|
|
352
|
+
"${DOCKER_CMD[@]}"
|
|
353
|
+
|
|
354
|
+
# If detached, tail logs
|
|
355
|
+
if ! $NEEDS_TTY && ! $INTERACTIVE; then
|
|
356
|
+
echo "Container started. Useful commands:"
|
|
357
|
+
echo ""
|
|
358
|
+
echo " docker logs -f $CONTAINER_NAME # tail logs"
|
|
359
|
+
echo " docker exec -it $CONTAINER_NAME bash # shell into container"
|
|
360
|
+
echo " docker stop $CONTAINER_NAME # stop gracefully"
|
|
361
|
+
echo " docker rm -f $CONTAINER_NAME # force remove"
|
|
362
|
+
echo ""
|
|
363
|
+
echo "Tailing logs now (Ctrl+C to detach, container keeps running)..."
|
|
364
|
+
echo ""
|
|
365
|
+
docker logs -f "$CONTAINER_NAME"
|
|
366
|
+
fi
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "liutaio",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Run AI coding agents in Docker containers — autonomously, safely, and with zero setup.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"liutaio": "./bin/liutaio"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"docker/",
|
|
12
|
+
"agents-template.md",
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ai",
|
|
18
|
+
"agent",
|
|
19
|
+
"claude",
|
|
20
|
+
"docker",
|
|
21
|
+
"sandbox",
|
|
22
|
+
"automation",
|
|
23
|
+
"coding-agent"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/narleybrittes/liutaio"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
}
|
|
32
|
+
}
|