@oh-my-pi/pi-coding-agent 16.1.8 → 16.1.9
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/CHANGELOG.md +25 -0
- package/dist/cli.js +4621 -3814
- package/dist/types/cli/flag-tables.d.ts +17 -0
- package/dist/types/cli-commands.d.ts +4 -2
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/types.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +13 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/package.json +12 -12
- package/src/cli/args.ts +0 -1
- package/src/cli/flag-tables.ts +42 -0
- package/src/cli/profile-bootstrap.ts +1 -11
- package/src/cli-commands.ts +48 -3
- package/src/config/model-registry.ts +6 -7
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/lsp/client.ts +39 -25
- package/src/modes/controllers/command-controller.ts +18 -3
- package/src/modes/controllers/mcp-command-controller.ts +40 -12
- package/src/modes/interactive-mode.ts +1 -1
- package/src/modes/types.ts +1 -1
- package/src/session/agent-session.ts +36 -1
- package/src/slash-commands/builtin-registry.ts +24 -66
- package/src/tools/browser/launch.ts +107 -31
- package/src/tools/puppeteer/00_stealth_tampering.txt +16 -35
- package/src/tools/puppeteer/01_stealth_activity.txt +79 -19
- package/src/tools/puppeteer/02_stealth_hairline.txt +57 -11
- package/src/tools/puppeteer/03_stealth_botd.txt +10 -14
- package/src/tools/puppeteer/04_stealth_iframe.txt +156 -63
- package/src/tools/puppeteer/05_stealth_webgl.txt +222 -64
- package/src/tools/puppeteer/06_stealth_screen.txt +255 -67
- package/src/tools/puppeteer/07_stealth_fonts.txt +17 -15
- package/src/tools/puppeteer/08_stealth_audio.txt +32 -20
- package/src/tools/puppeteer/09_stealth_locale.txt +45 -40
- package/src/tools/puppeteer/10_stealth_plugins.txt +12 -8
- package/src/tools/puppeteer/11_stealth_hardware.txt +57 -6
- package/src/tools/puppeteer/12_stealth_codecs.txt +20 -18
- package/src/tools/puppeteer/13_stealth_worker.txt +206 -45
|
@@ -47,7 +47,6 @@ const commonFonts = [
|
|
|
47
47
|
"Wingdings 3",
|
|
48
48
|
"Apple Color Emoji",
|
|
49
49
|
"Apple SD Gothic Neo",
|
|
50
|
-
"Helvetica Neue",
|
|
51
50
|
"Hoefler Text",
|
|
52
51
|
"Menlo",
|
|
53
52
|
"Monaco",
|
|
@@ -56,32 +55,31 @@ const commonFonts = [
|
|
|
56
55
|
"SF Pro Text",
|
|
57
56
|
];
|
|
58
57
|
|
|
59
|
-
// Override queryLocalFonts if present (Local Font Access API)
|
|
60
58
|
if ("queryLocalFonts" in window) {
|
|
59
|
+
const queryLocalFonts = async function queryLocalFonts() {
|
|
60
|
+
return commonFonts.map((family) => ({
|
|
61
|
+
family,
|
|
62
|
+
fullName: family,
|
|
63
|
+
postscriptName: family.replace(/\s+/g, ""),
|
|
64
|
+
style: "Regular",
|
|
65
|
+
blob: () => Promise_resolve(new Window_Blob([])),
|
|
66
|
+
}));
|
|
67
|
+
};
|
|
68
|
+
patchToString(queryLocalFonts, "queryLocalFonts");
|
|
61
69
|
Object_defineProperty(window, "queryLocalFonts", {
|
|
62
|
-
value:
|
|
63
|
-
return commonFonts.map((family) => ({
|
|
64
|
-
family,
|
|
65
|
-
fullName: family,
|
|
66
|
-
postscriptName: family.replace(/\s+/g, ""),
|
|
67
|
-
style: "Regular",
|
|
68
|
-
blob: () => Promise_resolve(new Window_Blob([])),
|
|
69
|
-
}));
|
|
70
|
-
},
|
|
70
|
+
value: queryLocalFonts,
|
|
71
71
|
writable: true,
|
|
72
72
|
configurable: true,
|
|
73
73
|
enumerable: true,
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// Hide fonts-unique tracking via canvas
|
|
78
77
|
const originalGetContext = HTMLCanvasElement.prototype.getContext;
|
|
79
|
-
|
|
78
|
+
const patchedGetContext = function getContext(type, options) {
|
|
80
79
|
const ctx = originalGetContext.call(this, type, options);
|
|
81
80
|
if (ctx && type === "2d") {
|
|
82
81
|
const originalFillText = ctx.fillText;
|
|
83
|
-
|
|
84
|
-
// Add tiny imperceptible noise to text rendering
|
|
82
|
+
const patchedFillText = function fillText(text, x, y, maxWidth) {
|
|
85
83
|
const noiseX = (Math_random() - 0.5) * 0.02;
|
|
86
84
|
const noiseY = (Math_random() - 0.5) * 0.02;
|
|
87
85
|
return originalFillText.call(
|
|
@@ -92,6 +90,10 @@ HTMLCanvasElement.prototype.getContext = function (type, options) {
|
|
|
92
90
|
maxWidth,
|
|
93
91
|
);
|
|
94
92
|
};
|
|
93
|
+
patchToString(patchedFillText, "fillText");
|
|
94
|
+
ctx.fillText = patchedFillText;
|
|
95
95
|
}
|
|
96
96
|
return ctx;
|
|
97
97
|
};
|
|
98
|
+
patchToString(patchedGetContext, "getContext");
|
|
99
|
+
HTMLCanvasElement.prototype.getContext = patchedGetContext;
|
|
@@ -1,32 +1,45 @@
|
|
|
1
|
-
// Spoof AudioContext latency values to look like real hardware
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
// Spoof AudioContext latency values to look like real hardware.
|
|
2
|
+
const audioLatencyAccessors = {
|
|
3
|
+
get baseLatency() {
|
|
4
|
+
return 0.005;
|
|
5
|
+
},
|
|
6
|
+
get outputLatency() {
|
|
7
|
+
return 0.01;
|
|
8
|
+
},
|
|
9
|
+
get sampleRate() {
|
|
10
|
+
return 48000;
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const defineAudioGetter = (proto, name) => {
|
|
15
|
+
const descriptor = Object_getOwnPropertyDescriptor(audioLatencyAccessors, name);
|
|
16
|
+
if (!descriptor || !descriptor.get) return;
|
|
17
|
+
patchToString(descriptor.get, "get " + name);
|
|
18
|
+
Object_defineProperty(proto, name, {
|
|
19
|
+
get: descriptor.get,
|
|
10
20
|
configurable: true,
|
|
11
21
|
enumerable: true,
|
|
12
22
|
});
|
|
13
23
|
};
|
|
14
24
|
|
|
25
|
+
const spoofLatency = (proto) => {
|
|
26
|
+
defineAudioGetter(proto, "baseLatency");
|
|
27
|
+
defineAudioGetter(proto, "outputLatency");
|
|
28
|
+
};
|
|
29
|
+
|
|
15
30
|
if (window.AudioContext) {
|
|
16
31
|
spoofLatency(AudioContext.prototype);
|
|
17
32
|
}
|
|
33
|
+
|
|
18
34
|
if (window.OfflineAudioContext) {
|
|
19
|
-
// For offline context, add subtle randomness to prevent deterministic fingerprints
|
|
20
35
|
const OriginalOfflineAudioContext = window.OfflineAudioContext;
|
|
21
|
-
|
|
36
|
+
const PatchedOfflineAudioContext = class OfflineAudioContext extends OriginalOfflineAudioContext {
|
|
22
37
|
constructor(numberOfChannels, length, sampleRate) {
|
|
23
38
|
super(numberOfChannels, length, sampleRate);
|
|
24
39
|
|
|
25
|
-
// Hook startRendering to add noise
|
|
26
40
|
const originalStartRendering = this.startRendering.bind(this);
|
|
27
|
-
|
|
41
|
+
const patchedStartRendering = async function startRendering() {
|
|
28
42
|
const buffer = await originalStartRendering();
|
|
29
|
-
// Add imperceptible noise to prevent deterministic hash
|
|
30
43
|
for (let c = 0; c < buffer.numberOfChannels; c++) {
|
|
31
44
|
const channel = buffer.getChannelData(c);
|
|
32
45
|
for (let i = 0; i < channel.length; i++) {
|
|
@@ -37,15 +50,14 @@ if (window.OfflineAudioContext) {
|
|
|
37
50
|
}
|
|
38
51
|
return buffer;
|
|
39
52
|
};
|
|
53
|
+
patchToString(patchedStartRendering, "startRendering");
|
|
54
|
+
this.startRendering = patchedStartRendering;
|
|
40
55
|
}
|
|
41
56
|
};
|
|
57
|
+
patchToString(PatchedOfflineAudioContext, "OfflineAudioContext");
|
|
58
|
+
window.OfflineAudioContext = PatchedOfflineAudioContext;
|
|
42
59
|
}
|
|
43
60
|
|
|
44
|
-
// Also spoof sampleRate consistency
|
|
45
61
|
if (window.AudioContext) {
|
|
46
|
-
|
|
47
|
-
get: () => 48000, // Common hardware rate
|
|
48
|
-
configurable: true,
|
|
49
|
-
enumerable: true,
|
|
50
|
-
});
|
|
62
|
+
defineAudioGetter(AudioContext.prototype, "sampleRate");
|
|
51
63
|
}
|
|
@@ -1,46 +1,51 @@
|
|
|
1
|
-
// Define a consistent locale profile
|
|
2
1
|
const locale = "en-US";
|
|
3
|
-
const languages = [
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
configurable: true,
|
|
10
|
-
enumerable: true,
|
|
11
|
-
});
|
|
12
|
-
Object_defineProperty(navigator, "languages", {
|
|
13
|
-
get: () => [...languages],
|
|
14
|
-
configurable: true,
|
|
15
|
-
enumerable: true,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
// Override Intl.DateTimeFormat for timezone consistency
|
|
19
|
-
const OriginalDateTimeFormat = Intl_DateTimeFormat;
|
|
20
|
-
Intl.DateTimeFormat = class extends OriginalDateTimeFormat {
|
|
21
|
-
constructor(locales, options) {
|
|
22
|
-
const mergedOptions = { ...options, timeZone: timezone };
|
|
23
|
-
super(locales, mergedOptions);
|
|
24
|
-
}
|
|
25
|
-
resolvedOptions() {
|
|
26
|
-
const options = super.resolvedOptions();
|
|
27
|
-
options.timeZone = timezone;
|
|
28
|
-
return options;
|
|
2
|
+
const languages = [locale, "en"];
|
|
3
|
+
|
|
4
|
+
const sameLanguages = (value) => {
|
|
5
|
+
if (!value || value.length !== languages.length) return false;
|
|
6
|
+
for (let index = 0; index < languages.length; index += 1) {
|
|
7
|
+
if (value[index] !== languages[index]) return false;
|
|
29
8
|
}
|
|
9
|
+
return true;
|
|
30
10
|
};
|
|
31
11
|
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.replace(/\(.*\)$/, "(Eastern Standard Time)");
|
|
12
|
+
const navigatorProto = Object_getPrototypeOf(navigator);
|
|
13
|
+
const navigatorAccessors = {
|
|
14
|
+
get language() {
|
|
15
|
+
return locale;
|
|
16
|
+
},
|
|
17
|
+
get languages() {
|
|
18
|
+
return [locale, "en"];
|
|
19
|
+
},
|
|
41
20
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
21
|
+
|
|
22
|
+
const defineNavigatorAccessor = (name) => {
|
|
23
|
+
const owner = navigatorProto || navigator;
|
|
24
|
+
const descriptor =
|
|
25
|
+
(navigatorProto && Object_getOwnPropertyDescriptor(navigatorProto, name)) ||
|
|
26
|
+
Object_getOwnPropertyDescriptor(navigator, name);
|
|
27
|
+
if (descriptor && descriptor.configurable === false) return;
|
|
28
|
+
|
|
29
|
+
const accessor = Object_getOwnPropertyDescriptor(navigatorAccessors, name);
|
|
30
|
+
if (!accessor || !accessor.get) return;
|
|
31
|
+
patchToString(accessor.get, "get " + name);
|
|
32
|
+
|
|
33
|
+
Object_defineProperty(owner, name, {
|
|
34
|
+
get: accessor.get,
|
|
35
|
+
configurable: descriptor ? descriptor.configurable : true,
|
|
36
|
+
enumerable: descriptor ? descriptor.enumerable : true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const ownDescriptor = Object_getOwnPropertyDescriptor(navigator, name);
|
|
40
|
+
if (owner !== navigator && ownDescriptor && ownDescriptor.configurable !== false) {
|
|
41
|
+
Reflect_deleteProperty(navigator, name);
|
|
42
|
+
}
|
|
46
43
|
};
|
|
44
|
+
|
|
45
|
+
if (navigator.language !== locale) {
|
|
46
|
+
defineNavigatorAccessor("language");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!sameLanguages(navigator.languages)) {
|
|
50
|
+
defineNavigatorAccessor("languages");
|
|
51
|
+
}
|
|
@@ -54,8 +54,8 @@ const defineProp = (obj, prop, value) =>
|
|
|
54
54
|
configurable: true,
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
const generateFunctionMocks = (proto, itemMainProp, dataArray) =>
|
|
58
|
-
item
|
|
57
|
+
const generateFunctionMocks = (proto, itemMainProp, dataArray) => {
|
|
58
|
+
const item = new Window_Proxy(proto.item, {
|
|
59
59
|
apply(target, ctx, args) {
|
|
60
60
|
if (!args.length) {
|
|
61
61
|
throw new TypeError(
|
|
@@ -65,8 +65,8 @@ const generateFunctionMocks = (proto, itemMainProp, dataArray) => ({
|
|
|
65
65
|
const isInteger = args[0] && Number.isInteger(Number(args[0]));
|
|
66
66
|
return (isInteger ? dataArray[Number(args[0])] : dataArray[0]) || null;
|
|
67
67
|
},
|
|
68
|
-
})
|
|
69
|
-
namedItem
|
|
68
|
+
});
|
|
69
|
+
const namedItem = new Window_Proxy(proto.namedItem, {
|
|
70
70
|
apply(target, ctx, args) {
|
|
71
71
|
if (!args.length) {
|
|
72
72
|
throw new TypeError(
|
|
@@ -75,15 +75,19 @@ const generateFunctionMocks = (proto, itemMainProp, dataArray) => ({
|
|
|
75
75
|
}
|
|
76
76
|
return dataArray.find(item => item[itemMainProp] === args[0]) || null;
|
|
77
77
|
},
|
|
78
|
-
})
|
|
79
|
-
|
|
78
|
+
});
|
|
79
|
+
patchToString(item, "item");
|
|
80
|
+
patchToString(namedItem, "namedItem");
|
|
81
|
+
const refresh = proto.refresh
|
|
80
82
|
? new Window_Proxy(proto.refresh, {
|
|
81
83
|
apply() {
|
|
82
84
|
return undefined;
|
|
83
85
|
},
|
|
84
86
|
})
|
|
85
|
-
: undefined
|
|
86
|
-
|
|
87
|
+
: undefined;
|
|
88
|
+
patchToString(refresh, "refresh");
|
|
89
|
+
return { item, namedItem, refresh };
|
|
90
|
+
};
|
|
87
91
|
|
|
88
92
|
const generateMagicArray = (dataArray, proto, itemProto, itemMainProp) => {
|
|
89
93
|
const makeItem = (data) => {
|
|
@@ -1,8 +1,59 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
const hardwareConcurrencyName = "hardwareConcurrency";
|
|
2
|
+
let hardwareConcurrencyProto = Object_getPrototypeOf(navigator);
|
|
3
|
+
let hardwareConcurrencyOwner;
|
|
4
|
+
let hardwareConcurrencyDescriptor;
|
|
5
|
+
|
|
6
|
+
while (hardwareConcurrencyProto && !hardwareConcurrencyDescriptor) {
|
|
7
|
+
hardwareConcurrencyDescriptor = Object_getOwnPropertyDescriptor(
|
|
8
|
+
hardwareConcurrencyProto,
|
|
9
|
+
hardwareConcurrencyName,
|
|
10
|
+
);
|
|
11
|
+
if (hardwareConcurrencyDescriptor) {
|
|
12
|
+
hardwareConcurrencyOwner = hardwareConcurrencyProto;
|
|
13
|
+
} else {
|
|
14
|
+
hardwareConcurrencyProto = Object_getPrototypeOf(hardwareConcurrencyProto);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const hardwareConcurrencyValue = 8;
|
|
19
|
+
let shouldPatchHardwareConcurrency = false;
|
|
20
|
+
|
|
21
|
+
if (
|
|
22
|
+
hardwareConcurrencyOwner &&
|
|
23
|
+
hardwareConcurrencyDescriptor &&
|
|
24
|
+
hardwareConcurrencyDescriptor.configurable &&
|
|
25
|
+
typeof hardwareConcurrencyDescriptor.get === "function"
|
|
26
|
+
) {
|
|
27
|
+
shouldPatchHardwareConcurrency = true;
|
|
28
|
+
try {
|
|
29
|
+
shouldPatchHardwareConcurrency =
|
|
30
|
+
Reflect_apply(hardwareConcurrencyDescriptor.get, navigator, []) !==
|
|
31
|
+
hardwareConcurrencyValue;
|
|
32
|
+
} catch {
|
|
33
|
+
shouldPatchHardwareConcurrency = false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (shouldPatchHardwareConcurrency) {
|
|
38
|
+
const hardwareConcurrencyAccessors = {
|
|
39
|
+
get hardwareConcurrency() {
|
|
40
|
+
Reflect_apply(hardwareConcurrencyDescriptor.get, this, []);
|
|
41
|
+
return hardwareConcurrencyValue;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
const getHardwareConcurrency = Object_getOwnPropertyDescriptor(
|
|
45
|
+
hardwareConcurrencyAccessors,
|
|
46
|
+
hardwareConcurrencyName,
|
|
47
|
+
).get;
|
|
48
|
+
|
|
49
|
+
if (typeof patchToString === "function") {
|
|
50
|
+
patchToString(getHardwareConcurrency, "get hardwareConcurrency");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Object_defineProperty(hardwareConcurrencyOwner, hardwareConcurrencyName, {
|
|
54
|
+
get: getHardwareConcurrency,
|
|
55
|
+
set: hardwareConcurrencyDescriptor.set,
|
|
56
|
+
enumerable: hardwareConcurrencyDescriptor.enumerable,
|
|
57
|
+
configurable: hardwareConcurrencyDescriptor.configurable,
|
|
7
58
|
});
|
|
8
59
|
}
|
|
@@ -15,25 +15,27 @@ const parseInput = (arg) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
const originalCanPlayType = HTMLMediaElement.prototype.canPlayType;
|
|
18
|
+
const proxiedCanPlayType = new Window_Proxy(originalCanPlayType, {
|
|
19
|
+
apply(target, ctx, args) {
|
|
20
|
+
if (!args || !args.length) {
|
|
21
|
+
return Reflect_apply(target, ctx, args);
|
|
22
|
+
}
|
|
23
|
+
const { mime, codecs } = parseInput(args[0]);
|
|
24
|
+
if (mime === "video/mp4" && codecs.includes("avc1.42E01E")) {
|
|
25
|
+
return "probably";
|
|
26
|
+
}
|
|
27
|
+
if (mime === "audio/x-m4a" && !codecs.length) {
|
|
28
|
+
return "maybe";
|
|
29
|
+
}
|
|
30
|
+
if (mime === "audio/aac" && !codecs.length) {
|
|
31
|
+
return "probably";
|
|
32
|
+
}
|
|
33
|
+
return Reflect_apply(target, ctx, args);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
patchToString(proxiedCanPlayType, "canPlayType");
|
|
18
37
|
Object_defineProperty(HTMLMediaElement.prototype, "canPlayType", {
|
|
19
|
-
value:
|
|
20
|
-
apply(target, ctx, args) {
|
|
21
|
-
if (!args || !args.length) {
|
|
22
|
-
return Reflect_apply(target, ctx, args);
|
|
23
|
-
}
|
|
24
|
-
const { mime, codecs } = parseInput(args[0]);
|
|
25
|
-
if (mime === "video/mp4" && codecs.includes("avc1.42E01E")) {
|
|
26
|
-
return "probably";
|
|
27
|
-
}
|
|
28
|
-
if (mime === "audio/x-m4a" && !codecs.length) {
|
|
29
|
-
return "maybe";
|
|
30
|
-
}
|
|
31
|
-
if (mime === "audio/aac" && !codecs.length) {
|
|
32
|
-
return "probably";
|
|
33
|
-
}
|
|
34
|
-
return Reflect_apply(target, ctx, args);
|
|
35
|
-
},
|
|
36
|
-
}),
|
|
38
|
+
value: proxiedCanPlayType,
|
|
37
39
|
writable: true,
|
|
38
40
|
configurable: true,
|
|
39
41
|
enumerable: true,
|
|
@@ -1,52 +1,214 @@
|
|
|
1
1
|
const patchWorkerConstructor = (name, OriginalWorker) => {
|
|
2
2
|
if (typeof OriginalWorker !== "function") return;
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
4
|
+
const windowDescriptor = Object_getOwnPropertyDescriptor(window, name);
|
|
5
|
+
if (windowDescriptor && windowDescriptor.configurable === false) return;
|
|
6
|
+
|
|
7
|
+
const NativeURL = window.URL;
|
|
8
|
+
const URL_createObjectURL = NativeURL && NativeURL.createObjectURL;
|
|
9
|
+
const URL_revokeObjectURL = NativeURL && NativeURL.revokeObjectURL;
|
|
10
|
+
if (
|
|
11
|
+
typeof NativeURL !== "function" ||
|
|
12
|
+
typeof URL_createObjectURL !== "function" ||
|
|
13
|
+
typeof URL_revokeObjectURL !== "function"
|
|
14
|
+
) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const sharedWorkerUrls = name === "SharedWorker" ? new Map() : undefined;
|
|
19
|
+
|
|
20
|
+
const revokeUrl = (url) => {
|
|
21
|
+
try {
|
|
22
|
+
Reflect_apply(URL_revokeObjectURL, NativeURL, [url]);
|
|
23
|
+
} catch {}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const scheduleWorkerUrlRevoke = (worker, url) => {
|
|
27
|
+
let revoked = false;
|
|
28
|
+
const revokeOnce = () => {
|
|
29
|
+
if (revoked) return;
|
|
30
|
+
revoked = true;
|
|
31
|
+
revokeUrl(url);
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
if (typeof worker.addEventListener === "function") {
|
|
35
|
+
Reflect_apply(worker.addEventListener, worker, ["error", revokeOnce, { once: true }]);
|
|
36
|
+
}
|
|
37
|
+
} catch {}
|
|
38
|
+
Window_setTimeout(revokeOnce, 1000);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (sharedWorkerUrls) {
|
|
42
|
+
try {
|
|
43
|
+
window.addEventListener("pagehide", (event) => {
|
|
44
|
+
if (event && event.persisted) return;
|
|
45
|
+
for (const url of sharedWorkerUrls.values()) {
|
|
46
|
+
revokeUrl(url);
|
|
47
|
+
}
|
|
48
|
+
sharedWorkerUrls.clear();
|
|
49
|
+
});
|
|
50
|
+
} catch {}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const canWrapArguments = (args) => {
|
|
54
|
+
if (!args || args.length !== 1) {
|
|
55
|
+
return name === "SharedWorker" && args && args.length === 2 && typeof args[1] === "string";
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const resolveWorkerUrl = (scriptURL) => {
|
|
61
|
+
const baseUrl = document.baseURI || window.location.href;
|
|
62
|
+
const scriptUrlString =
|
|
63
|
+
typeof scriptURL === "string"
|
|
64
|
+
? scriptURL
|
|
65
|
+
: scriptURL instanceof NativeURL
|
|
66
|
+
? scriptURL.href
|
|
67
|
+
: undefined;
|
|
68
|
+
if (scriptUrlString === undefined) return undefined;
|
|
69
|
+
let absoluteUrl;
|
|
70
|
+
try {
|
|
71
|
+
absoluteUrl = new NativeURL(scriptUrlString, baseUrl);
|
|
72
|
+
} catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
if (absoluteUrl.origin !== window.location.origin) return undefined;
|
|
76
|
+
if (absoluteUrl.protocol !== "http:" && absoluteUrl.protocol !== "https:") return undefined;
|
|
77
|
+
if (absoluteUrl.username || absoluteUrl.password) return undefined;
|
|
78
|
+
return absoluteUrl.href;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const buildWorkerPrelude = () => {
|
|
82
|
+
const values = [];
|
|
83
|
+
const addNavigatorString = (prop) => {
|
|
84
|
+
try {
|
|
85
|
+
const value = navigator[prop];
|
|
86
|
+
if (typeof value === "string") values.push([prop, value]);
|
|
87
|
+
} catch {}
|
|
88
|
+
};
|
|
89
|
+
addNavigatorString("userAgent");
|
|
90
|
+
addNavigatorString("platform");
|
|
91
|
+
if (!values.length) return "";
|
|
92
|
+
|
|
93
|
+
return `(() => {
|
|
94
|
+
try {
|
|
95
|
+
const values = ${JSON.stringify(values)};
|
|
96
|
+
const nav = self.navigator;
|
|
97
|
+
if (!nav) return;
|
|
98
|
+
const defineProperty = Object.defineProperty;
|
|
99
|
+
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
100
|
+
const getPrototypeOf = Object.getPrototypeOf;
|
|
101
|
+
const Reflect_apply = Reflect.apply;
|
|
102
|
+
const nativeFunctionToString = Function.prototype.toString;
|
|
103
|
+
const nativeSources = new WeakMap();
|
|
104
|
+
const WeakMap_get = WeakMap.prototype.get;
|
|
105
|
+
const WeakMap_has = WeakMap.prototype.has;
|
|
106
|
+
const WeakMap_set = WeakMap.prototype.set;
|
|
107
|
+
const rememberNative = (fn, source) => {
|
|
108
|
+
if (typeof fn === "function") Reflect_apply(WeakMap_set, nativeSources, [fn, source]);
|
|
109
|
+
return fn;
|
|
110
|
+
};
|
|
111
|
+
const findDescriptor = (prop) => {
|
|
112
|
+
let owner = nav;
|
|
113
|
+
while (owner) {
|
|
114
|
+
const descriptor = getOwnPropertyDescriptor(owner, prop);
|
|
115
|
+
if (descriptor) return [owner, descriptor];
|
|
116
|
+
owner = getPrototypeOf(owner);
|
|
117
|
+
}
|
|
118
|
+
return [getPrototypeOf(nav) || nav, undefined];
|
|
119
|
+
};
|
|
120
|
+
let patched = false;
|
|
121
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
122
|
+
const prop = values[i][0];
|
|
123
|
+
const value = values[i][1];
|
|
124
|
+
try {
|
|
125
|
+
if (nav[prop] === value) continue;
|
|
126
|
+
} catch (_) {}
|
|
127
|
+
const found = findDescriptor(prop);
|
|
128
|
+
const owner = found[0];
|
|
129
|
+
const descriptor = found[1];
|
|
130
|
+
if (!owner || (descriptor && descriptor.configurable === false)) continue;
|
|
131
|
+
const getterDescriptor = getOwnPropertyDescriptor({ get [prop]() { return value; } }, prop);
|
|
132
|
+
const getter = getterDescriptor && getterDescriptor.get;
|
|
133
|
+
if (typeof getter !== "function") continue;
|
|
134
|
+
rememberNative(getter, "function get " + prop + "() { [native code] }");
|
|
135
|
+
defineProperty(owner, prop, {
|
|
136
|
+
get: getter,
|
|
137
|
+
enumerable: descriptor ? descriptor.enumerable : true,
|
|
138
|
+
configurable: true,
|
|
139
|
+
});
|
|
140
|
+
patched = true;
|
|
24
141
|
}
|
|
142
|
+
if (!patched) return;
|
|
143
|
+
const toStringDescriptor = getOwnPropertyDescriptor(Function.prototype, "toString");
|
|
144
|
+
if (toStringDescriptor && toStringDescriptor.configurable === false) return;
|
|
145
|
+
const functionToString = new Proxy(nativeFunctionToString, {
|
|
146
|
+
apply(target, thisArg, args) {
|
|
147
|
+
if (Reflect_apply(WeakMap_has, nativeSources, [thisArg])) return Reflect_apply(WeakMap_get, nativeSources, [thisArg]);
|
|
148
|
+
return Reflect_apply(target, thisArg, args);
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
rememberNative(functionToString, "function toString() { [native code] }");
|
|
152
|
+
defineProperty(Function.prototype, "toString", {
|
|
153
|
+
value: functionToString,
|
|
154
|
+
writable: toStringDescriptor ? toStringDescriptor.writable : true,
|
|
155
|
+
configurable: toStringDescriptor ? toStringDescriptor.configurable : true,
|
|
156
|
+
enumerable: toStringDescriptor ? toStringDescriptor.enumerable : false,
|
|
157
|
+
});
|
|
158
|
+
} catch (_) {}
|
|
159
|
+
})();`;
|
|
160
|
+
};
|
|
25
161
|
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
162
|
+
const buildWrappedUrl = (scriptURL) => {
|
|
163
|
+
const originalUrl = resolveWorkerUrl(scriptURL);
|
|
164
|
+
if (!originalUrl) return undefined;
|
|
165
|
+
if (sharedWorkerUrls) {
|
|
166
|
+
const cachedUrl = sharedWorkerUrls.get(originalUrl);
|
|
167
|
+
if (cachedUrl) return { url: cachedUrl, cacheKey: originalUrl };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const prelude = buildWorkerPrelude();
|
|
171
|
+
if (!prelude) return undefined;
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const blob = new Window_Blob(
|
|
175
|
+
[prelude, "\n", `importScripts(${JSON.stringify(originalUrl)});`],
|
|
176
|
+
{ type: "application/javascript" },
|
|
177
|
+
);
|
|
178
|
+
const url = Reflect_apply(URL_createObjectURL, NativeURL, [blob]);
|
|
179
|
+
if (sharedWorkerUrls) sharedWorkerUrls.set(originalUrl, url);
|
|
180
|
+
return { url, cacheKey: sharedWorkerUrls ? originalUrl : undefined };
|
|
181
|
+
} catch {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
34
184
|
};
|
|
35
185
|
|
|
36
186
|
const handler = {
|
|
37
|
-
construct(target, args) {
|
|
187
|
+
construct(target, args, newTarget) {
|
|
188
|
+
if (!canWrapArguments(args)) {
|
|
189
|
+
return Reflect_construct(target, args || [], newTarget);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const wrapped = buildWrappedUrl(args[0]);
|
|
193
|
+
if (!wrapped) {
|
|
194
|
+
return Reflect_construct(target, args || [], newTarget);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const wrappedArgs = [wrapped.url];
|
|
198
|
+
for (let i = 1; i < args.length; i += 1) {
|
|
199
|
+
wrappedArgs[i] = args[i];
|
|
200
|
+
}
|
|
201
|
+
|
|
38
202
|
try {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return new target(...(args || []));
|
|
203
|
+
const worker = Reflect_construct(target, wrappedArgs, newTarget);
|
|
204
|
+
if (!sharedWorkerUrls) {
|
|
205
|
+
scheduleWorkerUrlRevoke(worker, wrapped.url);
|
|
43
206
|
}
|
|
44
|
-
const wrappedUrl = buildWrappedUrl(scriptURL, options);
|
|
45
|
-
const worker = new target(wrappedUrl, options);
|
|
46
|
-
URL.revokeObjectURL(wrappedUrl);
|
|
47
207
|
return worker;
|
|
48
208
|
} catch {
|
|
49
|
-
|
|
209
|
+
if (wrapped.cacheKey && sharedWorkerUrls) sharedWorkerUrls.delete(wrapped.cacheKey);
|
|
210
|
+
revokeUrl(wrapped.url);
|
|
211
|
+
return Reflect_construct(target, args || [], newTarget);
|
|
50
212
|
}
|
|
51
213
|
},
|
|
52
214
|
apply(target, thisArg, args) {
|
|
@@ -55,16 +217,15 @@ const patchWorkerConstructor = (name, OriginalWorker) => {
|
|
|
55
217
|
};
|
|
56
218
|
|
|
57
219
|
const proxied = new Window_Proxy(OriginalWorker, handler);
|
|
220
|
+
patchToString(proxied, name);
|
|
58
221
|
Object_defineProperty(window, name, {
|
|
59
222
|
value: proxied,
|
|
60
|
-
writable:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
configurable: true,
|
|
67
|
-
enumerable: false,
|
|
223
|
+
writable:
|
|
224
|
+
windowDescriptor && "writable" in windowDescriptor
|
|
225
|
+
? windowDescriptor.writable
|
|
226
|
+
: true,
|
|
227
|
+
configurable: windowDescriptor ? windowDescriptor.configurable : true,
|
|
228
|
+
enumerable: windowDescriptor ? windowDescriptor.enumerable : false,
|
|
68
229
|
});
|
|
69
230
|
};
|
|
70
231
|
|