@andrewting19/oracle 0.9.1 → 0.9.2

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.
@@ -38,8 +38,10 @@ export async function createRemoteServer(options = {}, deps = {}) {
38
38
  const color = process.stdout.isTTY
39
39
  ? (formatter, msg) => formatter(msg)
40
40
  : (_formatter, msg) => msg;
41
- // Single-flight guard: remote Chrome can only host one run at a time, so we serialize requests.
42
- let busy = false;
41
+ // Concurrency control: allow parallel browser runs (each gets its own Chrome tab).
42
+ // Set ORACLE_SERVE_MAX_CONCURRENT to limit simultaneous runs (default: unlimited).
43
+ let activeRuns = 0;
44
+ const maxConcurrent = parseInt(process.env.ORACLE_SERVE_MAX_CONCURRENT ?? "0", 10) || Infinity;
43
45
  if (!process.listenerCount("unhandledRejection")) {
44
46
  process.on("unhandledRejection", (reason) => {
45
47
  logger(`Unhandled promise rejection in remote server: ${reason instanceof Error ? reason.message : String(reason)}`);
@@ -67,6 +69,8 @@ export async function createRemoteServer(options = {}, deps = {}) {
67
69
  ok: true,
68
70
  version: getCliVersion(),
69
71
  uptimeSeconds: Math.round((Date.now() - startedAt) / 1000),
72
+ activeRuns,
73
+ maxConcurrent: maxConcurrent === Infinity ? null : maxConcurrent,
70
74
  }));
71
75
  return;
72
76
  }
@@ -84,15 +88,15 @@ export async function createRemoteServer(options = {}, deps = {}) {
84
88
  res.end(JSON.stringify({ error: "unauthorized" }));
85
89
  return;
86
90
  }
87
- if (busy) {
91
+ if (activeRuns >= maxConcurrent) {
88
92
  if (verbose) {
89
- logger(`[serve] Busy: rejecting new run from ${formatSocket(req)} while another run is active`);
93
+ logger(`[serve] At capacity (${activeRuns}/${maxConcurrent}): rejecting run from ${formatSocket(req)}`);
90
94
  }
91
- res.writeHead(409, { "Content-Type": "application/json" });
92
- res.end(JSON.stringify({ error: "busy" }));
95
+ res.writeHead(429, { "Content-Type": "application/json" });
96
+ res.end(JSON.stringify({ error: "at_capacity", activeRuns, maxConcurrent }));
93
97
  return;
94
98
  }
95
- busy = true;
99
+ activeRuns++;
96
100
  const runStartedAt = Date.now();
97
101
  let payload = null;
98
102
  try {
@@ -103,7 +107,7 @@ export async function createRemoteServer(options = {}, deps = {}) {
103
107
  }
104
108
  }
105
109
  catch {
106
- busy = false;
110
+ activeRuns--;
107
111
  res.writeHead(400, { "Content-Type": "application/json" });
108
112
  res.end(JSON.stringify({ error: "invalid_request" }));
109
113
  return;
@@ -173,7 +177,7 @@ export async function createRemoteServer(options = {}, deps = {}) {
173
177
  logger(`[serve] Run ${runId} failed after ${Date.now() - runStartedAt}ms: ${message}`);
174
178
  }
175
179
  finally {
176
- busy = false;
180
+ activeRuns--;
177
181
  res.end();
178
182
  try {
179
183
  await rm(runDir, { recursive: true, force: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrewting19/oracle",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "CLI wrapper around OpenAI Responses API with GPT-5.4 Pro, GPT-5.4, GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/steipete/oracle#readme",