vibe-cokit 1.6.2 → 1.6.4
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/cli.js +138 -59
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2672,6 +2672,14 @@ import { join as join5, dirname, basename } from "path";
|
|
|
2672
2672
|
import { readdir as readdir2, stat as stat2, cp as cp2, readFile, writeFile } from "fs/promises";
|
|
2673
2673
|
var PATCH_MARKER = "/* Vietnamese IME fix */";
|
|
2674
2674
|
var DEL_CHAR = "\x7F";
|
|
2675
|
+
var BINARY_BUG_PATTERN_REGEXES = [
|
|
2676
|
+
/\.includes\(\s*["']\\x7[fF]["']\s*\)/g,
|
|
2677
|
+
/\.includes\(\s*["']\\u007[fF]["']\s*\)/g,
|
|
2678
|
+
/\.indexOf\(\s*["']\\x7[fF]["']\s*\)\s*(?:>=?\s*0|>\s*-1|!==?\s*-1)/g,
|
|
2679
|
+
/\.indexOf\(\s*["']\\u007[fF]["']\s*\)\s*(?:>=?\s*0|>\s*-1|!==?\s*-1)/g
|
|
2680
|
+
];
|
|
2681
|
+
var BINARY_UNPATCHED_MARKER = "deleteToken" + "Before";
|
|
2682
|
+
var BINARY_UNPATCHED_MARKER_RE = new RegExp(`\\b${BINARY_UNPATCHED_MARKER}\\b`);
|
|
2675
2683
|
function detectFileType(filePath) {
|
|
2676
2684
|
if (filePath.includes("/claude/versions/") || filePath.includes("\\claude\\versions\\")) {
|
|
2677
2685
|
return "binary";
|
|
@@ -2770,7 +2778,108 @@ function getBugPattern(fileType) {
|
|
|
2770
2778
|
}
|
|
2771
2779
|
return `.includes("${DEL_CHAR}")`;
|
|
2772
2780
|
}
|
|
2781
|
+
function findNextBinaryPattern(content, from) {
|
|
2782
|
+
let best = null;
|
|
2783
|
+
for (const re of BINARY_BUG_PATTERN_REGEXES) {
|
|
2784
|
+
re.lastIndex = from;
|
|
2785
|
+
const m = re.exec(content);
|
|
2786
|
+
if (!m || m.index < 0)
|
|
2787
|
+
continue;
|
|
2788
|
+
const candidate = { index: m.index, length: m[0].length };
|
|
2789
|
+
if (!best || candidate.index < best.index) {
|
|
2790
|
+
best = candidate;
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
return best;
|
|
2794
|
+
}
|
|
2795
|
+
function findEnclosingIfBlock(content, anchorIndex) {
|
|
2796
|
+
const lookback = 260;
|
|
2797
|
+
const searchStart = Math.max(0, anchorIndex - lookback);
|
|
2798
|
+
const prefix = content.slice(searchStart, anchorIndex + 1);
|
|
2799
|
+
let ifRelStart = -1;
|
|
2800
|
+
const ifRegex = /if\s*\(/g;
|
|
2801
|
+
let m;
|
|
2802
|
+
while ((m = ifRegex.exec(prefix)) !== null) {
|
|
2803
|
+
ifRelStart = m.index;
|
|
2804
|
+
}
|
|
2805
|
+
if (ifRelStart === -1)
|
|
2806
|
+
return null;
|
|
2807
|
+
const blockStart = searchStart + ifRelStart;
|
|
2808
|
+
const firstBrace = content.indexOf("{", blockStart);
|
|
2809
|
+
if (firstBrace === -1 || firstBrace < blockStart || firstBrace > anchorIndex + 220) {
|
|
2810
|
+
return null;
|
|
2811
|
+
}
|
|
2812
|
+
let depth = 0;
|
|
2813
|
+
const maxEnd = Math.min(content.length, blockStart + 1600);
|
|
2814
|
+
for (let i = firstBrace;i < maxEnd; i++) {
|
|
2815
|
+
const ch = content[i];
|
|
2816
|
+
if (ch === "{")
|
|
2817
|
+
depth++;
|
|
2818
|
+
else if (ch === "}") {
|
|
2819
|
+
depth--;
|
|
2820
|
+
if (depth === 0)
|
|
2821
|
+
return { start: blockStart, end: i + 1 };
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
return null;
|
|
2825
|
+
}
|
|
2826
|
+
function isLikelyBinaryBugBlock(block) {
|
|
2827
|
+
const hasGuard = /if\s*\(\s*![\w$]+\.backspace\s*&&\s*![\w$]+\.delete\s*&&/.test(block);
|
|
2828
|
+
const hasDeleteChars = /\.match\(\/\\x7[fF]\/g\)/.test(block);
|
|
2829
|
+
const hasUpdateCalls = /[\w$]+\([\w$]+\.text\)\s*;\s*[\w$]+\([\w$]+\.offset\)/.test(block);
|
|
2830
|
+
return hasGuard && hasDeleteChars && hasUpdateCalls;
|
|
2831
|
+
}
|
|
2832
|
+
function scanBinaryBugBlocks(content) {
|
|
2833
|
+
let searchFrom = 0;
|
|
2834
|
+
let patchableBlocks = 0;
|
|
2835
|
+
let patternMatches = 0;
|
|
2836
|
+
while (true) {
|
|
2837
|
+
const match = findNextBinaryPattern(content, searchFrom);
|
|
2838
|
+
if (!match)
|
|
2839
|
+
break;
|
|
2840
|
+
patternMatches++;
|
|
2841
|
+
searchFrom = match.index + Math.max(match.length, 1);
|
|
2842
|
+
const range = findEnclosingIfBlock(content, match.index);
|
|
2843
|
+
if (!range)
|
|
2844
|
+
continue;
|
|
2845
|
+
const block = content.slice(range.start, range.end);
|
|
2846
|
+
if (!isLikelyBinaryBugBlock(block))
|
|
2847
|
+
continue;
|
|
2848
|
+
if (!BINARY_UNPATCHED_MARKER_RE.test(block))
|
|
2849
|
+
continue;
|
|
2850
|
+
try {
|
|
2851
|
+
extractBinaryVariables(block);
|
|
2852
|
+
patchableBlocks++;
|
|
2853
|
+
} catch {}
|
|
2854
|
+
}
|
|
2855
|
+
return { patchableBlocks, patternMatches };
|
|
2856
|
+
}
|
|
2773
2857
|
function findBugBlock(content, fileType = "js") {
|
|
2858
|
+
if (fileType === "binary") {
|
|
2859
|
+
let searchFrom2 = 0;
|
|
2860
|
+
while (true) {
|
|
2861
|
+
const match = findNextBinaryPattern(content, searchFrom2);
|
|
2862
|
+
if (!match) {
|
|
2863
|
+
throw new Error(`Kh\xF4ng t\xECm th\u1EA5y bug pattern c\u1EE7a binary.
|
|
2864
|
+
` + "Claude Code c\xF3 th\u1EC3 \u0111\xE3 \u0111\u01B0\u1EE3c Anthropic fix ho\u1EB7c \u0111\u1ED5i layout.");
|
|
2865
|
+
}
|
|
2866
|
+
searchFrom2 = match.index + Math.max(match.length, 1);
|
|
2867
|
+
const range = findEnclosingIfBlock(content, match.index);
|
|
2868
|
+
if (!range)
|
|
2869
|
+
continue;
|
|
2870
|
+
const block = content.slice(range.start, range.end);
|
|
2871
|
+
if (!isLikelyBinaryBugBlock(block))
|
|
2872
|
+
continue;
|
|
2873
|
+
if (!BINARY_UNPATCHED_MARKER_RE.test(block))
|
|
2874
|
+
continue;
|
|
2875
|
+
try {
|
|
2876
|
+
extractBinaryVariables(block);
|
|
2877
|
+
return { start: range.start, end: range.end, block };
|
|
2878
|
+
} catch {
|
|
2879
|
+
continue;
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2774
2883
|
const pattern = getBugPattern(fileType);
|
|
2775
2884
|
let searchFrom = 0;
|
|
2776
2885
|
while (true) {
|
|
@@ -2779,36 +2888,13 @@ function findBugBlock(content, fileType = "js") {
|
|
|
2779
2888
|
throw new Error(`Kh\xF4ng t\xECm th\u1EA5y bug pattern .includes("\\x7f").
|
|
2780
2889
|
` + "Claude Code c\xF3 th\u1EC3 \u0111\xE3 \u0111\u01B0\u1EE3c Anthropic fix.");
|
|
2781
2890
|
}
|
|
2782
|
-
const
|
|
2783
|
-
|
|
2784
|
-
if (blockStart === -1 || blockStart < searchStart) {
|
|
2785
|
-
searchFrom = idx + 1;
|
|
2786
|
-
continue;
|
|
2787
|
-
}
|
|
2788
|
-
let depth = 0;
|
|
2789
|
-
let blockEnd = idx;
|
|
2790
|
-
const slice = content.slice(blockStart, blockStart + 800);
|
|
2791
|
-
for (let i = 0;i < slice.length; i++) {
|
|
2792
|
-
if (slice[i] === "{")
|
|
2793
|
-
depth++;
|
|
2794
|
-
else if (slice[i] === "}") {
|
|
2795
|
-
depth--;
|
|
2796
|
-
if (depth === 0) {
|
|
2797
|
-
blockEnd = blockStart + i + 1;
|
|
2798
|
-
break;
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
if (depth !== 0) {
|
|
2891
|
+
const range = findEnclosingIfBlock(content, idx);
|
|
2892
|
+
if (!range) {
|
|
2803
2893
|
searchFrom = idx + 1;
|
|
2804
2894
|
continue;
|
|
2805
2895
|
}
|
|
2806
|
-
const block = content.slice(
|
|
2807
|
-
|
|
2808
|
-
searchFrom = idx + 1;
|
|
2809
|
-
continue;
|
|
2810
|
-
}
|
|
2811
|
-
return { start: blockStart, end: blockEnd, block };
|
|
2896
|
+
const block = content.slice(range.start, range.end);
|
|
2897
|
+
return { start: range.start, end: range.end, block };
|
|
2812
2898
|
}
|
|
2813
2899
|
}
|
|
2814
2900
|
function extractVariables(block) {
|
|
@@ -2841,7 +2927,8 @@ function generateBinaryFix(block, vars) {
|
|
|
2841
2927
|
const v = vars;
|
|
2842
2928
|
const counterMatch = block.match(/let ([\w$]+)=\(/);
|
|
2843
2929
|
const counter = counterMatch?.[1] ?? "XH";
|
|
2844
|
-
|
|
2930
|
+
const ke = v.keyEvent ?? "FH";
|
|
2931
|
+
let fix = `if(!${ke}.backspace&&!${ke}.delete&&${v.input}.includes("\\x7F")){` + `let ${counter}=(${v.input}.match(/\\x7f/g)||[]).length,` + `${v.state}=${v.curState};` + `while(${counter}--)${v.state}=${v.state}.backspace();` + `for(${counter} of ${v.input}.replace(/\\x7f/g,""))${v.state}=${v.state}.insert(${counter});` + `if(!${v.curState}.equals(${v.state})){` + `if(${v.curState}.text!==${v.state}.text)` + `${v.updateText}(${v.state}.text);` + `${v.updateOffset}(${v.state}.offset)` + `}return}`;
|
|
2845
2932
|
const diff = block.length - fix.length;
|
|
2846
2933
|
if (diff > 0) {
|
|
2847
2934
|
fix = fix.slice(0, -1) + " ".repeat(diff) + "}";
|
|
@@ -2866,9 +2953,9 @@ async function patchCliJs(filePath) {
|
|
|
2866
2953
|
const backupPath = join5(backupDir, `${basename(filePath)}.backup-${timestamp}`);
|
|
2867
2954
|
await cp2(filePath, backupPath);
|
|
2868
2955
|
try {
|
|
2869
|
-
const bugPattern = getBugPattern(fileType);
|
|
2870
2956
|
let patched = content;
|
|
2871
2957
|
if (fileType === "binary") {
|
|
2958
|
+
const beforeScan = scanBinaryBugBlocks(content);
|
|
2872
2959
|
let patchCount = 0;
|
|
2873
2960
|
while (patchCount < 10) {
|
|
2874
2961
|
try {
|
|
@@ -2882,7 +2969,10 @@ async function patchCliJs(filePath) {
|
|
|
2882
2969
|
}
|
|
2883
2970
|
}
|
|
2884
2971
|
if (patchCount === 0) {
|
|
2885
|
-
|
|
2972
|
+
if (beforeScan.patternMatches > 0) {
|
|
2973
|
+
throw new Error("Ph\xE1t hi\u1EC7n layout keyboard m\u1EDBi ch\u01B0a \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3 patch");
|
|
2974
|
+
}
|
|
2975
|
+
throw new Error("Kh\xF4ng t\xECm th\u1EA5y bug block (Claude c\xF3 th\u1EC3 \u0111\xE3 fix upstream)");
|
|
2886
2976
|
}
|
|
2887
2977
|
} else {
|
|
2888
2978
|
const { start, end, block } = findBugBlock(content, fileType);
|
|
@@ -2896,16 +2986,9 @@ async function patchCliJs(filePath) {
|
|
|
2896
2986
|
await writeContent(filePath, patched, fileType);
|
|
2897
2987
|
const verify = await readContent(filePath, fileType);
|
|
2898
2988
|
if (fileType === "binary") {
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
if (pos === -1)
|
|
2903
|
-
break;
|
|
2904
|
-
const vicinity = verify.slice(Math.max(0, pos - 100), pos + 300);
|
|
2905
|
-
if (vicinity.includes("deleteTokenBefore")) {
|
|
2906
|
-
throw new Error("Verify failed: unpatched bug block still present after binary patch");
|
|
2907
|
-
}
|
|
2908
|
-
searchPos = pos + 1;
|
|
2989
|
+
const afterScan = scanBinaryBugBlocks(verify);
|
|
2990
|
+
if (afterScan.patchableBlocks > 0) {
|
|
2991
|
+
throw new Error("Verify failed: unpatched bug block still present after binary patch");
|
|
2909
2992
|
}
|
|
2910
2993
|
} else {
|
|
2911
2994
|
if (!verify.includes(PATCH_MARKER)) {
|
|
@@ -2926,22 +3009,30 @@ async function patchCliJs(filePath) {
|
|
|
2926
3009
|
}
|
|
2927
3010
|
}
|
|
2928
3011
|
function extractBinaryVariables(block) {
|
|
2929
|
-
const
|
|
3012
|
+
const m0 = block.match(/if\s*\(\s*!([\w$]+)\.backspace\s*&&\s*!([\w$]+)\.delete\s*&&/);
|
|
3013
|
+
if (!m0)
|
|
3014
|
+
throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c bi\u1EBFn key event (binary)");
|
|
3015
|
+
const keyEvent = m0[1];
|
|
3016
|
+
const m1 = block.match(/(?:let|const)\s+([\w$]+)\s*=\s*\(([\w$]+)\.match\(\/\\x7[fF]\/g\)\|\|\[\]\)\.length\s*[,;]\s*([\w$]+)\s*=\s*([\w$]+)[;,]/);
|
|
2930
3017
|
if (!m1)
|
|
2931
3018
|
throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c bi\u1EBFn count/state (binary)");
|
|
2932
3019
|
const input = m1[2];
|
|
2933
3020
|
const state = m1[3];
|
|
2934
3021
|
const curState = m1[4];
|
|
2935
3022
|
const stateEsc = state.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2936
|
-
const m2 = block.match(new RegExp(`([\\w$]+)\\(${stateEsc}\\.text\\)
|
|
3023
|
+
const m2 = block.match(new RegExp(`([\\w$]+)\\(${stateEsc}\\.text\\)\\s*;\\s*([\\w$]+)\\(${stateEsc}\\.offset\\)`));
|
|
2937
3024
|
if (!m2)
|
|
2938
3025
|
throw new Error("Kh\xF4ng tr\xEDch xu\u1EA5t \u0111\u01B0\u1EE3c update functions (binary)");
|
|
3026
|
+
if (!block.includes(`${state}.text`) || !block.includes(`${state}.offset`)) {
|
|
3027
|
+
throw new Error("Block binary kh\xF4ng h\u1EE3p l\u1EC7: thi\u1EBFu state usage");
|
|
3028
|
+
}
|
|
2939
3029
|
return {
|
|
2940
3030
|
input,
|
|
2941
3031
|
state,
|
|
2942
3032
|
curState,
|
|
2943
3033
|
updateText: m2[1],
|
|
2944
|
-
updateOffset: m2[2]
|
|
3034
|
+
updateOffset: m2[2],
|
|
3035
|
+
keyEvent
|
|
2945
3036
|
};
|
|
2946
3037
|
}
|
|
2947
3038
|
async function checkKeyboardStatus() {
|
|
@@ -2949,26 +3040,14 @@ async function checkKeyboardStatus() {
|
|
|
2949
3040
|
const cliJsPath = await findCliJs();
|
|
2950
3041
|
const fileType = detectFileType(cliJsPath);
|
|
2951
3042
|
const content = await readContent(cliJsPath, fileType);
|
|
2952
|
-
const bugPattern = getBugPattern(fileType);
|
|
2953
3043
|
let patched;
|
|
2954
3044
|
let hasBug;
|
|
2955
3045
|
if (fileType === "binary") {
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
const pos = content.indexOf(bugPattern, searchPos);
|
|
2960
|
-
if (pos === -1)
|
|
2961
|
-
break;
|
|
2962
|
-
const vicinity = content.slice(Math.max(0, pos - 100), pos + 300);
|
|
2963
|
-
if (vicinity.includes("deleteTokenBefore")) {
|
|
2964
|
-
hasUnpatchedBlock = true;
|
|
2965
|
-
break;
|
|
2966
|
-
}
|
|
2967
|
-
searchPos = pos + 1;
|
|
2968
|
-
}
|
|
2969
|
-
hasBug = hasUnpatchedBlock;
|
|
2970
|
-
patched = content.includes(bugPattern) && !hasUnpatchedBlock;
|
|
3046
|
+
const scan = scanBinaryBugBlocks(content);
|
|
3047
|
+
hasBug = scan.patchableBlocks > 0;
|
|
3048
|
+
patched = !hasBug && scan.patternMatches > 0;
|
|
2971
3049
|
} else {
|
|
3050
|
+
const bugPattern = getBugPattern(fileType);
|
|
2972
3051
|
patched = content.includes(PATCH_MARKER);
|
|
2973
3052
|
hasBug = content.includes(bugPattern) && !patched;
|
|
2974
3053
|
}
|