Setting up Devman
Devman never writes code itself. You talk to Devman; Devman hands every coding task to your dev environment — an automated coding model and agent that does the actual work on your project. Setup is two things:
- A data2l.ink API key — powers Devman's conversation and sifts agent output into spoken reports.
- A dev environment hostname (and its API key, if it requires one) —
download and run the service before
creating the account, since signup asks for its hostname. Running locally?
http://localhost:8790is a valid hostname. - An ElevenLabs API key — so Devman can listen and speak.
All configuration lives behind your account: launch Devman, create an account, and enter all three there. The home page has no settings of its own — its task bar uses whatever the signed-in voice app has configured. You can change any of these later in the voice app's Settings.
1. The data2l.ink key
Powers Devman's brain. Requests go through Devman's own /d2l proxy
(a Netlify edge function), so no CORS setup is needed. Your workspace is
derived from your email at signup.
2. The dev environment hostname and dev key
The dev environment is its own automated coding model and coding agent —
Claude Code, a custom agent, whatever you run — wrapped in the three-verb
session protocol below. Account creation asks for its hostname, plus its
Dev Key if it requires one, which Devman sends on every request as
Authorization: Bearer <dev key>. Treat the dev key like an
SSH key: it gates an agent that can modify and run code in your project. Once
configured, every task Devman takes goes straight to it.
A task result's summary must be one plain, spoken-friendly
sentence or paragraph — Devman reads it aloud verbatim.
3. Voice (ElevenLabs)
Devman uses ElevenLabs for both
speech-to-text (wake word, dictation) and text-to-speech (Devman's spoken
replies). Your API key is collected at account creation (an optional
Voice ID picks the voice; it defaults to
EXAVITQu4vr4xnSDxMaL). The key is sent directly to ElevenLabs as
the xi-api-key header and is stored with your other
credentials.
Devman keys (portable login)
A Devman key (devman_...) is a portable credential bundle —
your data2l.ink key, workspace, ElevenLabs key, voice, and preferences encoded
into one token. Devman generates it locally in your browser: sign in to the
voice app, then use Copy Key in the debug console (or call
getDevmanKey() from the browser console). Paste it into the Devman-key
field at /dev, or open any deploy with
#devman_... in the URL, to sign in instantly — handy for moving
between deploys and devices. Don't confuse it with the Dev Key above,
which authenticates your dev environment service.
The Dev Environment API (session protocol)
Deliberately minimal: initialize a session, exec, kill the session.
exec delivers instructions to the dev environment's coding agent —
there are no file endpoints because exec covers reading, writing, and exploring
too, and the session's info conveys what kind of instructions the
environment understands (a shell, a REPL, or a natural-language coding agent).
All endpoints speak JSON, must allow CORS from Devman's origin, and authenticate
via Authorization: Bearer <dev key>.
| Endpoint | Request | Response |
|---|---|---|
POST /session | { project_id? } |
{ sessionId, info: { name, language?, shell?, root?, listCommand?, description? } } |
POST /session/{id}/exec | { command, stdin?, timeoutMs? } |
{ exitCode, stdout, stderr } |
DELETE /session/{id} | — | { success } (or 204) |
- Sessions are stateful: working directory, environment variables, and language-runtime state persist across
execcalls within a session. Devman opens a session lazily on first use and always kills it when the task ends — including on cancellation. project_idis sent onPOST /sessionwhen configured (in the voice app's Dev Environment settings, or by asking Devman to set it). If omitted, the dev environment selects its own default project — set it explicitly to target a specific project.stdinexists so file contents can travel without shell-escaping gymnastics:{ "command": "cat > src/app.js", "stdin": "<file content>" }.infois how "exec can do everything" stays true:shell(e.g.bash,powershell,agent) says what kind of instructions the environment accepts, and the optionallistCommandsays how to explore the project.- Errors are non-2xx status codes; bodies may carry
{ error }.
How Devman drives it
When you give Devman a task, it opens a session, delivers the task to the
environment's coding agent via exec, and kills the session when the
task ends — including on cancellation. With no dev environment configured,
D2L's AI stands in and answers the task directly instead.
The dev environment service (download)
Download devenv-service.js —
a zero-dependency Node implementation of the protocol. Run it locally next to
your project, or deploy it to a server; either way, its URL is the hostname you
enter at account creation:
DEVMAN_DEV_KEY=your-secret node devenv-service.js
# options: PORT (default 8790), DEVMAN_ROOT (project dir), DEVMAN_AGENT, DEVMAN_ORIGIN
Run it locally and your hostname is simply
http://localhost:8790 — localhost is a perfectly valid dev
environment, and the signup form suggests it by default. (The service sends
the Access-Control-Allow-Private-Network header so browsers that
enforce Private Network Access can reach localhost from the deployed app.)
Out of the box the service includes no specific coding agents — it
is a dummy: the built-in shell agent just runs instructions as
shell commands. Real coding agents plug in via template files (below);
bundled, selectable-at-install agents are planned.
Adding a coding agent
Agents are added with a template file — copy
agents/example-agent.json.template to
agents/<your-agent>.json next to the service, fill it in,
and start the service with DEVMAN_AGENT=<name>. The file
describes how to forward each instruction to your coding agent; wherever
{{instruction}} appears, the request coming in from Devman (or any
front-end speaking the protocol) is substituted. {{stdin}} and
{{root}} are also available.
For an agent reachable over HTTP, you provide the JSON request to its API:
{
"name": "my-coding-agent",
"kind": "http",
"request": {
"method": "POST",
"url": "http://localhost:11434/api/task",
"headers": { "Content-Type": "application/json",
"Authorization": "Bearer YOUR_AGENT_API_KEY" },
"body": { "prompt": "{{instruction}}", "project": "{{root}}" }
},
"responsePath": "result.text"
}
The API's response becomes the exec output (responsePath
optionally dot-paths into a JSON response). For a CLI agent, use
"kind": "command" with an argv array instead:
{ "name": "my-cli-agent", "kind": "command",
"command": ["my-agent-cli", "--project", "{{root}}", "--task", "{{instruction}}"] }
A minimal implementation, sketched
If you'd rather build your own, the protocol is small enough to sketch — a real dev environment puts its coding agent behind the same three endpoints.
// ~40 lines of Express: a real shell session per id
app.post('/session', auth, (req, res) => {
const id = crypto.randomUUID()
sessions[id] = { cwd: process.cwd() }
res.json({ sessionId: id, info: { name: 'my-project', shell: 'bash', root: process.cwd() } })
})
app.post('/session/:id/exec', auth, (req, res) => {
const { command, stdin, timeoutMs } = req.body
const p = spawn('bash', ['-c', command], { cwd: sessions[req.params.id].cwd, timeout: timeoutMs })
if (stdin) p.stdin.end(stdin)
// collect stdout/stderr, then:
// res.json({ exitCode, stdout, stderr })
})
app.delete('/session/:id', auth, (req, res) => {
delete sessions[req.params.id]
res.json({ success: true })
})
Using Devman
The voice app is the primary interface; the home page's task bar is the
typed equivalent for quick tests with your account's configuration: type a task and
press Enter, or use Status / Last result / Cancel. Everything flows
through one entry point, handleAction(action, params), with five
actions:
| Action | Params | Returns |
|---|---|---|
code | task (required), context |
{ taskId, status, coder, summary } — hands the task to the dev environment (or the D2L stand-in); rejects if one is already running |
getStatus | — | Running task, or the last finished one, plus whether a dev environment or the stand-in is answering |
getLastResult | — | { taskId, task, status, summary } of the most recent completed task |
listTasks | — | { tasks: [{ taskId, task, status }] } |
cancelTask | — | { cancelled, taskId } — aborts the running task and kills its dev environment session |
The raw JSON shown under the task bar is exactly what any external caller would receive.
Appendix: WWWAND compatibility
Devman is a full voice assistant with a dev environment endpoint built in — and WWWAND compatibility comes with that architecture rather than being bolted on. The wwwand-compatible API sits at Devman's core: if an external WWWAND orchestrator integrates, it isn't really using Devman at all — it bypasses Devman's voice and brain and drives that API straight into the dev environment protocol. This is documented last not because it isn't true, but because out of the box it isn't relevant: Devman needs none of it to do its job.
- Manifest at
/.wwwand/manifest.json(served fromwwwand/manifest.jsonvia_redirects, since Netlify won't publish dotfolders):actionMode: "client",handleUndispatched: true. Setsite.urlto the deployed URL before registering — client tabs are matched by it. - Dispatch flow: the page loads
wwwand-client.jsfrom wwwand.com, which injects a hidden bridge iframe. Actions are relayed entirely in the browser:
- Same entry point: dispatched actions land in the identical
handleAction()path as the task bar, so the full surface is testable on-page without any orchestrator. - Undispatched fallback: if the orchestrator's AI doesn't dispatch but the raw user text clearly asks for coding ("write…", "build…", "fix…"), Devman claims it and starts a
codetask. - Dev override:
localStorage.setItem('devman_wwwand_server', 'https://your-wwwand-server.example')changes where the client script loads from.
Devman's own app protocol
Devman is not only a WWWAND client — it is also an orchestrator in its own right, mirroring the WWWAND protocol with Devman in the driver's seat. Apps that want Devman's voice brain to drive them:
- Expose a manifest at
/.devman/manifest.jsondescribing the app's actions and intents. The format matches the WWWAND manifest — a top-leveldevman(or, for compatibility,wwwand) key holdsstandard,actionMode, andhandleUndispatched. - Client mode: include
devman-client.jsfrom Devman's origin and handle actions viaDevmanClient.onAction((action, params, userText) => ...). A hidden bridge iframe (devman-bridge.html) relays actions between the Devman tab and the app tab entirely in the browser. - Server mode: Devman POSTs
{ action, params }to/.devman/actionon the app's URL. - Registering: users add apps by URL in Devman's Settings → Apps tab.