@opencrater/sdk 0.8.49 → 0.8.50
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/paint.js +109 -13
- package/package.json +1 -1
package/dist/paint.js
CHANGED
|
@@ -298,11 +298,18 @@ var WIN_PUMP_PS = [
|
|
|
298
298
|
// parent chain, attach to each ancestor, and keep the first console that is
|
|
299
299
|
// NOT the 9001-row hidden buffer — that is the one with a window on screen.
|
|
300
300
|
"$self=Csbi (OpenConout); if($self){ L ('pump: SELF bufH='+$self.sz.Y+' winRows='+($self.win.B-$self.win.T+1)+' curY='+$self.cur.Y) }",
|
|
301
|
-
"$par=@{}; try{ Get-CimInstance Win32_Process -ErrorAction Stop | ForEach-Object { $par[[int]$_.ProcessId]=[int]$_.ParentProcessId } }catch{ L 'pump: WMI walk failed' }",
|
|
302
|
-
"$chain=New-Object System.Collections.ArrayList; $p=$PID; for($i=0;$i -lt 12;$i++){ if(-not $par.ContainsKey($p)){break}; $pp=$par[$p]; if($pp -le 4){break}; $nm='?'; try{ $nm=(Get-Process -Id $pp -ErrorAction Stop).ProcessName }catch{}; [void]$chain.Add(@{id=$pp;name=$nm}); $p=$pp }",
|
|
303
|
-
"L ('pump: ancestors = ' + (($chain | ForEach-Object { $_.name+'('+$_.id+')' }) -join ' -> '))",
|
|
304
301
|
"$how='self'; $winId=0",
|
|
305
|
-
|
|
302
|
+
// Given an explicit console-owner PID (the escaped painter passes it — its
|
|
303
|
+
// own parent chain is WmiPrvSE, so the walk below would never find Windows
|
|
304
|
+
// Terminal), attach to it directly and skip discovery.
|
|
305
|
+
"$given=[int]($env:OPENCRATER_WIN_CONSOLE_PID)",
|
|
306
|
+
"if($given -gt 0){ [void][OCP.N]::FreeConsole(); if([OCP.N]::AttachConsole([uint32]$given)){ $winId=$given; $how='given' } L ('pump: given console pid '+$given+' attached='+($winId -eq $given)) }",
|
|
307
|
+
"if($winId -eq 0){",
|
|
308
|
+
" $par=@{}; try{ Get-CimInstance Win32_Process -ErrorAction Stop | ForEach-Object { $par[[int]$_.ProcessId]=[int]$_.ParentProcessId } }catch{ L 'pump: WMI walk failed' }",
|
|
309
|
+
" $chain=New-Object System.Collections.ArrayList; $p=$PID; for($i=0;$i -lt 12;$i++){ if(-not $par.ContainsKey($p)){break}; $pp=$par[$p]; if($pp -le 4){break}; $nm='?'; try{ $nm=(Get-Process -Id $pp -ErrorAction Stop).ProcessName }catch{}; [void]$chain.Add(@{id=$pp;name=$nm}); $p=$pp }",
|
|
310
|
+
" L ('pump: ancestors = ' + (($chain | ForEach-Object { $_.name+'('+$_.id+')' }) -join ' -> '))",
|
|
311
|
+
" foreach($c in $chain){ [void][OCP.N]::FreeConsole(); if([OCP.N]::AttachConsole([uint32]$c.id)){ $b=Csbi (OpenConout); if($b){ L ('pump: '+$c.name+'('+$c.id+') bufH='+$b.sz.Y+' winRows='+($b.win.B-$b.win.T+1)+' curY='+$b.cur.Y); if($winId -eq 0 -and $b.sz.Y -lt 1000){ $winId=$c.id; $how=('attach:'+$c.name) } } } }",
|
|
312
|
+
"}",
|
|
306
313
|
"[void][OCP.N]::FreeConsole()",
|
|
307
314
|
"if($winId -ne 0){ [void][OCP.N]::AttachConsole([uint32]$winId) } else { [void][OCP.N]::AttachConsole(0xFFFFFFFF); $how='attach-parent' }",
|
|
308
315
|
"$h=OpenConout",
|
|
@@ -313,7 +320,7 @@ var WIN_PUMP_PS = [
|
|
|
313
320
|
// free). The painter blocks on this line, then anchors the card to the true
|
|
314
321
|
// right edge. win.R/L/B/T are the on-screen window rect (not the scrollback
|
|
315
322
|
// 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('
|
|
323
|
+
"if($fb){ $cols=$fb.win.R-$fb.win.L+1; $rows=$fb.win.B-$fb.win.T+1; [Console]::Out.WriteLine('OCWIN '+$winId+' '+$cols+' '+$rows); [Console]::Out.Flush() }",
|
|
317
324
|
"$m=[uint32]0;[void][OCP.N]::GetConsoleMode($h,[ref]$m);[void][OCP.N]::SetConsoleMode($h,($m -bor 4));[void][OCP.N]::SetConsoleOutputCP(65001)",
|
|
318
325
|
// Stream frames straight to the console as they arrive. The painter drives
|
|
319
326
|
// the timing — it repaints the overlay (live countdown, position defence)
|
|
@@ -373,10 +380,10 @@ function spawnWindowsConsoleSink() {
|
|
|
373
380
|
out.setEncoding("utf8");
|
|
374
381
|
out.on("data", (chunk) => {
|
|
375
382
|
acc += chunk;
|
|
376
|
-
const m = acc.match(/
|
|
383
|
+
const m = acc.match(/OCWIN\s+(\d+)\s+(\d+)\s+(\d+)/);
|
|
377
384
|
if (m) {
|
|
378
|
-
pdebug("pump reported console
|
|
379
|
-
done({
|
|
385
|
+
pdebug("pump reported console pid", m[1], "size", m[2] + "x" + m[3]);
|
|
386
|
+
done({ pid: Number(m[1]), cols: Number(m[2]), rows: Number(m[3]) });
|
|
380
387
|
}
|
|
381
388
|
});
|
|
382
389
|
out.on("error", () => done(null));
|
|
@@ -420,6 +427,50 @@ function spawnWindowsConsoleSink() {
|
|
|
420
427
|
return null;
|
|
421
428
|
}
|
|
422
429
|
}
|
|
430
|
+
function wmiSpawnDetached(cmdLine) {
|
|
431
|
+
try {
|
|
432
|
+
const esc = cmdLine.replace(/'/g, "''");
|
|
433
|
+
const ps = `$ErrorActionPreference='Stop';try{$r=Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine='${esc}'};if($r.ReturnValue -eq 0){[Console]::Out.Write('OCPID '+$r.ProcessId)}else{[Console]::Out.Write('OCERR rv='+$r.ReturnValue)}}catch{[Console]::Out.Write('OCERR '+$_.Exception.Message)}`;
|
|
434
|
+
const b64 = Buffer.from(ps, "utf16le").toString("base64");
|
|
435
|
+
const res = child_process.spawnSync(
|
|
436
|
+
powershellPath(),
|
|
437
|
+
["-NoProfile", "-NonInteractive", "-EncodedCommand", b64],
|
|
438
|
+
{ timeout: 5e3, encoding: "utf8" }
|
|
439
|
+
);
|
|
440
|
+
const m = (res.stdout ?? "").match(/OCPID\s+(\d+)/);
|
|
441
|
+
if (m) return Number(m[1]);
|
|
442
|
+
pdebug("wmi spawn failed:", (res.stdout ?? "").trim(), "|", (res.stderr ?? "").trim());
|
|
443
|
+
return 0;
|
|
444
|
+
} catch (err) {
|
|
445
|
+
pdebug("wmi spawn threw", err.message);
|
|
446
|
+
return 0;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function escapeToPersistentPainter(payload, consolePid) {
|
|
450
|
+
try {
|
|
451
|
+
const dir = path3__namespace.join(os.homedir(), ".config", "opencrater");
|
|
452
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
453
|
+
const file = path3__namespace.join(dir, `paint-${process.pid}-${Date.now()}.json`);
|
|
454
|
+
fs2.writeFileSync(file, JSON.stringify(payload), "utf8");
|
|
455
|
+
const q = (s) => `"${s}"`;
|
|
456
|
+
const parts = [
|
|
457
|
+
q(process.execPath),
|
|
458
|
+
q(__filename),
|
|
459
|
+
"--persist",
|
|
460
|
+
"--payload-file",
|
|
461
|
+
q(file),
|
|
462
|
+
"--console-pid",
|
|
463
|
+
String(consolePid)
|
|
464
|
+
];
|
|
465
|
+
if (DEBUG_ON) parts.push("--debug");
|
|
466
|
+
const pid = wmiSpawnDetached(parts.join(" "));
|
|
467
|
+
pdebug("escape: wmi persistent painter pid", pid);
|
|
468
|
+
return pid > 0;
|
|
469
|
+
} catch (err) {
|
|
470
|
+
pdebug("escape failed", err.message);
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
423
474
|
function windowsVtOk(env) {
|
|
424
475
|
if (env["WT_SESSION"]) return true;
|
|
425
476
|
if (env["TERM_PROGRAM"]) return true;
|
|
@@ -759,9 +810,10 @@ function releaseLock() {
|
|
|
759
810
|
} catch {
|
|
760
811
|
}
|
|
761
812
|
}
|
|
813
|
+
var DEBUG_ON = process.env["OPENCRATER_DEBUG"] === "1";
|
|
762
814
|
function pdebug(...args) {
|
|
763
815
|
try {
|
|
764
|
-
if (
|
|
816
|
+
if (!DEBUG_ON) return;
|
|
765
817
|
const line = `[opencrater-paint] ${args.map((a) => String(a)).join(" ")}`;
|
|
766
818
|
console.error(line);
|
|
767
819
|
try {
|
|
@@ -776,16 +828,38 @@ function pdebug(...args) {
|
|
|
776
828
|
}
|
|
777
829
|
}
|
|
778
830
|
function main() {
|
|
779
|
-
const
|
|
831
|
+
const argv = process.argv.slice(2);
|
|
832
|
+
const argVal = (flag) => {
|
|
833
|
+
const i = argv.indexOf(flag);
|
|
834
|
+
return i >= 0 ? argv[i + 1] : void 0;
|
|
835
|
+
};
|
|
836
|
+
if (argv.includes("--debug")) {
|
|
837
|
+
DEBUG_ON = true;
|
|
838
|
+
process.env["OPENCRATER_DEBUG"] = "1";
|
|
839
|
+
}
|
|
840
|
+
const persist = argv.includes("--persist") || process.env["OPENCRATER_WIN_PERSIST"] === "1";
|
|
841
|
+
const consolePidArg = Number(argVal("--console-pid") ?? "0");
|
|
842
|
+
if (consolePidArg > 0) {
|
|
843
|
+
process.env["OPENCRATER_WIN_CONSOLE_PID"] = String(consolePidArg);
|
|
844
|
+
}
|
|
845
|
+
const payloadFile = argVal("--payload-file");
|
|
846
|
+
let raw = process.env["OPENCRATER_PAINT"];
|
|
847
|
+
if (payloadFile) {
|
|
848
|
+
try {
|
|
849
|
+
raw = fs2.readFileSync(payloadFile, "utf8");
|
|
850
|
+
} catch (err) {
|
|
851
|
+
pdebug("payload-file unreadable:", err.message);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
780
854
|
if (!raw) {
|
|
781
|
-
pdebug("no
|
|
855
|
+
pdebug("no paint payload (env or --payload-file) \u2014 nothing to draw");
|
|
782
856
|
return;
|
|
783
857
|
}
|
|
784
858
|
let payload;
|
|
785
859
|
try {
|
|
786
860
|
payload = JSON.parse(raw);
|
|
787
861
|
} catch (err) {
|
|
788
|
-
pdebug("
|
|
862
|
+
pdebug("paint payload is not valid JSON:", err.message);
|
|
789
863
|
return;
|
|
790
864
|
}
|
|
791
865
|
const ttyPath = process.env["OPENCRATER_TTY"] ?? "/dev/tty";
|
|
@@ -1356,7 +1430,7 @@ function main() {
|
|
|
1356
1430
|
width = Math.min(maxWidth, cols - 4);
|
|
1357
1431
|
inner = width - 4;
|
|
1358
1432
|
col = Math.max(1, cols - width - 1);
|
|
1359
|
-
pdebug("windows geometry from pump:", "cols", cols, "width", width, "col", col);
|
|
1433
|
+
pdebug("windows geometry from pump:", "cols", cols, "width", width, "col", col, "ownerPid", real.pid);
|
|
1360
1434
|
}
|
|
1361
1435
|
if (format === "image" || format === "logo" || format === "gif" || format === "video") {
|
|
1362
1436
|
await upgradeVisual();
|
|
@@ -1364,6 +1438,28 @@ function main() {
|
|
|
1364
1438
|
await upgradeAudio();
|
|
1365
1439
|
}
|
|
1366
1440
|
if (payload.audioUrl && format !== "audio") void upgradeAudio(payload.audioUrl);
|
|
1441
|
+
if (persist) {
|
|
1442
|
+
const duration2 = Math.min(Math.max(payload.durationMs ?? DEFAULT_DURATION_MS, 3e3), 12e4);
|
|
1443
|
+
deadlineAt = Date.now() + duration2;
|
|
1444
|
+
if (payloadFile) {
|
|
1445
|
+
try {
|
|
1446
|
+
fs2.unlinkSync(payloadFile);
|
|
1447
|
+
} catch {
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
const framesNow = ansiFrames;
|
|
1451
|
+
const animated = !!(framesNow && framesNow.length > 1);
|
|
1452
|
+
paint();
|
|
1453
|
+
interval = setInterval(paint, animated ? ANIM_MS : REPAINT_MS);
|
|
1454
|
+
setTimeout(() => cleanup(), duration2);
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
const ownerPid = real?.pid ?? 0;
|
|
1458
|
+
if (ownerPid > 0 && escapeToPersistentPainter(payload, ownerPid)) {
|
|
1459
|
+
sink.close();
|
|
1460
|
+
process.exit(0);
|
|
1461
|
+
}
|
|
1462
|
+
pdebug("no job-escape (ownerPid", ownerPid, ") \u2014 bounded toast fallback");
|
|
1367
1463
|
const envHold = Number(process.env["OPENCRATER_WIN_HOLD_MS"]);
|
|
1368
1464
|
const holdMs = envHold > 0 ? envHold : Math.min(payload.durationMs ?? DEFAULT_DURATION_MS, 5e3);
|
|
1369
1465
|
deadlineAt = Date.now() + holdMs;
|