@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.
- package/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/bootstrap.d.ts +1 -0
- package/dist/bootstrap.js +22 -0
- package/dist/command-manager.d.ts +8 -0
- package/dist/command-manager.js +241 -0
- package/dist/config-field-definitions.d.ts +41 -0
- package/dist/config-field-definitions.js +37 -0
- package/dist/config-manager.d.ts +102 -0
- package/dist/config-manager.js +291 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.js +10 -0
- package/dist/custom-stdio.d.ts +51 -0
- package/dist/custom-stdio.js +358 -0
- package/dist/data/official-app-ca.pem +12 -0
- package/dist/data/onboarding-prompts.json +70 -0
- package/dist/device/device-state.d.ts +13 -0
- package/dist/device/device-state.js +100 -0
- package/dist/device/device-status.d.ts +81 -0
- package/dist/device/device-status.js +156 -0
- package/dist/device/device.d.ts +16 -0
- package/dist/device/device.js +190 -0
- package/dist/device/execution-engine.d.ts +144 -0
- package/dist/device/execution-engine.js +195 -0
- package/dist/device/gateway-channel.d.ts +48 -0
- package/dist/device/gateway-channel.js +384 -0
- package/dist/device/gateway-config.d.ts +40 -0
- package/dist/device/gateway-config.js +207 -0
- package/dist/device/gateway-identity.d.ts +35 -0
- package/dist/device/gateway-identity.js +178 -0
- package/dist/device/gateway-pairing.d.ts +25 -0
- package/dist/device/gateway-pairing.js +105 -0
- package/dist/device/gateway-secure-transport.d.ts +25 -0
- package/dist/device/gateway-secure-transport.js +117 -0
- package/dist/device/gateway-tool-adapter.d.ts +17 -0
- package/dist/device/gateway-tool-adapter.js +284 -0
- package/dist/device/gateway-url-policy.d.ts +3 -0
- package/dist/device/gateway-url-policy.js +43 -0
- package/dist/device/linux-service.d.ts +70 -0
- package/dist/device/linux-service.js +268 -0
- package/dist/device/official-trust.d.ts +7 -0
- package/dist/device/official-trust.js +49 -0
- package/dist/device/project-inspection.d.ts +1 -0
- package/dist/device/project-inspection.js +180 -0
- package/dist/device/runtime-owner.d.ts +41 -0
- package/dist/device/runtime-owner.js +177 -0
- package/dist/device/windows-dpapi.d.ts +11 -0
- package/dist/device/windows-dpapi.js +67 -0
- package/dist/device/windows-service.d.ts +56 -0
- package/dist/device/windows-service.js +238 -0
- package/dist/error-handlers.d.ts +7 -0
- package/dist/error-handlers.js +15 -0
- package/dist/handlers/edit-search-handlers.d.ts +6 -0
- package/dist/handlers/edit-search-handlers.js +6 -0
- package/dist/handlers/filesystem-handlers.d.ts +38 -0
- package/dist/handlers/filesystem-handlers.js +440 -0
- package/dist/handlers/history-handlers.d.ts +12 -0
- package/dist/handlers/history-handlers.js +67 -0
- package/dist/handlers/index.d.ts +6 -0
- package/dist/handlers/index.js +7 -0
- package/dist/handlers/process-handlers.d.ts +9 -0
- package/dist/handlers/process-handlers.js +15 -0
- package/dist/handlers/search-handlers.d.ts +17 -0
- package/dist/handlers/search-handlers.js +224 -0
- package/dist/handlers/terminal-handlers.d.ts +21 -0
- package/dist/handlers/terminal-handlers.js +35 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +138 -0
- package/dist/mcp-device.d.ts +2 -0
- package/dist/mcp-device.js +7 -0
- package/dist/npm-scripts/remote.d.ts +3 -0
- package/dist/npm-scripts/remote.js +314 -0
- package/dist/search-manager.d.ts +147 -0
- package/dist/search-manager.js +828 -0
- package/dist/server.d.ts +46 -0
- package/dist/server.js +1563 -0
- package/dist/terminal-manager.d.ts +104 -0
- package/dist/terminal-manager.js +635 -0
- package/dist/tools/config.d.ts +96 -0
- package/dist/tools/config.js +260 -0
- package/dist/tools/edit.d.ts +38 -0
- package/dist/tools/edit.js +435 -0
- package/dist/tools/feedback.d.ts +8 -0
- package/dist/tools/feedback.js +132 -0
- package/dist/tools/filesystem.d.ts +79 -0
- package/dist/tools/filesystem.js +947 -0
- package/dist/tools/fuzzySearch.d.ts +12 -0
- package/dist/tools/fuzzySearch.js +74 -0
- package/dist/tools/fuzzySearchCore.d.ts +52 -0
- package/dist/tools/fuzzySearchCore.js +125 -0
- package/dist/tools/improved-process-tools.d.ts +24 -0
- package/dist/tools/improved-process-tools.js +627 -0
- package/dist/tools/mime-types.d.ts +3 -0
- package/dist/tools/mime-types.js +28 -0
- package/dist/tools/pdf/extract-images.d.ts +34 -0
- package/dist/tools/pdf/extract-images.js +132 -0
- package/dist/tools/pdf/index.d.ts +6 -0
- package/dist/tools/pdf/index.js +3 -0
- package/dist/tools/pdf/lib/pdf2md.d.ts +36 -0
- package/dist/tools/pdf/lib/pdf2md.js +76 -0
- package/dist/tools/pdf/manipulations.d.ts +13 -0
- package/dist/tools/pdf/manipulations.js +99 -0
- package/dist/tools/pdf/markdown.d.ts +26 -0
- package/dist/tools/pdf/markdown.js +275 -0
- package/dist/tools/pdf/utils.d.ts +12 -0
- package/dist/tools/pdf/utils.js +34 -0
- package/dist/tools/process.d.ts +3 -0
- package/dist/tools/process.js +56 -0
- package/dist/tools/prompts.d.ts +28 -0
- package/dist/tools/prompts.js +259 -0
- package/dist/tools/schemas.d.ts +454 -0
- package/dist/tools/schemas.js +242 -0
- package/dist/tools/usage.d.ts +5 -0
- package/dist/tools/usage.js +24 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.js +1 -0
- package/dist/ui/config-editor/config-editor-runtime.js +150 -0
- package/dist/ui/config-editor/index.html +13 -0
- package/dist/ui/config-editor/src/app.d.ts +43 -0
- package/dist/ui/config-editor/src/app.js +831 -0
- package/dist/ui/config-editor/src/array-modal.d.ts +19 -0
- package/dist/ui/config-editor/src/array-modal.js +185 -0
- package/dist/ui/config-editor/src/main.d.ts +1 -0
- package/dist/ui/config-editor/src/main.js +2 -0
- package/dist/ui/config-editor/styles.css +587 -0
- package/dist/ui/contracts.d.ts +14 -0
- package/dist/ui/contracts.js +21 -0
- package/dist/ui/file-preview/index.html +16 -0
- package/dist/ui/file-preview/preview-runtime.js +469 -0
- package/dist/ui/file-preview/shared/preview-file-types.d.ts +5 -0
- package/dist/ui/file-preview/shared/preview-file-types.js +59 -0
- package/dist/ui/file-preview/src/app.d.ts +4 -0
- package/dist/ui/file-preview/src/app.js +680 -0
- package/dist/ui/file-preview/src/components/code-viewer.d.ts +6 -0
- package/dist/ui/file-preview/src/components/code-viewer.js +73 -0
- package/dist/ui/file-preview/src/components/highlighting.d.ts +2 -0
- package/dist/ui/file-preview/src/components/highlighting.js +54 -0
- package/dist/ui/file-preview/src/components/html-renderer.d.ts +5 -0
- package/dist/ui/file-preview/src/components/html-renderer.js +47 -0
- package/dist/ui/file-preview/src/components/markdown-renderer.d.ts +1 -0
- package/dist/ui/file-preview/src/components/markdown-renderer.js +59 -0
- package/dist/ui/file-preview/src/directory-controller.d.ts +8 -0
- package/dist/ui/file-preview/src/directory-controller.js +240 -0
- package/dist/ui/file-preview/src/document-layout.d.ts +20 -0
- package/dist/ui/file-preview/src/document-layout.js +109 -0
- package/dist/ui/file-preview/src/document-outline.d.ts +17 -0
- package/dist/ui/file-preview/src/document-outline.js +97 -0
- package/dist/ui/file-preview/src/document-workspace.d.ts +19 -0
- package/dist/ui/file-preview/src/document-workspace.js +33 -0
- package/dist/ui/file-preview/src/file-type-handlers.d.ts +10 -0
- package/dist/ui/file-preview/src/file-type-handlers.js +109 -0
- package/dist/ui/file-preview/src/host/external-actions.d.ts +8 -0
- package/dist/ui/file-preview/src/host/external-actions.js +55 -0
- package/dist/ui/file-preview/src/host/selection-context.d.ts +9 -0
- package/dist/ui/file-preview/src/host/selection-context.js +106 -0
- package/dist/ui/file-preview/src/image-preview.d.ts +3 -0
- package/dist/ui/file-preview/src/image-preview.js +21 -0
- package/dist/ui/file-preview/src/main.d.ts +1 -0
- package/dist/ui/file-preview/src/main.js +5 -0
- package/dist/ui/file-preview/src/markdown/conflict-dialog.d.ts +40 -0
- package/dist/ui/file-preview/src/markdown/conflict-dialog.js +163 -0
- package/dist/ui/file-preview/src/markdown/controller.d.ts +44 -0
- package/dist/ui/file-preview/src/markdown/controller.js +1043 -0
- package/dist/ui/file-preview/src/markdown/editor.d.ts +131 -0
- package/dist/ui/file-preview/src/markdown/editor.js +1479 -0
- package/dist/ui/file-preview/src/markdown/linking.d.ts +16 -0
- package/dist/ui/file-preview/src/markdown/linking.js +228 -0
- package/dist/ui/file-preview/src/markdown/outline.d.ts +2 -0
- package/dist/ui/file-preview/src/markdown/outline.js +16 -0
- package/dist/ui/file-preview/src/markdown/parser.d.ts +30 -0
- package/dist/ui/file-preview/src/markdown/parser.js +38 -0
- package/dist/ui/file-preview/src/markdown/preview.d.ts +1 -0
- package/dist/ui/file-preview/src/markdown/preview.js +20 -0
- package/dist/ui/file-preview/src/markdown/slugify.d.ts +3 -0
- package/dist/ui/file-preview/src/markdown/slugify.js +31 -0
- package/dist/ui/file-preview/src/markdown/utils.d.ts +1 -0
- package/dist/ui/file-preview/src/markdown/utils.js +15 -0
- package/dist/ui/file-preview/src/model.d.ts +35 -0
- package/dist/ui/file-preview/src/model.js +1 -0
- package/dist/ui/file-preview/src/panel-actions.d.ts +17 -0
- package/dist/ui/file-preview/src/panel-actions.js +182 -0
- package/dist/ui/file-preview/src/path-utils.d.ts +6 -0
- package/dist/ui/file-preview/src/path-utils.js +64 -0
- package/dist/ui/file-preview/src/payload-utils.d.ts +11 -0
- package/dist/ui/file-preview/src/payload-utils.js +119 -0
- package/dist/ui/file-preview/src/types.d.ts +1 -0
- package/dist/ui/file-preview/src/types.js +1 -0
- package/dist/ui/file-preview/styles.css +1631 -0
- package/dist/ui/resources.d.ts +28 -0
- package/dist/ui/resources.js +86 -0
- package/dist/ui/shared/compact-row.d.ts +11 -0
- package/dist/ui/shared/compact-row.js +18 -0
- package/dist/ui/shared/escape-html.d.ts +4 -0
- package/dist/ui/shared/escape-html.js +11 -0
- package/dist/ui/shared/host-context.d.ts +15 -0
- package/dist/ui/shared/host-context.js +51 -0
- package/dist/ui/shared/tool-bridge.d.ts +30 -0
- package/dist/ui/shared/tool-bridge.js +137 -0
- package/dist/ui/shared/tool-shell.d.ts +25 -0
- package/dist/ui/shared/tool-shell.js +107 -0
- package/dist/ui/shared/ui-event-tracker.d.ts +9 -0
- package/dist/ui/shared/ui-event-tracker.js +27 -0
- package/dist/ui/shared/widget-state.d.ts +33 -0
- package/dist/ui/shared/widget-state.js +158 -0
- package/dist/utils/ab-test.d.ts +12 -0
- package/dist/utils/ab-test.js +104 -0
- package/dist/utils/capture.d.ts +35 -0
- package/dist/utils/capture.js +481 -0
- package/dist/utils/dockerPrompt.d.ts +17 -0
- package/dist/utils/dockerPrompt.js +53 -0
- package/dist/utils/feature-flags.d.ts +59 -0
- package/dist/utils/feature-flags.js +215 -0
- package/dist/utils/files/base.d.ts +173 -0
- package/dist/utils/files/base.js +5 -0
- package/dist/utils/files/binary.d.ts +21 -0
- package/dist/utils/files/binary.js +65 -0
- package/dist/utils/files/docx.d.ts +47 -0
- package/dist/utils/files/docx.js +679 -0
- package/dist/utils/files/excel.d.ts +24 -0
- package/dist/utils/files/excel.js +437 -0
- package/dist/utils/files/factory.d.ts +41 -0
- package/dist/utils/files/factory.js +113 -0
- package/dist/utils/files/image.d.ts +21 -0
- package/dist/utils/files/image.js +78 -0
- package/dist/utils/files/index.d.ts +10 -0
- package/dist/utils/files/index.js +13 -0
- package/dist/utils/files/pdf.d.ts +32 -0
- package/dist/utils/files/pdf.js +142 -0
- package/dist/utils/files/text.d.ts +63 -0
- package/dist/utils/files/text.js +370 -0
- package/dist/utils/fuzzySearchLogger.d.ts +30 -0
- package/dist/utils/fuzzySearchLogger.js +126 -0
- package/dist/utils/lineEndingHandler.d.ts +21 -0
- package/dist/utils/lineEndingHandler.js +77 -0
- package/dist/utils/logger.d.ts +32 -0
- package/dist/utils/logger.js +72 -0
- package/dist/utils/mcp-ui-ab-test.d.ts +13 -0
- package/dist/utils/mcp-ui-ab-test.js +62 -0
- package/dist/utils/open-browser.d.ts +9 -0
- package/dist/utils/open-browser.js +42 -0
- package/dist/utils/process-detection.d.ts +23 -0
- package/dist/utils/process-detection.js +150 -0
- package/dist/utils/ripgrep-resolver.d.ts +9 -0
- package/dist/utils/ripgrep-resolver.js +74 -0
- package/dist/utils/system-info.d.ts +71 -0
- package/dist/utils/system-info.js +712 -0
- package/dist/utils/toolHistory.d.ts +86 -0
- package/dist/utils/toolHistory.js +257 -0
- package/dist/utils/trackTools.d.ts +6 -0
- package/dist/utils/trackTools.js +54 -0
- package/dist/utils/unsupportedParams.d.ts +24 -0
- package/dist/utils/unsupportedParams.js +66 -0
- package/dist/utils/usageTracker.d.ts +133 -0
- package/dist/utils/usageTracker.js +477 -0
- package/dist/utils/welcome-onboarding.d.ts +11 -0
- package/dist/utils/welcome-onboarding.js +99 -0
- package/dist/utils/withTimeout.d.ts +28 -0
- package/dist/utils/withTimeout.js +85 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- 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>;
|