Motty
原生桌面 SSH 工作台:终端、SFTP/SCP、端口转发、VNC 与 RDP 远程桌面、AI 助手都在一个窗口里。Go 编写,没有 Electron,空闲时保持安静。免费开源,支持 macOS、Windows 和 Linux。
Translation pending
Your agent asks. A phone answers. You get JSON, not the image.
Developer toolsTypeScript · HTMLGitHub @elsisiemOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
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.
Loading the full README.
Read the full 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:
This is the beginning of the README. Read the rest at the original. Read the full README ↗