@oh-my-pi/pi-coding-agent 16.1.7 → 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.
Files changed (120) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/dist/cli.js +4266 -3446
  3. package/dist/types/cli/flag-tables.d.ts +17 -0
  4. package/dist/types/cli/session-picker.d.ts +0 -1
  5. package/dist/types/cli/tiny-models-cli.d.ts +2 -0
  6. package/dist/types/cli-commands.d.ts +4 -2
  7. package/dist/types/collab/protocol.d.ts +20 -1
  8. package/dist/types/config/settings-schema.d.ts +57 -6
  9. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +5 -1
  10. package/dist/types/internal-urls/filesystem-resource.d.ts +9 -0
  11. package/dist/types/mcp/loader.d.ts +3 -2
  12. package/dist/types/mcp/manager.d.ts +4 -3
  13. package/dist/types/mcp/startup-events.d.ts +21 -4
  14. package/dist/types/mnemopi/config.d.ts +1 -0
  15. package/dist/types/modes/components/agent-hub.d.ts +1 -0
  16. package/dist/types/modes/components/agent-transcript-viewer.d.ts +1 -0
  17. package/dist/types/modes/components/assistant-message.d.ts +5 -1
  18. package/dist/types/modes/components/btw-panel.d.ts +2 -0
  19. package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -0
  20. package/dist/types/modes/components/session-selector.d.ts +0 -2
  21. package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
  22. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  23. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  24. package/dist/types/modes/controllers/streaming-reveal.d.ts +3 -2
  25. package/dist/types/modes/interactive-mode.d.ts +4 -1
  26. package/dist/types/modes/types.d.ts +4 -1
  27. package/dist/types/session/agent-session.d.ts +18 -3
  28. package/dist/types/session/session-history-format.d.ts +25 -0
  29. package/dist/types/session/snapcompact-inline.d.ts +1 -1
  30. package/dist/types/slash-commands/builtin-registry.d.ts +7 -4
  31. package/dist/types/slash-commands/types.d.ts +2 -0
  32. package/dist/types/system-prompt.d.ts +1 -0
  33. package/dist/types/tiny/models.d.ts +11 -7
  34. package/dist/types/tools/browser/launch.d.ts +5 -0
  35. package/dist/types/tools/todo.d.ts +1 -0
  36. package/dist/types/utils/thinking-display.d.ts +2 -0
  37. package/package.json +12 -12
  38. package/src/advisor/__tests__/advisor.test.ts +104 -0
  39. package/src/advisor/runtime.ts +38 -2
  40. package/src/cli/args.ts +0 -1
  41. package/src/cli/flag-tables.ts +42 -0
  42. package/src/cli/profile-bootstrap.ts +1 -11
  43. package/src/cli/session-picker.ts +1 -2
  44. package/src/cli/tiny-models-cli.ts +7 -2
  45. package/src/cli-commands.ts +48 -3
  46. package/src/collab/guest.ts +172 -20
  47. package/src/collab/host.ts +47 -5
  48. package/src/collab/protocol.ts +16 -1
  49. package/src/config/model-registry.ts +6 -7
  50. package/src/config/settings-schema.ts +59 -5
  51. package/src/edit/renderer.ts +8 -12
  52. package/src/extensibility/legacy-pi-ai-shim.ts +16 -4
  53. package/src/internal-urls/docs-index.generated.txt +1 -1
  54. package/src/internal-urls/filesystem-resource.ts +34 -0
  55. package/src/internal-urls/local-protocol.ts +7 -1
  56. package/src/internal-urls/memory-protocol.ts +5 -1
  57. package/src/internal-urls/skill-protocol.ts +20 -4
  58. package/src/internal-urls/vault-protocol.ts +5 -2
  59. package/src/lsp/client.ts +39 -25
  60. package/src/main.ts +8 -8
  61. package/src/mcp/loader.ts +4 -3
  62. package/src/mcp/manager.ts +35 -15
  63. package/src/mcp/startup-events.ts +106 -11
  64. package/src/mnemopi/config.ts +2 -0
  65. package/src/mnemopi/state.ts +3 -1
  66. package/src/modes/components/agent-hub.ts +4 -0
  67. package/src/modes/components/agent-transcript-viewer.ts +2 -0
  68. package/src/modes/components/assistant-message.ts +217 -18
  69. package/src/modes/components/btw-panel.ts +15 -3
  70. package/src/modes/components/chat-transcript-builder.ts +8 -2
  71. package/src/modes/components/model-selector.ts +72 -9
  72. package/src/modes/components/omfg-panel.ts +1 -1
  73. package/src/modes/components/session-selector.ts +4 -9
  74. package/src/modes/components/snapcompact-shape-preview.ts +1 -1
  75. package/src/modes/components/tool-execution.ts +10 -2
  76. package/src/modes/controllers/btw-controller.ts +32 -7
  77. package/src/modes/controllers/command-controller.ts +18 -3
  78. package/src/modes/controllers/event-controller.ts +24 -4
  79. package/src/modes/controllers/input-controller.ts +43 -21
  80. package/src/modes/controllers/mcp-command-controller.ts +40 -12
  81. package/src/modes/controllers/selector-controller.ts +23 -13
  82. package/src/modes/controllers/streaming-reveal.ts +78 -20
  83. package/src/modes/controllers/todo-command-controller.ts +9 -10
  84. package/src/modes/interactive-mode.ts +84 -9
  85. package/src/modes/types.ts +4 -1
  86. package/src/modes/utils/ui-helpers.ts +1 -0
  87. package/src/prompts/advisor/system.md +1 -1
  88. package/src/prompts/system/side-channel-no-tools.md +3 -0
  89. package/src/sdk.ts +12 -8
  90. package/src/session/agent-session.ts +385 -85
  91. package/src/session/history-storage.ts +15 -16
  92. package/src/session/session-history-format.ts +41 -1
  93. package/src/session/snapcompact-inline.ts +9 -6
  94. package/src/slash-commands/builtin-registry.ts +171 -87
  95. package/src/slash-commands/helpers/todo.ts +12 -6
  96. package/src/slash-commands/types.ts +2 -0
  97. package/src/system-prompt.ts +6 -11
  98. package/src/tiny/models.ts +7 -3
  99. package/src/tiny/title-client.ts +8 -2
  100. package/src/tiny/worker.ts +1 -0
  101. package/src/tools/browser/launch.ts +107 -31
  102. package/src/tools/puppeteer/00_stealth_tampering.txt +16 -35
  103. package/src/tools/puppeteer/01_stealth_activity.txt +79 -19
  104. package/src/tools/puppeteer/02_stealth_hairline.txt +57 -11
  105. package/src/tools/puppeteer/03_stealth_botd.txt +10 -14
  106. package/src/tools/puppeteer/04_stealth_iframe.txt +156 -63
  107. package/src/tools/puppeteer/05_stealth_webgl.txt +222 -64
  108. package/src/tools/puppeteer/06_stealth_screen.txt +255 -67
  109. package/src/tools/puppeteer/07_stealth_fonts.txt +17 -15
  110. package/src/tools/puppeteer/08_stealth_audio.txt +32 -20
  111. package/src/tools/puppeteer/09_stealth_locale.txt +45 -40
  112. package/src/tools/puppeteer/10_stealth_plugins.txt +12 -8
  113. package/src/tools/puppeteer/11_stealth_hardware.txt +57 -6
  114. package/src/tools/puppeteer/12_stealth_codecs.txt +20 -18
  115. package/src/tools/puppeteer/13_stealth_worker.txt +206 -45
  116. package/src/tools/search.ts +10 -1
  117. package/src/tools/sqlite-reader.ts +59 -3
  118. package/src/tools/todo.ts +6 -0
  119. package/src/tools/write.ts +4 -6
  120. package/src/utils/thinking-display.ts +78 -0
