pi-harness-runtime 1.1.429 → 1.1.430

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 (126) hide show
  1. package/package.json +1 -1
  2. package/packages/a2a-adapter/package.json +1 -1
  3. package/packages/agent-policy/package.json +1 -1
  4. package/packages/architecture-generator/package.json +1 -1
  5. package/packages/auth/package.json +1 -1
  6. package/packages/autonomous-refactor/package.json +1 -1
  7. package/packages/autonomous-runtime/package.json +1 -1
  8. package/packages/cache-strategy/package.json +1 -1
  9. package/packages/capability-registry/package.json +1 -1
  10. package/packages/checkpoint/package.json +1 -1
  11. package/packages/cli-plugin-sdk/package.json +1 -1
  12. package/packages/clipboard/package.json +1 -1
  13. package/packages/clipboard-plugin/package.json +1 -1
  14. package/packages/code-generation/package.json +1 -1
  15. package/packages/code-review/package.json +1 -1
  16. package/packages/codex-adapter/package.json +1 -1
  17. package/packages/codex-watcher/package.json +1 -1
  18. package/packages/config-capture/package.json +1 -1
  19. package/packages/connection-runtime/package.json +1 -1
  20. package/packages/context-compiler/package.json +1 -1
  21. package/packages/context-discovery/package.json +1 -1
  22. package/packages/context-manager/package.json +1 -1
  23. package/packages/contracts/package.json +1 -1
  24. package/packages/cookie-sanitizer/package.json +1 -1
  25. package/packages/cost-optimizer/package.json +1 -1
  26. package/packages/dependency-analyzer/package.json +1 -1
  27. package/packages/django-plugin/package.json +1 -1
  28. package/packages/doc-generator/package.json +1 -1
  29. package/packages/durable-review/package.json +1 -1
  30. package/packages/e2e-tester/package.json +1 -1
  31. package/packages/evaluation-engine/package.json +1 -1
  32. package/packages/evaluation-runner/package.json +1 -1
  33. package/packages/event-bus/package.json +1 -1
  34. package/packages/event-sink/package.json +1 -1
  35. package/packages/event-store/package.json +1 -1
  36. package/packages/experience-replay/package.json +1 -1
  37. package/packages/feedback-collector/package.json +1 -1
  38. package/packages/file-copy-helper/package.json +1 -1
  39. package/packages/framework-detector/package.json +1 -1
  40. package/packages/framework-plugin-sdk/package.json +1 -1
  41. package/packages/frappe-plugin/package.json +1 -1
  42. package/packages/generic-web-plugin/package.json +1 -1
  43. package/packages/health-monitor/package.json +1 -1
  44. package/packages/intent-analyzer/package.json +1 -1
  45. package/packages/jev-judge/package.json +1 -1
  46. package/packages/knowledge-graph/package.json +1 -1
  47. package/packages/knowledge-retrieval/package.json +1 -1
  48. package/packages/laravel-plugin/package.json +1 -1
  49. package/packages/learning-engine/package.json +1 -1
  50. package/packages/logging/package.json +1 -1
  51. package/packages/mcp-adapter/package.json +1 -1
  52. package/packages/memory-engine/package.json +1 -1
  53. package/packages/milestone-manager/package.json +1 -1
  54. package/packages/model-registry/package.json +1 -1
  55. package/packages/nextjs-plugin/package.json +1 -1
  56. package/packages/notification/package.json +1 -1
  57. package/packages/observability/package.json +1 -1
  58. package/packages/okf-indexer/package.json +1 -1
  59. package/packages/paperclip-integration/package.json +1 -1
  60. package/packages/performance-optimizer/package.json +1 -1
  61. package/packages/permission-mode/package.json +1 -1
  62. package/packages/privilege-broker/package.json +1 -1
  63. package/packages/project-analyzer/package.json +1 -1
  64. package/packages/project-bootstrap/package.json +1 -1
  65. package/packages/projection-engine/package.json +1 -1
  66. package/packages/prompt-compiler/package.json +1 -1
  67. package/packages/prompt-versioning/package.json +1 -1
  68. package/packages/provider-adapter-sdk/package.json +1 -1
  69. package/packages/provider-router/package.json +1 -1
  70. package/packages/provider-selector/package.json +1 -1
  71. package/packages/providers/package.json +1 -1
  72. package/packages/qdrant-skills/package.json +1 -1
  73. package/packages/quota-manager/package.json +1 -1
  74. package/packages/rate-limiter/package.json +1 -1
  75. package/packages/react-vite-plugin/package.json +1 -1
  76. package/packages/relay/package.json +1 -1
  77. package/packages/release-manager/package.json +1 -1
  78. package/packages/requirement-compiler/package.json +1 -1
  79. package/packages/runtime/dist/runtime-api.d.ts +61 -6
  80. package/packages/runtime/dist/runtime-api.d.ts.map +1 -1
  81. package/packages/runtime/dist/runtime-api.js +83 -30
  82. package/packages/runtime/dist/runtime-api.js.map +1 -1
  83. package/packages/runtime/package.json +1 -1
  84. package/packages/runtime/src/runtime-api.ts +168 -52
  85. package/packages/runtime/test/runtime-api.test.ts +169 -0
  86. package/packages/scheduler/package.json +1 -1
  87. package/packages/scheduler-adapter/package.json +1 -1
  88. package/packages/session/package.json +1 -1
  89. package/packages/session-api/package.json +1 -1
  90. package/packages/session-export/package.json +1 -1
  91. package/packages/shared-context/package.json +1 -1
  92. package/packages/skill-mcp-client/package.json +1 -1
  93. package/packages/skill-registry/package.json +1 -1
  94. package/packages/skills/package.json +1 -1
  95. package/packages/sprint-planner/package.json +1 -1
  96. package/packages/subscription-engine/package.json +1 -1
  97. package/packages/task-compiler/package.json +1 -1
  98. package/packages/tencentdb-memory/package.json +1 -1
  99. package/packages/tencentdb-sync/package.json +1 -1
  100. package/packages/test-data-generator/package.json +1 -1
  101. package/packages/test-generator/package.json +1 -1
  102. package/packages/todo-bd-sync/package.json +1 -1
  103. package/packages/token-estimation/package.json +1 -1
  104. package/packages/token-optimizer/package.json +1 -1
  105. package/packages/tooling-preference/package.json +1 -1
  106. package/packages/trajectory/package.json +1 -1
  107. package/packages/tui/package.json +1 -1
  108. package/packages/two-phase-review/package.json +1 -1
  109. package/packages/types/package.json +1 -1
  110. package/packages/voice-api/package.json +1 -1
  111. package/packages/webmcp-tester/package.json +1 -1
  112. package/packages/wiki/package.json +1 -1
  113. package/packages/workflow-events/package.json +1 -1
  114. package/packages/workspace-scanner/package.json +1 -1
  115. package/packages/worktree/package.json +1 -1
  116. package/packages/write-review/package.json +1 -1
  117. package/scripts/typecheck-packages.mjs +1 -1
  118. package/harness/task-graph.d.ts +0 -51
  119. package/packages/runtime/command-executor.ts +0 -523
  120. package/packages/runtime/index.ts +0 -50
  121. package/packages/runtime/local-runtime-agent.ts +0 -480
  122. package/packages/runtime/policy-engine.ts +0 -813
  123. package/packages/runtime/runtime-api.ts +0 -665
  124. package/packages/runtime/workspace-manager.ts +0 -591
  125. package/packages/types/src/ai-providers.js +0 -66
  126. package/packages/types/src/runtime-types.js +0 -3
