@auroraflow/code 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auroraflow/code",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "description": "Aurora launcher and sidecar for official Codex and Claude Code clients.",
6
6
  "repository": {
@@ -211,10 +211,11 @@ function tomlString(value) {
211
211
  }
212
212
 
213
213
  async function spawnAndExit(command, args, env) {
214
- const child = spawn(command, args, {
214
+ const spec = windowsCommandSpec(command, args);
215
+ const child = spawn(spec.command, spec.args, {
215
216
  stdio: "inherit",
216
217
  env,
217
- shell: process.platform === "win32" && command.endsWith(".cmd")
218
+ shell: spec.shell
218
219
  });
219
220
  child.on("error", error => {
220
221
  console.error(`Failed to start ${command}: ${error.message}`);
@@ -225,11 +226,12 @@ async function spawnAndExit(command, args, env) {
225
226
 
226
227
  function spawnAndWait(command, args, env) {
227
228
  return new Promise((resolve, reject) => {
228
- const child = spawn(command, args, {
229
+ const spec = windowsCommandSpec(command, args);
230
+ const child = spawn(spec.command, spec.args, {
229
231
  cwd: packageRoot,
230
232
  stdio: "inherit",
231
233
  env,
232
- shell: process.platform === "win32"
234
+ shell: spec.shell
233
235
  });
234
236
  child.on("error", reject);
235
237
  child.on("exit", code => {
@@ -240,8 +242,34 @@ function spawnAndWait(command, args, env) {
240
242
  }
241
243
 
242
244
  function spawnSyncCompat(command, args) {
243
- return spawnSync(command, args, {
245
+ const spec = windowsCommandSpec(command, args);
246
+ return spawnSync(spec.command, spec.args, {
244
247
  encoding: "utf8",
245
- shell: process.platform === "win32" && command.endsWith(".cmd")
248
+ shell: spec.shell
246
249
  });
247
250
  }
251
+
252
+ function windowsCommandSpec(command, args) {
253
+ if (process.platform !== "win32" || !command.toLowerCase().endsWith(".cmd")) {
254
+ return { command, args, shell: false };
255
+ }
256
+ // Windows npm global bins are .cmd shims, commonly under "C:\Program Files".
257
+ // Running that path through shell=true without quoting makes cmd.exe split at
258
+ // the space and try to execute "C:\Program". Invoke cmd.exe explicitly with
259
+ // one quoted command line so paths with spaces survive.
260
+ return {
261
+ command: process.env.ComSpec || "cmd.exe",
262
+ args: ["/d", "/s", "/c", windowsCmdLine([command, ...args])],
263
+ shell: false
264
+ };
265
+ }
266
+
267
+ function windowsCmdLine(parts) {
268
+ return parts.map(windowsCmdQuote).join(" ");
269
+ }
270
+
271
+ function windowsCmdQuote(value) {
272
+ const text = String(value);
273
+ if (/^[A-Za-z0-9_./:=+-]+$/.test(text)) return text;
274
+ return `"${text.replace(/"/g, '""')}"`;
275
+ }
@@ -15,6 +15,20 @@ const CODEX_MODEL_CAPABILITIES_PATH = join(CODEX_HOME, "model_capabilities.json"
15
15
  const GATEWAY_HEADER_TIMEOUT_MS = 60000;
16
16
  const GATEWAY_IDLE_TIMEOUT_MS = 120000;
17
17
 
18
+ // Keep the sidecar->gateway TLS connection warm. The expensive part of a cold
19
+ // request is the ~1s TLS handshake to the gateway edge; undici pools the
20
+ // connection but drops it after ~4s idle, so every think-pause re-handshakes
21
+ // ("时快时慢"). A lightweight heartbeat under that idle window keeps one pooled
22
+ // connection alive so real requests reuse a warm socket. It only runs within a
23
+ // window after real activity, so an idle/abandoned sidecar stops chattering.
24
+ const GATEWAY_KEEPALIVE_INTERVAL_MS = 3000;
25
+ const GATEWAY_KEEPWARM_WINDOW_MS = 10 * 60 * 1000;
26
+ let lastActivityAt = 0;
27
+
28
+ function markActivity() {
29
+ lastActivityAt = Date.now();
30
+ }
31
+
18
32
  // ensureSidecarIdentity generates the local token once and persists it so the
19
33
  // OS service, the launcher, and the running client all agree on a stable
20
34
  // token/port across restarts and node upgrades. Random-per-run tokens broke
@@ -79,9 +93,35 @@ export async function startSidecar(options = {}) {
79
93
  server.listen(port, AURORA_LOCALHOST, async () => {
80
94
  await writeSidecarInfo({ port, token, baseURL: `http://${AURORA_LOCALHOST}:${port}` });
81
95
  console.error(`Aurora sidecar listening on http://${AURORA_LOCALHOST}:${port}`);
96
+ // Open the warm window and pre-warm the gateway connection so the very
97
+ // first client request reuses an established TLS socket.
98
+ markActivity();
99
+ startGatewayKeepAlive();
82
100
  });
83
101
  }
84
102
 
103
+ function startGatewayKeepAlive() {
104
+ const beat = async () => {
105
+ if (Date.now() - lastActivityAt > GATEWAY_KEEPWARM_WINDOW_MS) return;
106
+ try {
107
+ const state = await readState();
108
+ const gatewayURL = trimSlash(state.gatewayURL ?? "https://auroramos.com");
109
+ // Same origin as proxyRuntime → same undici pool, so this keeps the edge
110
+ // TLS handshake amortized for real requests. The body MUST be drained:
111
+ // undici destroys (does not pool) a connection whose response body was
112
+ // left unconsumed, which silently defeats the warm-up. Result ignored.
113
+ const response = await fetch(gatewayURL, { signal: AbortSignal.timeout(GATEWAY_HEADER_TIMEOUT_MS) });
114
+ await response.text();
115
+ } catch {
116
+ // Best effort; a failed heartbeat just means the next real request pays
117
+ // a cold handshake, which is the pre-fix behaviour.
118
+ }
119
+ };
120
+ const timer = setInterval(beat, GATEWAY_KEEPALIVE_INTERVAL_MS);
121
+ timer.unref();
122
+ beat();
123
+ }
124
+
85
125
  async function pingLocalSidecar(port, token) {
86
126
  try {
87
127
  const response = await fetch(`http://${AURORA_LOCALHOST}:${port}/aurora/status`, {
@@ -134,6 +174,7 @@ async function handle(request, response, token) {
134
174
  }
135
175
 
136
176
  async function proxyModels(response, incomingURL) {
177
+ markActivity();
137
178
  const state = await readState();
138
179
  const gatewayURL = trimSlash(state.gatewayURL ?? "https://auroramos.com");
139
180
  const clientVersion = incomingURL.searchParams.get("client_version");
@@ -159,6 +200,7 @@ async function proxyModels(response, incomingURL) {
159
200
  }
160
201
 
161
202
  async function proxyRuntime(request, response, path) {
203
+ markActivity();
162
204
  const state = await readState();
163
205
  const body = await readBody(request);
164
206
  const capabilitiesByAlias = await readCodexModelCapabilities();