git-daemon 0.1.0
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/.eslintrc.cjs +18 -0
- package/README.md +143 -0
- package/config.schema.json +180 -0
- package/design.md +481 -0
- package/logo.png +0 -0
- package/openapi.yaml +678 -0
- package/package.json +41 -0
- package/src/app.ts +459 -0
- package/src/approvals.ts +35 -0
- package/src/config.ts +104 -0
- package/src/context.ts +64 -0
- package/src/daemon.ts +22 -0
- package/src/deps.ts +134 -0
- package/src/errors.ts +76 -0
- package/src/git.ts +160 -0
- package/src/jobs.ts +194 -0
- package/src/logger.ts +26 -0
- package/src/os.ts +45 -0
- package/src/pairing.ts +52 -0
- package/src/process.ts +55 -0
- package/src/security.ts +80 -0
- package/src/tokens.ts +95 -0
- package/src/tools.ts +45 -0
- package/src/types.ts +111 -0
- package/src/typings/tree-kill.d.ts +9 -0
- package/src/validation.ts +69 -0
- package/src/workspace.ts +83 -0
- package/tests/app.test.ts +122 -0
- package/tsconfig.json +14 -0
- package/vitest.config.ts +8 -0
package/design.md
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
# Git Daemon Design Document
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
**Git Daemon** is a local, background service that enables a trusted web UI (React app) to perform **privileged, local machine actions**—primarily Git operations and developer convenience actions—without granting the browser arbitrary system access.
|
|
6
|
+
|
|
7
|
+
This project includes **only the daemon**. The React UI is external and out of scope; UI references here exist solely to define integration expectations.
|
|
8
|
+
|
|
9
|
+
The daemon exposes a **localhost HTTP API** guarded by:
|
|
10
|
+
|
|
11
|
+
* **Origin allowlist** (only your UI URLs)
|
|
12
|
+
* **Pairing token** (per-user secret)
|
|
13
|
+
* **Workspace sandbox** (all filesystem actions constrained)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Goals
|
|
18
|
+
|
|
19
|
+
* Enable “Clone locally” from a web UI using native Git credentials (SSH / credential helper).
|
|
20
|
+
* Provide additional local dev actions:
|
|
21
|
+
|
|
22
|
+
* `fetch`
|
|
23
|
+
* `status`
|
|
24
|
+
* open folder / terminal / VS Code
|
|
25
|
+
* run dependency installation (`npm i` / pnpm / yarn)
|
|
26
|
+
* Prevent any website other than the approved UI origins from controlling the daemon.
|
|
27
|
+
* Prevent the approved UI from writing outside a user-approved local workspace root.
|
|
28
|
+
|
|
29
|
+
## Non-goals
|
|
30
|
+
|
|
31
|
+
* Acting as a GitHub API proxy or storing GitHub OAuth tokens.
|
|
32
|
+
* Building or shipping the React UI.
|
|
33
|
+
* Acting as a full Git GUI (diff viewer, staging UI, merge tools) — those are UI concerns.
|
|
34
|
+
* Running arbitrary shell commands. (Only explicit, whitelisted operations.)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Integration Context (External)
|
|
39
|
+
|
|
40
|
+
These components are **not part of this project**; they are listed only to define how the daemon is used.
|
|
41
|
+
|
|
42
|
+
### React UI (external)
|
|
43
|
+
|
|
44
|
+
* Hosted: `https://app.example.com` (Cloudflare Worker URL or custom domain)
|
|
45
|
+
* Local: `http://localhost:<uiPort>`
|
|
46
|
+
* Calls:
|
|
47
|
+
|
|
48
|
+
* GitHub API directly (optional, UI-side)
|
|
49
|
+
* Git Daemon API on `http://127.0.0.1:<daemonPort>`
|
|
50
|
+
* The UI may be hosted remotely or run locally, but **the daemon does not proxy GitHub API calls** and **does not accept GitHub tokens**.
|
|
51
|
+
|
|
52
|
+
### Cloudflare Worker (external)
|
|
53
|
+
|
|
54
|
+
* Serves hosted UI assets and routing.
|
|
55
|
+
* No GitHub API proxying.
|
|
56
|
+
* No local operations.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Architecture
|
|
61
|
+
|
|
62
|
+
### Components
|
|
63
|
+
1. **Git Daemon (local service)**
|
|
64
|
+
|
|
65
|
+
* Binds to `127.0.0.1` only
|
|
66
|
+
* Implements a small HTTP JSON API + streaming logs (SSE)
|
|
67
|
+
* Runs **system git** for native credentials & compatibility
|
|
68
|
+
* Runs **package manager installs** in sandboxed repos
|
|
69
|
+
* Provides OS integrations: open folder/terminal/VS Code
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Security Model
|
|
74
|
+
|
|
75
|
+
### Threats addressed
|
|
76
|
+
|
|
77
|
+
* Malicious websites attempting to send requests to localhost services.
|
|
78
|
+
* DNS rebinding and host header abuse against localhost services.
|
|
79
|
+
* Path traversal / writing outside intended directories.
|
|
80
|
+
* Unintended execution via `npm install` scripts.
|
|
81
|
+
* CSRF-like attacks from untrusted origins.
|
|
82
|
+
|
|
83
|
+
### Controls
|
|
84
|
+
|
|
85
|
+
#### 1) Bind to loopback only
|
|
86
|
+
|
|
87
|
+
* Listen on `127.0.0.1:<daemonPort>` (not `0.0.0.0`)
|
|
88
|
+
* Reject requests to non-loopback interfaces.
|
|
89
|
+
|
|
90
|
+
#### 2) Origin allowlist (hard gate)
|
|
91
|
+
|
|
92
|
+
For every request:
|
|
93
|
+
|
|
94
|
+
* Check `Origin` header (and optionally `Referer` for diagnostics).
|
|
95
|
+
* Allow only exact matches:
|
|
96
|
+
|
|
97
|
+
* `https://app.example.com`
|
|
98
|
+
* `http://localhost:<uiPort>` (optionally allow a small dev port set)
|
|
99
|
+
|
|
100
|
+
If not allowed: respond `403`.
|
|
101
|
+
|
|
102
|
+
* Require `Origin` for all requests (reject missing/empty).
|
|
103
|
+
|
|
104
|
+
#### 2b) DNS rebinding protections
|
|
105
|
+
|
|
106
|
+
* Verify remote address is loopback (`127.0.0.1`/`::1`).
|
|
107
|
+
* Verify `Host` is `127.0.0.1` or `localhost` (reject all others).
|
|
108
|
+
|
|
109
|
+
#### 3) Pairing token (second gate)
|
|
110
|
+
|
|
111
|
+
* Daemon generates a random secret on first run.
|
|
112
|
+
* UI must send `Authorization: Bearer <token>` on all non-public endpoints.
|
|
113
|
+
* Pairing UX options:
|
|
114
|
+
|
|
115
|
+
* **Copy/Paste code**: daemon prints pairing code; UI has input box.
|
|
116
|
+
* **Local confirmation page**: daemon serves `GET /pair` which user visits, clicks “Approve”, daemon issues token.
|
|
117
|
+
* Default to the local confirmation page; use copy/paste as a headless fallback.
|
|
118
|
+
|
|
119
|
+
Token storage:
|
|
120
|
+
|
|
121
|
+
* Stored locally in daemon config directory (OS-appropriate).
|
|
122
|
+
* Store a hash of the token at rest; keep a per-origin token record.
|
|
123
|
+
* Support rotation and revocation (per origin).
|
|
124
|
+
* Tokens expire after 30 days by default; allow refresh before expiry.
|
|
125
|
+
* UI stores token in browser storage (scoped to the UI origin).
|
|
126
|
+
|
|
127
|
+
#### 4) Workspace sandbox
|
|
128
|
+
|
|
129
|
+
* Daemon maintains a **workspaceRoot** directory.
|
|
130
|
+
* All filesystem paths are resolved to absolute canonical paths.
|
|
131
|
+
* Reject if the resolved path is outside `workspaceRoot` (including symlink escapes).
|
|
132
|
+
* Clone destinations must be workspace-relative or validated absolute paths inside root.
|
|
133
|
+
* On first run, prompt the user to select a workspace root; persist the choice in config.
|
|
134
|
+
|
|
135
|
+
#### 5) Capability gating (required)
|
|
136
|
+
|
|
137
|
+
First-time use of high-risk features requires explicit user approval:
|
|
138
|
+
|
|
139
|
+
* `open-terminal`
|
|
140
|
+
* `open-vscode`
|
|
141
|
+
* `deps/install`
|
|
142
|
+
Approval is remembered per (origin, repo) tuple, with an option to revoke.
|
|
143
|
+
|
|
144
|
+
#### 6) No arbitrary command execution
|
|
145
|
+
|
|
146
|
+
* Only whitelisted commands are implemented.
|
|
147
|
+
* Any endpoint that would accept arbitrary args must be constrained/validated.
|
|
148
|
+
* Validate `repoUrl` formats (SSH/HTTPS only); disallow `file://` and local paths.
|
|
149
|
+
|
|
150
|
+
#### 7) Abuse limits (recommended)
|
|
151
|
+
|
|
152
|
+
* Rate limit pairing attempts and auth failures.
|
|
153
|
+
* Cap request body size and path length.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Configuration & Storage
|
|
158
|
+
|
|
159
|
+
* Use OS-specific config directories:
|
|
160
|
+
|
|
161
|
+
* macOS: `~/Library/Application Support/Git Daemon`
|
|
162
|
+
* Linux: `~/.config/git-daemon`
|
|
163
|
+
* Windows: `%APPDATA%\\Git Daemon`
|
|
164
|
+
* Store:
|
|
165
|
+
|
|
166
|
+
* `config.json` (workspace root, approvals, options)
|
|
167
|
+
* `tokens.json` (hashed tokens, per-origin records)
|
|
168
|
+
* `logs/` (structured logs with rotation)
|
|
169
|
+
* Log rotation: keep 5 files × 5MB each.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Git & System Integration
|
|
174
|
+
|
|
175
|
+
### Git execution strategy
|
|
176
|
+
|
|
177
|
+
Use **system `git`** via process spawn:
|
|
178
|
+
|
|
179
|
+
* Supports SSH config, credential helpers, GPG signing, etc.
|
|
180
|
+
* Matches user environment and reduces edge cases.
|
|
181
|
+
|
|
182
|
+
Capture output:
|
|
183
|
+
|
|
184
|
+
* Stream `stdout`/`stderr` to job logs.
|
|
185
|
+
* Parse where useful (e.g., status porcelain).
|
|
186
|
+
|
|
187
|
+
### Runtime & packaging
|
|
188
|
+
|
|
189
|
+
* Plain Node.js script written in TypeScript.
|
|
190
|
+
* No binary packaging; run via Node (e.g., `node dist/daemon.js` or `npm run daemon`).
|
|
191
|
+
* Manual start only; no auto-start or OS service installation.
|
|
192
|
+
|
|
193
|
+
### Suggested libraries (optional)
|
|
194
|
+
|
|
195
|
+
* `express` for the HTTP API.
|
|
196
|
+
* `cors` with a strict allowlist for Origin handling.
|
|
197
|
+
* `zod` or `ajv` for request validation.
|
|
198
|
+
* `express-rate-limit` for pairing/auth failure throttles.
|
|
199
|
+
* `execa` for spawning git/deps with streamed output.
|
|
200
|
+
* `tree-kill` or `pidtree` for cross-platform process-tree termination.
|
|
201
|
+
* `env-paths` for OS-correct config directories.
|
|
202
|
+
* `pino` + `pino-http` for structured logging.
|
|
203
|
+
* `rotating-file-stream` for log rotation.
|
|
204
|
+
* `vitest` for unit/integration testing.
|
|
205
|
+
* `supertest` for HTTP API testing.
|
|
206
|
+
|
|
207
|
+
### CLI UX (local)
|
|
208
|
+
|
|
209
|
+
* Use `prompts` for interactive input flows.
|
|
210
|
+
* Use `ora` for status spinners and long-running task feedback.
|
|
211
|
+
* Use `chalk` for readable, color-coded output.
|
|
212
|
+
* Rationale: lightweight, simple to wire into a daemon with occasional user prompts.
|
|
213
|
+
|
|
214
|
+
### Testing approach
|
|
215
|
+
|
|
216
|
+
* Unit tests cover pure validation, path resolution, and policy checks (origins, workspace, capabilities).
|
|
217
|
+
* Integration tests start the daemon on an ephemeral port and call HTTP endpoints via `supertest`.
|
|
218
|
+
* Use temp directories for workspace roots and repos; never touch user paths.
|
|
219
|
+
* Stub Git/deps commands where possible; allow a small set of opt-in, real-git tests behind a flag.
|
|
220
|
+
* Validate SSE streams with deterministic event sequences for job lifecycles.
|
|
221
|
+
|
|
222
|
+
### Credential behavior (native)
|
|
223
|
+
|
|
224
|
+
* Clone defaults to SSH URL: `git@github.com:OWNER/REPO.git`
|
|
225
|
+
* If user prefers HTTPS and has credential helper configured, daemon can support HTTPS clone URL too—still without receiving tokens.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## API Design
|
|
230
|
+
|
|
231
|
+
### Conventions
|
|
232
|
+
|
|
233
|
+
* JSON request/response.
|
|
234
|
+
* Long operations create a **job** and return `jobId`.
|
|
235
|
+
* Logs/updates streamed via SSE.
|
|
236
|
+
* All “repoPath” inputs must be inside workspace root.
|
|
237
|
+
* Use direct responses for short operations; jobs for long-running operations.
|
|
238
|
+
|
|
239
|
+
### Public endpoints (no token required)
|
|
240
|
+
|
|
241
|
+
* `GET /v1/meta` → status + version/build + pairing state + capabilities
|
|
242
|
+
* includes a `capabilities` object for tool availability
|
|
243
|
+
|
|
244
|
+
### Pairing endpoints
|
|
245
|
+
|
|
246
|
+
* `POST /v1/pair` → `{ step: "start"|"confirm", code? }`
|
|
247
|
+
|
|
248
|
+
* `start` returns pairing instructions (and optionally a one-time code)
|
|
249
|
+
* `confirm` exchanges code for `accessToken`
|
|
250
|
+
|
|
251
|
+
Meta response fields (examples):
|
|
252
|
+
|
|
253
|
+
* `version: string`
|
|
254
|
+
* `build: { commit?: string, date?: string }`
|
|
255
|
+
* `pairing: { required: boolean, paired: boolean }`
|
|
256
|
+
* `workspace: { configured: boolean, root?: string }`
|
|
257
|
+
* `capabilities.tools.pnpm.installed: boolean`
|
|
258
|
+
* `capabilities.tools.code.installed: boolean`
|
|
259
|
+
* UI uses these to adapt features (e.g., show pnpm options)
|
|
260
|
+
|
|
261
|
+
### Job endpoints
|
|
262
|
+
|
|
263
|
+
* `GET /v1/jobs/:id` → status metadata
|
|
264
|
+
* `GET /v1/jobs/:id/stream` → SSE stream of log events
|
|
265
|
+
* `POST /v1/jobs/:id/cancel` → request cancellation
|
|
266
|
+
|
|
267
|
+
### Git endpoints
|
|
268
|
+
|
|
269
|
+
* `POST /v1/git/clone` → job
|
|
270
|
+
|
|
271
|
+
* body: `{ repoUrl, destRelative, options?: { branch?, depth? } }`
|
|
272
|
+
* `POST /v1/git/fetch` → job
|
|
273
|
+
|
|
274
|
+
* body: `{ repoPath, remote?: "origin", prune?: true }`
|
|
275
|
+
* note: fetch updates remote tracking only; no merge/rebase is performed
|
|
276
|
+
* `GET /v1/git/status?repoPath=...` → structured status
|
|
277
|
+
|
|
278
|
+
* returns: `{ branch, ahead, behind, stagedCount, unstagedCount, untrackedCount, conflictsCount, clean }`
|
|
279
|
+
|
|
280
|
+
### OS “open” endpoints
|
|
281
|
+
|
|
282
|
+
* `POST /v1/os/open` → `{ target: "folder"|"terminal"|"vscode", path }`
|
|
283
|
+
|
|
284
|
+
### Package installation endpoint
|
|
285
|
+
|
|
286
|
+
* `POST /v1/deps/install` → job
|
|
287
|
+
|
|
288
|
+
* body: `{ repoPath, manager?: "auto"|"npm"|"pnpm"|"yarn", mode?: "auto"|"ci"|"install", safer?: boolean }`
|
|
289
|
+
|
|
290
|
+
Recommendations:
|
|
291
|
+
|
|
292
|
+
* `manager=auto` chooses based on `packageManager` field / lockfiles / installed tools.
|
|
293
|
+
* `safer=true` maps to flags that reduce script execution risk (e.g., `--ignore-scripts` for npm/pnpm/yarn).
|
|
294
|
+
* Default to `safer=true` for all runs; allow a per-repo override to enable scripts.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Job Model
|
|
299
|
+
|
|
300
|
+
### Job states
|
|
301
|
+
|
|
302
|
+
* `queued` → `running` → `done | error | cancelled`
|
|
303
|
+
|
|
304
|
+
### Stream event format (SSE)
|
|
305
|
+
|
|
306
|
+
Each event is JSON:
|
|
307
|
+
|
|
308
|
+
* `{ type: "log", stream: "stdout"|"stderr", line: "..." }`
|
|
309
|
+
* `{ type: "progress", kind: "git"|"deps", percent?: number, detail?: string }`
|
|
310
|
+
* `{ type: "state", state: "running"|"done"|"error", message?: string }`
|
|
311
|
+
|
|
312
|
+
### Cancellation
|
|
313
|
+
|
|
314
|
+
* Attempt graceful termination:
|
|
315
|
+
|
|
316
|
+
* send SIGINT/SIGTERM (platform-dependent)
|
|
317
|
+
* terminate the entire process tree
|
|
318
|
+
* mark cancelled if process exits
|
|
319
|
+
|
|
320
|
+
### Concurrency & timeouts
|
|
321
|
+
|
|
322
|
+
* Default to one running job at a time (configurable).
|
|
323
|
+
* Enforce a max runtime of 1 hour per job (configurable); return a `timeout` error code.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Dependency Manager Selection
|
|
328
|
+
|
|
329
|
+
### Detection rules
|
|
330
|
+
|
|
331
|
+
* Daemon detects presence of tools (`pnpm`, `npm`, `yarn`) via PATH and `--version`.
|
|
332
|
+
|
|
333
|
+
### Auto selection order (example)
|
|
334
|
+
|
|
335
|
+
1. If `package.json.packageManager` specifies pnpm/yarn/npm → prefer that manager (if installed)
|
|
336
|
+
2. Else if lockfile exists:
|
|
337
|
+
|
|
338
|
+
* `pnpm-lock.yaml` → pnpm
|
|
339
|
+
* `yarn.lock` → yarn
|
|
340
|
+
* `package-lock.json` → npm
|
|
341
|
+
3. Else fallback to npm
|
|
342
|
+
|
|
343
|
+
### Command choice
|
|
344
|
+
|
|
345
|
+
* pnpm: `pnpm install` (add `--frozen-lockfile` for CI/lockfile enforcement; add `--ignore-scripts` when `safer=true`)
|
|
346
|
+
* npm:
|
|
347
|
+
|
|
348
|
+
* if lockfile exists: `npm ci`
|
|
349
|
+
* else: `npm install`
|
|
350
|
+
* add `--ignore-scripts` when `safer=true`
|
|
351
|
+
* yarn: `yarn install` (add `--immutable` if berry is detected; add `--ignore-scripts` when `safer=true`)
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Usage Flows
|
|
356
|
+
|
|
357
|
+
### 1) Hosted UI + daemon (normal user)
|
|
358
|
+
|
|
359
|
+
1. User opens `https://app.example.com`
|
|
360
|
+
2. UI checks daemon: `GET http://127.0.0.1:<port>/v1/meta`
|
|
361
|
+
3. If missing: UI shows “Run daemon” instructions (repo clone + `npm run daemon`)
|
|
362
|
+
4. Pair once (code/confirm)
|
|
363
|
+
5. User clicks “Clone locally”
|
|
364
|
+
6. UI calls daemon `/v1/git/clone`, streams job logs
|
|
365
|
+
7. UI shows “Open in VS Code / Terminal / Folder”
|
|
366
|
+
|
|
367
|
+
### 2) Local UI + daemon (dev)
|
|
368
|
+
|
|
369
|
+
Same, except UI origin is localhost and allowlisted.
|
|
370
|
+
|
|
371
|
+
### Usage considerations
|
|
372
|
+
|
|
373
|
+
* Provide a "Fetch" action that does not pull; use it to update ahead/behind indicators before any merge/rebase.
|
|
374
|
+
* Avoid implicit pull; all merges/rebases should be explicit, user-driven actions.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Local Setup (developer-oriented)
|
|
379
|
+
|
|
380
|
+
### Requirements
|
|
381
|
+
|
|
382
|
+
* Git installed
|
|
383
|
+
* Node installed (for running daemon from repo, per your approach)
|
|
384
|
+
* Optional:
|
|
385
|
+
|
|
386
|
+
* VS Code CLI (`code`) in PATH for “Open VS Code”
|
|
387
|
+
* pnpm installed for pnpm installs
|
|
388
|
+
|
|
389
|
+
### Typical commands
|
|
390
|
+
|
|
391
|
+
* `npm install`
|
|
392
|
+
* `npm run daemon`
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Example Requests (for reference)
|
|
397
|
+
|
|
398
|
+
### Check meta
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
curl -H "Origin: https://app.example.com" \
|
|
402
|
+
http://127.0.0.1:8787/v1/meta
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Clone
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
curl -X POST \
|
|
409
|
+
-H "Origin: https://app.example.com" \
|
|
410
|
+
-H "Authorization: Bearer <TOKEN>" \
|
|
411
|
+
-H "Content-Type: application/json" \
|
|
412
|
+
-d '{"repoUrl":"git@github.com:owner/repo.git","destRelative":"owner/repo"}' \
|
|
413
|
+
http://127.0.0.1:8787/v1/git/clone
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Stream job logs (SSE)
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
curl -N \
|
|
420
|
+
-H "Origin: https://app.example.com" \
|
|
421
|
+
-H "Authorization: Bearer <TOKEN>" \
|
|
422
|
+
http://127.0.0.1:8787/v1/jobs/<JOB_ID>/stream
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Error Handling
|
|
428
|
+
|
|
429
|
+
* `401` missing/invalid bearer token
|
|
430
|
+
* `403` origin not allowed
|
|
431
|
+
* `404` unknown job/repo
|
|
432
|
+
* `409` operation not allowed (e.g., outside workspace, repo not registered)
|
|
433
|
+
* `422` invalid input (malformed paths, missing fields)
|
|
434
|
+
* `413` request too large
|
|
435
|
+
* `429` rate limited
|
|
436
|
+
* `500` unexpected failure (include safe diagnostic code)
|
|
437
|
+
|
|
438
|
+
Errors should include a stable `errorCode` and a user-safe `message`.
|
|
439
|
+
|
|
440
|
+
Recommended `errorCode` values:
|
|
441
|
+
|
|
442
|
+
* `auth_required`
|
|
443
|
+
* `auth_invalid`
|
|
444
|
+
* `origin_not_allowed`
|
|
445
|
+
* `rate_limited`
|
|
446
|
+
* `request_too_large`
|
|
447
|
+
* `workspace_required`
|
|
448
|
+
* `path_outside_workspace`
|
|
449
|
+
* `invalid_repo_url`
|
|
450
|
+
* `capability_not_granted`
|
|
451
|
+
* `job_not_found`
|
|
452
|
+
* `timeout`
|
|
453
|
+
* `internal_error`
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Observability & Diagnostics
|
|
458
|
+
|
|
459
|
+
* `GET /v1/diagnostics` (optional) returns:
|
|
460
|
+
|
|
461
|
+
* daemon config summary (redacted)
|
|
462
|
+
* recent errors and job history (limited)
|
|
463
|
+
* log tail and last 20 job summaries
|
|
464
|
+
* Structured JSON logs written to `logs/` with request/job IDs.
|
|
465
|
+
* Keep the last 100 jobs in memory and the last 50 on disk.
|
|
466
|
+
* “Copy debug bundle” flow can zip logs + redacted config + job summaries.
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## Future Extensions
|
|
471
|
+
|
|
472
|
+
* Repo registry: “known repos” list for safer operations and better UX
|
|
473
|
+
* `git pull`, `checkout`, `branch list`, `log` endpoints
|
|
474
|
+
* Support multiple terminals/editors
|
|
475
|
+
* Signed request challenge (HMAC) in addition to bearer token
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## Open Questions (implementation choices)
|
|
480
|
+
|
|
481
|
+
* How to handle first-time SSH host key verification UX
|
package/logo.png
ADDED
|
Binary file
|