@@ -1,665 +0,0 @@
1
- /**
2
- * Runtime API — RFC-0027
3
- *
4
- * HTTP/RPC API for runtime control, job management, and status queries.
5
- *
6
- * Endpoints:
7
- * - GET /health - Health check
8
- * - GET /jobs - List jobs
9
- * - POST /jobs - Create job
10
- * - GET /jobs/:id - Get job status
11
- * - POST /jobs/:id/start - Start job
12
- * - POST /jobs/:id/pause - Pause job
13
- * - POST /jobs/:id/resume - Resume job
14
- * - POST /jobs/:id/cancel - Cancel job
15
- * - GET /jobs/:id/tasks - List tasks
16
- * - GET /jobs/:id/tasks/:tid - Get task status
17
- * - POST /jobs/:id/tasks/:tid - Update task
18
- * - GET /quota - Get quota status
19
- * - GET /config - Get runtime config
20
- * - PUT /config - Update runtime config
21
- * - WS /ws - WebSocket for real-time events
22
- */
23
-
24
- import { createServer, type Server, type IncomingMessage } from "node:http";
25
- import { WebSocketServer, WebSocket } from "ws";
26
- import { URL } from "node:url";
27
- import { EventEmitter } from "node:events";
28
- import type { JobStateMachine } from "../../harness/job-state-machine.js";
29
- import type { TaskGraphManager } from "../../harness/task-graph.js";
30
- import type { CheckpointManager } from "../../harness/job-state-machine.js";
31
-
32
- export interface RuntimeApiConfig {
33
- /** Port to listen on (default: 3849) */
34
- port?: number;
35
- /** Host to bind to (default: localhost) */
36
- host?: string;
37
- /** API key for authentication (optional) */
38
- apiKey?: string;
39
- /** Enable CORS (default: false) */
40
- cors?: boolean;
41
- /** Allowed origins for CORS */
42
- corsOrigins?: string[];
43
- /** Enable WebSocket (default: true) */
44
- enableWebSocket?: boolean;
45
- /** Request timeout in ms (default: 30000) */
46
- requestTimeoutMs?: number;
47
- }
48
-
49
- export interface ApiResponse<T = unknown> {
50
- success: boolean;
51
- data?: T;
52
- error?: string;
53
- timestamp: string;
54
- requestId: string;
55
- }
56
-
57
- export interface JobSummary {
58
- jobId: string;
59
- requirement: string;
60
- status: string;
61
- createdAt: string;
62
- updatedAt: string;
63
- progress: {
64
- total: number;
65
- done: number;
66
- running: number;
67
- failed: number;
68
- };
69
- }
70
-
71
- export interface TaskSummary {
72
- taskId: string;
73
- title: string;
74
- status: string;
75
- assignedAgent?: string;
76
- dependencies: string[];
77
- createdAt: string;
78
- updatedAt: string;
79
- }
80
-
81
- export interface RuntimeStatus {
82
- running: boolean;
83
- version: string;
84
- uptime: number;
85
- jobs: {
86
- total: number;
87
- active: number;
88
- paused: number;
89
- completed: number;
90
- };
91
- }
92
-
93
- const DEFAULT_CONFIG: Required<
94
- Omit<RuntimeApiConfig, "apiKey" | "corsOrigins">
95
- > = {
96
- port: 3849,
97
- host: "localhost",
98
- cors: false,
99
- enableWebSocket: true,
100
- requestTimeoutMs: 30000,
101
- };
102
-
103
- export class RuntimeApi extends EventEmitter {
104
- private readonly config: RuntimeApiConfig;
105
- private server: Server | null = null;
106
- private wsServer: WebSocketServer | null = null;
107
- private wsClients: Set<WebSocket> = new Set();
108
- private isRunning = false;
109
- private startTime: number = 0;
110
- private jobMachines: Map<string, JobStateMachine> = new Map();
111
- private taskGraphs: Map<string, TaskGraphManager> = new Map();
112
- private checkpoints: Map<string, CheckpointManager> = new Map();
113
- private requestCounter = 0;
114
-
115
- constructor(config: RuntimeApiConfig = {}) {
116
- super();
117
- this.config = { ...DEFAULT_CONFIG, ...config };
118
- this.startTime = Date.now();
119
- }
120
-
121
- /**
122
- * Start the Runtime API server
123
- */
124
- async start(): Promise<void> {
125
- if (this.isRunning) {
126
- throw new Error("Runtime API is already running");
127
- }
128
-
129
- return new Promise((resolve, reject) => {
130
- this.server = createServer(async (req, res) => {
131
- const requestId = this.generateRequestId();
132
-
133
- try {
134
- // Set timeout
135
- req.setTimeout(this.config.requestTimeoutMs ?? 30000);
136
-
137
- // Handle CORS
138
- if (this.config.cors) {
139
- this.setCorsHeaders(req, res);
140
- }
141
-
142
- // Authenticate
143
- if (!this.authenticate(req, res)) {
144
- return;
145
- }
146
-
147
- // Handle request
148
- await this.handleRequest(req, res, requestId);
149
- } catch (error) {
150
- console.error(`[RuntimeApi] Request error: ${error}`);
151
- this.sendJson(
152
- res,
153
- 500,
154
- { error: "Internal server error" },
155
- requestId,
156
- );
157
- }
158
- });
159
-
160
- this.server.on("error", (error) => {
161
- reject(error);
162
- });
163
-
164
- this.server.listen(this.config.port, this.config.host, () => {
165
- this.isRunning = true;
166
- this.startTime = Date.now();
167
- console.log(
168
- `[RuntimeApi] HTTP server listening on ${this.config.host}:${this.config.port}`,
169
- );
170
-
171
- // Start WebSocket server
172
- if (this.config.enableWebSocket) {
173
- this.startWebSocket();
174
- }
175
-
176
- resolve();
177
- });
178
- });
179
- }
180
-
181
- /**
182
- * Stop the Runtime API server
183
- */
184
- async stop(): Promise<void> {
185
- return new Promise((resolve) => {
186
- // Close WebSocket server
187
- if (this.wsServer) {
188
- this.wsServer.close();
189
- this.wsServer = null;
190
- }
191
-
192
- // Close all WebSocket clients
193
- for (const client of Array.from(this.wsClients)) {
194
- client.close();
195
- }
196
- this.wsClients.clear();
197
-
198
- // Close HTTP server
199
- if (this.server) {
200
- this.server.close(() => {
201
- this.isRunning = false;
202
- this.server = null;
203
- console.log("[RuntimeApi] Stopped");
204
- resolve();
205
- });
206
- } else {
207
- resolve();
208
- }
209
- });
210
- }
211
-
212
- /**
213
- * Register a job machine
214
- */
215
- registerJob(jobId: string, machine: JobStateMachine): void {
216
- this.jobMachines.set(jobId, machine);
217
- }
218
-
219
- /**
220
- * Register a task graph
221
- */
222
- registerTaskGraph(jobId: string, graph: TaskGraphManager): void {
223
- this.taskGraphs.set(jobId, graph);
224
- }
225
-
226
- /**
227
- * Register a checkpoint manager
228
- */
229
- registerCheckpointManager(jobId: string, manager: CheckpointManager): void {
230
- this.checkpoints.set(jobId, manager);
231
- }
232
-
233
- /**
234
- * Emit an event to all WebSocket clients
235
- */
236
- broadcast(event: string, data: unknown): void {
237
- const message = JSON.stringify({
238
- event,
239
- data,
240
- timestamp: new Date().toISOString(),
241
- });
242
-
243
- for (const client of Array.from(this.wsClients)) {
244
- if (client.readyState === WebSocket.OPEN) {
245
- client.send(message);
246
- }
247
- }
248
- }
249
-
250
- // ─── Private Methods ────────────────────────────────────────────────
251
-
252
- private startWebSocket(): void {
253
- this.wsServer = new WebSocketServer({
254
- noServer: true,
255
- });
256
-
257
- this.server?.on("upgrade", (request, socket, head) => {
258
- const url = new URL(request.url ?? "/", `http://${request.headers.host}`);
259
- if (url.pathname === "/ws") {
260
- this.wsServer?.handleUpgrade(request, socket, head, (ws) => {
261
- this.wsServer?.emit("connection", ws, request);
262
- });
263
- }
264
- });
265
-
266
- this.wsServer.on("connection", (ws: WebSocket, _req: IncomingMessage) => {
267
- this.wsClients.add(ws);
268
- console.log("[RuntimeApi] WebSocket client connected");
269
-
270
- ws.on("close", () => {
271
- this.wsClients.delete(ws);
272
- console.log("[RuntimeApi] WebSocket client disconnected");
273
- });
274
-
275
- ws.on("error", (error: Error) => {
276
- console.error("[RuntimeApi] WebSocket error:", error);
277
- this.wsClients.delete(ws);
278
- });
279
-
280
- // Send welcome message
281
- ws.send(
282
- JSON.stringify({
283
- event: "connected",
284
- data: { message: "Connected to Runtime API" },
285
- timestamp: new Date().toISOString(),
286
- }),
287
- );
288
- });
289
- }
290
-
291
- private authenticate(
292
- req: IncomingMessage,
293
- res: import("node:http").ServerResponse,
294
- ): boolean {
295
- if (!this.config.apiKey) {
296
- return true;
297
- }
298
-
299
- const apiKey = req.headers["x-api-key"];
300
- if (apiKey !== this.config.apiKey) {
301
- this.sendJson(
302
- res,
303
- 401,
304
- { error: "Unauthorized" },
305
- this.generateRequestId(),
306
- );
307
- return false;
308
- }
309
-
310
- return true;
311
- }
312
-
313
- private setCorsHeaders(
314
- req: IncomingMessage,
315
- res: import("node:http").ServerResponse,
316
- ): void {
317
- const origin = req.headers.origin;
318
- const allowedOrigins = this.config.corsOrigins ?? ["*"];
319
-
320
- if (
321
- allowedOrigins.includes("*") ||
322
- (origin && allowedOrigins.includes(origin))
323
- ) {
324
- res.setHeader("Access-Control-Allow-Origin", origin ?? "*");
325
- res.setHeader(
326
- "Access-Control-Allow-Methods",
327
- "GET, POST, PUT, DELETE, OPTIONS",
328
- );
329
- res.setHeader(
330
- "Access-Control-Allow-Headers",
331
- "Content-Type, Authorization, X-Api-Key",
332
- );
333
- }
334
- }
335
-
336
- private async handleRequest(
337
- req: IncomingMessage,
338
- res: import("node:http").ServerResponse,
339
- requestId: string,
340
- ): Promise<void> {
341
- const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
342
- const path = url.pathname;
343
- const method = req.method ?? "GET";
344
-
345
- // Handle OPTIONS for CORS
346
- if (method === "OPTIONS") {
347
- res.writeHead(204);
348
- res.end();
349
- return;
350
- }
351
-
352
- // Route requests
353
- if (path === "/health" && method === "GET") {
354
- this.handleHealth(res, requestId);
355
- } else if (path === "/status" && method === "GET") {
356
- this.handleStatus(res, requestId);
357
- } else if (path === "/jobs" && method === "GET") {
358
- await this.handleListJobs(res, requestId);
359
- } else if (path === "/jobs" && method === "POST") {
360
- await this.handleCreateJob(req, res, requestId);
361
- } else if (path === "/quota" && method === "GET") {
362
- await this.handleGetQuota(res, requestId);
363
- } else if (path === "/config" && method === "GET") {
364
- this.handleGetConfig(res, requestId);
365
- } else if (path.match(/^\/jobs\/([^/]+)\/?$/)) {
366
- const jobId = path.match(/^\/jobs\/([^/]+)\/?$/)?.[1];
367
- if (jobId) {
368
- await this.handleJob(jobId, method, url, req, res, requestId);
369
- }
370
- } else {
371
- this.sendJson(res, 404, { error: "Not found" }, requestId);
372
- }
373
- }
374
-
375
- private handleHealth(
376
- res: import("node:http").ServerResponse,
377
- requestId: string,
378
- ): void {
379
- this.sendJson(res, 200, { status: "healthy" }, requestId);
380
- }
381
-
382
- private handleStatus(
383
- res: import("node:http").ServerResponse,
384
- requestId: string,
385
- ): void {
386
- const status: RuntimeStatus = {
387
- running: this.isRunning,
388
- version: "1.0.0",
389
- uptime: Date.now() - this.startTime,
390
- jobs: {
391
- total: this.jobMachines.size,
392
- active: 0,
393
- paused: 0,
394
- completed: 0,
395
- },
396
- };
397
-
398
- // Count job statuses
399
- for (const machine of Array.from(this.jobMachines.values())) {
400
- const summary = machine.getStatusSummary();
401
- if (summary) {
402
- if (summary.status === "running") status.jobs.active++;
403
- else if (
404
- summary.status === "paused" ||
405
- summary.status === "paused_quota"
406
- )
407
- status.jobs.paused++;
408
- else if (summary.isTerminal) status.jobs.completed++;
409
- }
410
- }
411
-
412
- this.sendJson(res, 200, status, requestId);
413
- }
414
-
415
- private async handleListJobs(
416
- res: import("node:http").ServerResponse,
417
- requestId: string,
418
- ): Promise<void> {
419
- const jobs: JobSummary[] = [];
420
-
421
- for (const entry of Array.from(this.jobMachines.entries())) {
422
- const [jobId, machine] = entry;
423
- const checkpoint = await machine.getCheckpoint();
424
- const graph = this.taskGraphs.get(jobId);
425
-
426
- // Get progress from task graph
427
- let progress = { total: 0, done: 0, running: 0, failed: 0 };
428
- if (graph) {
429
- try {
430
- const tasks = graph.getAllTasks();
431
- progress = {
432
- total: tasks.length,
433
- done: tasks.filter((t) => t.status === "done").length,
434
- running: tasks.filter((t) => t.status === "running").length,
435
- failed: tasks.filter((t) => t.status === "failed").length,
436
- };
437
- } catch {
438
- // Fallback to default
439
- }
440
- }
441
-
442
- jobs.push({
443
- jobId,
444
- requirement: checkpoint?.requirement ?? "Unknown",
445
- status: machine.getStatusSummary()?.status ?? "unknown",
446
- createdAt: checkpoint?.createdAt ?? new Date().toISOString(),
447
- updatedAt: checkpoint?.updatedAt ?? new Date().toISOString(),
448
- progress,
449
- });
450
- }
451
-
452
- this.sendJson(res, 200, { jobs }, requestId);
453
- }
454
-
455
- private async handleCreateJob(
456
- _req: IncomingMessage,
457
- res: import("node:http").ServerResponse,
458
- requestId: string,
459
- ): Promise<void> {
460
- let body = "";
461
-
462
- for await (const chunk of _req) {
463
- body += chunk;
464
- }
465
-
466
- let data: { requirement?: string };
467
- try {
468
- data = JSON.parse(body);
469
- } catch {
470
- this.sendJson(res, 400, { error: "Invalid JSON" }, requestId);
471
- return;
472
- }
473
-
474
- if (!data.requirement) {
475
- this.sendJson(
476
- res,
477
- 400,
478
- { error: "Missing 'requirement' field" },
479
- requestId,
480
- );
481
- return;
482
- }
483
-
484
- // In a real implementation, this would create a new job
485
- // For now, just acknowledge the request
486
- this.sendJson(
487
- res,
488
- 201,
489
- {
490
- message: "Job creation not implemented - use harness CLI",
491
- requirement: data.requirement,
492
- },
493
- requestId,
494
- );
495
- }
496
-
497
- private async handleJob(
498
- jobId: string,
499
- method: string,
500
- url: URL,
501
- req: IncomingMessage,
502
- res: import("node:http").ServerResponse,
503
- requestId: string,
504
- ): Promise<void> {
505
- const machine = this.jobMachines.get(jobId);
506
-
507
- if (!machine) {
508
- this.sendJson(res, 404, { error: "Job not found" }, requestId);
509
- return;
510
- }
511
-
512
- // Get job details
513
- if (method === "GET") {
514
- const checkpoint = await machine.getCheckpoint();
515
- const graph = this.taskGraphs.get(jobId);
516
- // Get progress from task graph
517
- let progress = { total: 0, done: 0, running: 0, failed: 0 };
518
- if (graph) {
519
- try {
520
- const tasks = graph.getAllTasks();
521
- progress = {
522
- total: tasks.length,
523
- done: tasks.filter((t) => t.status === "done").length,
524
- running: tasks.filter((t) => t.status === "running").length,
525
- failed: tasks.filter((t) => t.status === "failed").length,
526
- };
527
- } catch {
528
- // Fallback to default
529
- }
530
- }
531
-
532
- this.sendJson(
533
- res,
534
- 200,
535
- {
536
- jobId,
537
- requirement: checkpoint?.requirement ?? "Unknown",
538
- status: machine.getStatusSummary()?.status ?? "unknown",
539
- canResume: machine.getStatusSummary()?.canResume ?? false,
540
- isTerminal: machine.getStatusSummary()?.isTerminal ?? false,
541
- progress,
542
- checkpoint,
543
- },
544
- requestId,
545
- );
546
- return;
547
- }
548
-
549
- // Job actions
550
- const action = url.searchParams.get("action");
551
-
552
- switch (method) {
553
- case "POST":
554
- if (action === "start") {
555
- await machine.resumeJob(jobId);
556
- this.broadcast("job.started", { jobId });
557
- this.sendJson(res, 200, { message: "Job started", jobId }, requestId);
558
- } else if (action === "pause") {
559
- const result = await machine.transition("paused");
560
- this.broadcast("job.paused", { jobId });
561
- this.sendJson(
562
- res,
563
- 200,
564
- { message: "Job paused", jobId, result },
565
- requestId,
566
- );
567
- } else if (action === "resume") {
568
- await machine.resumeJob(jobId);
569
- this.broadcast("job.resumed", { jobId });
570
- this.sendJson(res, 200, { message: "Job resumed", jobId }, requestId);
571
- } else if (action === "cancel") {
572
- const result = await machine.transition("cancelled");
573
- this.broadcast("job.cancelled", { jobId });
574
- this.sendJson(
575
- res,
576
- 200,
577
- { message: "Job cancelled", jobId, result },
578
- requestId,
579
- );
580
- } else {
581
- this.sendJson(res, 400, { error: "Unknown action" }, requestId);
582
- }
583
- break;
584
-
585
- default:
586
- this.sendJson(res, 405, { error: "Method not allowed" }, requestId);
587
- }
588
- }
589
-
590
- private async handleGetQuota(
591
- res: import("node:http").ServerResponse,
592
- requestId: string,
593
- ): Promise<void> {
594
- // In a real implementation, this would query the quota manager
595
- this.sendJson(
596
- res,
597
- 200,
598
- {
599
- message: "Quota endpoint - integrate with packages/quota-manager",
600
- usage: {
601
- h5_used_pct: 0,
602
- weekly_used_pct: 0,
603
- },
604
- },
605
- requestId,
606
- );
607
- }
608
-
609
- private handleGetConfig(
610
- res: import("node:http").ServerResponse,
611
- requestId: string,
612
- ): void {
613
- // Don't expose sensitive config
614
- this.sendJson(
615
- res,
616
- 200,
617
- {
618
- port: this.config.port,
619
- host: this.config.host,
620
- cors: this.config.cors,
621
- enableWebSocket: this.config.enableWebSocket,
622
- requestTimeoutMs: this.config.requestTimeoutMs,
623
- // Expose version without exposing apiKey
624
- version: "1.0.0",
625
- },
626
- requestId,
627
- );
628
- }
629
-
630
- private sendJson(
631
- res: import("node:http").ServerResponse,
632
- status: number,
633
- data: unknown,
634
- requestId: string,
635
- ): void {
636
- const response: ApiResponse = {
637
- success: status >= 200 && status < 300,
638
- ...(status >= 200 && status < 300
639
- ? { data }
640
- : { error: (data as { error?: string }).error }),
641
- timestamp: new Date().toISOString(),
642
- requestId,
643
- };
644
-
645
- res.writeHead(status, { "Content-Type": "application/json" });
646
- res.end(JSON.stringify(response));
647
- }
648
-
649
- private generateRequestId(): string {
650
- this.requestCounter++;
651
- return `req_${Date.now().toString(36)}_${this.requestCounter.toString(36)}`;
652
- }
653
- }
654
-
655
- /**
656
- * Create RuntimeApi with default config for harness runtime
657
- */
658
- export function createHarnessRuntimeApi(): RuntimeApi {
659
- return new RuntimeApi({
660
- port: 3849,
661
- host: "localhost",
662
- enableWebSocket: true,
663
- requestTimeoutMs: 30000,
664
- });
665
- }