7.0 KiB
Kotlin Script Host (Gradle Project)
This project provides two runtime entrypoints while keeping dynamic script loading from scripts/.
Run CLI host
cd /tmp/kotlin-scripts
./gradlew runCli --args='scripts/hello.hub.kts'
./gradlew runCli --args='scripts/hello.hub.kts --arg=name=Codex --arg=upper=true'
Watch mode:
./gradlew runCli --args='scripts/hello.hub.kts --watch --debounce-ms=200'
Run Web host (Ktor)
./gradlew runWeb --args='--host=0.0.0.0 --port=8080 --scripts-dir=./scripts'
Auth:
- Use
Authorization: Bearer <token>for all APIs except/health. - Token source:
- Preferred: set env
HOST_API_TOKEN. - Otherwise host auto-generates a token and stores it at
scripts/.host-api-token.
- Preferred: set env
- Token types:
root: full access to all endpoints.sub: onlyhealth, filteredscripts, and allowed-scriptmeta/run.
Routes:
GET /healthGET /typeGET /scriptsGET /scripts/{script}(raw script content)POST /scripts/{script}PUT /scripts/{script}DELETE /scripts/{script}GET /meta/{script}GET /run/{script}?k=vPOST /run/{script}?k=vGET /subtokens(root only)GET /subtokens/{name}(root only)POST /subtokens/{name}(root only, body = script names list)PUT /subtokens/{name}(root only, body = script names list)DELETE /subtokens/{name}(root only)
Examples:
curl 'http://127.0.0.1:8080/health'
TOKEN="$(cat scripts/.host-api-token)"
curl -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:8080/type'
curl -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:8080/scripts'
curl -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:8080/scripts/hello'
curl -H "Authorization: Bearer $TOKEN" -X POST 'http://127.0.0.1:8080/scripts/new-api' --data-binary $'// @desc: new api\nval args: Array<String> = emptyArray()\nprintln("ok")'
curl -H "Authorization: Bearer $TOKEN" -X PUT 'http://127.0.0.1:8080/scripts/new-api' --data-binary $'// @desc: new api v2\nval args: Array<String> = emptyArray()\nprintln("ok-v2")'
curl -H "Authorization: Bearer $TOKEN" -X DELETE 'http://127.0.0.1:8080/scripts/new-api'
curl -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:8080/meta/hello'
curl -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:8080/run/hello?name=Alice&upper=true'
curl -H "Authorization: Bearer $TOKEN" -X POST 'http://127.0.0.1:8080/run/hello?name=Alice' -d 'from-body'
curl -H "Authorization: Bearer $TOKEN" -X POST 'http://127.0.0.1:8080/subtokens/demo-sub' --data-binary $'hello\ntime'
curl -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:8080/subtokens'
Script Metadata & Args (*.hub.kts)
Scripts declare metadata in comments and receive request arguments through explicit args declaration:
// @desc: Demo greeting API
// @param: name | default=world | desc=Name to greet
// @param: token | required=true | desc=Required token
val args: Array<String> = emptyArray()
val kv = args.mapNotNull {
val i = it.indexOf('=')
if (i <= 0) null else it.substring(0, i) to it.substring(i + 1)
}.toMap()
val name = kv["name"] ?: "world"
val token = kv["token"] ?: error("token required")
println("hello $name, token=$token")
Dynamic scripts
You can add/remove *.hub.kts files in scripts/ at any time. The web host resolves scripts by route name (/run/{script} -> scripts/{script}.hub.kts) on each request, so newly added scripts are available immediately.
Notes
- This keeps runtime behavior dynamic; Gradle is used for dependency resolution and launching, not for precompiling scripts.
- IDE completion for regular Kotlin sources (
src/main/kotlin) is fully modelled by Gradle. - You do not need a package/build artifact step before each run.
runCliandrunWeblaunch directly from source; scripts are compiled on-demand per execution/request. - For script files with custom extension (
*.hub.kts), IDEA code insight is usually weaker than standard*.main.ktsor module Kotlin sources. This is an IDE limitation for custom script definitions.
Command CLI
A standalone CLI script is available at tools/slhaf-hub-cli.kts (independent from host internals, only HTTP calls).
Examples:
kotlin tools/slhaf-hub-cli.kts --base-url=http://127.0.0.1:8080 --token-file=./scripts/.host-api-token list
kotlin tools/slhaf-hub-cli.kts --token-file=./scripts/.host-api-token type
kotlin tools/slhaf-hub-cli.kts --token-file=./scripts/.host-api-token show hello
kotlin tools/slhaf-hub-cli.kts --token-file=./scripts/.host-api-token run hello --arg=name=Alice --arg=upper=true
kotlin tools/slhaf-hub-cli.kts --token-file=./scripts/.host-api-token create demo --text='// @desc: demo\nval args: Array<String> = emptyArray()\nprintln("ok")'
kotlin tools/slhaf-hub-cli.kts --token-file=./scripts/.host-api-token sub-create demo-sub --scripts=hello,time
kotlin tools/slhaf-hub-cli.kts --token-file=./scripts/.host-api-token sub-list
Note:
- In this environment,
elide run <kts> -- <args...>currently does not expose Kotlin script args reliably; usekotlinto run the CLI script.
Simple TUI
A minimal keyboard-driven TUI is available at tools/slhaf-hub-tui.kts.
Run:
kotlin tools/slhaf-hub-tui.kts --base-url=http://127.0.0.1:8080 --token-file=./scripts/.host-api-token
Keys:
Up/Downorj/k: switch scriptLeft/Rightorh/l: switch action (grouped byScript/SubToken/System)Enter: execute selected actionq: quit
Action model:
roottoken: full grouped actions includingSubtokenssubtoken: reduced action set (Refresh/Run/Meta/Type/Quit)Subtokensopens a keyboard sub-menu (List/Show/Create/Update/Delete/Back)
Create/Edit/Delete behavior:
Create: prompt script name, then choose source mode:e(default): create temp file, open terminal editor, then upload via APIf: read a specified local file and upload via API- In editor mode, if content is unchanged from initial template, creation is cancelled
Edit: fetch current script content (GET /scripts/{script}), write to temp file, open editor, save+exit, then upload viaPUTDelete: asks confirmation before callingDELETERun: prompts for optional query args (k=v, separated by&or space), and optional POST mode/body- Now uses a keyboard-driven sub-menu (
Method/Query/Body/Execute/Cancel) and remembers last run config per script during the session
- Now uses a keyboard-driven sub-menu (
Editor selection:
- First uses
$EDITOR - Fallback to first available of
nvim,vim,nano
Docker
Build image:
docker build -t slhaf-hub:latest .
Run container (mount local scripts directory):
docker run --rm -p 8080:8080 \
-v /tmp/kotlin-scripts/scripts:/app/scripts \
-e HOST_API_TOKEN=your-token \
slhaf-hub:latest
Then call APIs:
curl http://127.0.0.1:8080/health
curl -H "Authorization: Bearer your-token" http://127.0.0.1:8080/scripts
Docker Compose
Run with compose:
# optional: export HOST_API_TOKEN=your-token
# optional: export HOST_PORT=8080
docker compose up -d --build
Check status/logs:
docker compose ps
docker compose logs -f slhaf-hub
Stop:
docker compose down