Motty
터미널·SFTP·포트 포워딩·VNC·RDP·AI 도우미를 하나의 창에 통합, Go 기반 무료 오픈소스
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
A camera for headless agents. Ask a human's phone for a photo, location or choice; Claude vision checks it on the spot; the agent gets typed JSON.
README 전체를 불러오는 중입니다.
README 전문 보기 ↗An agent mints a link that asks its human's phone for something in the real world. Claude vision checks each photo on the spot (blurry, too dark, wrong thing: the human retakes it right there), and the agent receives typed JSON, never the image. A sequence chains up to six captures behind one link, which is enough to run a vehicle handoff, a property inspection or a delivery proof as a single call.
Agents with no upload UI (voice agents, background jobs, swarms, anything that talks to a person through SMS or a phone call) cannot take a picture. Today that means building an upload page and storage, then spending agent turns on "that's blurry, please retake" while every attempt costs about 1.5k image tokens.
With Squint the agent makes one call, hands the URL to the human over whatever channel it already has, and waits. The result is a few dozen tokens of JSON. The retakes, the vision check and the checklist bookkeeping all happen between the phone and the Worker, outside the agent's context.
# 1. ask
curl -s https://squint.elsisi.workers.dev/v1/asks -H 'content-type: application/json' -d '{
"kind": "photo",
"ask": "a clear photo of the water meter",
"extract": { "reading": "number: the digits on the meter" }
}'
# 2. wait (long-poll, up to 25 s per call)
curl -s "https://squint.elsisi.workers.dev/v1/asks/<id>?wait=25" -H "authorization: Bearer <token>"
The ask call returns 201 with the ask and a one-time token. Give url to the human (qr_url is a QR image of the same link):
{
"id": "k7p2m9x4q8w3t6vn",
"status": "pending",
"kind": "photo",
"ask": "a clear photo of the water meter",
"url": "https://squint.elsisi.workers.dev/s/k7p2m9x4q8w3t6vn",
"qr_url": "https://squint.elsisi.workers.dev/q/k7p2m9x4q8w3t6vn.svg",
"wait_url": "https://squint.elsisi.workers.dev/v1/asks/k7p2m9x4q8w3t6vn?wait=25",
"attempts": 0,
"max_attempts": 5,
"attempt_log": [],
"result": null,
"expires_at": "2026-10-26T15:15:00.000Z",
"created_at": "2026-10-26T15:00:00.000Z",
"opened_at": null,
"done_at": null,
"tokens": null,
"token": "sq_..."
}
The wait call returns the same object. It blocks until status is done, expired or failed, or until wait seconds pass (0 to 25), so call it again while status is pending or opened. This is a finished photo ask after one retake:
{
"id": "k7p2m9x4q8w3t6vn",
"status": "done",
"kind": "photo",
"attempts": 2,
"max_attempts": 5,
"attempt_log": [
{ "n": 1, "ok": false, "issue": "Too blurry: hold steady and tap to focus." },
{ "n": 2, "ok": true, "issue": null }
],
"result": {
"fields": { "reading": 4821 },
"description": "A water meter showing 4821 cubic meters.",
"confidence": 0.96,
"quality": "good"
},
"opened_at": "2026-10-26T15:01:12.000Z",
"done_at": "2026-10-26T15:02:03.000Z",
"tokens": {
"agent_saw": 30,
"raw_image_loop_would_cost": 3350,
"spared": 3320,
"note": "estimate: what an agent would have spent receiving each photo itself and asking for retakes"
}
}
(The unchanged ask, url, qr_url, wait_url, expires_at and created_at fields are omitted here.) tokens is an estimate and only present for photo asks with at least one attempt.
Errors are { "error": "...", "message": "..." }: 400 invalid request, 404 unknown ask or bad token, 422 sensitive_request, 429 rate limited.
| Kind | What the human does | result |
|---|---|---|
photo | Takes a photo. Claude vision validates it and reads the optional extract fields ({"name": "type: description"}, types string, number, boolean, date, up to 8 fields). | { fields, description, confidence, quality } |
location | Shares their location from the browser. | { lat, lng, accuracy_m } |
choice | Taps one of 2 to 8 options. | { choice, index } |
text | Types up to 280 characters (hint is the placeholder). | { text } |
Optional fields on every ask: ttl_seconds (60 to 3600, default 900), max_attempts (1 to 8, default 5), and callback_url. When the ask is done, Squint POSTs { id, kind, status, result, tokens, done_at } to callback_url (https, public host, no redirects, 5 s timeout, not signed). Polling still works, and is the way to confirm a callback.
Claude Code, Cursor, or anything that speaks remote MCP:
claude mcp add --transport http squint https://squint.elsisi.workers.dev/mcp
Tools: squint_ask (returns the link immediately unless wait_seconds is set), squint_wait, squint_cancel. Agent-readable docs: /llms.txt and /openapi.json.
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/asks | Create an ask. |
| GET | /v1/asks/:id?wait=0..25 | Read an ask, long-polling (agent token required). |
| DELETE | /v1/asks/:id | Cancel an ask. Status becomes expired. |
| GET | /s/:id | The phone page for the human. |
| GET | /q/:id.svg | QR code of the phone page link. |
| GET, POST | /v1/public/asks/:id, /v1/public/asks/:id/submit | API used by the phone page. Never returns the result or token. |
| ANY | /mcp | Remote MCP server. |
| GET | /llms.txt, /agents.md, /openapi.json, /healthz | Agent docs, OpenAPI, liveness. |
One link can walk a human through several captures in order:
curl -s https://squint.elsisi.workers.dev/v1/asks -H 'content-type: application/json' -d '{
"kind": "sequence",
"ask": "Vehicle pickup check-in",
"steps": [
{ "kind": "photo", "ask": "the odometer", "extract": { "km": "number: the reading" } },
{ "kind": "photo", "ask": "the left side of the car" },
{ "kind": "location", "ask": "where the car is parked" }
]
}'
Each step takes the same fields as a single ask and gets its own retake budget. The phone page shows "step 2 of 3", photo steps run through vision, and when the last step lands the agent receives one result per step:
여기까지가 README 앞부분입니다. 나머지는 원문에서 읽을 수 있습니다. README 전문 보기 ↗