pylot-workers

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

pylot-workers — Worker API Reference

pylot-workers — Worker API 参考文档

Every operator has this skill loaded as a baseline. Worker-driving skills (
speckit-runner
,
deps-runner-proc
,
release-train-runner-proc
) use these endpoints to spawn and drive a repo devbox worker via the gateway.
All calls require
Authorization: Bearer $PYLOT_DISPATCH_TOKEN
. Gateway base:
$PYLOT_API
(or
$PYLOT_GATEWAY_URL
). Mission ID:
$PYLOT_JOB_ID
.

每个Operator都会加载该技能作为基准模块。驱动worker的技能(如
speckit-runner
deps-runner-proc
release-train-runner-proc
)通过网关调用这些接口来生成并驱动仓库devbox worker。
所有调用都需要携带
Authorization: Bearer $PYLOT_DISPATCH_TOKEN
请求头。 网关基础地址:
$PYLOT_API
(或
$PYLOT_GATEWAY_URL
)。 任务ID:
$PYLOT_JOB_ID

1. Spawn a worker

1. 生成Worker

bash
SPAWN_RESP=$(curl -s --max-time 90 -X POST \
  -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"repo\": \"$REPO\"}" \
  "${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers")
WID=$(echo "$SPAWN_RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("worker_id",""))' 2>/dev/null)
Returns
{"ok": true, "worker_id": <number>}
(201) or an error body.
WID
is the worker row id used in all subsequent calls.

bash
SPAWN_RESP=$(curl -s --max-time 90 -X POST \
  -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"repo\": \"$REPO\"}" \
  "${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers")
WID=$(echo "$SPAWN_RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("worker_id",""))' 2>/dev/null)
返回
{"ok": true, "worker_id": <number>}
(状态码201)或错误响应体。
WID
是后续所有调用中使用的worker行ID。

2. Queue a prompt

2. 提交任务提示

bash
PROMPT_RESP=$(curl -s --max-time 30 -X POST \
  -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"prompt\": \"$PROMPT\"}" \
  "${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/prompt")
TURN_SEQ=$(echo "$PROMPT_RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("turn_seq",""))' 2>/dev/null)
Returns
{"ok": true, "turn_seq": <number>}
(202) or 409 if the worker is busy. Always capture
turn_seq
— it's the handle for polling.

bash
PROMPT_RESP=$(curl -s --max-time 30 -X POST \
  -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"prompt\": \"$PROMPT\"}" \
  "${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/prompt")
TURN_SEQ=$(echo "$PROMPT_RESP" | python3 -c 'import sys,json; print(json.load(sys.stdin).get("turn_seq",""))' 2>/dev/null)
返回
{"ok": true, "turn_seq": <number>}
(状态码202),若worker正忙则返回409。 务必捕获
turn_seq
——它是轮询状态的唯一标识。

3. Poll to idle

3. 轮询至空闲状态

Poll until
turn_state == "idle"
AND
turn_seq == TURN_SEQ
.
bash
WORKER_EXIT=""; POLL_EL=0; POLL_MAX="${PYLOT_WORKER_POLL_MAX:-2400}"
while [ "$POLL_EL" -lt "$POLL_MAX" ]; do
  sleep 15; POLL_EL=$((POLL_EL + 15))
  ST=$(curl -s --max-time 20 \
    -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
    "${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}")
  ST_LINE=$(echo "$ST" | python3 -c '
import sys, json
try: d = json.load(sys.stdin)
except: d = {}
ec = d.get("last_exit_code")
print("%s|%s|%s" % (d.get("turn_state",""), d.get("turn_seq",""), "" if ec is None else ec))
' 2>/dev/null)
  W_STATE="${ST_LINE%%|*}"; W_REST="${ST_LINE#*|}"; W_SEQ="${W_REST%%|*}"; W_EC="${W_REST##*|}"
  if [ "$W_STATE" = "idle" ] && [ "$W_SEQ" = "$TURN_SEQ" ]; then WORKER_EXIT="$W_EC"; break; fi
done
last_output
in the final
$ST
response contains the worker's turn output (capped at 16 KB tail). The executor stale-turn reaper sets
exit_code=-1
if the devbox dies mid-turn, so this never hangs past the reaper window.

持续轮询,直到
turn_state == "idle"
turn_seq == TURN_SEQ
bash
WORKER_EXIT=""; POLL_EL=0; POLL_MAX="${PYLOT_WORKER_POLL_MAX:-2400}"
while [ "$POLL_EL" -lt "$POLL_MAX" ]; do
  sleep 15; POLL_EL=$((POLL_EL + 15))
  ST=$(curl -s --max-time 20 \
    -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
    "${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}")
  ST_LINE=$(echo "$ST" | python3 -c '
import sys, json
try: d = json.load(sys.stdin)
except: d = {}
ec = d.get("last_exit_code")
print("%s|%s|%s" % (d.get("turn_state",""), d.get("turn_seq",""), "" if ec is None else ec))
' 2>/dev/null)
  W_STATE="${ST_LINE%%|*}"; W_REST="${ST_LINE#*|}"; W_SEQ="${W_REST%%|*}"; W_EC="${W_REST##*|}"
  if [ "$W_STATE" = "idle" ] && [ "$W_SEQ" = "$TURN_SEQ" ]; then WORKER_EXIT="$W_EC"; break; fi
done
最终
$ST
响应中的
last_output
字段包含worker的任务输出(仅保留末尾16 KB内容)。 若devbox在任务执行中途崩溃,执行器的过期任务清理器会将
exit_code
设为-1,因此轮询不会无限期挂起。

4. Stop the worker

4. 停止Worker

bash
curl -s --max-time 30 -X POST \
  -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
  "${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/stop" >/dev/null 2>&1 || true
Idempotent. Always call stop when the skill is done — even if the turn failed. The harvest backstop also stops workers, but explicit stop is faster and cheaper.

bash
curl -s --max-time 30 -X POST \
  -H "Authorization: Bearer $PYLOT_DISPATCH_TOKEN" \
  "${PYLOT_API}/missions/${PYLOT_JOB_ID}/workers/${WID}/stop" >/dev/null 2>&1 || true
该操作具有幂等性。无论任务是否成功,技能执行完成后务必调用停止接口。 回收兜底机制也会停止worker,但显式调用停止接口速度更快、成本更低。

Drive loop pattern

驱动循环模式

spawn → prompt(phase1) → poll-to-idle → check output →
        prompt(phase2) → poll-to-idle → check output →
        ...
        stop → emit [pylot] outcome= marker
Between phases, read
last_output
from the final poll response to detect phase-level failures before sending the next prompt.
Emit the outcome marker AFTER stopping the worker:
[pylot] outcome="<summary>" status=<success|partial|failed|blocked>
生成 → 提交任务提示(阶段1) → 轮询至空闲 → 检查输出 →
        提交任务提示(阶段2) → 轮询至空闲 → 检查输出 →
        ...
        停止Worker → 输出 [pylot] outcome= 标记
在不同阶段之间,从最终轮询响应中读取
last_output
,以便在提交下一个任务提示前检测当前阶段是否失败。
停止Worker后再输出结果标记:
[pylot] outcome="<summary>" status=<success|partial|failed|blocked>