@hcu-lab.me/mcp-device 1.0.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.
Files changed (261) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/dist/bootstrap.d.ts +1 -0
  4. package/dist/bootstrap.js +22 -0
  5. package/dist/command-manager.d.ts +8 -0
  6. package/dist/command-manager.js +241 -0
  7. package/dist/config-field-definitions.d.ts +41 -0
  8. package/dist/config-field-definitions.js +37 -0
  9. package/dist/config-manager.d.ts +102 -0
  10. package/dist/config-manager.js +291 -0
  11. package/dist/config.d.ts +6 -0
  12. package/dist/config.js +10 -0
  13. package/dist/custom-stdio.d.ts +51 -0
  14. package/dist/custom-stdio.js +358 -0
  15. package/dist/data/official-app-ca.pem +12 -0
  16. package/dist/data/onboarding-prompts.json +70 -0
  17. package/dist/device/device-state.d.ts +13 -0
  18. package/dist/device/device-state.js +100 -0
  19. package/dist/device/device-status.d.ts +81 -0
  20. package/dist/device/device-status.js +156 -0
  21. package/dist/device/device.d.ts +16 -0
  22. package/dist/device/device.js +190 -0
  23. package/dist/device/execution-engine.d.ts +144 -0
  24. package/dist/device/execution-engine.js +195 -0
  25. package/dist/device/gateway-channel.d.ts +48 -0
  26. package/dist/device/gateway-channel.js +384 -0
  27. package/dist/device/gateway-config.d.ts +40 -0
  28. package/dist/device/gateway-config.js +207 -0
  29. package/dist/device/gateway-identity.d.ts +35 -0
  30. package/dist/device/gateway-identity.js +178 -0
  31. package/dist/device/gateway-pairing.d.ts +25 -0
  32. package/dist/device/gateway-pairing.js +105 -0
  33. package/dist/device/gateway-secure-transport.d.ts +25 -0
  34. package/dist/device/gateway-secure-transport.js +117 -0
  35. package/dist/device/gateway-tool-adapter.d.ts +17 -0
  36. package/dist/device/gateway-tool-adapter.js +284 -0
  37. package/dist/device/gateway-url-policy.d.ts +3 -0
  38. package/dist/device/gateway-url-policy.js +43 -0
  39. package/dist/device/linux-service.d.ts +70 -0
  40. package/dist/device/linux-service.js +268 -0
  41. package/dist/device/official-trust.d.ts +7 -0
  42. package/dist/device/official-trust.js +49 -0
  43. package/dist/device/project-inspection.d.ts +1 -0
  44. package/dist/device/project-inspection.js +180 -0
  45. package/dist/device/runtime-owner.d.ts +41 -0
  46. package/dist/device/runtime-owner.js +177 -0
  47. package/dist/device/windows-dpapi.d.ts +11 -0
  48. package/dist/device/windows-dpapi.js +67 -0
  49. package/dist/device/windows-service.d.ts +56 -0
  50. package/dist/device/windows-service.js +238 -0
  51. package/dist/error-handlers.d.ts +7 -0
  52. package/dist/error-handlers.js +15 -0
  53. package/dist/handlers/edit-search-handlers.d.ts +6 -0
  54. package/dist/handlers/edit-search-handlers.js +6 -0
  55. package/dist/handlers/filesystem-handlers.d.ts +38 -0
  56. package/dist/handlers/filesystem-handlers.js +440 -0
  57. package/dist/handlers/history-handlers.d.ts +12 -0
  58. package/dist/handlers/history-handlers.js +67 -0
  59. package/dist/handlers/index.d.ts +6 -0
  60. package/dist/handlers/index.js +7 -0
  61. package/dist/handlers/process-handlers.d.ts +9 -0
  62. package/dist/handlers/process-handlers.js +15 -0
  63. package/dist/handlers/search-handlers.d.ts +17 -0
  64. package/dist/handlers/search-handlers.js +224 -0
  65. package/dist/handlers/terminal-handlers.d.ts +21 -0
  66. package/dist/handlers/terminal-handlers.js +35 -0
  67. package/dist/index.d.ts +2 -0
  68. package/dist/index.js +138 -0
  69. package/dist/mcp-device.d.ts +2 -0
  70. package/dist/mcp-device.js +7 -0
  71. package/dist/npm-scripts/remote.d.ts +3 -0
  72. package/dist/npm-scripts/remote.js +314 -0
  73. package/dist/search-manager.d.ts +147 -0
  74. package/dist/search-manager.js +828 -0
  75. package/dist/server.d.ts +46 -0
  76. package/dist/server.js +1563 -0
  77. package/dist/terminal-manager.d.ts +104 -0
  78. package/dist/terminal-manager.js +635 -0
  79. package/dist/tools/config.d.ts +96 -0
  80. package/dist/tools/config.js +260 -0
  81. package/dist/tools/edit.d.ts +38 -0
  82. package/dist/tools/edit.js +435 -0
  83. package/dist/tools/feedback.d.ts +8 -0
  84. package/dist/tools/feedback.js +132 -0
  85. package/dist/tools/filesystem.d.ts +79 -0
  86. package/dist/tools/filesystem.js +947 -0
  87. package/dist/tools/fuzzySearch.d.ts +12 -0
  88. package/dist/tools/fuzzySearch.js +74 -0
  89. package/dist/tools/fuzzySearchCore.d.ts +52 -0
  90. package/dist/tools/fuzzySearchCore.js +125 -0
  91. package/dist/tools/improved-process-tools.d.ts +24 -0
  92. package/dist/tools/improved-process-tools.js +627 -0
  93. package/dist/tools/mime-types.d.ts +3 -0
  94. package/dist/tools/mime-types.js +28 -0
  95. package/dist/tools/pdf/extract-images.d.ts +34 -0
  96. package/dist/tools/pdf/extract-images.js +132 -0
  97. package/dist/tools/pdf/index.d.ts +6 -0
  98. package/dist/tools/pdf/index.js +3 -0
  99. package/dist/tools/pdf/lib/pdf2md.d.ts +36 -0
  100. package/dist/tools/pdf/lib/pdf2md.js +76 -0
  101. package/dist/tools/pdf/manipulations.d.ts +13 -0
  102. package/dist/tools/pdf/manipulations.js +99 -0
  103. package/dist/tools/pdf/markdown.d.ts +26 -0
  104. package/dist/tools/pdf/markdown.js +275 -0
  105. package/dist/tools/pdf/utils.d.ts +12 -0
  106. package/dist/tools/pdf/utils.js +34 -0
  107. package/dist/tools/process.d.ts +3 -0
  108. package/dist/tools/process.js +56 -0
  109. package/dist/tools/prompts.d.ts +28 -0
  110. package/dist/tools/prompts.js +259 -0
  111. package/dist/tools/schemas.d.ts +454 -0
  112. package/dist/tools/schemas.js +242 -0
  113. package/dist/tools/usage.d.ts +5 -0
  114. package/dist/tools/usage.js +24 -0
  115. package/dist/types.d.ts +83 -0
  116. package/dist/types.js +1 -0
  117. package/dist/ui/config-editor/config-editor-runtime.js +150 -0
  118. package/dist/ui/config-editor/index.html +13 -0
  119. package/dist/ui/config-editor/src/app.d.ts +43 -0
  120. package/dist/ui/config-editor/src/app.js +831 -0
  121. package/dist/ui/config-editor/src/array-modal.d.ts +19 -0
  122. package/dist/ui/config-editor/src/array-modal.js +185 -0
  123. package/dist/ui/config-editor/src/main.d.ts +1 -0
  124. package/dist/ui/config-editor/src/main.js +2 -0
  125. package/dist/ui/config-editor/styles.css +587 -0
  126. package/dist/ui/contracts.d.ts +14 -0
  127. package/dist/ui/contracts.js +21 -0
  128. package/dist/ui/file-preview/index.html +16 -0
  129. package/dist/ui/file-preview/preview-runtime.js +469 -0
  130. package/dist/ui/file-preview/shared/preview-file-types.d.ts +5 -0
  131. package/dist/ui/file-preview/shared/preview-file-types.js +59 -0
  132. package/dist/ui/file-preview/src/app.d.ts +4 -0
  133. package/dist/ui/file-preview/src/app.js +680 -0
  134. package/dist/ui/file-preview/src/components/code-viewer.d.ts +6 -0
  135. package/dist/ui/file-preview/src/components/code-viewer.js +73 -0
  136. package/dist/ui/file-preview/src/components/highlighting.d.ts +2 -0
  137. package/dist/ui/file-preview/src/components/highlighting.js +54 -0
  138. package/dist/ui/file-preview/src/components/html-renderer.d.ts +5 -0
  139. package/dist/ui/file-preview/src/components/html-renderer.js +47 -0
  140. package/dist/ui/file-preview/src/components/markdown-renderer.d.ts +1 -0
  141. package/dist/ui/file-preview/src/components/markdown-renderer.js +59 -0
  142. package/dist/ui/file-preview/src/directory-controller.d.ts +8 -0
  143. package/dist/ui/file-preview/src/directory-controller.js +240 -0
  144. package/dist/ui/file-preview/src/document-layout.d.ts +20 -0
  145. package/dist/ui/file-preview/src/document-layout.js +109 -0
  146. package/dist/ui/file-preview/src/document-outline.d.ts +17 -0
  147. package/dist/ui/file-preview/src/document-outline.js +97 -0
  148. package/dist/ui/file-preview/src/document-workspace.d.ts +19 -0
  149. package/dist/ui/file-preview/src/document-workspace.js +33 -0
  150. package/dist/ui/file-preview/src/file-type-handlers.d.ts +10 -0
  151. package/dist/ui/file-preview/src/file-type-handlers.js +109 -0
  152. package/dist/ui/file-preview/src/host/external-actions.d.ts +8 -0
  153. package/dist/ui/file-preview/src/host/external-actions.js +55 -0
  154. package/dist/ui/file-preview/src/host/selection-context.d.ts +9 -0
  155. package/dist/ui/file-preview/src/host/selection-context.js +106 -0
  156. package/dist/ui/file-preview/src/image-preview.d.ts +3 -0
  157. package/dist/ui/file-preview/src/image-preview.js +21 -0
  158. package/dist/ui/file-preview/src/main.d.ts +1 -0
  159. package/dist/ui/file-preview/src/main.js +5 -0
  160. package/dist/ui/file-preview/src/markdown/conflict-dialog.d.ts +40 -0
  161. package/dist/ui/file-preview/src/markdown/conflict-dialog.js +163 -0
  162. package/dist/ui/file-preview/src/markdown/controller.d.ts +44 -0
  163. package/dist/ui/file-preview/src/markdown/controller.js +1043 -0
  164. package/dist/ui/file-preview/src/markdown/editor.d.ts +131 -0
  165. package/dist/ui/file-preview/src/markdown/editor.js +1479 -0
  166. package/dist/ui/file-preview/src/markdown/linking.d.ts +16 -0
  167. package/dist/ui/file-preview/src/markdown/linking.js +228 -0
  168. package/dist/ui/file-preview/src/markdown/outline.d.ts +2 -0
  169. package/dist/ui/file-preview/src/markdown/outline.js +16 -0
  170. package/dist/ui/file-preview/src/markdown/parser.d.ts +30 -0
  171. package/dist/ui/file-preview/src/markdown/parser.js +38 -0
  172. package/dist/ui/file-preview/src/markdown/preview.d.ts +1 -0
  173. package/dist/ui/file-preview/src/markdown/preview.js +20 -0
  174. package/dist/ui/file-preview/src/markdown/slugify.d.ts +3 -0
  175. package/dist/ui/file-preview/src/markdown/slugify.js +31 -0
  176. package/dist/ui/file-preview/src/markdown/utils.d.ts +1 -0
  177. package/dist/ui/file-preview/src/markdown/utils.js +15 -0
  178. package/dist/ui/file-preview/src/model.d.ts +35 -0
  179. package/dist/ui/file-preview/src/model.js +1 -0
  180. package/dist/ui/file-preview/src/panel-actions.d.ts +17 -0
  181. package/dist/ui/file-preview/src/panel-actions.js +182 -0
  182. package/dist/ui/file-preview/src/path-utils.d.ts +6 -0
  183. package/dist/ui/file-preview/src/path-utils.js +64 -0
  184. package/dist/ui/file-preview/src/payload-utils.d.ts +11 -0
  185. package/dist/ui/file-preview/src/payload-utils.js +119 -0
  186. package/dist/ui/file-preview/src/types.d.ts +1 -0
  187. package/dist/ui/file-preview/src/types.js +1 -0
  188. package/dist/ui/file-preview/styles.css +1631 -0
  189. package/dist/ui/resources.d.ts +28 -0
  190. package/dist/ui/resources.js +86 -0
  191. package/dist/ui/shared/compact-row.d.ts +11 -0
  192. package/dist/ui/shared/compact-row.js +18 -0
  193. package/dist/ui/shared/escape-html.d.ts +4 -0
  194. package/dist/ui/shared/escape-html.js +11 -0
  195. package/dist/ui/shared/host-context.d.ts +15 -0
  196. package/dist/ui/shared/host-context.js +51 -0
  197. package/dist/ui/shared/tool-bridge.d.ts +30 -0
  198. package/dist/ui/shared/tool-bridge.js +137 -0
  199. package/dist/ui/shared/tool-shell.d.ts +25 -0
  200. package/dist/ui/shared/tool-shell.js +107 -0
  201. package/dist/ui/shared/ui-event-tracker.d.ts +9 -0
  202. package/dist/ui/shared/ui-event-tracker.js +27 -0
  203. package/dist/ui/shared/widget-state.d.ts +33 -0
  204. package/dist/ui/shared/widget-state.js +158 -0
  205. package/dist/utils/ab-test.d.ts +12 -0
  206. package/dist/utils/ab-test.js +104 -0
  207. package/dist/utils/capture.d.ts +35 -0
  208. package/dist/utils/capture.js +481 -0
  209. package/dist/utils/dockerPrompt.d.ts +17 -0
  210. package/dist/utils/dockerPrompt.js +53 -0
  211. package/dist/utils/feature-flags.d.ts +59 -0
  212. package/dist/utils/feature-flags.js +215 -0
  213. package/dist/utils/files/base.d.ts +173 -0
  214. package/dist/utils/files/base.js +5 -0
  215. package/dist/utils/files/binary.d.ts +21 -0
  216. package/dist/utils/files/binary.js +65 -0
  217. package/dist/utils/files/docx.d.ts +47 -0
  218. package/dist/utils/files/docx.js +679 -0
  219. package/dist/utils/files/excel.d.ts +24 -0
  220. package/dist/utils/files/excel.js +437 -0
  221. package/dist/utils/files/factory.d.ts +41 -0
  222. package/dist/utils/files/factory.js +113 -0
  223. package/dist/utils/files/image.d.ts +21 -0
  224. package/dist/utils/files/image.js +78 -0
  225. package/dist/utils/files/index.d.ts +10 -0
  226. package/dist/utils/files/index.js +13 -0
  227. package/dist/utils/files/pdf.d.ts +32 -0
  228. package/dist/utils/files/pdf.js +142 -0
  229. package/dist/utils/files/text.d.ts +63 -0
  230. package/dist/utils/files/text.js +370 -0
  231. package/dist/utils/fuzzySearchLogger.d.ts +30 -0
  232. package/dist/utils/fuzzySearchLogger.js +126 -0
  233. package/dist/utils/lineEndingHandler.d.ts +21 -0
  234. package/dist/utils/lineEndingHandler.js +77 -0
  235. package/dist/utils/logger.d.ts +32 -0
  236. package/dist/utils/logger.js +72 -0
  237. package/dist/utils/mcp-ui-ab-test.d.ts +13 -0
  238. package/dist/utils/mcp-ui-ab-test.js +62 -0
  239. package/dist/utils/open-browser.d.ts +9 -0
  240. package/dist/utils/open-browser.js +42 -0
  241. package/dist/utils/process-detection.d.ts +23 -0
  242. package/dist/utils/process-detection.js +150 -0
  243. package/dist/utils/ripgrep-resolver.d.ts +9 -0
  244. package/dist/utils/ripgrep-resolver.js +74 -0
  245. package/dist/utils/system-info.d.ts +71 -0
  246. package/dist/utils/system-info.js +712 -0
  247. package/dist/utils/toolHistory.d.ts +86 -0
  248. package/dist/utils/toolHistory.js +257 -0
  249. package/dist/utils/trackTools.d.ts +6 -0
  250. package/dist/utils/trackTools.js +54 -0
  251. package/dist/utils/unsupportedParams.d.ts +24 -0
  252. package/dist/utils/unsupportedParams.js +66 -0
  253. package/dist/utils/usageTracker.d.ts +133 -0
  254. package/dist/utils/usageTracker.js +477 -0
  255. package/dist/utils/welcome-onboarding.d.ts +11 -0
  256. package/dist/utils/welcome-onboarding.js +99 -0
  257. package/dist/utils/withTimeout.d.ts +28 -0
  258. package/dist/utils/withTimeout.js +85 -0
  259. package/dist/version.d.ts +1 -0
  260. package/dist/version.js +1 -0
  261. package/package.json +131 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Widget state persistence for MCP Apps hosts.
