@pixelbyte-software/pixcode 1.33.9 → 1.33.10

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.
Files changed (67) hide show
  1. package/dist/api-docs.html +395 -879
  2. package/dist/assets/{index-DpIcI9Q1.js → index-B_dU5AHA.js} +153 -165
  3. package/dist/favicon.svg +8 -8
  4. package/dist/icons/icon-128x128.svg +9 -9
  5. package/dist/icons/icon-144x144.svg +9 -9
  6. package/dist/icons/icon-152x152.svg +9 -9
  7. package/dist/icons/icon-192x192.svg +9 -9
  8. package/dist/icons/icon-384x384.svg +9 -9
  9. package/dist/icons/icon-512x512.svg +9 -9
  10. package/dist/icons/icon-72x72.svg +9 -9
  11. package/dist/icons/icon-96x96.svg +9 -9
  12. package/dist/icons/icon-template.svg +9 -9
  13. package/dist/index.html +1 -1
  14. package/dist/logo.svg +12 -12
  15. package/dist/openapi.yaml +1311 -0
  16. package/dist-server/server/gemini-cli.js +59 -0
  17. package/dist-server/server/gemini-cli.js.map +1 -1
  18. package/dist-server/server/index.js +6 -1
  19. package/dist-server/server/index.js.map +1 -1
  20. package/dist-server/server/middleware/auth.js +51 -9
  21. package/dist-server/server/middleware/auth.js.map +1 -1
  22. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +54 -15
  23. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
  24. package/dist-server/server/modules/providers/list/qwen/qwen-sessions.provider.js +46 -0
  25. package/dist-server/server/modules/providers/list/qwen/qwen-sessions.provider.js.map +1 -1
  26. package/dist-server/server/modules/providers/provider.routes.js +32 -1
  27. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  28. package/dist-server/server/opencode-cli.js +37 -1
  29. package/dist-server/server/opencode-cli.js.map +1 -1
  30. package/dist-server/server/opencode-response-handler.js +36 -34
  31. package/dist-server/server/opencode-response-handler.js.map +1 -1
  32. package/dist-server/server/routes/agent.js +187 -56
  33. package/dist-server/server/routes/agent.js.map +1 -1
  34. package/dist-server/server/routes/projects.js +134 -8
  35. package/dist-server/server/routes/projects.js.map +1 -1
  36. package/dist-server/server/services/provider-credentials.js +42 -8
  37. package/dist-server/server/services/provider-credentials.js.map +1 -1
  38. package/package.json +178 -178
  39. package/scripts/rest-sweep.mjs +93 -0
  40. package/server/database/db.js +794 -794
  41. package/server/database/json-store.js +194 -194
  42. package/server/gemini-cli.js +60 -0
  43. package/server/index.js +6 -1
  44. package/server/middleware/auth.js +50 -9
  45. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +130 -130
  46. package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +126 -126
  47. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +232 -193
  48. package/server/modules/providers/list/opencode/opencode.provider.ts +29 -29
  49. package/server/modules/providers/list/qwen/qwen-auth.provider.ts +145 -145
  50. package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -114
  51. package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -218
  52. package/server/modules/providers/list/qwen/qwen.provider.ts +21 -21
  53. package/server/modules/providers/provider.routes.ts +37 -4
  54. package/server/modules/providers/shared/provider-configs.ts +142 -142
  55. package/server/opencode-cli.js +37 -1
  56. package/server/opencode-response-handler.js +107 -100
  57. package/server/qwen-code-cli.js +395 -395
  58. package/server/qwen-response-handler.js +73 -73
  59. package/server/routes/agent.js +178 -58
  60. package/server/routes/projects.js +136 -8
  61. package/server/routes/qwen.js +27 -27
  62. package/server/services/external-access.js +171 -171
  63. package/server/services/provider-credentials.js +189 -155
  64. package/server/services/provider-models.js +381 -381
  65. package/server/services/telegram/telegram-http-client.js +130 -130
  66. package/server/services/vapid-keys.js +36 -36
  67. package/server/utils/port-access.js +209 -209