@@ -1,72 +1,260 @@
1
- // Generate consistent "real" screen dimensions based on viewport
2
- const width = window.innerWidth;
3
- const height = window.innerHeight;
4
- const availWidth = width;
5
- const availHeight = Math_max(height - 40, 0); // Account for taskbar
6
- const colorDepth = 24;
7
- const pixelDepth = 24;
8
- const devicePixelRatio = window.devicePixelRatio && window.devicePixelRatio > 1 ? window.devicePixelRatio : 1.25;
9
-
10
- const defineScreenProp = (prop, value) => {
11
- try {
12
- Object_defineProperty(window.Screen?.prototype ?? window.screen, prop, {
13
- get: () => value,
14
- configurable: true,
15
- enumerable: true,
1
+ ;(() => {
2
+ if (typeof Window_Proxy !== "function" || typeof Reflect_apply !== "function") return;
3
+
4
+ const screenObject = window.screen;
5
+ if (!screenObject) return;
6
+
7
+ const isFiniteNumber = (value) =>
8
+ typeof value === "number" &&
9
+ value === value &&
10
+ value !== Infinity &&
11
+ value !== -Infinity;
12
+
13
+ const readValue = (object, prop, fallback) => {
14
+ try {
15
+ const value = object[prop];
16
+ return value === undefined ? fallback : value;
17
+ } catch {
18
+ return fallback;
19
+ }
20
+ };
21
+
22
+ const finiteNumber = (value, fallback) =>
23
+ isFiniteNumber(value) ? value : fallback;
24
+
25
+ const positiveNumber = (value, fallback) =>
26
+ isFiniteNumber(value) && value > 0 ? value : fallback;
27
+
28
+ const integerNumber = (value, fallback) =>
29
+ isFiniteNumber(value) ? Math_floor(value) : fallback;
30
+
31
+ const positiveInteger = (value, fallback) =>
32
+ isFiniteNumber(value) && value > 0 ? Math_floor(value) : fallback;
33
+
34
+ const findDescriptorOwner = (object, prop) => {
35
+ let owner = object;
36
+ while (owner) {
37
+ let descriptor;
38
+ try {
39
+ descriptor = Object_getOwnPropertyDescriptor(owner, prop);
40
+ } catch {
41
+ return undefined;
42
+ }
43
+ if (descriptor) return [owner, descriptor];
44
+ try {
45
+ owner = Object_getPrototypeOf(owner);
46
+ } catch {
47
+ return undefined;
48
+ }
49
+ }
50
+ return undefined;
51
+ };
52
+
53
+ const patchGetter = (object, prop, getValue) => {
54
+ const found = findDescriptorOwner(object, prop);
55
+ if (!found) return false;
56
+
57
+ const owner = found[0];
58
+ const descriptor = found[1];
59
+ if (descriptor.configurable !== true || typeof descriptor.get !== "function") {
60
+ return false;
61
+ }
62
+
63
+ const originalGet = descriptor.get;
64
+ const patchedGet = new Window_Proxy(originalGet, {
65
+ apply(target, thisArg, args) {
66
+ const nativeValue = Reflect_apply(target, thisArg, args);
67
+ return getValue(nativeValue, thisArg);
68
+ },
16
69
  });
17
- } catch {}
18
- };
19
-
20
- // Override screen properties
21
- for (const [prop, descriptor] of Object_entries({
22
- width,
23
- height,
24
- availWidth,
25
- availHeight,
26
- availLeft: 0,
27
- availTop: 0,
28
- colorDepth,
29
- pixelDepth,
30
- })) {
31
- defineScreenProp(prop, descriptor);
32
- }
33
-
34
- // Ensure outer dimensions match screen for consistency
35
- const chromeFrameHeight = 85;
36
- Object_defineProperty(window, "outerWidth", {
37
- get: () => window.innerWidth,
38
- configurable: true,
39
- enumerable: true,
40
- });
41
- Object_defineProperty(window, "outerHeight", {
42
- get: () => window.innerHeight + chromeFrameHeight,
43
- configurable: true,
44
- enumerable: true,
45
- });
46
-
47
- if (window.visualViewport) {
48
- const defineVvpProp = (prop, value) => {
70
+ patchToString(patchedGet, "get " + prop);
49
71
  try {
50
- Object_defineProperty(window.visualViewport, prop, {
51
- get: () => value,
52
- configurable: true,
53
- enumerable: true,
72
+ Object_defineProperty(owner, prop, {
73
+ configurable: descriptor.configurable,
74
+ enumerable: descriptor.enumerable,
75
+ get: patchedGet,
76
+ set: descriptor.set,
54
77
  });
55
- } catch {}
78
+ return true;
79
+ } catch {
80
+ return false;
81
+ }
56
82
  };
57
83
 
58
- defineVvpProp("width", width);
59
- defineVvpProp("height", height);
60
- defineVvpProp("scale", 1);
61
- defineVvpProp("offsetLeft", 0);
62
- defineVvpProp("offsetTop", 0);
63
- defineVvpProp("pageLeft", 0);
64
- defineVvpProp("pageTop", 0);
65
- }
66
-
67
- // Consistent devicePixelRatio
68
- Object_defineProperty(window, "devicePixelRatio", {
69
- get: () => devicePixelRatio,
70
- configurable: true,
71
- enumerable: true,
72
- });
84
+ const patchStableNumber = (object, prop, value) => {
85
+ const current = readValue(object, prop, undefined);
86
+ if (current === value) return;
87
+ patchGetter(object, prop, () => value);
88
+ };
89
+
90
+ const initialInnerWidth = positiveInteger(readValue(window, "innerWidth", 0), 0);
91
+ const initialInnerHeight = positiveInteger(readValue(window, "innerHeight", 0), 0);
92
+ const initialOuterWidth = positiveInteger(
93
+ readValue(window, "outerWidth", Math_max(initialInnerWidth, 1)),
94
+ Math_max(initialInnerWidth, 1),
95
+ );
96
+ const initialOuterHeight = positiveInteger(
97
+ readValue(window, "outerHeight", Math_max(initialInnerHeight, 1)),
98
+ Math_max(initialInnerHeight, 1),
99
+ );
100
+ // Real Chrome reserves vertical space for the tab strip + URL bar, so
101
+ // outerHeight is always taller than innerHeight (~88px on a stock window).
102
+ // Headless reports outerHeight === innerHeight, which is a well-known tell, so
103
+ // synthesize a realistic chrome height when the window has no visible chrome.
104
+ const browserChromeHeight = 88;
105
+ const targetOuterHeight =
106
+ initialOuterHeight > initialInnerHeight
107
+ ? initialOuterHeight
108
+ : initialInnerHeight + browserChromeHeight;
109
+ const requiredWidth = Math_max(initialInnerWidth, initialOuterWidth, 1);
110
+ const requiredHeight = Math_max(initialInnerHeight, initialOuterHeight, targetOuterHeight, 1);
111
+
112
+ const screenWidth = Math_max(
113
+ positiveInteger(readValue(screenObject, "width", requiredWidth), requiredWidth),
114
+ requiredWidth,
115
+ );
116
+ const screenHeight = Math_max(
117
+ positiveInteger(readValue(screenObject, "height", requiredHeight), requiredHeight),
118
+ requiredHeight,
119
+ );
120
+
121
+ let screenAvailWidth = positiveInteger(
122
+ readValue(screenObject, "availWidth", screenWidth),
123
+ screenWidth,
124
+ );
125
+ if (screenAvailWidth < requiredWidth || screenAvailWidth > screenWidth) {
126
+ screenAvailWidth = screenWidth;
127
+ }
128
+
129
+ let screenAvailHeight = positiveInteger(
130
+ readValue(screenObject, "availHeight", screenHeight),
131
+ screenHeight,
132
+ );
133
+ if (screenAvailHeight < requiredHeight || screenAvailHeight > screenHeight) {
134
+ screenAvailHeight = screenHeight;
135
+ }
136
+
137
+ const screenAvailLeft = integerNumber(readValue(screenObject, "availLeft", 0), 0);
138
+ const screenAvailTop = integerNumber(readValue(screenObject, "availTop", 0), 0);
139
+ const screenColorDepth = positiveInteger(readValue(screenObject, "colorDepth", 24), 24);
140
+ const screenPixelDepth = positiveInteger(
141
+ readValue(screenObject, "pixelDepth", screenColorDepth),
142
+ screenColorDepth,
143
+ );
144
+
145
+ for (const entry of [
146
+ ["width", screenWidth],
147
+ ["height", screenHeight],
148
+ ["availWidth", screenAvailWidth],
149
+ ["availHeight", screenAvailHeight],
150
+ ["availLeft", screenAvailLeft],
151
+ ["availTop", screenAvailTop],
152
+ ["colorDepth", screenColorDepth],
153
+ ["pixelDepth", screenPixelDepth],
154
+ ]) {
155
+ patchStableNumber(screenObject, entry[0], entry[1]);
156
+ }
157
+
158
+ const outerWidthNeedsPatch =
159
+ !isFiniteNumber(readValue(window, "outerWidth", undefined)) ||
160
+ readValue(window, "outerWidth", 0) <= 0 ||
161
+ Math_floor(readValue(window, "outerWidth", 0)) < initialInnerWidth;
162
+ if (outerWidthNeedsPatch) {
163
+ patchGetter(window, "outerWidth", (nativeValue) =>
164
+ Math_max(
165
+ positiveInteger(nativeValue, initialOuterWidth),
166
+ positiveInteger(readValue(window, "innerWidth", requiredWidth), requiredWidth),
167
+ ),
168
+ );
169
+ }
170
+
171
+ const outerHeightNeedsPatch =
172
+ !isFiniteNumber(readValue(window, "outerHeight", undefined)) ||
173
+ readValue(window, "outerHeight", 0) <= 0 ||
174
+ Math_floor(readValue(window, "outerHeight", 0)) <= initialInnerHeight;
175
+ if (outerHeightNeedsPatch) {
176
+ patchGetter(window, "outerHeight", (nativeValue) =>
177
+ Math_max(
178
+ positiveInteger(nativeValue, initialOuterHeight),
179
+ positiveInteger(readValue(window, "innerHeight", requiredHeight), requiredHeight) +
180
+ browserChromeHeight,
181
+ ),
182
+ );
183
+ }
184
+
185
+ const initialDevicePixelRatio = positiveNumber(
186
+ readValue(window, "devicePixelRatio", 1),
187
+ 1,
188
+ );
189
+ if (readValue(window, "devicePixelRatio", undefined) !== initialDevicePixelRatio) {
190
+ patchGetter(window, "devicePixelRatio", () => initialDevicePixelRatio);
191
+ }
192
+
193
+ const visualViewportObject = readValue(window, "visualViewport", null);
194
+ if (visualViewportObject) {
195
+ const initialScale = positiveNumber(readValue(visualViewportObject, "scale", 1), 1);
196
+ if (readValue(visualViewportObject, "scale", undefined) !== initialScale) {
197
+ patchGetter(visualViewportObject, "scale", (nativeValue) =>
198
+ positiveNumber(nativeValue, initialScale),
199
+ );
200
+ }
201
+
202
+ const getViewportScale = () =>
203
+ positiveNumber(readValue(visualViewportObject, "scale", initialScale), initialScale);
204
+
205
+ const getViewportWidthFallback = () =>
206
+ positiveNumber(readValue(window, "innerWidth", requiredWidth), requiredWidth) /
207
+ getViewportScale();
208
+
209
+ const getViewportHeightFallback = () =>
210
+ positiveNumber(readValue(window, "innerHeight", requiredHeight), requiredHeight) /
211
+ getViewportScale();
212
+
213
+ if (positiveNumber(readValue(visualViewportObject, "width", 0), 0) <= 0) {
214
+ patchGetter(visualViewportObject, "width", (nativeValue) =>
215
+ positiveNumber(nativeValue, getViewportWidthFallback()),
216
+ );
217
+ }
218
+
219
+ if (positiveNumber(readValue(visualViewportObject, "height", 0), 0) <= 0) {
220
+ patchGetter(visualViewportObject, "height", (nativeValue) =>
221
+ positiveNumber(nativeValue, getViewportHeightFallback()),
222
+ );
223
+ }
224
+
225
+ const getViewportOffsetLeft = () =>
226
+ finiteNumber(readValue(visualViewportObject, "offsetLeft", 0), 0);
227
+ const getViewportOffsetTop = () =>
228
+ finiteNumber(readValue(visualViewportObject, "offsetTop", 0), 0);
229
+
230
+ if (!isFiniteNumber(readValue(visualViewportObject, "offsetLeft", undefined))) {
231
+ patchGetter(visualViewportObject, "offsetLeft", (nativeValue) =>
232
+ finiteNumber(nativeValue, 0),
233
+ );
234
+ }
235
+
236
+ if (!isFiniteNumber(readValue(visualViewportObject, "offsetTop", undefined))) {
237
+ patchGetter(visualViewportObject, "offsetTop", (nativeValue) =>
238
+ finiteNumber(nativeValue, 0),
239
+ );
240
+ }
241
+
242
+ if (!isFiniteNumber(readValue(visualViewportObject, "pageLeft", undefined))) {
243
+ patchGetter(visualViewportObject, "pageLeft", (nativeValue) =>
244
+ finiteNumber(
245
+ nativeValue,
246
+ finiteNumber(readValue(window, "scrollX", 0), 0) + getViewportOffsetLeft(),
247
+ ),
248
+ );
249
+ }
250
+
251
+ if (!isFiniteNumber(readValue(visualViewportObject, "pageTop", undefined))) {
252
+ patchGetter(visualViewportObject, "pageTop", (nativeValue) =>
253
+ finiteNumber(
254
+ nativeValue,
255
+ finiteNumber(readValue(window, "scrollY", 0), 0) + getViewportOffsetTop(),
256
+ ),
257
+ );
258
+ }
259
+ }
260
+ })();
@@ -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: async () => {
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
- HTMLCanvasElement.prototype.getContext = function (type, options) {
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
- ctx.fillText = function (text, x, y, maxWidth) {
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 spoofLatency = (proto) => {
3
- Object_defineProperty(proto, "baseLatency", {
4
- get: () => 0.005, // ~5ms typical for real hardware
5
- configurable: true,
6
- enumerable: true,
7
- });
8
- Object_defineProperty(proto, "outputLatency", {
9
- get: () => 0.01, // ~10ms typical
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
- window.OfflineAudioContext = class extends OriginalOfflineAudioContext {
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
- this.startRendering = async () => {
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
- Object_defineProperty(AudioContext.prototype, "sampleRate", {
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 = ["en-US", "en"];
4
- const timezone = "America/New_York";
5
-
6
- // Override navigator language properties
7
- Object_defineProperty(navigator, "language", {
8
- get: () => locale,
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
- // Ensure Date timezone is consistent
33
- const originalDateConstructor = Date_constructor;
34
- const originalToString = originalDateConstructor.prototype.toString;
35
- const originalToTimeString = originalDateConstructor.prototype.toTimeString;
36
-
37
- Date.prototype.toString = function () {
38
- return originalToString
39
- .call(this)
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
- Date.prototype.toTimeString = function () {
43
- return originalToTimeString
44
- .call(this)
45
- .replace(/\(.*\)$/, "(Eastern Standard Time)");
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: new Window_Proxy(proto.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: new Window_Proxy(proto.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
- refresh: proto.refresh
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 navigatorProto = Object_getPrototypeOf(navigator);
2
- if (navigatorProto && "hardwareConcurrency" in navigatorProto) {
3
- Object_defineProperty(navigatorProto, "hardwareConcurrency", {
4
- get: () => 4,
5
- configurable: true,
6
- enumerable: true,
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
  }