agent-lookout 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/PRIVACY.md +60 -16
- package/README.md +12 -10
- package/SECURITY.md +7 -5
- package/bin/agent-lookout.mjs +49 -24
- package/dist/assets/HistoryPanel-DMnhIoFB.js +1 -0
- package/dist/assets/SessionPanel-BNm9wCzf.js +1 -0
- package/dist/assets/index-Ckda8OV4.js +2 -0
- package/dist/assets/index-DFR77qn5.css +1 -0
- package/dist/cli/agent-lookout.js +4 -4
- package/dist/cli/{chunk-UYGESSPG.js → chunk-4C34YX6S.js} +1 -1
- package/dist/cli/{chunk-XJM5RZNV.js → chunk-54ODUY75.js} +1 -1
- package/dist/cli/{chunk-PQ3B5TN6.js → chunk-GOX5UB5L.js} +4 -0
- package/dist/cli/{mcpServer-CGLIGL4X.js → mcpServer-QQFQR6DC.js} +3 -3
- package/dist/cli/{startCommand-ZV2NQ6YG.js → startCommand-H6O35VYR.js} +1769 -202
- package/dist/index.html +2 -2
- package/docs/API.md +176 -61
- package/docs/GUIDE.md +93 -14
- package/package.json +2 -2
- package/dist/assets/HistoryPanel-Llcz5XPi.js +0 -1
- package/dist/assets/SessionPanel-B4g_6Unu.js +0 -1
- package/dist/assets/index-DRcy1hT5.js +0 -2
- package/dist/assets/index-Dz4q_OYZ.css +0 -1
package/dist/index.html
CHANGED
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
document.documentElement.setAttribute("data-theme", theme);
|
|
29
29
|
})();
|
|
30
30
|
</script>
|
|
31
|
-
<script type="module" crossorigin src="/assets/index-
|
|
31
|
+
<script type="module" crossorigin src="/assets/index-Ckda8OV4.js"></script>
|
|
32
32
|
<link rel="modulepreload" crossorigin href="/assets/react-C2DiJaR6.js">
|
|
33
33
|
<link rel="modulepreload" crossorigin href="/assets/motion-BAhdRuCw.js">
|
|
34
34
|
<link rel="modulepreload" crossorigin href="/assets/radix-27o0jeIu.js">
|
|
35
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
35
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DFR77qn5.css">
|
|
36
36
|
</head>
|
|
37
37
|
<body>
|
|
38
38
|
<div id="root"></div>
|
package/docs/API.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# API
|
|
2
2
|
|
|
3
|
-
Agent Lookout's server answers a small HTTP API under `/api/`. The dashboard reads it, and so do `agent-lookout status` and `agent-lookout mcp`. It is meant for this computer only: it listens on a loopback address, and it answers only requests made to a loopback name. Every route returns JSON, and every route but
|
|
3
|
+
Agent Lookout's server answers a small HTTP API under `/api/`. The dashboard reads it, and so do `agent-lookout status` and `agent-lookout mcp`. It is meant for this computer only: it listens on a loopback address, and it answers only requests made to a loopback name. Every route returns JSON, and every route but two only reads.
|
|
4
4
|
|
|
5
5
|
The answers hold your session names and folder paths. Check one before you share it.
|
|
6
6
|
|
|
@@ -31,25 +31,27 @@ curl -s http://127.0.0.1:4777/api/sessions
|
|
|
31
31
|
|
|
32
32
|
Every answer has `Content-Type: application/json; charset=utf-8`, `Cache-Control: no-store`, `X-Content-Type-Options: nosniff` and `Cross-Origin-Resource-Policy: same-origin`. Every answer that is not a 200 has the body `{ "error": "<one sentence>" }`.
|
|
33
33
|
|
|
34
|
-
| Status | When
|
|
35
|
-
| ------ |
|
|
36
|
-
| 400 | The address, `since` or `windowMs` could not be read
|
|
37
|
-
| 403 | The `Host`, the `Origin` or `Sec-Fetch-Site` above
|
|
38
|
-
| 404 | There is no route at that path
|
|
39
|
-
| 405 | Any method but `GET`, with `Allow: GET`. `/api/jump`
|
|
40
|
-
| 500 | Something went wrong that the server did not expect
|
|
34
|
+
| Status | When |
|
|
35
|
+
| ------ | ------------------------------------------------------------------------------------------------------------------ |
|
|
36
|
+
| 400 | The address, `since` or `windowMs` could not be read |
|
|
37
|
+
| 403 | The `Host`, the `Origin` or `Sec-Fetch-Site` above |
|
|
38
|
+
| 404 | There is no route at that path |
|
|
39
|
+
| 405 | Any method but `GET`, with `Allow: GET`. `/api/jump` and `/api/history/clear` take only `POST`, with `Allow: POST` |
|
|
40
|
+
| 500 | Something went wrong that the server did not expect |
|
|
41
41
|
|
|
42
42
|
## Routes
|
|
43
43
|
|
|
44
|
-
| Route | Answers
|
|
45
|
-
| ---------------------------------- |
|
|
46
|
-
| `GET /api/health` | `{ ok: true, version }`
|
|
47
|
-
| `GET /api/sessions` | The latest snapshot: `{ generatedAt, sources, sessions }`
|
|
48
|
-
| `GET /api/events?since=<epoch ms>` | `{ events }`, newest first, at most 200
|
|
49
|
-
| `GET /api/history?windowMs=<ms>` | `{ points, startedAt }`, one point for each poll, oldest first
|
|
50
|
-
| `GET /api/email` | `{ on, to, events, afterMs, problem, last, limitedUntil }`
|
|
51
|
-
| `GET /api/webhook` | `{ on, host, events, afterMs, problem, last, limitedUntil }`
|
|
52
|
-
| `
|
|
44
|
+
| Route | Answers |
|
|
45
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------- |
|
|
46
|
+
| `GET /api/health` | `{ ok: true, version }` |
|
|
47
|
+
| `GET /api/sessions` | The latest snapshot: `{ generatedAt, sources, sessions }` |
|
|
48
|
+
| `GET /api/events?since=<epoch ms>` | `{ events }`, newest first, at most 200 |
|
|
49
|
+
| `GET /api/history?windowMs=<ms>` | `{ points, startedAt, since, kept, restarts }`, one point for each poll, oldest first |
|
|
50
|
+
| `GET /api/email` | `{ on, to, events, afterMs, asking, problem, last, limitedUntil }` |
|
|
51
|
+
| `GET /api/webhook` | `{ on, host, events, afterMs, asking, problem, last, limitedUntil }` |
|
|
52
|
+
| `GET /api/waits` | `{ at, today, sevenDays, since, where }`: how long sessions waited on you |
|
|
53
|
+
| `POST /api/jump` | `{ ok: true, kind, place }`, with `app` for a terminal tab. One of two routes that act |
|
|
54
|
+
| `POST /api/history/clear` | `{ ok: true, clearedAt }`. The other route that acts |
|
|
53
55
|
|
|
54
56
|
Times are milliseconds since 1970, and lengths of time are milliseconds.
|
|
55
57
|
|
|
@@ -99,28 +101,28 @@ The snapshot the collector made at its last poll, two seconds apart at most:
|
|
|
99
101
|
|
|
100
102
|
Sessions come in this order: those that need you first, longest wait first, then working, idle, finished, failed and unknown, each with the most recent change first.
|
|
101
103
|
|
|
102
|
-
| Session field | Holds
|
|
103
|
-
| ---------------- |
|
|
104
|
-
| `id` | Stable across polls: the source, a colon, and the agent's own id for the session
|
|
105
|
-
| `source` | `claude-code`, `codex` or `status-files`
|
|
106
|
-
| `agent` | The agent's own name, for a session from a status file. Left out for the others, whose agent is the source's `label`
|
|
107
|
-
| `surface` | `terminal`, `vscode`, `desktop`, `cloud`, `browser` or `unknown`
|
|
108
|
-
| `name` | What the dashboard calls it
|
|
109
|
-
| `cwd` | The folder it works in, or `null`
|
|
110
|
-
| `project` | The last part of `cwd`, or `null`
|
|
111
|
-
| `git` | `{ branch }`, or `{ commit }` with seven characters when no branch is checked out, each with `repository`. Left out when the folder is in no repository that could be read
|
|
112
|
-
| `git.repository` | `{ id, name }`: the repository the folder belongs to, the same for its main folder and every worktree of it. `name` is the main folder's name. `id` is 16 hexadecimal characters, a hash of the path of the repository's own git folder, which its worktrees share, for telling repositories apart. Left out for a repository with no name, at the root, or a worktree whose `commondir` cannot be read
|
|
113
|
-
| `status` | `needs-you`, `working`, `idle`, `finished`, `failed` or `unknown`
|
|
114
|
-
| `waitingReason` | For `needs-you` only: `permission`, `question` or `other`
|
|
115
|
-
| `waitingDetail` | The agent's own words for the wait, when it gives any
|
|
116
|
-
| `waitingText` | For `needs-you` Claude Code sessions only: what the session is asking, in a line or two, read from the last message of its transcript, such as `Run: npm test`, `Edit: src/app.ts` or the question it put. Plain text of at most 200 characters. Left out when the transcript does not say, or with `AGENT_LOOKOUT_WAITING_TEXT=off`. No event in `/api/events`, and no history point, holds it
|
|
117
|
-
| `startedAt` | When it started, or `null`
|
|
118
|
-
| `statusSince` | When its status last changed, or `null`. It does not move while a wait goes on
|
|
119
|
-
| `lastWriteAt` | When its agent last wrote the file it is read from, for Codex and status files
|
|
120
|
-
| `pid`, `alive` | Its process and whether that still runs, when known
|
|
121
|
-
| `links.open` | A `vscode://` address that opens it, for a session in VS Code
|
|
122
|
-
| `jump` | `{ kind: "tmux", place }` or `{ kind: "terminal", app, place }` when `POST /api/jump` can take you to it
|
|
123
|
-
| `stale` | `true` once it has been idle for 24 hours
|
|
104
|
+
| Session field | Holds |
|
|
105
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| `id` | Stable across polls: the source, a colon, and the agent's own id for the session |
|
|
107
|
+
| `source` | `claude-code`, `codex` or `status-files` |
|
|
108
|
+
| `agent` | The agent's own name, for a session from a status file. Left out for the others, whose agent is the source's `label` |
|
|
109
|
+
| `surface` | `terminal`, `vscode`, `desktop`, `cloud`, `browser` or `unknown` |
|
|
110
|
+
| `name` | What the dashboard calls it |
|
|
111
|
+
| `cwd` | The folder it works in, or `null` |
|
|
112
|
+
| `project` | The last part of `cwd`, or `null` |
|
|
113
|
+
| `git` | `{ branch }`, or `{ commit }` with seven characters when no branch is checked out, each with `repository`. Left out when the folder is in no repository that could be read |
|
|
114
|
+
| `git.repository` | `{ id, name }`: the repository the folder belongs to, the same for its main folder and every worktree of it. `name` is the main folder's name. `id` is 16 hexadecimal characters, a hash of the path of the repository's own git folder, which its worktrees share, for telling repositories apart. Left out for a repository with no name, at the root, or a worktree whose `commondir` cannot be read |
|
|
115
|
+
| `status` | `needs-you`, `working`, `idle`, `finished`, `failed` or `unknown` |
|
|
116
|
+
| `waitingReason` | For `needs-you` only: `permission`, `question` or `other` |
|
|
117
|
+
| `waitingDetail` | The agent's own words for the wait, when it gives any |
|
|
118
|
+
| `waitingText` | For `needs-you` Claude Code sessions only: what the session is asking, in a line or two, read from the last message of its transcript, such as `Run: npm test`, `Edit: src/app.ts` or the question it put. Plain text of at most 200 characters. Left out when the transcript does not say, or with `AGENT_LOOKOUT_WAITING_TEXT=off`. No event in `/api/events`, and no history point, holds it. An email or webhook post holds it only with `AGENT_LOOKOUT_EMAIL_ASKING=on` or `AGENT_LOOKOUT_WEBHOOK_ASKING=on` |
|
|
119
|
+
| `startedAt` | When it started, or `null` |
|
|
120
|
+
| `statusSince` | When its status last changed, or `null`. It does not move while a wait goes on |
|
|
121
|
+
| `lastWriteAt` | When its agent last wrote the file it is read from, for Codex and status files |
|
|
122
|
+
| `pid`, `alive` | Its process and whether that still runs, when known |
|
|
123
|
+
| `links.open` | A `vscode://` address that opens it, for a session in VS Code |
|
|
124
|
+
| `jump` | `{ kind: "tmux", place }` or `{ kind: "terminal", app, place }` when `POST /api/jump` can take you to it |
|
|
125
|
+
| `stale` | `true` once it has been idle for 24 hours |
|
|
124
126
|
|
|
125
127
|
| Source field | Holds |
|
|
126
128
|
| -------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -137,32 +139,121 @@ The [guide](GUIDE.md#what-each-agent-can-report) has the table `capabilities` ho
|
|
|
137
139
|
|
|
138
140
|
### `GET /api/events?since=<epoch ms>`
|
|
139
141
|
|
|
140
|
-
`{ "events": [...] }`: what changed, newest first, at most 200, and with `since` only those after it. Each event is `{ id, at, sessionId, sessionName, kind, from, to, severity }`, where `kind` is `appeared`, `status-changed` or `ended`, `from` and `to` are statuses, and `severity` is `advisory`, `warning` for a change to `needs-you` or `critical` for a change to `failed`. The collector keeps the last 1,000 in memory.
|
|
142
|
+
`{ "events": [...] }`: what changed, newest first, at most 200, and with `since` only those after it. Each event is `{ id, at, sessionId, sessionName, kind, from, to, severity }`, where `kind` is `appeared`, `status-changed` or `ended`, `from` and `to` are statuses, and `severity` is `advisory`, `warning` for a change to `needs-you` or `critical` for a change to `failed`. The collector keeps the last 1,000 in memory, and with history kept on disk it reads them back when it starts again.
|
|
141
143
|
|
|
142
144
|
### `GET /api/history?windowMs=<ms>`
|
|
143
145
|
|
|
144
|
-
|
|
146
|
+
One point for each poll, oldest first, each `{ at, needsYou, working, idle, total }`, over the last 15 minutes or the `windowMs` given, up to six hours, with where the history begins, where it is kept and when Agent Lookout was started again:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"points": [{ "at": 1791204002000, "needsYou": 1, "working": 2, "idle": 3, "total": 6 }],
|
|
151
|
+
"startedAt": 1791204000000,
|
|
152
|
+
"since": { "at": 1790944800000, "by": "started" },
|
|
153
|
+
"kept": {
|
|
154
|
+
"where": "disk",
|
|
155
|
+
"folder": "~/.agent-lookout/history",
|
|
156
|
+
"bytes": 1468006,
|
|
157
|
+
"maxBytes": 20971520,
|
|
158
|
+
"maxAgeMs": 691200000,
|
|
159
|
+
"canClear": true,
|
|
160
|
+
"problem": null
|
|
161
|
+
},
|
|
162
|
+
"restarts": [{ "at": 1791204000000, "lastBefore": 1791190800000 }]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
| Field | Holds |
|
|
167
|
+
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
168
|
+
| `startedAt` | When this run of Agent Lookout began. A new one means it was started again |
|
|
169
|
+
| `since` | Where the history it holds begins: `at`, a time, and `by`, which is `started` when Agent Lookout started watching then, `cleared` when the history was cleared then, or `trimmed` when that is the oldest history kept and what came before it was deleted for its age or the size. Time before `since.at` was not measured. With history kept on disk it can be days before `startedAt` |
|
|
170
|
+
| `kept` | `where` is `disk` or `memory`, with `AGENT_LOOKOUT_HISTORY=off`. On disk, `folder` is the folder with the home folder written `~`, and `bytes` how much the files hold. `maxBytes` is the cap, 20 MB, and `maxAgeMs` how long after its day a day's history is kept, 8 days. `canClear` says whether this copy can clear it, and `problem`, while it does not write the files, one sentence saying why, such as another copy writing them |
|
|
171
|
+
| `restarts` | Each time Agent Lookout started again after `since.at` with history kept from before, oldest first, this run's start among them: `at`, when it started, and `lastBefore`, the newest moment the history before it holds. Nothing was measured between the two, however close they are. Empty in memory only, and before the first restart |
|
|
172
|
+
|
|
173
|
+
A poll adds no point when no source answered, or when a source that answered before did not. Two points further apart than a few polls had time between them that was not measured: the computer asleep, a source that stopped answering, or Agent Lookout not running. So had two points either side of a restart. The dashboard draws that time hatched.
|
|
174
|
+
|
|
175
|
+
After a restart, the first poll that reads a source compares each session with the last event the history kept of it. A session whose status changed while Agent Lookout was stopped gets a `status-changed` event, one that ended an `ended` event, and one back after it ended an `appeared` event, all at that poll. A session the history kept no event of is taken as it is found.
|
|
176
|
+
|
|
177
|
+
### `GET /api/waits`
|
|
178
|
+
|
|
179
|
+
How long sessions waited on you today, from local midnight, and over the last seven days, which are today and the six days before it, from midnight. It is worked out from the whole of the history kept, eight days of it, and not only the six hours `/api/history` serves, and counts only the time Agent Lookout measured: while it was running and every source it reads answered. Days are this computer's own, by its clock and time zone.
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"at": 1791298800000,
|
|
184
|
+
"today": {
|
|
185
|
+
"from": 1791244800000,
|
|
186
|
+
"to": 1791298800000,
|
|
187
|
+
"waitedMs": 2100000,
|
|
188
|
+
"openMs": 298000,
|
|
189
|
+
"waits": 3,
|
|
190
|
+
"measuredMs": 8998000,
|
|
191
|
+
"days": [
|
|
192
|
+
{
|
|
193
|
+
"day": "2026-10-06",
|
|
194
|
+
"from": 1791244800000,
|
|
195
|
+
"to": 1791298800000,
|
|
196
|
+
"waitedMs": 2100000,
|
|
197
|
+
"openMs": 298000,
|
|
198
|
+
"waits": 3,
|
|
199
|
+
"measuredMs": 8998000
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
"sessions": [
|
|
203
|
+
{
|
|
204
|
+
"sessionId": "claude-code:00000000-0000-4000-8000-000000000001",
|
|
205
|
+
"name": "demo-project",
|
|
206
|
+
"waitedMs": 1200000,
|
|
207
|
+
"waits": 1,
|
|
208
|
+
"open": false
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
"sessionCount": 3
|
|
212
|
+
},
|
|
213
|
+
"sevenDays": { "from": 1790726400000, "to": 1791298800000, "days": [], "sessions": [] },
|
|
214
|
+
"since": { "at": 1791018000000, "by": "started" },
|
|
215
|
+
"where": "disk"
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`today` and `sevenDays` have the same fields. `sevenDays` above is cut short: it has its own totals, seven `days`, oldest first, and its own `sessions`.
|
|
220
|
+
|
|
221
|
+
| Field | Holds |
|
|
222
|
+
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
223
|
+
| `at` | When it was worked out, by the server's clock. Both periods end here |
|
|
224
|
+
| `waitedMs` | How long sessions waited on you. Each session's waits are added up, so two waiting at once for a minute are two minutes. Only time that was measured counts |
|
|
225
|
+
| `openMs` | The part of `waitedMs` that is a wait still open: a session that needs you now |
|
|
226
|
+
| `waits` | How many waits had time in the period. A wait that runs on across midnight, or across time that was not measured, is one wait |
|
|
227
|
+
| `measuredMs` | How much of the period Agent Lookout measured. The rest it was not running, the computer was asleep, a source did not answer, or it came before `since` |
|
|
228
|
+
| `days` | Each local day in the period, oldest first, with the same four figures: `day` as `2026-10-06`, `from` its midnight and `to` the next, or `at` for today |
|
|
229
|
+
| `sessions` | The ten sessions that waited longest, longest first: `sessionId`, `name` as it was last seen, `waitedMs`, `waits`, and `open`, true while it needs you |
|
|
230
|
+
| `sessionCount` | How many sessions waited in the period in all |
|
|
231
|
+
| `since` | Where the history held begins, as in `/api/history`. Nothing before it is counted |
|
|
232
|
+
| `where` | `disk`, or `memory` with `AGENT_LOOKOUT_HISTORY=off`, when the history begins as this run of Agent Lookout started, so only the time since then counts |
|
|
233
|
+
|
|
234
|
+
A wait begins with a move into `needs-you` and ends with the session's next move out of it, or its end. A wait already under way when Agent Lookout found the session, as when it started while the session waited, is counted from when that run of Agent Lookout started. A wait the history holds no end for, of a session that does not need you now, ended while Agent Lookout was stopped, so it is counted up to the start of this run and no further. Only Claude Code sessions and sessions from a status file can be seen waiting: a Codex session never needs you, so it is never in this answer.
|
|
145
235
|
|
|
146
236
|
### `GET /api/email` and `GET /api/webhook`
|
|
147
237
|
|
|
148
238
|
Whether [email](GUIDE.md#email) or a [webhook](GUIDE.md#webhook) is set up, and how the last one went. They only read: both are set up in the environment Agent Lookout starts with.
|
|
149
239
|
|
|
150
|
-
| Field | Holds
|
|
151
|
-
| -------------- |
|
|
152
|
-
| `on` | Whether it sends
|
|
153
|
-
| `to` | Email only: the address, with all but its first letter before the `@` hidden
|
|
154
|
-
| `host` | Webhook only: the host the posts go to, and never the rest of the address
|
|
155
|
-
| `events` | What is sent: `needs-you`, `finished`, `failed` and `ended`, any of them
|
|
156
|
-
| `afterMs` | How long a wait lasts before it is sent
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
240
|
+
| Field | Holds |
|
|
241
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
242
|
+
| `on` | Whether it sends |
|
|
243
|
+
| `to` | Email only: the address, with all but its first letter before the `@` hidden |
|
|
244
|
+
| `host` | Webhook only: the host the posts go to, and never the rest of the address |
|
|
245
|
+
| `events` | What is sent: `needs-you`, `finished`, `failed` and `ended`, any of them |
|
|
246
|
+
| `afterMs` | How long a wait lasts before it is sent |
|
|
247
|
+
| `asking` | Whether the email or post for a wait says what the session is asking: `true` with `AGENT_LOOKOUT_EMAIL_ASKING=on` or `AGENT_LOOKOUT_WEBHOOK_ASKING=on`, `false` when that setting is `off` or left out, and `null` while email or the webhook is off |
|
|
248
|
+
| `problem` | While off because a setting is wrong, one sentence naming the setting, never its value |
|
|
249
|
+
| `last` | `{ at, sent: true }` or `{ at, sent: false, reason }` for the last one tried, or `null` |
|
|
250
|
+
| `limitedUntil` | While the limit of 20 an hour holds them back, when the next may go |
|
|
160
251
|
|
|
161
|
-
Neither ever holds the mail server's address, its user name or its password,
|
|
252
|
+
Neither ever holds the mail server's address, its user name or its password, the webhook's path, or anything a session was asking.
|
|
162
253
|
|
|
163
254
|
### `POST /api/jump`
|
|
164
255
|
|
|
165
|
-
|
|
256
|
+
One of the two routes that act. It selects the tmux pane a session runs in, or brings its tab of Terminal or iTerm2 to the front, as the Jump button does. Its checks, on top of the ones every request passes, are `jumpRefusalFor` in `src/collector/jumpRoute.ts`, which makes those `actionRefusalFor` in `src/collector/handler.ts` makes for every route that acts. It answers only a request that:
|
|
166
257
|
|
|
167
258
|
- is a `POST`. Any other method gets 405, with `Allow: POST`.
|
|
168
259
|
- has an `Origin` that names this computer. A request with no `Origin` gets 403 here, though a read may go without one.
|
|
@@ -184,18 +275,42 @@ The server then looks the session up in its own latest snapshot and acts on the
|
|
|
184
275
|
|
|
185
276
|
Each of these has an `error` sentence beside its `reason`. [SECURITY.md](../SECURITY.md) says what the route can and cannot change.
|
|
186
277
|
|
|
278
|
+
### `POST /api/history/clear`
|
|
279
|
+
|
|
280
|
+
The other route that acts. It deletes the files the history is kept in, except any that a later version of Agent Lookout wrote, and empties the Events log and the history in memory, as Clear history in Settings does. The history then begins again from that moment, with `since` set to `{ at, by: "cleared" }`. It makes the same checks as `POST /api/jump`, with its own action, in `src/collector/history/clearRoute.ts`, and answers only a request that:
|
|
281
|
+
|
|
282
|
+
- is a `POST`. Any other method gets 405, with `Allow: POST`.
|
|
283
|
+
- has an `Origin` that names this computer, or gets 403.
|
|
284
|
+
- is marked `same-origin` in `Sec-Fetch-Site`, when that header is sent, or gets 403.
|
|
285
|
+
- carries `X-Agent-Lookout-Action: clear-history`, or gets 403.
|
|
286
|
+
- has `Content-Type: application/json`, or gets 415.
|
|
287
|
+
- has a body of 64 bytes or less, or gets 413, that is exactly `{}`, or gets 400.
|
|
288
|
+
|
|
289
|
+
Nothing in the request names a file: the files are the ones in the history's own folder.
|
|
290
|
+
|
|
291
|
+
| Status | Body |
|
|
292
|
+
| ------ | ------------------------------------------------------------------------------------------------------------------------ |
|
|
293
|
+
| 200 | `{ "ok": true, "clearedAt": 1791204000000 }` |
|
|
294
|
+
| 409 | `reason: "memory-only"`: history is kept in memory only, with `AGENT_LOOKOUT_HISTORY=off`, and there is nothing to clear |
|
|
295
|
+
| 409 | `reason: "not-writing"`: another copy of Agent Lookout writes the files, or they cannot be written here |
|
|
296
|
+
| 500 | `reason: "failed"`: the files could not all be deleted, and nothing in memory was emptied |
|
|
297
|
+
|
|
298
|
+
Each of these has an `error` sentence beside its `reason`.
|
|
299
|
+
|
|
187
300
|
## In the Mac app only
|
|
188
301
|
|
|
189
|
-
The Mac app serves the dashboard and this API from its own address, `agent-lookout://app/`, with no port, and answers
|
|
302
|
+
The Mac app serves the dashboard and this API from its own address, `agent-lookout://app/`, with no port, and answers six more routes there, for its updates and its menu bar item. `npm start`, `npx agent-lookout` and `npm run dev` do not have them: there, each answers 404.
|
|
190
303
|
|
|
191
|
-
| Route
|
|
192
|
-
|
|
|
193
|
-
| `GET /api/app/update`
|
|
194
|
-
| `POST /api/app/update/check`
|
|
195
|
-
| `POST /api/app/update/install`
|
|
196
|
-
| `POST /api/app/update/setting`
|
|
304
|
+
| Route | Body | Does |
|
|
305
|
+
| -------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
306
|
+
| `GET /api/app/update` | | Where updates stand: the version, the switch, the last check, what was found and how far a download has got |
|
|
307
|
+
| `POST /api/app/update/check` | `{}` | Checks GitHub now, and answers once it has |
|
|
308
|
+
| `POST /api/app/update/install` | `{}` | Installs the version that is ready, then quits and opens it. 409 when none is ready, or it cannot be installed where the app runs |
|
|
309
|
+
| `POST /api/app/update/setting` | `{ "automatic": true }` or `false` | Turns the daily check on or off |
|
|
310
|
+
| `GET /api/app/menu-bar` | | Whether the menu bar item is shown: `{ "show": true }` or `false` |
|
|
311
|
+
| `POST /api/app/menu-bar/setting` | `{ "show": true }` or `false` | Shows the menu bar item or takes it away |
|
|
197
312
|
|
|
198
|
-
Each POST passes the checks `POST /api/jump` does, with its own `X-Agent-Lookout-Action
|
|
313
|
+
Each POST passes the checks `POST /api/jump` does, with its own `X-Agent-Lookout-Action` and a body that is exactly what the table says. For updates, the action is `check-for-updates`, `install-update` or `update-setting`, the body is 256 bytes or less, and each answers with the status `GET /api/app/update` gives, `AppUpdateStatus` in `src/core/appUpdate.ts`. For the menu bar item, the action is `menu-bar-setting`, the body is 64 bytes or less, and it answers with what `GET /api/app/menu-bar` gives, `MenuBarStatus` in `src/core/appMenuBar.ts`. That says `false` while the item could not be put in the menu bar, even with the switch on. Nothing in a request reaches a command or a path.
|
|
199
314
|
|
|
200
315
|
## The notifications header
|
|
201
316
|
|