greprag 5.61.1 → 5.62.0
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/dist/commands/mechanic-manifest.d.ts +12 -0
- package/dist/commands/mechanic-manifest.js +62 -0
- package/dist/commands/mechanic-manifest.js.map +1 -1
- package/dist/commands/mechanic-spawn.js +10 -2
- package/dist/commands/mechanic-spawn.js.map +1 -1
- package/dist/commands/mechanic.js +29 -1
- package/dist/commands/mechanic.js.map +1 -1
- package/dist/commands/opencode-chip-mission.js +1 -0
- package/dist/commands/opencode-chip-mission.js.map +1 -1
- package/dist/commands/state-trigger.d.ts +17 -0
- package/dist/commands/state-trigger.js +152 -16
- package/dist/commands/state-trigger.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/opencode-plugin-png.d.ts +151 -0
- package/dist/opencode-plugin-png.js +275 -0
- package/dist/opencode-plugin-png.js.map +1 -0
- package/dist/opencode-plugin.bundle.js +633 -9
- package/dist/opencode-plugin.js +62 -1
- package/dist/opencode-plugin.js.map +1 -1
- package/package.json +1 -1
- package/skill/templates/chip-spawn.md +8 -0
|
@@ -2501,6 +2501,588 @@ function activeProcedureAnnounces(store) {
|
|
|
2501
2501
|
return lines;
|
|
2502
2502
|
}
|
|
2503
2503
|
|
|
2504
|
+
// src/archive/glyph-font.ts
|
|
2505
|
+
var GLYPH_W = 8;
|
|
2506
|
+
var GLYPH_H = 8;
|
|
2507
|
+
var FIRST_CODE = 32;
|
|
2508
|
+
var LAST_CODE = 126;
|
|
2509
|
+
var FONT_HEX = [
|
|
2510
|
+
"0000000000000000",
|
|
2511
|
+
"183c3c1818001800",
|
|
2512
|
+
"3636000000000000",
|
|
2513
|
+
"36367f367f363600",
|
|
2514
|
+
"0c3e031e301f0c00",
|
|
2515
|
+
"006333180c666300",
|
|
2516
|
+
"1c361c6e3b336e00",
|
|
2517
|
+
"0606030000000000",
|
|
2518
|
+
"180c0606060c1800",
|
|
2519
|
+
"060c1818180c0600",
|
|
2520
|
+
"00663cff3c660000",
|
|
2521
|
+
"000c0c3f0c0c0000",
|
|
2522
|
+
"00000000000c0c06",
|
|
2523
|
+
"0000003f00000000",
|
|
2524
|
+
"00000000000c0c00",
|
|
2525
|
+
"6030180c06030100",
|
|
2526
|
+
"3e63737b6f673e00",
|
|
2527
|
+
"0c0e0c0c0c0c3f00",
|
|
2528
|
+
"1e33301c06333f00",
|
|
2529
|
+
"1e33301c30331e00",
|
|
2530
|
+
"383c36337f307800",
|
|
2531
|
+
"3f031f3030331e00",
|
|
2532
|
+
"1c06031f33331e00",
|
|
2533
|
+
"3f3330180c0c0c00",
|
|
2534
|
+
"1e33331e33331e00",
|
|
2535
|
+
"1e33333e30180e00",
|
|
2536
|
+
"000c0c00000c0c00",
|
|
2537
|
+
"000c0c00000c0c06",
|
|
2538
|
+
"180c0603060c1800",
|
|
2539
|
+
"00003f00003f0000",
|
|
2540
|
+
"060c1830180c0600",
|
|
2541
|
+
"1e3330180c000c00",
|
|
2542
|
+
"3e637b7b7b031e00",
|
|
2543
|
+
"0c1e33333f333300",
|
|
2544
|
+
"3f66663e66663f00",
|
|
2545
|
+
"3c66030303663c00",
|
|
2546
|
+
"1f36666666361f00",
|
|
2547
|
+
"7f46161e16467f00",
|
|
2548
|
+
"7f46161e16060f00",
|
|
2549
|
+
"3c66030373667c00",
|
|
2550
|
+
"3333333f33333300",
|
|
2551
|
+
"1e0c0c0c0c0c1e00",
|
|
2552
|
+
"7830303033331e00",
|
|
2553
|
+
"6766361e36666700",
|
|
2554
|
+
"0f06060646667f00",
|
|
2555
|
+
"63777f7f6b636300",
|
|
2556
|
+
"63676f7b73636300",
|
|
2557
|
+
"1c36636363361c00",
|
|
2558
|
+
"3f66663e06060f00",
|
|
2559
|
+
"1e3333333b1e3800",
|
|
2560
|
+
"3f66663e36666700",
|
|
2561
|
+
"1e33070e38331e00",
|
|
2562
|
+
"3f2d0c0c0c0c1e00",
|
|
2563
|
+
"3333333333333f00",
|
|
2564
|
+
"33333333331e0c00",
|
|
2565
|
+
"6363636b7f776300",
|
|
2566
|
+
"6363361c1c366300",
|
|
2567
|
+
"3333331e0c0c1e00",
|
|
2568
|
+
"7f6331184c667f00",
|
|
2569
|
+
"1e06060606061e00",
|
|
2570
|
+
"03060c1830604000",
|
|
2571
|
+
"1e18181818181e00",
|
|
2572
|
+
"081c366300000000",
|
|
2573
|
+
"00000000000000ff",
|
|
2574
|
+
"0c0c180000000000",
|
|
2575
|
+
"00001e303e336e00",
|
|
2576
|
+
"0706063e66663b00",
|
|
2577
|
+
"00001e3303331e00",
|
|
2578
|
+
"3830303e33336e00",
|
|
2579
|
+
"00001e333f031e00",
|
|
2580
|
+
"1c36060f06060f00",
|
|
2581
|
+
"00006e33333e301f",
|
|
2582
|
+
"0706366e66666700",
|
|
2583
|
+
"0c000e0c0c0c1e00",
|
|
2584
|
+
"300030303033331e",
|
|
2585
|
+
"070666361e366700",
|
|
2586
|
+
"0e0c0c0c0c0c1e00",
|
|
2587
|
+
"0000337f7f6b6300",
|
|
2588
|
+
"00001f3333333300",
|
|
2589
|
+
"00001e3333331e00",
|
|
2590
|
+
"00003b66663e060f",
|
|
2591
|
+
"00006e33333e3078",
|
|
2592
|
+
"00003b6e66060f00",
|
|
2593
|
+
"00003e031e301f00",
|
|
2594
|
+
"080c3e0c0c2c1800",
|
|
2595
|
+
"0000333333336e00",
|
|
2596
|
+
"00003333331e0c00",
|
|
2597
|
+
"0000636b7f7f3600",
|
|
2598
|
+
"000063361c366300",
|
|
2599
|
+
"00003333333e301f",
|
|
2600
|
+
"00003f190c263f00",
|
|
2601
|
+
"380c0c070c0c3800",
|
|
2602
|
+
"1818180018181800",
|
|
2603
|
+
"070c0c380c0c0700",
|
|
2604
|
+
"6e3b000000000000"
|
|
2605
|
+
];
|
|
2606
|
+
function glyphRows(code) {
|
|
2607
|
+
if (code < FIRST_CODE || code > LAST_CODE)
|
|
2608
|
+
return null;
|
|
2609
|
+
const hex = FONT_HEX[code - FIRST_CODE];
|
|
2610
|
+
const out = [];
|
|
2611
|
+
for (let i = 0; i < 8; i++)
|
|
2612
|
+
out.push(parseInt(hex.slice(i * 2, i * 2 + 2), 16));
|
|
2613
|
+
return out;
|
|
2614
|
+
}
|
|
2615
|
+
var reverse = null;
|
|
2616
|
+
function codeForSignature(sigHex) {
|
|
2617
|
+
if (!reverse) {
|
|
2618
|
+
reverse = /* @__PURE__ */ new Map();
|
|
2619
|
+
for (let c = FIRST_CODE; c <= LAST_CODE; c++)
|
|
2620
|
+
reverse.set(FONT_HEX[c - FIRST_CODE], c);
|
|
2621
|
+
}
|
|
2622
|
+
return reverse.get(sigHex) ?? null;
|
|
2623
|
+
}
|
|
2624
|
+
function signatureForRows(bytes) {
|
|
2625
|
+
return bytes.map((b) => (b & 255).toString(16).padStart(2, "0")).join("");
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
// src/archive/png-codec.ts
|
|
2629
|
+
var zlib = __toESM(require("zlib"));
|
|
2630
|
+
var PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
2631
|
+
var crcTable = null;
|
|
2632
|
+
function getCrcTable() {
|
|
2633
|
+
if (crcTable)
|
|
2634
|
+
return crcTable;
|
|
2635
|
+
crcTable = new Uint32Array(256);
|
|
2636
|
+
for (let n = 0; n < 256; n++) {
|
|
2637
|
+
let c = n;
|
|
2638
|
+
for (let k = 0; k < 8; k++) {
|
|
2639
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
2640
|
+
}
|
|
2641
|
+
crcTable[n] = c >>> 0;
|
|
2642
|
+
}
|
|
2643
|
+
return crcTable;
|
|
2644
|
+
}
|
|
2645
|
+
function crc32(buf) {
|
|
2646
|
+
const table = getCrcTable();
|
|
2647
|
+
let c = 4294967295;
|
|
2648
|
+
for (let i = 0; i < buf.length; i++) {
|
|
2649
|
+
c = table[(c ^ buf[i]) & 255] ^ c >>> 8;
|
|
2650
|
+
}
|
|
2651
|
+
return (c ^ 4294967295) >>> 0;
|
|
2652
|
+
}
|
|
2653
|
+
function chunk(type, data) {
|
|
2654
|
+
const len = Buffer.alloc(4);
|
|
2655
|
+
len.writeUInt32BE(data.length, 0);
|
|
2656
|
+
const typeAndData = Buffer.concat([Buffer.from(type, "ascii"), data]);
|
|
2657
|
+
const crc = Buffer.alloc(4);
|
|
2658
|
+
crc.writeUInt32BE(crc32(typeAndData), 0);
|
|
2659
|
+
return Buffer.concat([len, typeAndData, crc]);
|
|
2660
|
+
}
|
|
2661
|
+
function encodePng(img) {
|
|
2662
|
+
const { width, height, pixels } = img;
|
|
2663
|
+
if (pixels.length !== width * height) {
|
|
2664
|
+
throw new Error(`pixel buffer ${pixels.length} != ${width}x${height}`);
|
|
2665
|
+
}
|
|
2666
|
+
const ihdr = Buffer.alloc(13);
|
|
2667
|
+
ihdr.writeUInt32BE(width, 0);
|
|
2668
|
+
ihdr.writeUInt32BE(height, 4);
|
|
2669
|
+
ihdr[8] = 8;
|
|
2670
|
+
ihdr[9] = 0;
|
|
2671
|
+
ihdr[10] = 0;
|
|
2672
|
+
ihdr[11] = 0;
|
|
2673
|
+
ihdr[12] = 0;
|
|
2674
|
+
const raw = Buffer.alloc(height * (width + 1));
|
|
2675
|
+
for (let y = 0; y < height; y++) {
|
|
2676
|
+
raw[y * (width + 1)] = 0;
|
|
2677
|
+
pixels.copy(raw, y * (width + 1) + 1, y * width, (y + 1) * width);
|
|
2678
|
+
}
|
|
2679
|
+
const idat = zlib.deflateSync(raw, { level: 9 });
|
|
2680
|
+
return Buffer.concat([
|
|
2681
|
+
PNG_SIGNATURE,
|
|
2682
|
+
chunk("IHDR", ihdr),
|
|
2683
|
+
chunk("IDAT", idat),
|
|
2684
|
+
chunk("IEND", Buffer.alloc(0))
|
|
2685
|
+
]);
|
|
2686
|
+
}
|
|
2687
|
+
function paeth(a, b, c) {
|
|
2688
|
+
const p = a + b - c;
|
|
2689
|
+
const pa = Math.abs(p - a);
|
|
2690
|
+
const pb = Math.abs(p - b);
|
|
2691
|
+
const pc = Math.abs(p - c);
|
|
2692
|
+
if (pa <= pb && pa <= pc)
|
|
2693
|
+
return a;
|
|
2694
|
+
if (pb <= pc)
|
|
2695
|
+
return b;
|
|
2696
|
+
return c;
|
|
2697
|
+
}
|
|
2698
|
+
function decodePng(buf) {
|
|
2699
|
+
if (buf.length < 8 || !buf.subarray(0, 8).equals(PNG_SIGNATURE)) {
|
|
2700
|
+
throw new Error("not a PNG (bad signature)");
|
|
2701
|
+
}
|
|
2702
|
+
let width = 0;
|
|
2703
|
+
let height = 0;
|
|
2704
|
+
const idatParts = [];
|
|
2705
|
+
let off = 8;
|
|
2706
|
+
while (off + 8 <= buf.length) {
|
|
2707
|
+
const len = buf.readUInt32BE(off);
|
|
2708
|
+
const type = buf.toString("ascii", off + 4, off + 8);
|
|
2709
|
+
const data = buf.subarray(off + 8, off + 8 + len);
|
|
2710
|
+
if (type === "IHDR") {
|
|
2711
|
+
width = data.readUInt32BE(0);
|
|
2712
|
+
height = data.readUInt32BE(4);
|
|
2713
|
+
const bitDepth = data[8];
|
|
2714
|
+
const colorType = data[9];
|
|
2715
|
+
const interlace = data[12];
|
|
2716
|
+
if (bitDepth !== 8 || colorType !== 0 || interlace !== 0) {
|
|
2717
|
+
throw new Error(`unsupported PNG (bitDepth=${bitDepth} colorType=${colorType} interlace=${interlace})`);
|
|
2718
|
+
}
|
|
2719
|
+
} else if (type === "IDAT") {
|
|
2720
|
+
idatParts.push(data);
|
|
2721
|
+
} else if (type === "IEND") {
|
|
2722
|
+
break;
|
|
2723
|
+
}
|
|
2724
|
+
off += 8 + len + 4;
|
|
2725
|
+
}
|
|
2726
|
+
if (!width || !height)
|
|
2727
|
+
throw new Error("PNG missing IHDR");
|
|
2728
|
+
if (idatParts.length === 0)
|
|
2729
|
+
throw new Error("PNG missing IDAT");
|
|
2730
|
+
const raw = zlib.inflateSync(Buffer.concat(idatParts));
|
|
2731
|
+
const stride = width + 1;
|
|
2732
|
+
if (raw.length < height * stride) {
|
|
2733
|
+
throw new Error(`PNG data short: ${raw.length} < ${height * stride}`);
|
|
2734
|
+
}
|
|
2735
|
+
const pixels = Buffer.alloc(width * height);
|
|
2736
|
+
for (let y = 0; y < height; y++) {
|
|
2737
|
+
const filter = raw[y * stride];
|
|
2738
|
+
const rowIn = raw.subarray(y * stride + 1, y * stride + 1 + width);
|
|
2739
|
+
const rowOut = pixels.subarray(y * width, (y + 1) * width);
|
|
2740
|
+
for (let x = 0; x < width; x++) {
|
|
2741
|
+
const a = x > 0 ? rowOut[x - 1] : 0;
|
|
2742
|
+
const b = y > 0 ? pixels[(y - 1) * width + x] : 0;
|
|
2743
|
+
const c = x > 0 && y > 0 ? pixels[(y - 1) * width + x - 1] : 0;
|
|
2744
|
+
let v;
|
|
2745
|
+
switch (filter) {
|
|
2746
|
+
case 0:
|
|
2747
|
+
v = rowIn[x];
|
|
2748
|
+
break;
|
|
2749
|
+
case 1:
|
|
2750
|
+
v = rowIn[x] + a;
|
|
2751
|
+
break;
|
|
2752
|
+
case 2:
|
|
2753
|
+
v = rowIn[x] + b;
|
|
2754
|
+
break;
|
|
2755
|
+
case 3:
|
|
2756
|
+
v = rowIn[x] + (a + b >> 1);
|
|
2757
|
+
break;
|
|
2758
|
+
case 4:
|
|
2759
|
+
v = rowIn[x] + paeth(a, b, c);
|
|
2760
|
+
break;
|
|
2761
|
+
default:
|
|
2762
|
+
throw new Error(`unsupported PNG filter ${filter} on row ${y}`);
|
|
2763
|
+
}
|
|
2764
|
+
rowOut[x] = v & 255;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
return { width, height, pixels };
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
// src/archive/page-renderer.ts
|
|
2771
|
+
var PAGE_SIZE = 1568;
|
|
2772
|
+
var CELL_W = GLYPH_W;
|
|
2773
|
+
var CELL_H = 10;
|
|
2774
|
+
var COLS = Math.floor(PAGE_SIZE / CELL_W);
|
|
2775
|
+
var ROWS = Math.floor(PAGE_SIZE / CELL_H);
|
|
2776
|
+
var CHARS_PER_PAGE = COLS * ROWS;
|
|
2777
|
+
var BG = 255;
|
|
2778
|
+
var FG = 0;
|
|
2779
|
+
function sanitizeForGrid(text) {
|
|
2780
|
+
const out = [];
|
|
2781
|
+
for (const ch of text.replace(/\r\n/g, "\n").replace(/\r/g, "\n")) {
|
|
2782
|
+
const code = ch.codePointAt(0);
|
|
2783
|
+
if (ch === "\n") {
|
|
2784
|
+
out.push("\n");
|
|
2785
|
+
} else if (ch === " ") {
|
|
2786
|
+
out.push("\\t");
|
|
2787
|
+
} else if (code >= 32 && code <= 126) {
|
|
2788
|
+
out.push(ch);
|
|
2789
|
+
} else if (code <= 65535) {
|
|
2790
|
+
out.push("\\u" + code.toString(16).padStart(4, "0"));
|
|
2791
|
+
} else {
|
|
2792
|
+
out.push("\\u{" + code.toString(16) + "}");
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
return out.join("");
|
|
2796
|
+
}
|
|
2797
|
+
function layoutRows(sanitized, cols = COLS) {
|
|
2798
|
+
const rows = [];
|
|
2799
|
+
let offset = 0;
|
|
2800
|
+
for (const line of sanitized.split("\n")) {
|
|
2801
|
+
if (line.length === 0) {
|
|
2802
|
+
rows.push({ text: " ".repeat(cols), charStart: offset });
|
|
2803
|
+
} else {
|
|
2804
|
+
for (let i = 0; i < line.length; i += cols) {
|
|
2805
|
+
const slice = line.slice(i, i + cols);
|
|
2806
|
+
rows.push({ text: slice.padEnd(cols, " "), charStart: offset + i });
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
offset += line.length + 1;
|
|
2810
|
+
}
|
|
2811
|
+
return rows;
|
|
2812
|
+
}
|
|
2813
|
+
function renderPage(pageRows, cols = COLS, rows = ROWS) {
|
|
2814
|
+
const rowCount = Math.max(1, Math.min(pageRows.length, rows));
|
|
2815
|
+
const width = cols * CELL_W;
|
|
2816
|
+
const height = rowCount * CELL_H;
|
|
2817
|
+
const pixels = Buffer.alloc(width * height, BG);
|
|
2818
|
+
for (let r = 0; r < Math.min(pageRows.length, rowCount); r++) {
|
|
2819
|
+
const row = pageRows[r];
|
|
2820
|
+
for (let c = 0; c < cols; c++) {
|
|
2821
|
+
const glyph = glyphRows(row.text.charCodeAt(c));
|
|
2822
|
+
if (!glyph)
|
|
2823
|
+
continue;
|
|
2824
|
+
const px = c * CELL_W;
|
|
2825
|
+
const py = r * CELL_H;
|
|
2826
|
+
for (let gy = 0; gy < GLYPH_H; gy++) {
|
|
2827
|
+
const bits = glyph[gy];
|
|
2828
|
+
if (bits === 0)
|
|
2829
|
+
continue;
|
|
2830
|
+
const base = (py + gy) * width + px;
|
|
2831
|
+
for (let gx = 0; gx < GLYPH_W; gx++) {
|
|
2832
|
+
if (bits & 1 << gx)
|
|
2833
|
+
pixels[base + gx] = FG;
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
const img = { width, height, pixels };
|
|
2839
|
+
const charStart = pageRows.length ? pageRows[0].charStart : 0;
|
|
2840
|
+
const last = pageRows[pageRows.length - 1];
|
|
2841
|
+
const charEnd = last ? last.charStart + last.text.trimEnd().length : charStart;
|
|
2842
|
+
return {
|
|
2843
|
+
png: encodePng(img),
|
|
2844
|
+
gridText: pageRows.map((r) => r.text).join("\n"),
|
|
2845
|
+
charStart,
|
|
2846
|
+
charEnd
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
function paginate(rowsIn, rowsPerPage = ROWS) {
|
|
2850
|
+
const pages = [];
|
|
2851
|
+
for (let i = 0; i < rowsIn.length; i += rowsPerPage) {
|
|
2852
|
+
pages.push(rowsIn.slice(i, i + rowsPerPage));
|
|
2853
|
+
}
|
|
2854
|
+
return pages;
|
|
2855
|
+
}
|
|
2856
|
+
function decodePageText(png, cols = COLS) {
|
|
2857
|
+
const img = decodePng(png);
|
|
2858
|
+
const rows = Math.floor(img.height / CELL_H);
|
|
2859
|
+
const usableCols = Math.min(cols, Math.floor(img.width / CELL_W));
|
|
2860
|
+
const lines = [];
|
|
2861
|
+
for (let r = 0; r < rows; r++) {
|
|
2862
|
+
const chars = [];
|
|
2863
|
+
for (let c = 0; c < usableCols; c++) {
|
|
2864
|
+
const bytes = [];
|
|
2865
|
+
for (let gy = 0; gy < GLYPH_H; gy++) {
|
|
2866
|
+
let b = 0;
|
|
2867
|
+
const base = (r * CELL_H + gy) * img.width + c * CELL_W;
|
|
2868
|
+
for (let gx = 0; gx < GLYPH_W; gx++) {
|
|
2869
|
+
if (img.pixels[base + gx] < 128)
|
|
2870
|
+
b |= 1 << gx;
|
|
2871
|
+
}
|
|
2872
|
+
bytes.push(b);
|
|
2873
|
+
}
|
|
2874
|
+
const code = codeForSignature(signatureForRows(bytes));
|
|
2875
|
+
chars.push(code === null ? "\uFFFD" : String.fromCharCode(code));
|
|
2876
|
+
}
|
|
2877
|
+
lines.push(chars.join(""));
|
|
2878
|
+
}
|
|
2879
|
+
return lines.join("\n");
|
|
2880
|
+
}
|
|
2881
|
+
function verifyRoundTrip(page) {
|
|
2882
|
+
const decoded = decodePageText(page.png);
|
|
2883
|
+
if (decoded === page.gridText)
|
|
2884
|
+
return null;
|
|
2885
|
+
const a = page.gridText;
|
|
2886
|
+
const b = decoded;
|
|
2887
|
+
const n = Math.min(a.length, b.length);
|
|
2888
|
+
for (let i = 0; i < n; i++) {
|
|
2889
|
+
if (a[i] !== b[i]) {
|
|
2890
|
+
return `mismatch at char ${i}: rendered ${JSON.stringify(a[i])} decoded ${JSON.stringify(b[i])}`;
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
return `length mismatch: rendered ${a.length} decoded ${b.length}`;
|
|
2894
|
+
}
|
|
2895
|
+
|
|
2896
|
+
// src/opencode-plugin-png.ts
|
|
2897
|
+
var CCR_MARKER_HASH_LEN2 = 16;
|
|
2898
|
+
function ccrPngMarker(hash) {
|
|
2899
|
+
return `<<ccr:${hash.slice(0, CCR_MARKER_HASH_LEN2)}>>`;
|
|
2900
|
+
}
|
|
2901
|
+
var CCR_MARKER_RE2 = /<<ccr:/;
|
|
2902
|
+
var DEFAULT_THRESHOLD_CHARS = 4e5;
|
|
2903
|
+
var DEFAULT_MIN_PART_CHARS = 4e3;
|
|
2904
|
+
var DEFAULT_LIVE_TAIL_MESSAGES = 1;
|
|
2905
|
+
var DEFAULT_VISION_MODEL_SUBSTRINGS = [
|
|
2906
|
+
"gpt-4o",
|
|
2907
|
+
"gpt-4.1",
|
|
2908
|
+
"gpt-4-turbo",
|
|
2909
|
+
"gpt-5",
|
|
2910
|
+
"o1",
|
|
2911
|
+
"o3",
|
|
2912
|
+
"o4",
|
|
2913
|
+
"claude-3",
|
|
2914
|
+
"claude-4",
|
|
2915
|
+
"claude-opus",
|
|
2916
|
+
"claude-sonnet",
|
|
2917
|
+
"claude-haiku",
|
|
2918
|
+
"claude-fable",
|
|
2919
|
+
"gemini",
|
|
2920
|
+
"llama-3.2-vision",
|
|
2921
|
+
"llama-4",
|
|
2922
|
+
"qwen2.5-vl",
|
|
2923
|
+
"qwen2-vl",
|
|
2924
|
+
"qwen3-vl",
|
|
2925
|
+
"pixtral",
|
|
2926
|
+
"mistral-small-3",
|
|
2927
|
+
"grok-vision",
|
|
2928
|
+
"grok-4",
|
|
2929
|
+
"internvl",
|
|
2930
|
+
"-vl",
|
|
2931
|
+
"vision"
|
|
2932
|
+
];
|
|
2933
|
+
function inputModalities(modalities) {
|
|
2934
|
+
if (!modalities)
|
|
2935
|
+
return null;
|
|
2936
|
+
if (Array.isArray(modalities))
|
|
2937
|
+
return modalities.map((m) => String(m));
|
|
2938
|
+
if (typeof modalities === "object" && Array.isArray(modalities.input)) {
|
|
2939
|
+
return modalities.input.map((m) => String(m));
|
|
2940
|
+
}
|
|
2941
|
+
return null;
|
|
2942
|
+
}
|
|
2943
|
+
function isVisionCapable(opts) {
|
|
2944
|
+
const mods = inputModalities(opts.modalities);
|
|
2945
|
+
if (mods) {
|
|
2946
|
+
return mods.some((m) => m.toLowerCase() === "image");
|
|
2947
|
+
}
|
|
2948
|
+
const id = (opts.modelId || "").toLowerCase();
|
|
2949
|
+
if (!id)
|
|
2950
|
+
return false;
|
|
2951
|
+
const allow = [...DEFAULT_VISION_MODEL_SUBSTRINGS, ...opts.visionAllowlist || []];
|
|
2952
|
+
return allow.some((sub) => sub && id.includes(String(sub).toLowerCase()));
|
|
2953
|
+
}
|
|
2954
|
+
function renderTextToImageParts(text, hash) {
|
|
2955
|
+
const sanitized = sanitizeForGrid(text);
|
|
2956
|
+
const pages = paginate(layoutRows(sanitized));
|
|
2957
|
+
const parts = [];
|
|
2958
|
+
let pngBytes = 0;
|
|
2959
|
+
let selfTestError = null;
|
|
2960
|
+
for (let i = 0; i < pages.length; i++) {
|
|
2961
|
+
const rendered = renderPage(pages[i]);
|
|
2962
|
+
const err = verifyRoundTrip(rendered);
|
|
2963
|
+
if (err && !selfTestError)
|
|
2964
|
+
selfTestError = `page ${i + 1}: ${err}`;
|
|
2965
|
+
pngBytes += rendered.png.length;
|
|
2966
|
+
parts.push({
|
|
2967
|
+
type: "file",
|
|
2968
|
+
mime: "image/png",
|
|
2969
|
+
url: `data:image/png;base64,${rendered.png.toString("base64")}`,
|
|
2970
|
+
filename: `greprag-png-${hash.slice(0, CCR_MARKER_HASH_LEN2)}-p${String(i + 1).padStart(3, "0")}.png`
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
return { parts, pages: pages.length, pngBytes, selfTestError };
|
|
2974
|
+
}
|
|
2975
|
+
function partsOf(msg) {
|
|
2976
|
+
if (!msg || typeof msg !== "object")
|
|
2977
|
+
return null;
|
|
2978
|
+
const parts = msg.parts;
|
|
2979
|
+
return Array.isArray(parts) ? parts : null;
|
|
2980
|
+
}
|
|
2981
|
+
function partCharLen(p) {
|
|
2982
|
+
if (!p || typeof p !== "object")
|
|
2983
|
+
return 0;
|
|
2984
|
+
const part = p;
|
|
2985
|
+
if (part.type === "text" && typeof part.text === "string")
|
|
2986
|
+
return part.text.length;
|
|
2987
|
+
if (part.type === "file" && typeof part.url === "string")
|
|
2988
|
+
return part.url.length;
|
|
2989
|
+
if (part.type === "tool" && part.state && typeof part.state === "object") {
|
|
2990
|
+
const out = part.state.output;
|
|
2991
|
+
if (typeof out === "string")
|
|
2992
|
+
return out.length;
|
|
2993
|
+
}
|
|
2994
|
+
return 0;
|
|
2995
|
+
}
|
|
2996
|
+
function maybeRecodePart(p, minPartChars, stash) {
|
|
2997
|
+
if (!p || typeof p !== "object")
|
|
2998
|
+
return null;
|
|
2999
|
+
const part = p;
|
|
3000
|
+
if (part.type !== "text" || typeof part.text !== "string")
|
|
3001
|
+
return null;
|
|
3002
|
+
const text = part.text;
|
|
3003
|
+
if (CCR_MARKER_RE2.test(text))
|
|
3004
|
+
return null;
|
|
3005
|
+
if (text.length < minPartChars)
|
|
3006
|
+
return null;
|
|
3007
|
+
const hash = stash(text);
|
|
3008
|
+
const { parts: fileParts, pages, pngBytes, selfTestError } = renderTextToImageParts(text, hash);
|
|
3009
|
+
const breadcrumb = {
|
|
3010
|
+
type: "text",
|
|
3011
|
+
text: `[greprag-png-recode: ${pages} page(s) of prior history rendered as pixel-text image(s) above; verbatim original ${ccrPngMarker(hash)} (greprag retrieve ${hash.slice(0, CCR_MARKER_HASH_LEN2)})]`
|
|
3012
|
+
};
|
|
3013
|
+
return { fileParts, breadcrumb, pages, pngBytes, selfTestError };
|
|
3014
|
+
}
|
|
3015
|
+
function recodeMessagesToPng(messages, opts) {
|
|
3016
|
+
const thresholdChars = opts.thresholdChars ?? DEFAULT_THRESHOLD_CHARS;
|
|
3017
|
+
const minPartChars = opts.minPartChars ?? DEFAULT_MIN_PART_CHARS;
|
|
3018
|
+
const liveTail = opts.liveTailMessages ?? DEFAULT_LIVE_TAIL_MESSAGES;
|
|
3019
|
+
const stats = {
|
|
3020
|
+
fired: false,
|
|
3021
|
+
visionCapable: false,
|
|
3022
|
+
totalChars: 0,
|
|
3023
|
+
thresholdChars,
|
|
3024
|
+
partsRecoded: 0,
|
|
3025
|
+
pagesRendered: 0,
|
|
3026
|
+
pngBytes: 0,
|
|
3027
|
+
selfTestError: null,
|
|
3028
|
+
reason: ""
|
|
3029
|
+
};
|
|
3030
|
+
if (!Array.isArray(messages)) {
|
|
3031
|
+
stats.reason = "messages not an array";
|
|
3032
|
+
return stats;
|
|
3033
|
+
}
|
|
3034
|
+
for (const msg of messages) {
|
|
3035
|
+
const parts = partsOf(msg);
|
|
3036
|
+
if (!parts)
|
|
3037
|
+
continue;
|
|
3038
|
+
for (const p of parts)
|
|
3039
|
+
stats.totalChars += partCharLen(p);
|
|
3040
|
+
}
|
|
3041
|
+
const vision = isVisionCapable(opts);
|
|
3042
|
+
stats.visionCapable = vision;
|
|
3043
|
+
if (!vision) {
|
|
3044
|
+
stats.reason = `model not vision-capable (modelId=${opts.modelId || "?"}) \u2014 pass-through`;
|
|
3045
|
+
return stats;
|
|
3046
|
+
}
|
|
3047
|
+
if (stats.totalChars < thresholdChars) {
|
|
3048
|
+
stats.reason = `below threshold (${stats.totalChars} < ${thresholdChars} chars)`;
|
|
3049
|
+
return stats;
|
|
3050
|
+
}
|
|
3051
|
+
const lastRecodableIdx = Math.max(0, messages.length - Math.max(0, liveTail));
|
|
3052
|
+
for (let mi = 0; mi < lastRecodableIdx; mi++) {
|
|
3053
|
+
const msg = messages[mi];
|
|
3054
|
+
const parts = partsOf(msg);
|
|
3055
|
+
if (!parts)
|
|
3056
|
+
continue;
|
|
3057
|
+
let changed = false;
|
|
3058
|
+
const newParts = [];
|
|
3059
|
+
for (const p of parts) {
|
|
3060
|
+
let rec = null;
|
|
3061
|
+
try {
|
|
3062
|
+
rec = maybeRecodePart(p, minPartChars, opts.stash);
|
|
3063
|
+
} catch {
|
|
3064
|
+
rec = null;
|
|
3065
|
+
}
|
|
3066
|
+
if (rec) {
|
|
3067
|
+
newParts.push(...rec.fileParts, rec.breadcrumb);
|
|
3068
|
+
changed = true;
|
|
3069
|
+
stats.partsRecoded++;
|
|
3070
|
+
stats.pagesRendered += rec.pages;
|
|
3071
|
+
stats.pngBytes += rec.pngBytes;
|
|
3072
|
+
if (rec.selfTestError && !stats.selfTestError)
|
|
3073
|
+
stats.selfTestError = rec.selfTestError;
|
|
3074
|
+
} else {
|
|
3075
|
+
newParts.push(p);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
if (changed)
|
|
3079
|
+
msg.parts = newParts;
|
|
3080
|
+
}
|
|
3081
|
+
stats.fired = stats.partsRecoded > 0;
|
|
3082
|
+
stats.reason = stats.fired ? `recoded ${stats.partsRecoded} part(s) -> ${stats.pagesRendered} page(s), ${stats.pngBytes} png bytes` : "threshold crossed but no recodable text parts (all below per-part floor or already marked)";
|
|
3083
|
+
return stats;
|
|
3084
|
+
}
|
|
3085
|
+
|
|
2504
3086
|
// src/opencode-plugin.ts
|
|
2505
3087
|
var DEBUG_LOG_PATH = path3.join(os.homedir(), ".greprag", "opencode-plugin-debug.log");
|
|
2506
3088
|
var _debugLogReady = false;
|
|
@@ -2749,11 +3331,11 @@ function startCaptureWatcher() {
|
|
|
2749
3331
|
fs3.writeFileSync(WATCHER_LOCK, String(child.pid));
|
|
2750
3332
|
} catch {
|
|
2751
3333
|
}
|
|
2752
|
-
child.stderr?.on("data", (
|
|
2753
|
-
process.stderr.write(`[greprag-watch] ${
|
|
3334
|
+
child.stderr?.on("data", (chunk2) => {
|
|
3335
|
+
process.stderr.write(`[greprag-watch] ${chunk2.toString("utf-8")}`);
|
|
2754
3336
|
});
|
|
2755
|
-
child.stdout?.on("data", (
|
|
2756
|
-
process.stderr.write(`[greprag-watch] ${
|
|
3337
|
+
child.stdout?.on("data", (chunk2) => {
|
|
3338
|
+
process.stderr.write(`[greprag-watch] ${chunk2.toString("utf-8")}`);
|
|
2757
3339
|
});
|
|
2758
3340
|
child.on("exit", (code, signal) => {
|
|
2759
3341
|
dlog(`startCaptureWatcher: child pid=${child.pid ?? "<none>"} exited code=${code} signal=${signal || "none"}`);
|
|
@@ -2958,11 +3540,11 @@ function startSessionRelay(sessionId, serverUrl) {
|
|
|
2958
3540
|
fs3.writeFileSync(lockPath, String(child.pid));
|
|
2959
3541
|
} catch {
|
|
2960
3542
|
}
|
|
2961
|
-
child.stderr?.on("data", (
|
|
2962
|
-
process.stderr.write(`[greprag-relay] ${
|
|
3543
|
+
child.stderr?.on("data", (chunk2) => {
|
|
3544
|
+
process.stderr.write(`[greprag-relay] ${chunk2.toString("utf-8")}`);
|
|
2963
3545
|
});
|
|
2964
|
-
child.stdout?.on("data", (
|
|
2965
|
-
process.stderr.write(`[greprag-relay] ${
|
|
3546
|
+
child.stdout?.on("data", (chunk2) => {
|
|
3547
|
+
process.stderr.write(`[greprag-relay] ${chunk2.toString("utf-8")}`);
|
|
2966
3548
|
});
|
|
2967
3549
|
child.on("exit", (code, signal) => {
|
|
2968
3550
|
try {
|
|
@@ -3052,6 +3634,39 @@ function inlineCrushEnabled() {
|
|
|
3052
3634
|
const v = process.env.GREPRAG_OPENCODE_CRUSH;
|
|
3053
3635
|
return v === "true" || v === "1";
|
|
3054
3636
|
}
|
|
3637
|
+
function pngRecodeEnabled() {
|
|
3638
|
+
const v = process.env.GREPRAG_OPENCODE_PNG;
|
|
3639
|
+
return v === "true" || v === "1";
|
|
3640
|
+
}
|
|
3641
|
+
function intEnv(name, allowZero = false) {
|
|
3642
|
+
const n = parseInt(process.env[name] || "", 10);
|
|
3643
|
+
if (!Number.isFinite(n))
|
|
3644
|
+
return void 0;
|
|
3645
|
+
return n > 0 || allowZero && n === 0 ? n : void 0;
|
|
3646
|
+
}
|
|
3647
|
+
function runPngRecode(input, msgs) {
|
|
3648
|
+
const model = input?.model;
|
|
3649
|
+
const modelId = model?.modelID || model?.id || model?.name || null;
|
|
3650
|
+
const modalities = model?.modalities || model?.info?.modalities || null;
|
|
3651
|
+
const allowEnv = (process.env.GREPRAG_OPENCODE_PNG_VISION_MODELS || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
3652
|
+
const stash = makeCcrStash();
|
|
3653
|
+
const stats = recodeMessagesToPng(msgs, {
|
|
3654
|
+
stash: (original) => stash(original, "log"),
|
|
3655
|
+
modelId,
|
|
3656
|
+
modalities,
|
|
3657
|
+
thresholdChars: intEnv("GREPRAG_OPENCODE_PNG_THRESHOLD"),
|
|
3658
|
+
minPartChars: intEnv("GREPRAG_OPENCODE_PNG_MIN_PART"),
|
|
3659
|
+
liveTailMessages: intEnv("GREPRAG_OPENCODE_PNG_TAIL", true),
|
|
3660
|
+
visionAllowlist: allowEnv.length ? allowEnv : void 0
|
|
3661
|
+
});
|
|
3662
|
+
if (stats.partsRecoded > 0) {
|
|
3663
|
+
dlog(
|
|
3664
|
+
`messages.transform png: recoded=${stats.partsRecoded} pages=${stats.pagesRendered} pngBytes=${stats.pngBytes} totalChars=${stats.totalChars} vision=${stats.visionCapable}` + (stats.selfTestError ? ` selfTestError=${stats.selfTestError}` : "")
|
|
3665
|
+
);
|
|
3666
|
+
} else {
|
|
3667
|
+
dlog(`messages.transform png: no-op (${stats.reason})`);
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3055
3670
|
function makeCcrStash() {
|
|
3056
3671
|
return (original, sourceType) => {
|
|
3057
3672
|
const hash = crypto2.createHash("sha256").update(original, "utf8").digest("hex").slice(0, 16);
|
|
@@ -3190,7 +3805,16 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
|
|
|
3190
3805
|
// Contract: we mutate ONLY this request-scoped `output.messages` copy,
|
|
3191
3806
|
// never opencode's persisted session history. adr:
|
|
3192
3807
|
// adr/opencode-context-compressor.md
|
|
3193
|
-
"experimental.chat.messages.transform": async (
|
|
3808
|
+
"experimental.chat.messages.transform": async (input, output) => {
|
|
3809
|
+
try {
|
|
3810
|
+
if (pngRecodeEnabled()) {
|
|
3811
|
+
const msgs0 = output && output.messages;
|
|
3812
|
+
if (Array.isArray(msgs0))
|
|
3813
|
+
runPngRecode(input, msgs0);
|
|
3814
|
+
}
|
|
3815
|
+
} catch (e) {
|
|
3816
|
+
dlog(`messages.transform png: swallowed error (turn unaffected): ${e.message}`);
|
|
3817
|
+
}
|
|
3194
3818
|
try {
|
|
3195
3819
|
if (!inlineCrushEnabled())
|
|
3196
3820
|
return;
|