@opencrater/sdk 0.8.45 → 0.8.49

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.

Potentially problematic release.


This version of @opencrater/sdk might be problematic. Click here for more details.

package/dist/hook.js CHANGED
@@ -600,7 +600,7 @@ function topicsForRequest(stored, now) {
600
600
  }
601
601
 
602
602
  // src/hook.ts
603
- var TOTAL_BUDGET_MS = process.platform === "win32" ? 6e3 : 3200;
603
+ var TOTAL_BUDGET_MS = process.platform === "win32" ? 8e3 : 3200;
604
604
  var STDIN_BUDGET_MS = 150;
605
605
  var IMPRESSION_BUDGET_MS = 250;
606
606
  var SERVE_TIMEOUT_CEILING_MS = 2800;
@@ -769,7 +769,7 @@ async function renderPlain(card, state, media, impressionId, campaignId) {
769
769
  };
770
770
  child.on("exit", finish);
771
771
  child.on("error", finish);
772
- setTimeout(finish, 5500);
772
+ setTimeout(finish, 7500);
773
773
  });
774
774
  } else {
775
775
  child.unref();
package/dist/paint.js CHANGED
@@ -308,22 +308,20 @@ var WIN_PUMP_PS = [
308
308
  "$h=OpenConout",
309
309
  "if($h -eq [IntPtr]-1){ L 'pump: NO CONSOLE after attach walk'; exit 1 }",
310
310
  "$fb=Csbi $h; if($fb){ L ('pump: FINAL via '+$how+' bufH='+$fb.sz.Y+' winRows='+($fb.win.B-$fb.win.T+1)+' curY='+$fb.cur.Y) } else { L ('pump: FINAL via '+$how) }",
311
+ // Report the REAL visible-console size back to the painter (over stdout, a
312
+ // pipe — the card itself goes to CONOUT$ via WriteFile, so this channel is
313
+ // free). The painter blocks on this line, then anchors the card to the true
314
+ // right edge. win.R/L/B/T are the on-screen window rect (not the scrollback
315
+ // buffer), so this is exactly what `mode con` SHOULD have returned.
316
+ "if($fb){ $cols=$fb.win.R-$fb.win.L+1; $rows=$fb.win.B-$fb.win.T+1; [Console]::Out.WriteLine('OCSIZE '+$cols+' '+$rows); [Console]::Out.Flush() }",
311
317
  "$m=[uint32]0;[void][OCP.N]::GetConsoleMode($h,[ref]$m);[void][OCP.N]::SetConsoleMode($h,($m -bor 4));[void][OCP.N]::SetConsoleOutputCP(65001)",
312
- // Read the ENTIRE overlay frame from stdin first, THEN redraw it on a timer
313
- // for the hold window. A single write loses a race with Claude Code's TUI
314
- // (it repaints the console buffer on its own clock and overwrites our bytes
315
- // before ConPTY flushes them); re-asserting the same absolute-positioned
316
- // frame every ~150ms keeps the card continuously present until the hook
317
- // returns. The frame uses ESC7/ESC8 (save/restore cursor) so each redraw
318
- // leaves CC's own cursor untouched.
319
- "$hold=[int]($env:OPENCRATER_WIN_HOLD_MS); if($hold -le 0){$hold=1800}",
320
- "$in=[Console]::OpenStandardInput();$ms=New-Object System.IO.MemoryStream;$buf=New-Object byte[] 65536",
321
- "while(($n=$in.Read($buf,0,$buf.Length)) -gt 0){$ms.Write($buf,0,$n)}",
322
- "$data=$ms.ToArray()",
323
- "L ('pump: frame received '+$data.Length+' bytes, holding '+$hold+'ms')",
324
- "$sw=[System.Diagnostics.Stopwatch]::StartNew();$reps=0",
325
- "do{$w=[uint32]0;[void][OCP.N]::WriteFile($h,$data,$data.Length,[ref]$w,[IntPtr]::Zero);$reps++;Start-Sleep -Milliseconds 150}while($sw.ElapsedMilliseconds -lt $hold)",
326
- "L ('pump: held '+$sw.ElapsedMilliseconds+'ms, '+$reps+' redraws')"
318
+ // Stream frames straight to the console as they arrive. The painter drives
319
+ // the timing it repaints the overlay (live countdown, position defence)
320
+ // every ~300ms and finally writes an erase frame, then closes the pipe; we
321
+ // simply forward each chunk to the visible buffer until EOF.
322
+ "$in=[Console]::OpenStandardInput();$buf=New-Object byte[] 65536;$tot=0;$frames=0",
323
+ "while(($n=$in.Read($buf,0,$buf.Length)) -gt 0){$w=[uint32]0;[void][OCP.N]::WriteFile($h,$buf,$n,[ref]$w,[IntPtr]::Zero);$tot+=$w;$frames++}",
324
+ "L ('pump: stdin closed, streamed '+$tot+' bytes in '+$frames+' chunks')"
327
325
  ].join("\n");
328
326
  function powershellPath() {
329
327
  const root = process.env["SystemRoot"] || process.env["windir"] || "C:\\Windows";
@@ -345,7 +343,7 @@ function spawnWindowsConsoleSink() {
345
343
  }
346
344
  pdebug("spawning pump:", exe);
347
345
  const child = child_process.spawn(exe, ["-NoProfile", "-NonInteractive", "-EncodedCommand", b64], {
348
- stdio: ["pipe", errStdio, errStdio],
346
+ stdio: ["pipe", "pipe", errStdio],
349
347
  windowsHide: false
350
348
  });
351
349
  child.on("error", (e) => pdebug("pump spawn error:", exe, "-", e.message));
@@ -361,7 +359,32 @@ function spawnWindowsConsoleSink() {
361
359
  child.on("exit", () => {
362
360
  exited = true;
363
361
  });
362
+ const size = new Promise((resolve) => {
363
+ let acc = "";
364
+ let settled = false;
365
+ const done = (v) => {
366
+ if (!settled) {
367
+ settled = true;
368
+ resolve(v);
369
+ }
370
+ };
371
+ const out = child.stdout;
372
+ if (!out) return done(null);
373
+ out.setEncoding("utf8");
374
+ out.on("data", (chunk) => {
375
+ acc += chunk;
376
+ const m = acc.match(/OCSIZE\s+(\d+)\s+(\d+)/);
377
+ if (m) {
378
+ pdebug("pump reported console size", m[1], "x", m[2]);
379
+ done({ cols: Number(m[1]), rows: Number(m[2]) });
380
+ }
381
+ });
382
+ out.on("error", () => done(null));
383
+ out.on("end", () => done(null));
384
+ setTimeout(() => done(null), 2500);
385
+ });
364
386
  return {
387
+ size,
365
388
  write: (d) => {
366
389
  try {
367
390
  stdin.write(d);
@@ -815,13 +838,13 @@ function main() {
815
838
  }
816
839
  const mediaRowCount = isVisualMedia ? Math.max(8, Math.min(16, Math.floor((termRows - 14) / 2) * 2)) : MEDIA_ROWS;
817
840
  const effectiveRows = sideMedia ? 5 : mediaRowCount;
818
- const width = Math.min(maxWidth, cols - 4);
841
+ let width = Math.min(maxWidth, cols - 4);
819
842
  if (width < 24) {
820
843
  sink.close();
821
844
  return;
822
845
  }
823
- const inner = width - 4;
824
- const col = Math.max(1, cols - width - 1);
846
+ let inner = width - 4;
847
+ let col = Math.max(1, cols - width - 1);
825
848
  const startRow = 2;
826
849
  const painterStartedAt = Date.now();
827
850
  const dim = color ? `${ESC}[2m` : "";
@@ -829,7 +852,7 @@ function main() {
829
852
  const reset = color ? `${ESC}[0m` : "";
830
853
  const format = fmt;
831
854
  const protocol = detectImageProtocol(process.env);
832
- const flowMode = process.env["OPENCRATER_FLOW"] === "1" || IS_WINDOWS;
855
+ const flowMode = process.env["OPENCRATER_FLOW"] === "1";
833
856
  const muted = process.env["OPENCRATER_MUTE"] === "1";
834
857
  const origin = payload.apiOrigin ?? "https://api.opencrater.to";
835
858
  const badge = format === "video" ? "\u25B6 " : format === "audio" ? "\u266A " : "";
@@ -1327,17 +1350,35 @@ function main() {
1327
1350
  };
1328
1351
  if (IS_WINDOWS) {
1329
1352
  void (async () => {
1353
+ const real = sink.size ? await sink.size : null;
1354
+ if (real && real.cols >= 24) {
1355
+ cols = real.cols;
1356
+ width = Math.min(maxWidth, cols - 4);
1357
+ inner = width - 4;
1358
+ col = Math.max(1, cols - width - 1);
1359
+ pdebug("windows geometry from pump:", "cols", cols, "width", width, "col", col);
1360
+ }
1330
1361
  if (format === "image" || format === "logo" || format === "gif" || format === "video") {
1331
1362
  await upgradeVisual();
1332
1363
  } else if (format === "audio") {
1333
1364
  await upgradeAudio();
1334
1365
  }
1335
1366
  if (payload.audioUrl && format !== "audio") void upgradeAudio(payload.audioUrl);
1336
- const framesNow = ansiFrames;
1337
- if (framesNow && framesNow.length > 1) {
1338
- ansiFrames = [framesNow[0]];
1339
- }
1367
+ const envHold = Number(process.env["OPENCRATER_WIN_HOLD_MS"]);
1368
+ const holdMs = envHold > 0 ? envHold : Math.min(payload.durationMs ?? DEFAULT_DURATION_MS, 5e3);
1369
+ deadlineAt = Date.now() + holdMs;
1340
1370
  sink.write(buildFrame());
1371
+ await new Promise((resolve) => {
1372
+ const iv = setInterval(() => {
1373
+ if (Date.now() >= deadlineAt) {
1374
+ clearInterval(iv);
1375
+ resolve();
1376
+ return;
1377
+ }
1378
+ sink.write(buildFrame());
1379
+ }, REPAINT_MS);
1380
+ });
1381
+ clear();
1341
1382
  if (tmpDir) {
1342
1383
  try {
1343
1384
  fs2.rmSync(tmpDir, { recursive: true, force: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencrater/sdk",
3
- "version": "0.8.45",
3
+ "version": "0.8.49",
4
4
  "description": "OpenCrater SDK — sponsor cards for CLI tools and MCP servers. Fail-silent, zero dependencies.",
5
5
  "keywords": [
6
6
  "opencrater",