@@ -0,0 +1,1311 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: Pixcode REST API
4
+ version: 1.33.10
5
+ description: |
6
+ REST API for Pixcode — the multi-CLI web UI for Claude Code, Cursor CLI,
7
+ Codex, Gemini CLI, Qwen Code, and OpenCode.
8
+
9
+ ## Authentication
10
+
11
+ Most endpoints require authentication. Two schemes are accepted:
12
+
13
+ - **JWT cookie** (`token` HTTP-only cookie) — set automatically when the
14
+ browser logs in via `POST /api/auth/login`.
15
+ - **API key** (`Authorization: Bearer ck_...`) — generated under
16
+ Settings → API. API keys never expire and survive server restarts.
17
+
18
+ Endpoints flagged `apiKey` accept both.
19
+
20
+ ## Streaming endpoints
21
+
22
+ Three classes of endpoint stream beyond plain JSON:
23
+
24
+ - **WebSocket** (`/ws`, `/shell`) — chat session bidirectional traffic. Not
25
+ documented here; see `docs/websocket-protocol.md` if it exists, or read
26
+ `server/index.js` `handleChatConnection`.
27
+ - **SSE** (Server-Sent Events) — long-running jobs. Each `data:` frame
28
+ contains a JSON event; the stream ends with `event: done` or `event: error`.
29
+ - **NDJSON** — provider chat output (one JSON event per line) when the
30
+ backend forwards from a CLI's `--format json` mode.
31
+
32
+ ## Errors
33
+
34
+ Errors share a consistent envelope:
35
+
36
+ ```json
37
+ { "success": false, "error": { "code": "ROUTE_NOT_FOUND", "message": "..." } }
38
+ ```
39
+
40
+ contact:
41
+ name: Pixcode on GitHub
42
+ url: https://github.com/alicomert/pixcode
43
+ license:
44
+ name: AGPL-3.0-or-later
45
+ url: https://www.gnu.org/licenses/agpl-3.0.txt
46
+
47
+ servers:
48
+ - url: http://localhost:3001
49
+ description: Local development server
50
+ - url: '{protocol}://{host}'
51
+ description: Self-hosted Pixcode instance
52
+ variables:
53
+ protocol:
54
+ enum: [http, https]
55
+ default: http
56
+ host:
57
+ default: localhost:3001
58
+
59
+ tags:
60
+ - name: System
61
+ description: Health, version, and self-update.
62
+ - name: Authentication
63
+ description: Login, register, JWT lifecycle, API keys.
64
+ - name: Projects
65
+ description: Project CRUD and session enumeration.
66
+ - name: Files
67
+ description: Project filesystem read/write and directory traversal.
68
+ - name: Sessions
69
+ description: Conversation sessions across providers.
70
+ - name: Git
71
+ description: Git status, diffs, branches, commits, push/pull.
72
+ - name: Providers
73
+ description: Multi-CLI provider auth, install, sessions, configuration.
74
+ - name: Network
75
+ description: LAN discovery, UPnP, public tunnel.
76
+ - name: Settings
77
+ description: User-scoped preferences and notification channels.
78
+ - name: Search
79
+ description: Full-text search across conversations.
80
+ - name: MCP
81
+ description: Model Context Protocol servers and tooling.
82
+
83
+ components:
84
+ securitySchemes:
85
+ bearerAuth:
86
+ type: http
87
+ scheme: bearer
88
+ bearerFormat: JWT
89
+ description: |
90
+ `Authorization: Bearer <token>` — token may be a JWT (issued by
91
+ `/api/auth/login`) or an API key (prefix `ck_`, generated under
92
+ Settings → API). The middleware sniffs the prefix to decide.
93
+ apiKeyAuth:
94
+ type: apiKey
95
+ in: header
96
+ name: X-API-Key
97
+ description: |
98
+ Alternative for tools that can't set `Authorization`. Accepts the same
99
+ `ck_...` keys as `bearerAuth`. Either header works on every secured
100
+ endpoint — pick whichever your client supports.
101
+ cookieAuth:
102
+ type: apiKey
103
+ in: cookie
104
+ name: token
105
+ description: HTTP-only `token` cookie set by `/api/auth/login`.
106
+
107
+ schemas:
108
+ Error:
109
+ type: object
110
+ required: [success, error]
111
+ properties:
112
+ success: { type: boolean, enum: [false] }
113
+ error:
114
+ type: object
115
+ required: [code, message]
116
+ properties:
117
+ code: { type: string, example: ROUTE_NOT_FOUND }
118
+ message: { type: string }
119
+
120
+ Health:
121
+ type: object
122
+ required: [status, timestamp]
123
+ properties:
124
+ status: { type: string, enum: [ok] }
125
+ timestamp: { type: string, format: date-time }
126
+ version:
127
+ type: string
128
+ description: Server version (matches semver `MAJOR.MINOR.PATCH`).
129
+ example: 1.33.9
130
+ installMode:
131
+ type: string
132
+ enum: [git, npm-global, runtime-dir, docker]
133
+
134
+ User:
135
+ type: object
136
+ properties:
137
+ id: { type: integer }
138
+ username: { type: string }
139
+ email: { type: string, format: email, nullable: true }
140
+ created_at: { type: string, format: date-time }
141
+
142
+ AuthStatus:
143
+ type: object
144
+ properties:
145
+ success: { type: boolean }
146
+ isAuthenticated: { type: boolean }
147
+ needsSetup: { type: boolean, description: 'true if no users exist yet — UI shows registration screen.' }
148
+ user: { $ref: '#/components/schemas/User' }
149
+
150
+ LoginRequest:
151
+ type: object
152
+ required: [username, password]
153
+ properties:
154
+ username: { type: string }
155
+ password: { type: string, format: password }
156
+
157
+ LoginResponse:
158
+ type: object
159
+ properties:
160
+ success: { type: boolean }
161
+ token: { type: string, description: JWT (also set as `token` cookie). }
162
+ user: { $ref: '#/components/schemas/User' }
163
+
164
+ ApiKey:
165
+ type: object
166
+ properties:
167
+ id: { type: integer }
168
+ key_name: { type: string }
169
+ api_key: { type: string, description: 'Format: `ck_<32-hex>`. Returned in full on creation only.' }
170
+ created_at: { type: string, format: date-time }
171
+ last_used: { type: string, format: date-time, nullable: true }
172
+ is_active: { type: boolean }
173
+
174
+ Project:
175
+ type: object
176
+ properties:
177
+ name: { type: string }
178
+ displayName: { type: string }
179
+ path: { type: string }
180
+ fullPath: { type: string }
181
+ sessions:
182
+ type: array
183
+ items: { $ref: '#/components/schemas/SessionMeta' }
184
+
185
+ SessionMeta:
186
+ type: object
187
+ properties:
188
+ id: { type: string }
189
+ title: { type: string, nullable: true }
190
+ provider:
191
+ type: string
192
+ enum: [claude, cursor, codex, gemini, qwen, opencode]
193
+ lastActivity: { type: string, format: date-time }
194
+
195
+ GitStatus:
196
+ type: object
197
+ properties:
198
+ branch: { type: string }
199
+ ahead: { type: integer }
200
+ behind: { type: integer }
201
+ modified: { type: array, items: { type: string } }
202
+ added: { type: array, items: { type: string } }
203
+ deleted: { type: array, items: { type: string } }
204
+ untracked: { type: array, items: { type: string } }
205
+
206
+ ProviderInfo:
207
+ type: object
208
+ properties:
209
+ id:
210
+ type: string
211
+ enum: [claude, cursor, codex, gemini, qwen, opencode]
212
+ installed: { type: boolean }
213
+ authenticated: { type: boolean }
214
+ version: { type: string, nullable: true }
215
+ binaryPath: { type: string, nullable: true }
216
+
217
+ InstallJob:
218
+ type: object
219
+ properties:
220
+ jobId: { type: string }
221
+ provider: { type: string }
222
+ status: { type: string, enum: [pending, running, succeeded, failed, cancelled] }
223
+
224
+ security:
225
+ - bearerAuth: []
226
+ - apiKeyAuth: []
227
+ - cookieAuth: []
228
+
229
+ paths:
230
+ /health:
231
+ get:
232
+ tags: [System]
233
+ summary: Liveness + version probe
234
+ description: |
235
+ Public, unauthenticated. Returns server version and install mode so the
236
+ UI can detect updates and stale daemons. Used by:
237
+
238
+ - The version-upgrade modal — polls during/after `/api/system/update`
239
+ to detect when the new version is up.
240
+ - `useVersionCheck` — falls back to the bundle's baked
241
+ `__PIXCODE_UI_VERSION__` if `version` is missing or non-semver.
242
+ security: []
243
+ responses:
244
+ '200':
245
+ description: Server is up.
246
+ content:
247
+ application/json:
248
+ schema: { $ref: '#/components/schemas/Health' }
249
+
250
+ /api/system/update:
251
+ post:
252
+ tags: [System]
253
+ summary: Self-update (SSE)
254
+ description: |
255
+ Streams the update process via SSE. Behavior depends on
256
+ `installMode`:
257
+
258
+ - **runtime-dir** (desktop wrapper): downloads the latest npm tarball,
259
+ atomically swaps files, emits `done { selfRestarting: true }`, then
260
+ `process.exit(42)` so the wrapper respawns. **Don't `POST /restart`
261
+ when `selfRestarting` is true.**
262
+ - **git/npm-global**: runs `git pull && npm install` or
263
+ `npm install -g`. The supervising daemon may restart the server
264
+ mid-stream — clients should treat a bare close + `/health` version
265
+ bump as success.
266
+ responses:
267
+ '200':
268
+ description: 'SSE stream of `event: log`, `event: progress`, `event: done`, `event: error`.'
269
+ content:
270
+ text/event-stream:
271
+ schema: { type: string }
272
+
273
+ /api/system/restart:
274
+ post:
275
+ tags: [System]
276
+ summary: Restart the server process
277
+ description: |
278
+ Triggers a graceful exit; the supervising daemon (systemd/pm2/electron
279
+ wrapper) is expected to respawn. Don't call after a `selfRestarting`
280
+ update event — the wrapper has already been signalled.
281
+ responses:
282
+ '200':
283
+ description: Restart scheduled.
284
+
285
+ /api/auth/status:
286
+ get:
287
+ tags: [Authentication]
288
+ summary: Probe authentication state
289
+ description: Returns whether the current request carries a valid token, and whether the system has any users at all (`needsSetup`).
290
+ security: []
291
+ responses:
292
+ '200':
293
+ description: Auth status.
294
+ content:
295
+ application/json:
296
+ schema: { $ref: '#/components/schemas/AuthStatus' }
297
+
298
+ /api/auth/register:
299
+ post:
300
+ tags: [Authentication]
301
+ summary: Register the first user
302
+ description: Only allowed while `needsSetup` is true. Subsequent calls return 403.
303
+ security: []
304
+ requestBody:
305
+ required: true
306
+ content:
307
+ application/json:
308
+ schema: { $ref: '#/components/schemas/LoginRequest' }
309
+ responses:
310
+ '200':
311
+ description: Registered + auto-logged-in.
312
+ content:
313
+ application/json:
314
+ schema: { $ref: '#/components/schemas/LoginResponse' }
315
+ '403':
316
+ description: Setup already complete.
317
+ content:
318
+ application/json:
319
+ schema: { $ref: '#/components/schemas/Error' }
320
+
321
+ /api/auth/login:
322
+ post:
323
+ tags: [Authentication]
324
+ summary: Log in
325
+ security: []
326
+ requestBody:
327
+ required: true
328
+ content:
329
+ application/json:
330
+ schema: { $ref: '#/components/schemas/LoginRequest' }
331
+ responses:
332
+ '200':
333
+ description: Login OK; sets `token` cookie + returns JWT in body.
334
+ content:
335
+ application/json:
336
+ schema: { $ref: '#/components/schemas/LoginResponse' }
337
+ '401':
338
+ description: Invalid credentials.
339
+
340
+ /api/auth/user:
341
+ get:
342
+ tags: [Authentication]
343
+ summary: Current user
344
+ responses:
345
+ '200':
346
+ description: Authenticated user details.
347
+ content:
348
+ application/json:
349
+ schema:
350
+ type: object
351
+ properties:
352
+ user: { $ref: '#/components/schemas/User' }
353
+
354
+ /api/auth/logout:
355
+ post:
356
+ tags: [Authentication]
357
+ summary: Log out
358
+ description: Clears the `token` cookie. JWT remains valid until expiry — server-side blacklist isn't implemented.
359
+ responses:
360
+ '200': { description: OK }
361
+
362
+ /api/settings/api-keys:
363
+ get:
364
+ tags: [Authentication]
365
+ summary: List user API keys
366
+ responses:
367
+ '200':
368
+ description: List of keys (the `api_key` field is masked except on creation).
369
+ content:
370
+ application/json:
371
+ schema:
372
+ type: array
373
+ items: { $ref: '#/components/schemas/ApiKey' }
374
+ post:
375
+ tags: [Authentication]
376
+ summary: Create an API key
377
+ requestBody:
378
+ required: true
379
+ content:
380
+ application/json:
381
+ schema:
382
+ type: object
383
+ required: [name]
384
+ properties:
385
+ name: { type: string, description: 'Human-readable label.' }
386
+ responses:
387
+ '201':
388
+ description: Key created. The full `api_key` value is returned **once** — store it.
389
+ content:
390
+ application/json:
391
+ schema: { $ref: '#/components/schemas/ApiKey' }
392
+
393
+ /api/settings/api-keys/{keyId}:
394
+ delete:
395
+ tags: [Authentication]
396
+ summary: Delete an API key
397
+ parameters:
398
+ - name: keyId
399
+ in: path
400
+ required: true
401
+ schema: { type: integer }
402
+ responses:
403
+ '200': { description: Deleted. }
404
+
405
+ /api/settings/api-keys/{keyId}/toggle:
406
+ patch:
407
+ tags: [Authentication]
408
+ summary: Activate / deactivate an API key
409
+ parameters:
410
+ - name: keyId
411
+ in: path
412
+ required: true
413
+ schema: { type: integer }
414
+ requestBody:
415
+ required: true
416
+ content:
417
+ application/json:
418
+ schema:
419
+ type: object
420
+ properties:
421
+ is_active: { type: boolean }
422
+ responses:
423
+ '200': { description: Updated. }
424
+
425
+ /api/projects:
426
+ get:
427
+ tags: [Projects]
428
+ summary: List all projects
429
+ description: Walks `~/.claude/projects/*` plus any registered workspaces and returns a unified list. Sessions are flattened by provider.
430
+ responses:
431
+ '200':
432
+ description: Project list.
433
+ content:
434
+ application/json:
435
+ schema:
436
+ type: array
437
+ items: { $ref: '#/components/schemas/Project' }
438
+
439
+ /api/projects/quick-start:
440
+ post:
441
+ tags: [Projects]
442
+ summary: Open or create a project from an absolute path
443
+ requestBody:
444
+ required: true
445
+ content:
446
+ application/json:
447
+ schema:
448
+ type: object
449
+ required: [path]
450
+ properties:
451
+ path: { type: string, description: 'Absolute filesystem path.' }
452
+ responses:
453
+ '200':
454
+ description: Project record (created if it didn't exist).
455
+ content:
456
+ application/json:
457
+ schema: { $ref: '#/components/schemas/Project' }
458
+
459
+ /api/projects/{projectName}/rename:
460
+ put:
461
+ tags: [Projects]
462
+ summary: Rename project display
463
+ parameters:
464
+ - { name: projectName, in: path, required: true, schema: { type: string } }
465
+ requestBody:
466
+ required: true
467
+ content:
468
+ application/json:
469
+ schema:
470
+ type: object
471
+ properties:
472
+ displayName: { type: string }
473
+ responses:
474
+ '200': { description: Renamed. }
475
+
476
+ /api/projects/{projectName}:
477
+ delete:
478
+ tags: [Projects]
479
+ summary: Delete (deregister) a project
480
+ parameters:
481
+ - { name: projectName, in: path, required: true, schema: { type: string } }
482
+ responses:
483
+ '200': { description: Deleted. }
484
+
485
+ /api/projects/{projectName}/sessions:
486
+ get:
487
+ tags: [Sessions]
488
+ summary: List sessions for a project
489
+ parameters:
490
+ - { name: projectName, in: path, required: true, schema: { type: string } }
491
+ responses:
492
+ '200':
493
+ description: Sessions sorted by lastActivity desc.
494
+ content:
495
+ application/json:
496
+ schema:
497
+ type: array
498
+ items: { $ref: '#/components/schemas/SessionMeta' }
499
+
500
+ /api/projects/{projectName}/sessions/{sessionId}:
501
+ delete:
502
+ tags: [Sessions]
503
+ summary: Delete a session
504
+ parameters:
505
+ - { name: projectName, in: path, required: true, schema: { type: string } }
506
+ - { name: sessionId, in: path, required: true, schema: { type: string } }
507
+ responses:
508
+ '200': { description: Deleted. }
509
+
510
+ /api/sessions/{sessionId}/rename:
511
+ put:
512
+ tags: [Sessions]
513
+ summary: Rename a session
514
+ parameters:
515
+ - { name: sessionId, in: path, required: true, schema: { type: string } }
516
+ requestBody:
517
+ required: true
518
+ content:
519
+ application/json:
520
+ schema:
521
+ type: object
522
+ properties:
523
+ title: { type: string }
524
+ responses:
525
+ '200': { description: Renamed. }
526
+
527
+ /api/sessions/{sessionId}/messages:
528
+ get:
529
+ tags: [Sessions]
530
+ summary: Get session message history
531
+ description: |
532
+ Note: this is mounted at `/api/sessions`, NOT under `/api/projects` — the
533
+ `projectName` is supplied as a query parameter rather than a path
534
+ segment. The mount lives in `server/routes/messages.js`.
535
+ parameters:
536
+ - { name: sessionId, in: path, required: true, schema: { type: string } }
537
+ - { name: projectName, in: query, required: true, schema: { type: string }, description: Pixcode project the session belongs to. }
538
+ - { name: limit, in: query, required: false, schema: { type: integer, default: 100 } }
539
+ - { name: offset, in: query, required: false, schema: { type: integer, default: 0 } }
540
+ responses:
541
+ '200':
542
+ description: Normalized message stream.
543
+ content:
544
+ application/json:
545
+ schema:
546
+ type: object
547
+ properties:
548
+ messages: { type: array, items: { type: object, additionalProperties: true } }
549
+ total: { type: integer }
550
+ hasMore: { type: boolean }
551
+
552
+ /api/search/conversations:
553
+ get:
554
+ tags: [Search]
555
+ summary: Full-text search across all sessions
556
+ parameters:
557
+ - { name: q, in: query, required: true, schema: { type: string }, description: Search query. }
558
+ - { name: provider, in: query, required: false, schema: { type: string, enum: [claude, cursor, codex, gemini, qwen, opencode] } }
559
+ - { name: limit, in: query, required: false, schema: { type: integer, default: 50 } }
560
+ responses:
561
+ '200':
562
+ description: Ranked matches.
563
+ content:
564
+ application/json:
565
+ schema:
566
+ type: object
567
+ properties:
568
+ results: { type: array, items: { type: object, additionalProperties: true } }
569
+ total: { type: integer }
570
+
571
+ /api/projects/{projectName}/files:
572
+ get:
573
+ tags: [Files]
574
+ summary: List files in a project subtree
575
+ parameters:
576
+ - { name: projectName, in: path, required: true, schema: { type: string } }
577
+ - { name: path, in: query, required: false, schema: { type: string }, description: Subdirectory relative to project root. }
578
+ responses:
579
+ '200':
580
+ description: Directory listing.
581
+ content:
582
+ application/json:
583
+ schema:
584
+ type: array
585
+ items:
586
+ type: object
587
+ properties:
588
+ name: { type: string }
589
+ path: { type: string }
590
+ type: { type: string, enum: [file, directory] }
591
+ size: { type: integer, nullable: true }
592
+ delete:
593
+ tags: [Files]
594
+ summary: Delete a file or folder (path in body)
595
+ description: |
596
+ Same path as the listing endpoint above — discriminated by HTTP method.
597
+ The target's project-relative path goes in the body, NOT the query
598
+ string (so directory deletes can't be partially URL-encoded into a
599
+ broken state).
600
+ parameters:
601
+ - { name: projectName, in: path, required: true, schema: { type: string } }
602
+ requestBody:
603
+ required: true
604
+ content:
605
+ application/json:
606
+ schema:
607
+ type: object
608
+ required: [path]
609
+ properties:
610
+ path: { type: string, description: 'Project-relative path of the file or directory to delete.' }
611
+ type: { type: string, enum: [file, directory], description: 'Optional hint; server detects automatically if omitted.' }
612
+ responses:
613
+ '200': { description: Deleted. }
614
+
615
+ /api/projects/{projectName}/file:
616
+ get:
617
+ tags: [Files]
618
+ summary: Read a single file
619
+ parameters:
620
+ - { name: projectName, in: path, required: true, schema: { type: string } }
621
+ - { name: path, in: query, required: true, schema: { type: string } }
622
+ responses:
623
+ '200':
624
+ description: File contents (utf-8 text or base64 binary).
625
+ content:
626
+ application/json:
627
+ schema:
628
+ type: object
629
+ properties:
630
+ content: { type: string }
631
+ encoding: { type: string, enum: [utf-8, base64] }
632
+ put:
633
+ tags: [Files]
634
+ summary: Overwrite a file
635
+ parameters:
636
+ - { name: projectName, in: path, required: true, schema: { type: string } }
637
+ requestBody:
638
+ required: true
639
+ content:
640
+ application/json:
641
+ schema:
642
+ type: object
643
+ required: [path, content]
644
+ properties:
645
+ path: { type: string }
646
+ content: { type: string }
647
+ responses:
648
+ '200': { description: Saved. }
649
+
650
+ /api/projects/{projectName}/files/create:
651
+ post:
652
+ tags: [Files]
653
+ summary: Create a new file or folder
654
+ parameters:
655
+ - { name: projectName, in: path, required: true, schema: { type: string } }
656
+ requestBody:
657
+ required: true
658
+ content:
659
+ application/json:
660
+ schema:
661
+ type: object
662
+ required: [path, type]
663
+ properties:
664
+ path: { type: string }
665
+ type: { type: string, enum: [file, directory] }
666
+ content: { type: string, description: 'Initial content for files; ignored for directories.' }
667
+ responses:
668
+ '201': { description: Created. }
669
+
670
+ /api/projects/{projectName}/files/rename:
671
+ put:
672
+ tags: [Files]
673
+ summary: Rename / move a file
674
+ parameters:
675
+ - { name: projectName, in: path, required: true, schema: { type: string } }
676
+ requestBody:
677
+ required: true
678
+ content:
679
+ application/json:
680
+ schema:
681
+ type: object
682
+ required: [oldPath, newPath]
683
+ properties:
684
+ oldPath: { type: string }
685
+ newPath: { type: string }
686
+ responses:
687
+ '200': { description: Renamed. }
688
+
689
+
690
+ /api/browse-filesystem:
691
+ get:
692
+ tags: [Files]
693
+ summary: Native filesystem browser (outside project tree)
694
+ description: Used by Quick Start to let the user pick a folder anywhere on the host. Honours `~` expansion.
695
+ parameters:
696
+ - { name: path, in: query, required: false, schema: { type: string, default: '~' } }
697
+ responses:
698
+ '200':
699
+ description: Directory listing.
700
+ content:
701
+ application/json:
702
+ schema:
703
+ type: object
704
+ properties:
705
+ path: { type: string }
706
+ parent: { type: string, nullable: true }
707
+ entries:
708
+ type: array
709
+ items:
710
+ type: object
711
+ properties:
712
+ name: { type: string }
713
+ type: { type: string, enum: [file, directory] }
714
+
715
+ /api/git/status:
716
+ get:
717
+ tags: [Git]
718
+ summary: Working tree status
719
+ parameters:
720
+ - { name: project, in: query, required: true, schema: { type: string } }
721
+ responses:
722
+ '200':
723
+ description: Git status snapshot.
724
+ content:
725
+ application/json:
726
+ schema: { $ref: '#/components/schemas/GitStatus' }
727
+
728
+ /api/git/diff:
729
+ get:
730
+ tags: [Git]
731
+ summary: Unified diff for one file or the whole tree
732
+ parameters:
733
+ - { name: project, in: query, required: true, schema: { type: string } }
734
+ - { name: file, in: query, required: false, schema: { type: string } }
735
+ - { name: staged, in: query, required: false, schema: { type: boolean, default: false } }
736
+ responses:
737
+ '200':
738
+ description: Diff text.
739
+ content:
740
+ application/json:
741
+ schema:
742
+ type: object
743
+ properties:
744
+ diff: { type: string }
745
+
746
+ /api/git/branches:
747
+ get:
748
+ tags: [Git]
749
+ summary: List local + remote branches
750
+ parameters:
751
+ - { name: project, in: query, required: true, schema: { type: string } }
752
+ responses:
753
+ '200':
754
+ description: Branch list.
755
+ content:
756
+ application/json:
757
+ schema:
758
+ type: object
759
+ properties:
760
+ current: { type: string }
761
+ local: { type: array, items: { type: string } }
762
+ remote: { type: array, items: { type: string } }
763
+
764
+ /api/git/commit:
765
+ post:
766
+ tags: [Git]
767
+ summary: Create a commit
768
+ requestBody:
769
+ required: true
770
+ content:
771
+ application/json:
772
+ schema:
773
+ type: object
774
+ required: [project, message]
775
+ properties:
776
+ project: { type: string }
777
+ message: { type: string }
778
+ files: { type: array, items: { type: string }, description: 'When omitted, all staged changes are committed.' }
779
+ responses:
780
+ '200': { description: Commit created. }
781
+
782
+ /api/git/push:
783
+ post:
784
+ tags: [Git]
785
+ summary: Push to remote
786
+ requestBody:
787
+ required: true
788
+ content:
789
+ application/json:
790
+ schema:
791
+ type: object
792
+ required: [project]
793
+ properties:
794
+ project: { type: string }
795
+ remote: { type: string, default: origin }
796
+ branch: { type: string }
797
+ force: { type: boolean, default: false }
798
+ responses:
799
+ '200': { description: Pushed. }
800
+
801
+ /api/git/pull:
802
+ post:
803
+ tags: [Git]
804
+ summary: Pull from remote
805
+ requestBody:
806
+ required: true
807
+ content:
808
+ application/json:
809
+ schema:
810
+ type: object
811
+ required: [project]
812
+ properties:
813
+ project: { type: string }
814
+ responses:
815
+ '200': { description: Pulled. }
816
+
817
+ /api/git/checkout:
818
+ post:
819
+ tags: [Git]
820
+ summary: Switch branch
821
+ requestBody:
822
+ required: true
823
+ content:
824
+ application/json:
825
+ schema:
826
+ type: object
827
+ required: [project, branch]
828
+ properties:
829
+ project: { type: string }
830
+ branch: { type: string }
831
+ responses:
832
+ '200': { description: Switched. }
833
+
834
+ /api/providers/credentials:
835
+ get:
836
+ tags: [Providers]
837
+ summary: List stored credentials across providers
838
+ description: Returns a sanitised inventory of API keys / OAuth tokens Pixcode is currently holding for each provider. Real secret values are NOT returned — only presence + masked tail.
839
+ responses:
840
+ '200':
841
+ description: Credentials inventory.
842
+ content:
843
+ application/json:
844
+ schema:
845
+ type: object
846
+ additionalProperties:
847
+ type: object
848
+ properties:
849
+ hasCredential: { type: boolean }
850
+ masked: { type: string, nullable: true, example: '...beef' }
851
+
852
+ /api/providers/{provider}/auth/status:
853
+ get:
854
+ tags: [Providers]
855
+ summary: Auth status for a single provider
856
+ description: |
857
+ Returns whether the provider's CLI is installed, whether it has a
858
+ valid auth credential (api key / OAuth token), and the resolved
859
+ binary path the spawn layer will use.
860
+ parameters:
861
+ - { name: provider, in: path, required: true, schema: { type: string, enum: [claude, cursor, codex, gemini, qwen, opencode] } }
862
+ responses:
863
+ '200':
864
+ description: Auth status snapshot.
865
+ content:
866
+ application/json:
867
+ schema: { $ref: '#/components/schemas/ProviderInfo' }
868
+
869
+ /api/providers/{provider}/auth/api-key:
870
+ post:
871
+ tags: [Providers]
872
+ summary: Save / replace an API-key credential for a provider
873
+ parameters:
874
+ - { name: provider, in: path, required: true, schema: { type: string } }
875
+ requestBody:
876
+ required: true
877
+ content:
878
+ application/json:
879
+ schema:
880
+ type: object
881
+ required: [apiKey]
882
+ properties:
883
+ apiKey: { type: string }
884
+ baseUrl: { type: string, description: Optional override (e.g. self-hosted Anthropic compat endpoint). }
885
+ responses:
886
+ '200': { description: Credential saved. }
887
+
888
+ /api/providers/{provider}/oauth-paste:
889
+ post:
890
+ tags: [Providers]
891
+ summary: Paste an OAuth callback URL to complete login
892
+ description: |
893
+ For providers (Claude, Cursor) whose login flow opens a browser tab and
894
+ bounces back to a URL the user copies into Pixcode. The handler parses
895
+ the token and stores it as the active credential.
896
+ parameters:
897
+ - { name: provider, in: path, required: true, schema: { type: string } }
898
+ requestBody:
899
+ required: true
900
+ content:
901
+ application/json:
902
+ schema:
903
+ type: object
904
+ required: [url]
905
+ properties:
906
+ url: { type: string, format: uri }
907
+ responses:
908
+ '200': { description: Token extracted and stored. }
909
+ '400': { description: URL did not contain a recognisable token. }
910
+
911
+ /api/providers/{provider}/models:
912
+ get:
913
+ tags: [Providers]
914
+ summary: List models the provider's CLI exposes
915
+ description: |
916
+ Pixcode caches the model list per provider on first call (or when the
917
+ cache is busted via `DELETE …/models/cache`). Each entry is the
918
+ `provider/model` form OpenCode expects.
919
+ parameters:
920
+ - { name: provider, in: path, required: true, schema: { type: string } }
921
+ responses:
922
+ '200':
923
+ description: Model catalog.
924
+ content:
925
+ application/json:
926
+ schema:
927
+ type: array
928
+ items:
929
+ type: object
930
+ properties:
931
+ id: { type: string, example: 'opencode/gpt-5.2' }
932
+ name: { type: string }
933
+ contextWindow: { type: integer, nullable: true }
934
+
935
+ /api/providers/{provider}/models/cache:
936
+ delete:
937
+ tags: [Providers]
938
+ summary: Bust the cached model list for one provider
939
+ parameters:
940
+ - { name: provider, in: path, required: true, schema: { type: string } }
941
+ responses:
942
+ '200': { description: Cache cleared; next GET re-fetches. }
943
+
944
+ /api/providers/{provider}/install:
945
+ post:
946
+ tags: [Providers]
947
+ summary: Install / update a provider CLI in the Pixcode sandbox
948
+ description: |
949
+ Installs the provider's npm package into `~/.pixcode/cli-bin/` (no
950
+ `-g`, no sudo/UAC). Returns a `jobId` immediately; subscribe to
951
+ `/install/{jobId}/stream` for live logs.
952
+ parameters:
953
+ - { name: provider, in: path, required: true, schema: { type: string } }
954
+ responses:
955
+ '202':
956
+ description: Job accepted.
957
+ content:
958
+ application/json:
959
+ schema: { $ref: '#/components/schemas/InstallJob' }
960
+
961
+ /api/providers/{provider}/install/{jobId}/stream:
962
+ get:
963
+ tags: [Providers]
964
+ summary: Stream install job output (SSE)
965
+ description: Replays buffered output, then streams live until the job ends. Send `Last-Event-ID` to resume after disconnect.
966
+ parameters:
967
+ - { name: provider, in: path, required: true, schema: { type: string } }
968
+ - { name: jobId, in: path, required: true, schema: { type: string } }
969
+ responses:
970
+ '200':
971
+ description: 'SSE event stream — `event: log`, `event: status`, `event: done`, `event: error`.'
972
+ content:
973
+ text/event-stream:
974
+ schema: { type: string }
975
+
976
+ /api/providers/{provider}/install/{jobId}:
977
+ delete:
978
+ tags: [Providers]
979
+ summary: Cancel an in-flight install
980
+ parameters:
981
+ - { name: provider, in: path, required: true, schema: { type: string } }
982
+ - { name: jobId, in: path, required: true, schema: { type: string } }
983
+ responses:
984
+ '200': { description: Cancelled. }
985
+
986
+ /api/providers/{provider}/mcp/servers:
987
+ get:
988
+ tags: [MCP]
989
+ summary: List MCP servers configured for one provider
990
+ parameters:
991
+ - { name: provider, in: path, required: true, schema: { type: string } }
992
+ responses:
993
+ '200':
994
+ description: MCP server list (matches the provider's native config schema).
995
+ content:
996
+ application/json:
997
+ schema:
998
+ type: array
999
+ items: { type: object, additionalProperties: true }
1000
+ post:
1001
+ tags: [MCP]
1002
+ summary: Add or replace an MCP server entry
1003
+ parameters:
1004
+ - { name: provider, in: path, required: true, schema: { type: string } }
1005
+ requestBody:
1006
+ required: true
1007
+ content:
1008
+ application/json:
1009
+ schema:
1010
+ type: object
1011
+ required: [name, command]
1012
+ properties:
1013
+ name: { type: string }
1014
+ command: { type: string }
1015
+ args: { type: array, items: { type: string } }
1016
+ env: { type: object, additionalProperties: { type: string } }
1017
+ scope: { type: string, enum: [local, user], default: user }
1018
+ responses:
1019
+ '200': { description: Server saved. }
1020
+
1021
+ /api/providers/{provider}/mcp/servers/{name}:
1022
+ delete:
1023
+ tags: [MCP]
1024
+ summary: Remove an MCP server entry
1025
+ parameters:
1026
+ - { name: provider, in: path, required: true, schema: { type: string } }
1027
+ - { name: name, in: path, required: true, schema: { type: string } }
1028
+ responses:
1029
+ '200': { description: Removed. }
1030
+
1031
+ /api/providers/mcp/servers/global:
1032
+ post:
1033
+ tags: [MCP]
1034
+ summary: Add an MCP server to every provider that supports MCP
1035
+ description: Convenience endpoint that fans out the same server config to all MCP-capable providers (Claude, Cursor, Codex, OpenCode). Per-provider edits override.
1036
+ requestBody:
1037
+ required: true
1038
+ content:
1039
+ application/json:
1040
+ schema:
1041
+ type: object
1042
+ required: [name, command]
1043
+ properties:
1044
+ name: { type: string }
1045
+ command: { type: string }
1046
+ args: { type: array, items: { type: string } }
1047
+ env: { type: object, additionalProperties: { type: string } }
1048
+ responses:
1049
+ '200': { description: Server fanned out. }
1050
+
1051
+ /api/providers/{provider}/config-files:
1052
+ get:
1053
+ tags: [Providers]
1054
+ summary: List config files Pixcode knows about for a provider
1055
+ description: |
1056
+ Returns the catalog of editable config files for a provider — typically
1057
+ `settings.json`, `auth.json`, `opencode.json`, etc. Each entry has an
1058
+ opaque `fileId` used by `GET/PUT …/{fileId}`.
1059
+ parameters:
1060
+ - { name: provider, in: path, required: true, schema: { type: string } }
1061
+ responses:
1062
+ '200':
1063
+ description: Config file catalog.
1064
+ content:
1065
+ application/json:
1066
+ schema:
1067
+ type: array
1068
+ items:
1069
+ type: object
1070
+ properties:
1071
+ fileId: { type: string }
1072
+ path: { type: string }
1073
+ label: { type: string }
1074
+ exists: { type: boolean }
1075
+ size: { type: integer, nullable: true }
1076
+
1077
+ /api/providers/{provider}/config-files/{fileId}:
1078
+ get:
1079
+ tags: [Providers]
1080
+ summary: Read a provider config file's contents
1081
+ parameters:
1082
+ - { name: provider, in: path, required: true, schema: { type: string } }
1083
+ - { name: fileId, in: path, required: true, schema: { type: string } }
1084
+ responses:
1085
+ '200':
1086
+ description: File contents.
1087
+ content:
1088
+ application/json:
1089
+ schema:
1090
+ type: object
1091
+ properties:
1092
+ content: { type: string }
1093
+ path: { type: string }
1094
+ encoding: { type: string, enum: [utf-8] }
1095
+ put:
1096
+ tags: [Providers]
1097
+ summary: Overwrite a provider config file
1098
+ parameters:
1099
+ - { name: provider, in: path, required: true, schema: { type: string } }
1100
+ - { name: fileId, in: path, required: true, schema: { type: string } }
1101
+ requestBody:
1102
+ required: true
1103
+ content:
1104
+ application/json:
1105
+ schema:
1106
+ type: object
1107
+ required: [content]
1108
+ properties:
1109
+ content: { type: string }
1110
+ responses:
1111
+ '200': { description: Saved. }
1112
+ '400': { description: Content failed validation (e.g. malformed JSON). }
1113
+
1114
+ /api/network/endpoints:
1115
+ get:
1116
+ tags: [Network]
1117
+ summary: LAN endpoints for mobile QR pairing
1118
+ description: Returns every reachable IPv4 the host has bound, formatted as `http://<ip>:<port>`.
1119
+ responses:
1120
+ '200':
1121
+ description: LAN endpoint list.
1122
+ content:
1123
+ application/json:
1124
+ schema:
1125
+ type: object
1126
+ properties:
1127
+ endpoints: { type: array, items: { type: string } }
1128
+ port: { type: integer }
1129
+
1130
+ /api/network/external:
1131
+ get:
1132
+ tags: [Network]
1133
+ summary: Current external-access state (UPnP + tunnel)
1134
+ responses:
1135
+ '200':
1136
+ description: Snapshot.
1137
+ content:
1138
+ application/json:
1139
+ schema:
1140
+ type: object
1141
+ properties:
1142
+ upnp:
1143
+ type: object
1144
+ properties:
1145
+ active: { type: boolean }
1146
+ externalIp: { type: string, nullable: true }
1147
+ mappedPort: { type: integer, nullable: true }
1148
+ tunnel:
1149
+ type: object
1150
+ properties:
1151
+ active: { type: boolean }
1152
+ provider: { type: string, enum: [cloudflared, ngrok], nullable: true }
1153
+ url: { type: string, nullable: true }
1154
+
1155
+ /api/network/upnp:
1156
+ post:
1157
+ tags: [Network]
1158
+ summary: Open the LAN port on the router
1159
+ responses:
1160
+ '200': { description: Mapping created (or already present). }
1161
+ delete:
1162
+ tags: [Network]
1163
+ summary: Remove the UPnP mapping
1164
+ responses:
1165
+ '200': { description: Removed. }
1166
+
1167
+ /api/network/tunnel:
1168
+ post:
1169
+ tags: [Network]
1170
+ summary: Start a public tunnel (cloudflared / ngrok auto-detect)
1171
+ responses:
1172
+ '200':
1173
+ description: Tunnel started; URL returned.
1174
+ content:
1175
+ application/json:
1176
+ schema:
1177
+ type: object
1178
+ properties:
1179
+ url: { type: string, format: uri }
1180
+ provider: { type: string }
1181
+ delete:
1182
+ tags: [Network]
1183
+ summary: Stop the tunnel
1184
+ responses:
1185
+ '200': { description: Stopped. }
1186
+
1187
+ /api/settings/notification-preferences:
1188
+ get:
1189
+ tags: [Settings]
1190
+ summary: Get notification preferences
1191
+ responses:
1192
+ '200':
1193
+ description: Per-channel + per-event toggles.
1194
+ content:
1195
+ application/json:
1196
+ schema:
1197
+ type: object
1198
+ properties:
1199
+ preferences:
1200
+ type: object
1201
+ properties:
1202
+ channels:
1203
+ type: object
1204
+ properties:
1205
+ inApp: { type: boolean }
1206
+ webPush: { type: boolean }
1207
+ events:
1208
+ type: object
1209
+ properties:
1210
+ actionRequired: { type: boolean }
1211
+ stop: { type: boolean }
1212
+ error: { type: boolean }
1213
+ put:
1214
+ tags: [Settings]
1215
+ summary: Update notification preferences
1216
+ requestBody:
1217
+ required: true
1218
+ content:
1219
+ application/json:
1220
+ schema: { type: object }
1221
+ responses:
1222
+ '200': { description: Saved. }
1223
+
1224
+ /api/agent:
1225
+ post:
1226
+ tags: [Providers]
1227
+ summary: External REST entry point (API-key authenticated)
1228
+ description: |
1229
+ One-shot non-interactive run for automation/CI. Spawns the chosen
1230
+ provider, optionally cloning a GitHub repo first, optionally cutting
1231
+ a branch + opening a PR. **API key required** — accepts the same `ck_`
1232
+ keys as the rest of the API on `Authorization: Bearer`, `X-API-Key`,
1233
+ or `?apiKey=`.
1234
+
1235
+ Default response is an SSE stream of provider-native events; pass
1236
+ `stream: false` to buffer and return JSON.
1237
+ security:
1238
+ - bearerAuth: []
1239
+ - apiKeyAuth: []
1240
+ requestBody:
1241
+ required: true
1242
+ content:
1243
+ application/json:
1244
+ schema:
1245
+ type: object
1246
+ required: [message]
1247
+ properties:
1248
+ message:
1249
+ type: string
1250
+ description: Prompt text to send to the agent.
1251
+ provider:
1252
+ type: string
1253
+ enum: [claude, cursor, codex, gemini, qwen, opencode]
1254
+ default: claude
1255
+ model:
1256
+ type: string
1257
+ description: Provider-specific model id (e.g. `opencode/gpt-5.2`).
1258
+ projectPath:
1259
+ type: string
1260
+ description: Absolute path on the host. Required if `githubUrl` is omitted.
1261
+ githubUrl:
1262
+ type: string
1263
+ format: uri
1264
+ description: Clone this repo before running. Mutually inclusive with `projectPath` (used as the clone target if both are given).
1265
+ githubToken:
1266
+ type: string
1267
+ description: Optional override for the per-user GitHub PAT stored under Settings → Git.
1268
+ branchName:
1269
+ type: string
1270
+ description: 'Cutting `branchName` implies `createBranch: true`.'
1271
+ createBranch:
1272
+ type: boolean
1273
+ default: false
1274
+ createPR:
1275
+ type: boolean
1276
+ default: false
1277
+ sessionId:
1278
+ type: string
1279
+ description: Resume an existing session instead of starting a new one.
1280
+ stream:
1281
+ type: boolean
1282
+ default: true
1283
+ description: 'Set false to buffer to a single JSON response.'
1284
+ cleanup:
1285
+ type: boolean
1286
+ default: true
1287
+ description: When `githubUrl` was used, delete the temp clone after the run.
1288
+ responses:
1289
+ '200':
1290
+ description: |
1291
+ When `stream:true` — SSE stream of provider events. When `stream:false` — final JSON payload with `text` + `sessionId` + `usage`.
1292
+ content:
1293
+ application/json:
1294
+ schema:
1295
+ type: object
1296
+ properties:
1297
+ text: { type: string }
1298
+ sessionId: { type: string }
1299
+ usage:
1300
+ type: object
1301
+ properties:
1302
+ input: { type: integer }
1303
+ output: { type: integer }
1304
+ total: { type: integer }
1305
+ cost: { type: number }
1306
+ text/event-stream:
1307
+ schema: { type: string }
1308
+ '400':
1309
+ description: 'Missing required fields, unknown provider, or invalid GitHub URL.'
1310
+ '401':
1311
+ description: Missing or invalid API key.