3
+ *
4
+ * ChatGPT has a special extension (window.openai.widgetState) for persisting
5
+ * widget state across page refreshes. Other hosts use the standard MCP Apps
6
+ * pattern where ui/notifications/tool-result is re-sent when needed.
7
+ *
8
+ * This module provides a simple abstraction:
9
+ * - ChatGPT: Uses window.openai.widgetState
10
+ * - Other hosts: No-op (rely on standard ui/notifications/tool-result)
11
+ */
12
+ const FALLBACK_WIDGET_STATE_KEY_PREFIX = 'mcp-device:widget-state';
13
+ const FALLBACK_WIDGET_INSTANCE_MARKER = '__mcp_device_widget_id__:';
14
+ function createWidgetInstanceId() {
15
+ const cryptoObject = typeof globalThis.crypto === 'object' ? globalThis.crypto : undefined;
16
+ if (typeof cryptoObject?.randomUUID === 'function') {
17
+ return cryptoObject.randomUUID();
18
+ }
19
+ return `widget-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
20
+ }
21
+ function readWidgetInstanceIdFromWindowName(windowName) {
22
+ const markerIndex = windowName.indexOf(FALLBACK_WIDGET_INSTANCE_MARKER);
23
+ if (markerIndex === -1) {
24
+ return undefined;
25
+ }
26
+ const encodedValue = windowName
27
+ .slice(markerIndex + FALLBACK_WIDGET_INSTANCE_MARKER.length)
28
+ .split('|', 1)[0];
29
+ if (!encodedValue) {
30
+ return undefined;
31
+ }
32
+ try {
33
+ return decodeURIComponent(encodedValue);
34
+ }
35
+ catch {
36
+ return encodedValue;
37
+ }
38
+ }
39
+ function getFallbackWidgetInstanceId() {
40
+ if (typeof window === 'undefined') {
41
+ return 'unknown-instance';
42
+ }
43
+ const currentWindowName = typeof window.name === 'string' ? window.name : '';
44
+ const existingInstanceId = readWidgetInstanceIdFromWindowName(currentWindowName);
45
+ if (existingInstanceId) {
46
+ return existingInstanceId;
47
+ }
48
+ const instanceId = createWidgetInstanceId();
49
+ const marker = `${FALLBACK_WIDGET_INSTANCE_MARKER}${encodeURIComponent(instanceId)}`;
50
+ try {
51
+ window.name = currentWindowName ? `${currentWindowName}|${marker}` : marker;
52
+ }
53
+ catch {
54
+ // Ignore window.name write failures and fall back to the in-memory id.
55
+ }
56
+ return instanceId;
57
+ }
58
+ function getFallbackWidgetStateKey() {
59
+ if (typeof window === 'undefined') {
60
+ return `${FALLBACK_WIDGET_STATE_KEY_PREFIX}:unknown`;
61
+ }
62
+ const appPath = window.location.pathname || 'unknown';
63
+ const instanceId = getFallbackWidgetInstanceId();
64
+ return `${FALLBACK_WIDGET_STATE_KEY_PREFIX}:${appPath}:${encodeURIComponent(instanceId)}`;
65
+ }
66
+ function getSessionStorage() {
67
+ if (typeof window === 'undefined') {
68
+ return undefined;
69
+ }
70
+ try {
71
+ return window.sessionStorage;
72
+ }
73
+ catch {
74
+ return undefined;
75
+ }
76
+ }
77
+ /**
78
+ * Check if we're running in ChatGPT (has special widget state API)
79
+ */
80
+ export function isChatGPT() {
81
+ return typeof window !== 'undefined' &&
82
+ typeof window.openai?.setWidgetState === 'function';
83
+ }
84
+ /**
85
+ * Create a widget state storage adapter.
86
+ *
87
+ * On ChatGPT: Uses window.openai.widgetState for persistence
88
+ * On other hosts: Uses sessionStorage as a fallback so the preview can survive
89
+ * transient interruptions (page refresh on hosts that don't re-send tool_result,
90
+ * visibility/focus loss, etc.).
91
+ * The fallback cache key is scoped by app pathname and a per-frame widget id
92
+ * persisted in window.name, so different widgets in the same origin/session
93
+ * do not overwrite one another's cached state.
94
+ */
95
+ export function createWidgetStateStorage(validator) {
96
+ if (!isChatGPT()) {
97
+ const storage = getSessionStorage();
98
+ const storageKey = getFallbackWidgetStateKey();
99
+ return {
100
+ read() {
101
+ if (!storage)
102
+ return undefined;
103
+ try {
104
+ const raw = storage.getItem(storageKey);
105
+ if (!raw)
106
+ return undefined;
107
+ const parsed = JSON.parse(raw);
108
+ const payload = parsed?.payload;
109
+ if (payload === undefined)
110
+ return undefined;
111
+ if (validator && !validator(payload))
112
+ return undefined;
113
+ return payload;
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ },
119
+ write(state) {
120
+ if (!storage)
121
+ return;
122
+ try {
123
+ storage.setItem(storageKey, JSON.stringify({ payload: state }));
124
+ }
125
+ catch {
126
+ // Ignore storage failures
127
+ }
128
+ }
129
+ };
130
+ }
131
+ // ChatGPT-specific implementation
132
+ return {
133
+ read() {
134
+ try {
135
+ const state = window.openai?.widgetState;
136
+ if (state === undefined || state === null)
137
+ return undefined;
138
+ const payload = state.payload;
139
+ if (payload === undefined)
140
+ return undefined;
141
+ if (validator && !validator(payload))
142
+ return undefined;
143
+ return payload;
144
+ }
145
+ catch {
146
+ return undefined;
147
+ }
148
+ },
149
+ write(state) {
150
+ try {
151
+ window.openai?.setWidgetState?.({ payload: state });
152
+ }
153
+ catch {
154
+ // Ignore write failures
155
+ }
156
+ }
157
+ };
158
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Get the exact assigned variant for a named experiment.
3
+ */
4
+ export declare function getABTestVariant(experimentName: string): Promise<string | null>;
5
+ /**
6
+ * Check if a feature (variant name) is enabled for current user
7
+ */
8
+ export declare function hasFeature(featureName: string): Promise<boolean>;
9
+ /**
10
+ * Get all A/B test assignments for analytics (reads from config)
11
+ */
12
+ export declare function getABTestAssignments(): Promise<Record<string, string>>;
@@ -0,0 +1,104 @@
1
+ import { configManager } from '../config-manager.js';
2
+ import { featureFlagManager } from './feature-flags.js';
3
+ // Cache for variant assignments (loaded once per session)
4
+ const variantCache = {};
5
+ /**
6
+ * Get experiments config from feature flags
7
+ */
8
+ function getExperiments() {
9
+ return featureFlagManager.get('experiments', {});
10
+ }
11
+ /**
12
+ * Get user's variant for an experiment (cached, deterministic)
13
+ * Supports weighted variants for unequal splits
14
+ */
15
+ async function getVariant(experimentName) {
16
+ const experiments = getExperiments();
17
+ const experiment = experiments[experimentName];
18
+ if (!experiment?.variants?.length)
19
+ return null;
20
+ // Check cache
21
+ if (variantCache[experimentName]) {
22
+ return variantCache[experimentName];
23
+ }
24
+ // Check persisted assignment
25
+ const configKey = `abTest_${experimentName}`;
26
+ const existing = await configManager.getValue(configKey);
27
+ // Validate existing assignment is still a valid variant
28
+ const variantNames = experiment.variants.map(v => v.name);
29
+ if (existing && variantNames.includes(existing)) {
30
+ variantCache[experimentName] = existing;
31
+ return existing;
32
+ }
33
+ // New assignment based on clientId with weighted selection
34
+ const clientId = await configManager.getOrCreateClientId();
35
+ const hash = hashCode(clientId + experimentName);
36
+ // Calculate total weight and select variant
37
+ const totalWeight = experiment.variants.reduce((sum, v) => sum + v.weight, 0);
38
+ let variant;
39
+ if (totalWeight > 0) {
40
+ const roll = hash % totalWeight;
41
+ let cumulative = 0;
42
+ variant = experiment.variants[0].name; // fallback
43
+ for (const v of experiment.variants) {
44
+ cumulative += v.weight;
45
+ if (roll < cumulative) {
46
+ variant = v.name;
47
+ break;
48
+ }
49
+ }
50
+ }
51
+ else {
52
+ // Fallback to equal split when weights are misconfigured (all zero)
53
+ const index = hash % experiment.variants.length;
54
+ variant = experiment.variants[index].name;
55
+ }
56
+ await configManager.setValue(configKey, variant);
57
+ variantCache[experimentName] = variant;
58
+ return variant;
59
+ }
60
+ /**
61
+ * Get the exact assigned variant for a named experiment.
62
+ */
63
+ export async function getABTestVariant(experimentName) {
64
+ return getVariant(experimentName);
65
+ }
66
+ /**
67
+ * Check if a feature (variant name) is enabled for current user
68
+ */
69
+ export async function hasFeature(featureName) {
70
+ const experiments = getExperiments();
71
+ if (!experiments || typeof experiments !== 'object')
72
+ return false;
73
+ for (const [expName, experiment] of Object.entries(experiments)) {
74
+ const variantNames = experiment?.variants?.map(v => v.name) || [];
75
+ if (variantNames.includes(featureName)) {
76
+ const variant = await getVariant(expName);
77
+ return variant === featureName;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+ /**
83
+ * Get all A/B test assignments for analytics (reads from config)
84
+ */
85
+ export async function getABTestAssignments() {
86
+ const experiments = getExperiments();
87
+ const assignments = {};
88
+ for (const expName of Object.keys(experiments)) {
89
+ const configKey = `abTest_${expName}`;
90
+ const variant = await configManager.getValue(configKey);
91
+ if (variant) {
92
+ assignments[`ab_${expName}`] = variant;
93
+ }
94
+ }
95
+ return assignments;
96
+ }
97
+ function hashCode(str) {
98
+ let hash = 0;
99
+ for (let i = 0; i < str.length; i++) {
100
+ hash = ((hash << 5) - hash) + str.charCodeAt(i);
101
+ hash |= 0;
102
+ }
103
+ return Math.abs(hash);
104
+ }
@@ -0,0 +1,35 @@
1
+ export declare function runInUiOriginCallContext<T>(fn: () => T): T;
2
+ export declare function isInsideUiOriginCall(): boolean;
3
+ /**
4
+ * Hard kill-switch for telemetry via environment variable.
5
+ *
6
+ * Independent of the persisted `telemetryEnabled` config so that tests, CI and
7
+ * one-off runs can suppress all analytics without mutating the user's config.
8
+ * Set DESKTOP_COMMANDER_DISABLE_TELEMETRY to 1/true/yes/on to disable.
9
+ */
10
+ export declare function isTelemetryDisabledByEnv(): boolean;
11
+ /**
12
+ * Sanitizes error objects to remove potentially sensitive information like file paths
13
+ * @param error Error object or string to sanitize
14
+ * @returns An object with sanitized message and optional error code
15
+ */
16
+ export declare function sanitizeError(error: any): {
17
+ message: string;
18
+ code?: string;
19
+ };
20
+ /**
21
+ * Send an event to telemetry
22
+ * @param event Event name
23
+ * @param properties Optional event properties
24
+ */
25
+ export declare const captureBase: (captureURL: string, event: string, properties?: any) => Promise<void>;
26
+ export declare const capture: (event: string, properties?: any) => Promise<void>;
27
+ export declare const capture_call_tool: (event: string, properties?: any) => Promise<void>;
28
+ export declare const capture_ui_event: (event: string, properties?: any) => Promise<void>;
29
+ /**
30
+ * Wrapper for capture() that automatically adds remote flag for device telemetry
31
+ * Also adds additional privacy filtering to remove sensitive identity information
32
+ * @param event Event name
33
+ * @param properties Optional event properties
34
+ */
35
+ export declare const captureRemote: (event: string, properties?: any) => Promise<void>;