@pixelbyte-software/pixcode 1.43.0 → 1.45.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.
@@ -0,0 +1,233 @@
1
+ import crypto from 'node:crypto';
2
+ import { appConfigDb } from '../database/db.js';
3
+ const CONFIG_KEY = 'production_agent_loop';
4
+ export const DESKTOP_RELEASE_ASSET_TYPES = [
5
+ { id: 'windows-x64', extension: '.exe', required: true },
6
+ { id: 'linux-x64', extension: '.AppImage', required: true },
7
+ { id: 'linux-deb', extension: '.deb', required: true },
8
+ { id: 'macos-x64', extension: 'x64.dmg', required: true },
9
+ { id: 'macos-arm64', extension: 'arm64.dmg', required: true },
10
+ ];
11
+ function nowIso() {
12
+ return new Date().toISOString();
13
+ }
14
+ function readStore() {
15
+ const raw = appConfigDb.get(CONFIG_KEY);
16
+ if (!raw) {
17
+ return {
18
+ issueRuns: [],
19
+ reviewQueue: [],
20
+ schedulerJobs: [],
21
+ checkpoints: [],
22
+ };
23
+ }
24
+ try {
25
+ const parsed = JSON.parse(raw);
26
+ return {
27
+ issueRuns: Array.isArray(parsed.issueRuns) ? parsed.issueRuns : [],
28
+ reviewQueue: Array.isArray(parsed.reviewQueue) ? parsed.reviewQueue : [],
29
+ schedulerJobs: Array.isArray(parsed.schedulerJobs) ? parsed.schedulerJobs : [],
30
+ checkpoints: Array.isArray(parsed.checkpoints) ? parsed.checkpoints : [],
31
+ };
32
+ }
33
+ catch {
34
+ return {
35
+ issueRuns: [],
36
+ reviewQueue: [],
37
+ schedulerJobs: [],
38
+ checkpoints: [],
39
+ };
40
+ }
41
+ }
42
+ function writeStore(store) {
43
+ appConfigDb.set(CONFIG_KEY, JSON.stringify(store));
44
+ }
45
+ function compact(text, max = 90) {
46
+ const value = String(text || '').replace(/\s+/g, ' ').trim();
47
+ return value.length > max ? value.slice(0, max).replace(/[-_\s]+$/g, '') : value;
48
+ }
49
+ function slugify(value) {
50
+ const slug = compact(value, 64)
51
+ .toLowerCase()
52
+ .replace(/[^a-z0-9]+/g, '-')
53
+ .replace(/^-+|-+$/g, '');
54
+ return slug || 'agent-task';
55
+ }
56
+ export function parseGitHubIssueRef(input = {}) {
57
+ const url = typeof input.issueUrl === 'string' ? input.issueUrl.trim() : '';
58
+ const directNumber = Number.parseInt(String(input.issueNumber || ''), 10);
59
+ const urlMatch = url.match(/github\.com\/([^/]+)\/([^/]+)\/issues\/(\d+)/i);
60
+ return {
61
+ owner: input.owner || urlMatch?.[1] || null,
62
+ repo: input.repo || urlMatch?.[2] || null,
63
+ issueNumber: Number.isFinite(directNumber) ? directNumber : Number.parseInt(urlMatch?.[3] || '0', 10) || null,
64
+ issueUrl: url || null,
65
+ };
66
+ }
67
+ export function createIssueToPrRun(input = {}, userId = null) {
68
+ const issue = parseGitHubIssueRef(input);
69
+ if (!issue.issueNumber && !input.title) {
70
+ throw new Error('Issue-to-PR run requires an issue number, issue URL, or title.');
71
+ }
72
+ const title = compact(input.title || `Issue ${issue.issueNumber}`);
73
+ const branchName = input.branchName || `pixcode/issue-${issue.issueNumber || 'manual'}-${slugify(title)}`;
74
+ const run = {
75
+ id: crypto.randomUUID(),
76
+ type: 'github_issue_to_pr',
77
+ status: 'queued',
78
+ createdAt: nowIso(),
79
+ updatedAt: nowIso(),
80
+ userId,
81
+ issue,
82
+ projectName: input.projectName || null,
83
+ projectPath: input.projectPath || null,
84
+ provider: input.provider || 'opencode',
85
+ model: input.model || null,
86
+ branchName,
87
+ baseBranch: input.baseBranch || 'main',
88
+ acceptanceCriteria: Array.isArray(input.acceptanceCriteria) ? input.acceptanceCriteria : [],
89
+ agentRequest: {
90
+ projectPath: input.projectPath || undefined,
91
+ githubUrl: input.githubUrl || undefined,
92
+ message: [
93
+ `Resolve ${issue.issueUrl || `GitHub issue #${issue.issueNumber || ''}`}`.trim(),
94
+ input.body || title,
95
+ 'Create a branch, run verification, and prepare a pull request summary.',
96
+ ].filter(Boolean).join('\n\n'),
97
+ provider: input.provider || 'opencode',
98
+ model: input.model || undefined,
99
+ branchName,
100
+ createBranch: true,
101
+ createPR: true,
102
+ },
103
+ };
104
+ const store = readStore();
105
+ store.issueRuns.unshift(run);
106
+ writeStore(store);
107
+ return run;
108
+ }
109
+ export function parseCiRepairSignals(logText = '') {
110
+ const text = String(logText || '');
111
+ const lines = text.split(/\r?\n/);
112
+ const failedCommands = [];
113
+ const files = new Set();
114
+ const errors = [];
115
+ for (const line of lines) {
116
+ const trimmed = line.trim();
117
+ if (!trimmed)
118
+ continue;
119
+ if (/npm ERR!|error TS\d+|FAIL|failed|exit code/i.test(trimmed)) {
120
+ errors.push(trimmed);
121
+ }
122
+ const command = trimmed.match(/(?:run|command|script)\s+[`'"]?([a-z0-9:_-]+)[`'"]?/i)?.[1];
123
+ if (command)
124
+ failedCommands.push(command);
125
+ const file = trimmed.match(/((?:src|server|shared|scripts|desktop)\/[^\s:)]+)/)?.[1];
126
+ if (file)
127
+ files.add(file);
128
+ }
129
+ return {
130
+ failedCommands: Array.from(new Set(failedCommands)),
131
+ files: Array.from(files),
132
+ errors: errors.slice(0, 25),
133
+ repairPrompt: [
134
+ 'CI-aware repair loop:',
135
+ '1. Reproduce the failing command locally.',
136
+ '2. Fix only the failing behavior.',
137
+ '3. Re-run the failed command plus related smoke checks.',
138
+ '',
139
+ errors.slice(0, 8).join('\n'),
140
+ ].join('\n').trim(),
141
+ };
142
+ }
143
+ export function createReviewQueueItem(input = {}, userId = null) {
144
+ const item = {
145
+ id: crypto.randomUUID(),
146
+ status: input.status || 'review_requested',
147
+ createdAt: nowIso(),
148
+ updatedAt: nowIso(),
149
+ userId,
150
+ projectName: input.projectName || null,
151
+ projectPath: input.projectPath || null,
152
+ title: compact(input.title || 'Review requested'),
153
+ changedFiles: Array.isArray(input.changedFiles) ? input.changedFiles : [],
154
+ notes: input.notes || '',
155
+ };
156
+ const store = readStore();
157
+ store.reviewQueue.unshift(item);
158
+ writeStore(store);
159
+ return item;
160
+ }
161
+ export function updateReviewQueueItem(itemId, patch = {}) {
162
+ const store = readStore();
163
+ let updated = null;
164
+ store.reviewQueue = store.reviewQueue.map((item) => {
165
+ if (item.id !== itemId)
166
+ return item;
167
+ updated = {
168
+ ...item,
169
+ ...patch,
170
+ id: item.id,
171
+ updatedAt: nowIso(),
172
+ };
173
+ return updated;
174
+ });
175
+ writeStore(store);
176
+ return updated;
177
+ }
178
+ export function scheduleBackgroundAgentJob(input = {}, userId = null) {
179
+ const job = {
180
+ id: crypto.randomUUID(),
181
+ status: 'scheduled',
182
+ createdAt: nowIso(),
183
+ updatedAt: nowIso(),
184
+ userId,
185
+ name: compact(input.name || 'Background agent job'),
186
+ mode: input.mode || 'manual',
187
+ cron: input.cron || null,
188
+ watch: input.watch || null,
189
+ projectName: input.projectName || null,
190
+ provider: input.provider || 'opencode',
191
+ prompt: input.prompt || '',
192
+ nextRunAt: input.nextRunAt || null,
193
+ };
194
+ const store = readStore();
195
+ store.schedulerJobs.unshift(job);
196
+ writeStore(store);
197
+ return job;
198
+ }
199
+ export function createWorkspaceCheckpoint(input = {}, userId = null) {
200
+ const checkpoint = {
201
+ id: crypto.randomUUID(),
202
+ protocol: 'pixcode.workspace-checkpoint.v1',
203
+ createdAt: nowIso(),
204
+ userId,
205
+ projectName: input.projectName || null,
206
+ projectPath: input.projectPath || null,
207
+ reason: compact(input.reason || 'manual checkpoint'),
208
+ gitHead: input.gitHead || null,
209
+ changedFiles: Array.isArray(input.changedFiles) ? input.changedFiles : [],
210
+ metadata: input.metadata && typeof input.metadata === 'object' ? input.metadata : {},
211
+ };
212
+ const store = readStore();
213
+ store.checkpoints.unshift(checkpoint);
214
+ writeStore(store);
215
+ return checkpoint;
216
+ }
217
+ export function getProductionAgentLoopState() {
218
+ return readStore();
219
+ }
220
+ export function evaluateDesktopReleaseAssetPolicy(assetNames = []) {
221
+ const names = Array.isArray(assetNames) ? assetNames.map(String) : [];
222
+ const required = DESKTOP_RELEASE_ASSET_TYPES.map((assetType) => ({
223
+ ...assetType,
224
+ present: names.some((name) => name.endsWith(assetType.extension)),
225
+ }));
226
+ return {
227
+ protocol: 'pixcode.desktop-release-assets.v1',
228
+ required,
229
+ complete: required.every((asset) => asset.present),
230
+ rule: 'Every GitHub release must include Windows exe, Linux AppImage, Linux deb, macOS x64 dmg, and macOS arm64 dmg assets. Assets may be carried forward and renamed when the app updates internally.',
231
+ };
232
+ }
233
+ //# sourceMappingURL=production-agent-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"production-agent-loop.js","sourceRoot":"","sources":["../../../server/services/production-agent-loop.js"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixelbyte-software/pixcode",
3
- "version": "1.43.0",
3
+ "version": "1.45.0",
4
4
  "description": "Self-hosted AI coding agent control room for Claude Code, Cursor CLI, OpenAI Codex, Gemini CLI, Qwen Code, and OpenCode with chat, files, shell, Git, orchestration, API keys, Telegram, MCP, plugins, themes, and desktop/server deployment.",
5
5
  "type": "module",
6
6
  "main": "dist-server/server/index.js",
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import assert from 'node:assert/strict';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+
7
+ const root = process.cwd();
8
+
9
+ function read(relativePath) {
10
+ return fs.readFileSync(path.join(root, relativePath), 'utf8');
11
+ }
12
+
13
+ const service = read('server/services/production-agent-loop.js');
14
+ assert.match(service, /createIssueToPrRun/, 'Production loop should create GitHub issue-to-PR runs.');
15
+ assert.match(service, /parseCiRepairSignals/, 'Production loop should parse CI repair signals.');
16
+ assert.match(service, /createReviewQueueItem/, 'Production loop should create code review queue items.');
17
+ assert.match(service, /scheduleBackgroundAgentJob/, 'Production loop should schedule background agent jobs.');
18
+ assert.match(service, /createWorkspaceCheckpoint/, 'Production loop should create workspace checkpoints.');
19
+ assert.match(service, /DESKTOP_RELEASE_ASSET_TYPES/, 'Production loop should define required desktop release assets.');
20
+
21
+ const routes = read('server/routes/production-agent-loop.js');
22
+ assert.match(routes, /\/github\/issue-to-pr/, 'Production loop routes should expose issue-to-PR kickoff.');
23
+ assert.match(routes, /\/ci\/repair-plan/, 'Production loop routes should expose CI repair planning.');
24
+ assert.match(routes, /\/review-queue/, 'Production loop routes should expose review queue APIs.');
25
+ assert.match(routes, /\/scheduler\/jobs/, 'Production loop routes should expose background scheduler jobs.');
26
+ assert.match(routes, /\/snapshots/, 'Production loop routes should expose workspace snapshots.');
27
+ assert.match(routes, /\/desktop-release\/assets-policy/, 'Production loop routes should expose desktop asset policy.');
28
+
29
+ const server = read('server/index.js');
30
+ assert.match(server, /productionAgentLoopRoutes/, 'Server should import production loop routes.');
31
+ assert.match(server, /\/api\/production-agent-loop/, 'Server should mount production loop routes.');
32
+
33
+ const diffAnchors = read('src/utils/diffAnchors.ts');
34
+ assert.match(diffAnchors, /firstChangedLine/, 'Frontend should compute first changed diff line.');
35
+ assert.match(diffAnchors, /buildDiffLineHref/, 'Frontend should build editor line anchors for changed files.');
36
+
37
+ const changesRail = read('src/components/main-content/view/subcomponents/ChangedFilesActivityRail.tsx');
38
+ assert.match(changesRail, /firstChangedLine/, 'Changed files rail should compute changed-line anchors.');
39
+ assert.match(changesRail, /lineHint/, 'Changed files rail should show changed-line hints.');
40
+
41
+ const docs = read('docs/production-agent-loop.md');
42
+ assert.match(docs, /Issue-to-PR/, 'Docs should explain issue-to-PR flow.');
43
+ assert.match(docs, /CI-aware repair/, 'Docs should explain CI-aware repair.');
44
+ assert.match(docs, /checkpoint/i, 'Docs should explain workspace checkpoints.');
45
+ assert.match(docs, /desktop asset/i, 'Docs should explain desktop asset requirements.');
46
+
47
+ console.log('v1.44 production loop smoke passed');
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+
3
+ import assert from 'node:assert/strict';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+
7
+ const root = process.cwd();
8
+
9
+ function read(relativePath) {
10
+ return fs.readFileSync(path.join(root, relativePath), 'utf8');
11
+ }
12
+
13
+ const service = read('server/services/platformization.js');
14
+ assert.match(service, /TEAM_ROLES/, 'Platformization should define enterprise RBAC roles.');
15
+ assert.match(service, /createTeamMember/, 'Platformization should create team members.');
16
+ assert.match(service, /sealSecret/, 'Platformization should seal scoped secrets.');
17
+ assert.match(service, /materializeScopedEnv/, 'Platformization should materialize scoped env previews.');
18
+ assert.match(service, /upsertMarketplacePlugin/, 'Platformization should manage plugin marketplace entries.');
19
+ assert.match(service, /createEvaluationSuite/, 'Platformization should create evaluation suites.');
20
+ assert.match(service, /createEvaluationRun/, 'Platformization should create evaluation runs.');
21
+ assert.match(service, /summarizeUsageEvents/, 'Platformization should summarize cost, token, and latency usage.');
22
+ assert.match(service, /createSecurityAuditRun/, 'Platformization should create security audit runs.');
23
+ assert.match(service, /agent_output_leak_detection/, 'Security audit mode should include output leak detection.');
24
+
25
+ const routes = read('server/routes/platformization.js');
26
+ assert.match(routes, /\/team\/members/, 'Platformization routes should expose team management.');
27
+ assert.match(routes, /\/secrets\/scoped-env/, 'Platformization routes should expose scoped env assembly.');
28
+ assert.match(routes, /\/marketplace\/plugins/, 'Platformization routes should expose marketplace management.');
29
+ assert.match(routes, /\/eval\/runs/, 'Platformization routes should expose evaluation runs.');
30
+ assert.match(routes, /\/usage\/summary/, 'Platformization routes should expose usage dashboards.');
31
+ assert.match(routes, /\/security\/audit-runs/, 'Platformization routes should expose security audit mode.');
32
+ assert.match(routes, /\/audit-log/, 'Platformization routes should expose audit logs.');
33
+
34
+ const server = read('server/index.js');
35
+ assert.match(server, /platformizationRoutes/, 'Server should import platformization routes.');
36
+ assert.match(server, /\/api\/platformization/, 'Server should mount platformization routes.');
37
+
38
+ const docs = read('docs/platformization.md');
39
+ assert.match(docs, /RBAC/i, 'Docs should explain RBAC/team mode.');
40
+ assert.match(docs, /Secret Vault/i, 'Docs should explain the secret vault.');
41
+ assert.match(docs, /MCP\/plugin Marketplace/i, 'Docs should explain marketplace management.');
42
+ assert.match(docs, /Evaluation Harness/i, 'Docs should explain evaluations.');
43
+ assert.match(docs, /Cost, Token, and Latency/i, 'Docs should explain usage dashboards.');
44
+ assert.match(docs, /Security\/audit Mode/i, 'Docs should explain security audit mode.');
45
+
46
+ console.log('v1.45 platformization smoke passed');
package/server/index.js CHANGED
@@ -79,6 +79,8 @@ import diagnosticsRoutes from './routes/diagnostics.js';
79
79
  import remoteRoutes from './routes/remote.js';
80
80
  import publicApiRoutes from './routes/public-api.js';
81
81
  import webhooksRoutes from './routes/webhooks.js';
82
+ import productionAgentLoopRoutes from './routes/production-agent-loop.js';
83
+ import platformizationRoutes from './routes/platformization.js';
82
84
  import liveViewRoutes, { createLiveViewPublicRouter } from './routes/live-view.js';
83
85
  import providerRoutes from './modules/providers/provider.routes.js';
84
86
  import {
@@ -410,6 +412,12 @@ app.use('/api/public', authenticateToken, publicApiRoutes);
410
412
  // Outbound webhook automation (protected)
411
413
  app.use('/api/webhooks', authenticateToken, webhooksRoutes);
412
414
 
415
+ // Production agent loop APIs (protected)
416
+ app.use('/api/production-agent-loop', authenticateToken, productionAgentLoopRoutes);
417
+
418
+ // Platform control plane APIs (protected)
419
+ app.use('/api/platformization', authenticateToken, platformizationRoutes);
420
+
413
421
  // Project Live View (protected control API + public share proxy)
414
422
  app.use('/api/live-view', authenticateToken, liveViewRoutes);
415
423
 
@@ -0,0 +1,129 @@
1
+ import express from 'express';
2
+
3
+ import {
4
+ createEvaluationRun,
5
+ createEvaluationSuite,
6
+ createSecret,
7
+ createSecurityAuditRun,
8
+ createTeamMember,
9
+ getPlatformizationState,
10
+ listSecrets,
11
+ materializeScopedEnv,
12
+ recordUsageEvent,
13
+ summarizeUsageEvents,
14
+ updateMarketplacePluginHealth,
15
+ updateTeamMember,
16
+ upsertMarketplacePlugin,
17
+ } from '../services/platformization.js';
18
+
19
+ const router = express.Router();
20
+
21
+ function userId(req) {
22
+ return req.user?.id ?? req.user?.userId ?? null;
23
+ }
24
+
25
+ function handleError(res, error) {
26
+ res.status(400).json({ success: false, error: error.message });
27
+ }
28
+
29
+ router.get('/', (_req, res) => {
30
+ res.json({ success: true, state: getPlatformizationState() });
31
+ });
32
+
33
+ router.get('/roles', (_req, res) => {
34
+ const state = getPlatformizationState();
35
+ res.json({ success: true, roles: state.roles });
36
+ });
37
+
38
+ router.get('/team/members', (_req, res) => {
39
+ res.json({ success: true, members: getPlatformizationState().teamMembers });
40
+ });
41
+
42
+ router.post('/team/members', (req, res) => {
43
+ try {
44
+ res.status(201).json({ success: true, member: createTeamMember(req.body || {}, userId(req)) });
45
+ } catch (error) {
46
+ handleError(res, error);
47
+ }
48
+ });
49
+
50
+ router.patch('/team/members/:id', (req, res) => {
51
+ const member = updateTeamMember(req.params.id, req.body || {}, userId(req));
52
+ if (!member) {
53
+ res.status(404).json({ success: false, error: 'Team member not found.' });
54
+ return;
55
+ }
56
+ res.json({ success: true, member });
57
+ });
58
+
59
+ router.get('/secrets', (_req, res) => {
60
+ res.json({ success: true, secrets: listSecrets() });
61
+ });
62
+
63
+ router.post('/secrets', (req, res) => {
64
+ try {
65
+ res.status(201).json({ success: true, secret: createSecret(req.body || {}, userId(req)) });
66
+ } catch (error) {
67
+ handleError(res, error);
68
+ }
69
+ });
70
+
71
+ router.post('/secrets/scoped-env', (req, res) => {
72
+ try {
73
+ res.json({ success: true, scopedEnv: materializeScopedEnv(req.body || {}, { reveal: req.body?.reveal === true }) });
74
+ } catch (error) {
75
+ handleError(res, error);
76
+ }
77
+ });
78
+
79
+ router.get('/marketplace/plugins', (_req, res) => {
80
+ res.json({ success: true, plugins: getPlatformizationState().marketplacePlugins });
81
+ });
82
+
83
+ router.post('/marketplace/plugins', (req, res) => {
84
+ res.status(201).json({ success: true, plugin: upsertMarketplacePlugin(req.body || {}, userId(req)) });
85
+ });
86
+
87
+ router.post('/marketplace/plugins/:id/health', (req, res) => {
88
+ const plugin = updateMarketplacePluginHealth(req.params.id, req.body || {}, userId(req));
89
+ if (!plugin) {
90
+ res.status(404).json({ success: false, error: 'Marketplace plugin not found.' });
91
+ return;
92
+ }
93
+ res.json({ success: true, plugin });
94
+ });
95
+
96
+ router.get('/eval/suites', (_req, res) => {
97
+ const state = getPlatformizationState();
98
+ res.json({ success: true, suites: state.evaluationSuites, runs: state.evaluationRuns });
99
+ });
100
+
101
+ router.post('/eval/suites', (req, res) => {
102
+ res.status(201).json({ success: true, suite: createEvaluationSuite(req.body || {}, userId(req)) });
103
+ });
104
+
105
+ router.post('/eval/runs', (req, res) => {
106
+ res.status(201).json({ success: true, run: createEvaluationRun(req.body || {}, userId(req)) });
107
+ });
108
+
109
+ router.get('/usage/summary', (_req, res) => {
110
+ res.json({ success: true, summary: summarizeUsageEvents() });
111
+ });
112
+
113
+ router.post('/usage/events', (req, res) => {
114
+ res.status(201).json({ success: true, event: recordUsageEvent(req.body || {}, userId(req)) });
115
+ });
116
+
117
+ router.get('/security/audit-runs', (_req, res) => {
118
+ res.json({ success: true, runs: getPlatformizationState().securityAuditRuns });
119
+ });
120
+
121
+ router.post('/security/audit-runs', (req, res) => {
122
+ res.status(201).json({ success: true, run: createSecurityAuditRun(req.body || {}, userId(req)) });
123
+ });
124
+
125
+ router.get('/audit-log', (_req, res) => {
126
+ res.json({ success: true, auditLog: getPlatformizationState().auditLog });
127
+ });
128
+
129
+ export default router;
@@ -0,0 +1,90 @@
1
+ import express from 'express';
2
+
3
+ import {
4
+ createIssueToPrRun,
5
+ createReviewQueueItem,
6
+ createWorkspaceCheckpoint,
7
+ evaluateDesktopReleaseAssetPolicy,
8
+ getProductionAgentLoopState,
9
+ parseCiRepairSignals,
10
+ scheduleBackgroundAgentJob,
11
+ updateReviewQueueItem,
12
+ } from '../services/production-agent-loop.js';
13
+
14
+ const router = express.Router();
15
+
16
+ function userId(req) {
17
+ return req.user?.id ?? req.user?.userId ?? null;
18
+ }
19
+
20
+ router.get('/', (_req, res) => {
21
+ res.json({ success: true, state: getProductionAgentLoopState() });
22
+ });
23
+
24
+ router.post('/github/issue-to-pr', (req, res) => {
25
+ try {
26
+ const run = createIssueToPrRun(req.body || {}, userId(req));
27
+ res.status(202).json({ success: true, run });
28
+ } catch (error) {
29
+ res.status(400).json({ success: false, error: error.message });
30
+ }
31
+ });
32
+
33
+ router.post('/ci/repair-plan', (req, res) => {
34
+ res.json({
35
+ success: true,
36
+ repairPlan: parseCiRepairSignals(req.body?.log || req.body?.output || ''),
37
+ });
38
+ });
39
+
40
+ router.get('/review-queue', (_req, res) => {
41
+ res.json({ success: true, reviewQueue: getProductionAgentLoopState().reviewQueue });
42
+ });
43
+
44
+ router.post('/review-queue', (req, res) => {
45
+ const item = createReviewQueueItem(req.body || {}, userId(req));
46
+ res.status(201).json({ success: true, item });
47
+ });
48
+
49
+ router.patch('/review-queue/:id', (req, res) => {
50
+ const item = updateReviewQueueItem(req.params.id, req.body || {});
51
+ if (!item) {
52
+ res.status(404).json({ success: false, error: 'Review queue item not found.' });
53
+ return;
54
+ }
55
+ res.json({ success: true, item });
56
+ });
57
+
58
+ router.get('/scheduler/jobs', (_req, res) => {
59
+ res.json({ success: true, jobs: getProductionAgentLoopState().schedulerJobs });
60
+ });
61
+
62
+ router.post('/scheduler/jobs', (req, res) => {
63
+ const job = scheduleBackgroundAgentJob(req.body || {}, userId(req));
64
+ res.status(201).json({ success: true, job });
65
+ });
66
+
67
+ router.get('/snapshots', (_req, res) => {
68
+ res.json({ success: true, checkpoints: getProductionAgentLoopState().checkpoints });
69
+ });
70
+
71
+ router.post('/snapshots', (req, res) => {
72
+ const checkpoint = createWorkspaceCheckpoint(req.body || {}, userId(req));
73
+ res.status(201).json({ success: true, checkpoint });
74
+ });
75
+
76
+ router.post('/desktop-release/assets-policy', (req, res) => {
77
+ res.json({
78
+ success: true,
79
+ policy: evaluateDesktopReleaseAssetPolicy(req.body?.assets || req.body?.assetNames || []),
80
+ });
81
+ });
82
+
83
+ router.get('/desktop-release/assets-policy', (_req, res) => {
84
+ res.json({
85
+ success: true,
86
+ policy: evaluateDesktopReleaseAssetPolicy([]),
87
+ });
88
+ });
89
+
90
+ export default router;