@wonderwhy-er/desktop-commander 0.2.40 → 0.2.42
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/README.md +4 -4
- package/dist/handlers/filesystem-handlers.js +28 -3
- package/dist/server.d.ts +2 -1
- package/dist/server.js +50 -13
- package/dist/setup-claude-server.js +56 -50
- package/dist/terminal-manager.js +46 -0
- package/dist/tools/edit.js +7 -1
- package/dist/tools/filesystem.d.ts +5 -0
- package/dist/tools/filesystem.js +91 -14
- package/dist/tools/pdf/markdown.d.ts +13 -0
- package/dist/tools/pdf/markdown.js +93 -29
- package/dist/track-installation.js +57 -38
- package/dist/types.d.ts +4 -0
- package/dist/ui/contracts.d.ts +1 -1
- package/dist/ui/contracts.js +4 -1
- package/dist/ui/file-preview/preview-runtime.js +114 -116
- package/dist/ui/file-preview/src/app.js +19 -22
- package/dist/ui/file-preview/src/directory-controller.js +9 -2
- package/dist/ui/file-preview/src/file-type-handlers.js +20 -9
- package/dist/ui/file-preview/src/host/external-actions.d.ts +0 -11
- package/dist/ui/file-preview/src/host/external-actions.js +0 -39
- package/dist/ui/file-preview/src/payload-utils.js +10 -1
- package/dist/uninstall-claude-server.js +54 -47
- package/dist/utils/ab-test.d.ts +4 -0
- package/dist/utils/ab-test.js +6 -0
- package/dist/utils/capture.d.ts +10 -2
- package/dist/utils/capture.js +80 -54
- package/dist/utils/feature-flags.d.ts +3 -0
- package/dist/utils/feature-flags.js +34 -5
- package/dist/utils/files/excel.js +26 -5
- package/dist/utils/mcp-ui-ab-test.d.ts +13 -0
- package/dist/utils/mcp-ui-ab-test.js +62 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
|
@@ -11,7 +11,7 @@ import { attachDirectoryHandlers } from './directory-controller.js';
|
|
|
11
11
|
import { buildDocumentLayout } from './document-layout.js';
|
|
12
12
|
import { getDocumentFullscreenAvailability, parseReadRange, stripReadStatusLine } from './document-workspace.js';
|
|
13
13
|
import { getFileTypeCapabilities, renderPayloadBody } from './file-type-handlers.js';
|
|
14
|
-
import { buildOpenInEditorCommand, buildOpenInFolderCommand,
|
|
14
|
+
import { buildOpenInEditorCommand, buildOpenInFolderCommand, renderMarkdownEditorAppIcon } from './host/external-actions.js';
|
|
15
15
|
import { attachSelectionContext } from './host/selection-context.js';
|
|
16
16
|
import { createMarkdownController } from './markdown/controller.js';
|
|
17
17
|
import { createConflictDialogController, renderConflictDialogMarkup, } from './markdown/conflict-dialog.js';
|
|
@@ -40,7 +40,9 @@ let inlinePayloadBeforeFullscreen;
|
|
|
40
40
|
let directoryBackPayload;
|
|
41
41
|
let selectionAbortController = null;
|
|
42
42
|
const markdownEditorAppCache = new Map();
|
|
43
|
-
|
|
43
|
+
function getTelemetryToolName(payload) {
|
|
44
|
+
return typeof payload?.sourceTool === 'string' ? payload.sourceTool : 'read_file';
|
|
45
|
+
}
|
|
44
46
|
async function callToolIfReady(name, args) {
|
|
45
47
|
return rpcCallTool ? rpcCallTool(name, args) : undefined;
|
|
46
48
|
}
|
|
@@ -147,7 +149,10 @@ async function readAndResolvePayload(payload, onReady) {
|
|
|
147
149
|
try {
|
|
148
150
|
const freshPayload = await markdownController.readPayload(payload.filePath);
|
|
149
151
|
if (freshPayload) {
|
|
150
|
-
onReady(
|
|
152
|
+
onReady({
|
|
153
|
+
...freshPayload,
|
|
154
|
+
sourceTool: payload.sourceTool ?? freshPayload.sourceTool,
|
|
155
|
+
});
|
|
151
156
|
if (freshPayload.fileType === 'markdown') {
|
|
152
157
|
void markdownController.refreshFromDisk(freshPayload);
|
|
153
158
|
}
|
|
@@ -212,21 +217,15 @@ export function renderApp(container, payload, htmlMode = 'rendered', expandedSta
|
|
|
212
217
|
const canGoFullscreen = !isFullscreen && getDocumentFullscreenAvailability({
|
|
213
218
|
availableDisplayModes: getAvailableDisplayModes(),
|
|
214
219
|
}).canFullscreen;
|
|
220
|
+
if (payload.fileType === 'markdown' && payload.defaultEditorName) {
|
|
221
|
+
markdownEditorAppCache.set(payload.filePath, {
|
|
222
|
+
appName: payload.defaultEditorName,
|
|
223
|
+
appPath: payload.defaultEditorPath,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
215
226
|
const defaultMarkdownEditor = payload.fileType === 'markdown'
|
|
216
227
|
? markdownEditorAppCache.get(payload.filePath)
|
|
217
228
|
: undefined;
|
|
218
|
-
if (payload.fileType === 'markdown' && !defaultMarkdownEditor) {
|
|
219
|
-
void detectDefaultMarkdownEditor({
|
|
220
|
-
filePath: payload.filePath,
|
|
221
|
-
editorAppCache: markdownEditorAppCache,
|
|
222
|
-
editorAppPending: markdownEditorAppPending,
|
|
223
|
-
callTool: callToolIfReady,
|
|
224
|
-
extractToolText,
|
|
225
|
-
onDetected: () => {
|
|
226
|
-
rerenderCurrent?.();
|
|
227
|
-
},
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
229
|
const layout = buildDocumentLayout({
|
|
231
230
|
payload,
|
|
232
231
|
body,
|
|
@@ -415,9 +414,12 @@ export function bootstrapApp() {
|
|
|
415
414
|
const result = await app.requestDisplayMode({ mode });
|
|
416
415
|
return typeof result.mode === 'string' ? result.mode : null;
|
|
417
416
|
};
|
|
418
|
-
|
|
417
|
+
const filePreviewUiEvent = createUiEventTracker((name, args) => app.callServerTool({ name, arguments: args }), {
|
|
419
418
|
component: 'file_preview',
|
|
420
|
-
|
|
419
|
+
});
|
|
420
|
+
trackUiEvent = (event, params = {}) => filePreviewUiEvent(event, {
|
|
421
|
+
tool_name: getTelemetryToolName(currentPayload ?? hostPayload),
|
|
422
|
+
...params,
|
|
421
423
|
});
|
|
422
424
|
app.ontoolinput = (params) => {
|
|
423
425
|
const requestedPath = typeof params.arguments?.path === 'string' ? params.arguments.path : undefined;
|
|
@@ -548,11 +550,6 @@ export function bootstrapApp() {
|
|
|
548
550
|
onConnected: () => {
|
|
549
551
|
currentHostContext = app.getHostContext();
|
|
550
552
|
pendingCachedPayload = widgetState.read() ?? undefined;
|
|
551
|
-
window.setTimeout(() => {
|
|
552
|
-
if (!initialStateResolved) {
|
|
553
|
-
resolveInitialState(undefined, 'Preview unavailable after page refresh. Switch threads or re-run the tool.');
|
|
554
|
-
}
|
|
555
|
-
}, 8000);
|
|
556
553
|
},
|
|
557
554
|
}).catch(() => {
|
|
558
555
|
renderStatusState(container, 'Failed to connect to host.');
|
|
@@ -5,7 +5,7 @@ function parseDirectoryEntries(content) {
|
|
|
5
5
|
const hintLines = [];
|
|
6
6
|
const entryLines = [];
|
|
7
7
|
for (const line of lines) {
|
|
8
|
-
if (/^\[(DIR|FILE|DENIED|WARNING)\]/.test(line.trim())) {
|
|
8
|
+
if (/^\[(DIR|FILE|DENIED|NOT_FOUND|WARNING)\]/.test(line.trim())) {
|
|
9
9
|
entryLines.push(line.trim());
|
|
10
10
|
}
|
|
11
11
|
else if (entryLines.length === 0) {
|
|
@@ -25,6 +25,7 @@ function parseDirectoryEntries(content) {
|
|
|
25
25
|
fullPath: dirName,
|
|
26
26
|
isDir: false,
|
|
27
27
|
isDenied: false,
|
|
28
|
+
isNotFound: false,
|
|
28
29
|
isWarning: true,
|
|
29
30
|
warningText: msg,
|
|
30
31
|
depth: parts.length,
|
|
@@ -33,13 +34,15 @@ function parseDirectoryEntries(content) {
|
|
|
33
34
|
}
|
|
34
35
|
const isDir = line.startsWith('[DIR]');
|
|
35
36
|
const isDenied = line.startsWith('[DENIED]');
|
|
36
|
-
const
|
|
37
|
+
const isNotFound = line.startsWith('[NOT_FOUND]');
|
|
38
|
+
const name = line.replace(/^\[(DIR|FILE|DENIED|NOT_FOUND)\]\s*/, '');
|
|
37
39
|
const parts = name.replace(/\\/g, '/').split('/');
|
|
38
40
|
flat.push({
|
|
39
41
|
name,
|
|
40
42
|
fullPath: name,
|
|
41
43
|
isDir,
|
|
42
44
|
isDenied,
|
|
45
|
+
isNotFound,
|
|
43
46
|
isWarning: false,
|
|
44
47
|
warningText: '',
|
|
45
48
|
depth: parts.length - 1,
|
|
@@ -53,6 +56,7 @@ function parseDirectoryEntries(content) {
|
|
|
53
56
|
name: baseName,
|
|
54
57
|
isDir: item.isDir,
|
|
55
58
|
isDenied: item.isDenied,
|
|
59
|
+
isNotFound: item.isNotFound,
|
|
56
60
|
isWarning: item.isWarning,
|
|
57
61
|
warningText: item.warningText,
|
|
58
62
|
children: [],
|
|
@@ -85,6 +89,9 @@ function renderDirTree(entries, rootPath) {
|
|
|
85
89
|
if (item.isDenied) {
|
|
86
90
|
return `<div class="dir-entry"><span class="dir-icon">🚫</span> <span class="dir-name-denied">${escapeHtml(item.name)}</span></div>`;
|
|
87
91
|
}
|
|
92
|
+
if (item.isNotFound) {
|
|
93
|
+
return `<div class="dir-entry"><span class="dir-icon">❓</span> <span class="dir-name-denied">${escapeHtml(item.name)}</span></div>`;
|
|
94
|
+
}
|
|
88
95
|
if (item.isDir) {
|
|
89
96
|
const hasChildren = item.children.length > 0;
|
|
90
97
|
const chevron = `<span class="dir-chevron${hasChildren ? ' expanded' : ''}">${hasChildren ? '▼' : '▶'}</span>`;
|
|
@@ -74,15 +74,26 @@ const handlerRegistry = {
|
|
|
74
74
|
},
|
|
75
75
|
},
|
|
76
76
|
unsupported: {
|
|
77
|
-
getCapabilities: () =>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
})
|
|
77
|
+
getCapabilities: (payload) => {
|
|
78
|
+
const hasRawContent = stripReadStatusLine(payload.content).trim().length > 0;
|
|
79
|
+
return {
|
|
80
|
+
supportsPreview: hasRawContent,
|
|
81
|
+
canCopy: hasRawContent,
|
|
82
|
+
canOpenInFolder: !isLikelyUrl(payload.filePath),
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
renderBody: ({ payload }) => {
|
|
86
|
+
const rawContent = stripReadStatusLine(payload.content);
|
|
87
|
+
if (rawContent.trim().length === 0) {
|
|
88
|
+
return {
|
|
89
|
+
notice: 'Preview is not available for this file type.',
|
|
90
|
+
html: '<div class="panel-content source-content"></div>',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
html: `<div class="panel-content source-content">${renderRawFallback(rawContent)}</div>`,
|
|
95
|
+
};
|
|
96
|
+
},
|
|
86
97
|
},
|
|
87
98
|
};
|
|
88
99
|
export function getFileTypeCapabilities(payload) {
|
|
@@ -5,15 +5,4 @@ export declare function buildOpenInEditorCommand(filePath: string, isLikelyUrl:
|
|
|
5
5
|
appName: string;
|
|
6
6
|
appPath?: string;
|
|
7
7
|
}>): string | undefined;
|
|
8
|
-
export declare function detectDefaultMarkdownEditor(options: {
|
|
9
|
-
filePath: string;
|
|
10
|
-
editorAppCache: Map<string, {
|
|
11
|
-
appName: string;
|
|
12
|
-
appPath?: string;
|
|
13
|
-
}>;
|
|
14
|
-
editorAppPending: Set<string>;
|
|
15
|
-
callTool?: (name: string, args: Record<string, unknown>) => Promise<unknown | undefined>;
|
|
16
|
-
extractToolText: (value: unknown) => string | undefined;
|
|
17
|
-
onDetected?: () => void;
|
|
18
|
-
}): Promise<void>;
|
|
19
8
|
export declare function renderMarkdownEditorAppIcon(): string;
|
|
@@ -50,45 +50,6 @@ export function buildOpenInEditorCommand(filePath, isLikelyUrl, editorAppCache)
|
|
|
50
50
|
}
|
|
51
51
|
return `xdg-open ${shellQuote(trimmedPath)}`;
|
|
52
52
|
}
|
|
53
|
-
export async function detectDefaultMarkdownEditor(options) {
|
|
54
|
-
const trimmedPath = options.filePath.trim();
|
|
55
|
-
if (!trimmedPath || options.editorAppCache.has(trimmedPath) || options.editorAppPending.has(trimmedPath)) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
|
59
|
-
if (!userAgent.includes('mac')) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
options.editorAppPending.add(trimmedPath);
|
|
63
|
-
try {
|
|
64
|
-
const detectCommand = `osascript -e ${shellQuote(`set appAlias to default application of (info for POSIX file "${trimmedPath.replace(/"/g, '\\"')}")
|
|
65
|
-
return (name of (info for appAlias)) & linefeed & POSIX path of appAlias`)}`;
|
|
66
|
-
const detectResult = await options.callTool?.('start_process', {
|
|
67
|
-
command: detectCommand,
|
|
68
|
-
timeout_ms: 12000,
|
|
69
|
-
});
|
|
70
|
-
const text = options.extractToolText(detectResult) ?? '';
|
|
71
|
-
if (!text || text.toLowerCase().includes('error') || text.toLowerCase().includes('execution')) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
const lines = text.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
75
|
-
const appName = lines[lines.length - 2]?.replace(/\.app$/i, '') ?? '';
|
|
76
|
-
const appPath = lines[lines.length - 1] ?? '';
|
|
77
|
-
if (appName && appPath.startsWith('/')) {
|
|
78
|
-
options.editorAppCache.set(trimmedPath, {
|
|
79
|
-
appName,
|
|
80
|
-
appPath,
|
|
81
|
-
});
|
|
82
|
-
options.onDetected?.();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
// Fall back to generic editor label.
|
|
87
|
-
}
|
|
88
|
-
finally {
|
|
89
|
-
options.editorAppPending.delete(trimmedPath);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
53
|
export function renderMarkdownEditorAppIcon() {
|
|
93
54
|
return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 1 1 3 3L7 19l-4 1 1-4Z"/></svg>';
|
|
94
55
|
}
|
|
@@ -41,6 +41,12 @@ export function extractToolText(value) {
|
|
|
41
41
|
}
|
|
42
42
|
return undefined;
|
|
43
43
|
}
|
|
44
|
+
function extractStructuredContentText(value) {
|
|
45
|
+
if (!isObjectRecord(value)) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
return typeof value.content === 'string' ? value.content : undefined;
|
|
49
|
+
}
|
|
44
50
|
export function extractRenderPayload(value) {
|
|
45
51
|
if (!isObjectRecord(value)) {
|
|
46
52
|
return undefined;
|
|
@@ -52,7 +58,10 @@ export function extractRenderPayload(value) {
|
|
|
52
58
|
: null;
|
|
53
59
|
if (!meta)
|
|
54
60
|
return undefined;
|
|
55
|
-
const text =
|
|
61
|
+
const text = extractStructuredContentText(value.structuredContent)
|
|
62
|
+
?? extractToolText(value)
|
|
63
|
+
?? extractToolText(value.structuredContent)
|
|
64
|
+
?? '';
|
|
56
65
|
return buildRenderPayload(meta, text);
|
|
57
66
|
}
|
|
58
67
|
export function assertSuccessfulEditBlockResult(result) {
|
|
@@ -10,10 +10,9 @@ import { version as nodeVersion } from 'process';
|
|
|
10
10
|
import * as https from 'https';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const GA_BASE_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
|
|
13
|
+
// Telemetry proxy configuration
|
|
14
|
+
const TELEMETRY_PROXY_URL = 'https://telemetry.desktopcommander.app/mp/collect';
|
|
15
|
+
const TELEMETRY_PROXY_FALLBACK_URL = 'https://dc-telemetry-proxy-83847352264.europe-west1.run.app/mp/collect';
|
|
17
16
|
|
|
18
17
|
// Read clientId and telemetry settings from existing config
|
|
19
18
|
let uniqueUserId = 'unknown';
|
|
@@ -222,11 +221,6 @@ async function trackEvent(eventName, additionalProps = {}) {
|
|
|
222
221
|
return true; // Return success since this is expected behavior
|
|
223
222
|
}
|
|
224
223
|
|
|
225
|
-
if (!GA_MEASUREMENT_ID || !GA_API_SECRET) {
|
|
226
|
-
updateUninstallStep(trackingStep, 'skipped', new Error('GA not configured'));
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
224
|
const maxRetries = 2;
|
|
231
225
|
let attempt = 0;
|
|
232
226
|
let lastError = null;
|
|
@@ -257,43 +251,8 @@ async function trackEvent(eventName, additionalProps = {}) {
|
|
|
257
251
|
}
|
|
258
252
|
};
|
|
259
253
|
|
|
260
|
-
const result = await
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const timeoutId = setTimeout(() => {
|
|
264
|
-
req.destroy();
|
|
265
|
-
reject(new Error('Request timeout'));
|
|
266
|
-
}, 5000);
|
|
267
|
-
|
|
268
|
-
req.on('error', (error) => {
|
|
269
|
-
clearTimeout(timeoutId);
|
|
270
|
-
reject(error);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
req.on('response', (res) => {
|
|
274
|
-
clearTimeout(timeoutId);
|
|
275
|
-
let data = '';
|
|
276
|
-
|
|
277
|
-
res.on('data', (chunk) => {
|
|
278
|
-
data += chunk;
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
res.on('error', (error) => {
|
|
282
|
-
reject(error);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
res.on('end', () => {
|
|
286
|
-
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
287
|
-
resolve({ success: true, data });
|
|
288
|
-
} else {
|
|
289
|
-
reject(new Error(`HTTP error ${res.statusCode}: ${data}`));
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
req.write(postData);
|
|
295
|
-
req.end();
|
|
296
|
-
});
|
|
254
|
+
const result = await postTelemetryPayload(postData, options);
|
|
255
|
+
if (!result.success) throw new Error('Telemetry proxy request failed');
|
|
297
256
|
|
|
298
257
|
updateUninstallStep(trackingStep, 'completed');
|
|
299
258
|
return result;
|
|
@@ -309,6 +268,54 @@ async function trackEvent(eventName, additionalProps = {}) {
|
|
|
309
268
|
updateUninstallStep(trackingStep, 'failed', lastError);
|
|
310
269
|
return false;
|
|
311
270
|
}
|
|
271
|
+
|
|
272
|
+
async function postTelemetryPayload(postData, options) {
|
|
273
|
+
for (const endpoint of [TELEMETRY_PROXY_URL, TELEMETRY_PROXY_FALLBACK_URL]) {
|
|
274
|
+
const result = await new Promise((resolve) => {
|
|
275
|
+
let settled = false;
|
|
276
|
+
let timeoutId;
|
|
277
|
+
const finish = (result) => {
|
|
278
|
+
if (settled) return;
|
|
279
|
+
settled = true;
|
|
280
|
+
clearTimeout(timeoutId);
|
|
281
|
+
resolve(result);
|
|
282
|
+
};
|
|
283
|
+
const req = https.request(endpoint, options);
|
|
284
|
+
|
|
285
|
+
timeoutId = setTimeout(() => {
|
|
286
|
+
req.destroy();
|
|
287
|
+
finish({ success: false, data: '' });
|
|
288
|
+
}, 5000);
|
|
289
|
+
|
|
290
|
+
req.on('error', () => {
|
|
291
|
+
finish({ success: false, data: '' });
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
req.on('response', (res) => {
|
|
295
|
+
let data = '';
|
|
296
|
+
res.on('data', (chunk) => {
|
|
297
|
+
data += chunk;
|
|
298
|
+
});
|
|
299
|
+
res.on('error', () => {
|
|
300
|
+
finish({ success: false, data: '' });
|
|
301
|
+
});
|
|
302
|
+
res.on('end', () => {
|
|
303
|
+
finish({ success: res.statusCode >= 200 && res.statusCode < 300, data });
|
|
304
|
+
});
|
|
305
|
+
res.on('close', () => {
|
|
306
|
+
finish({ success: false, data: '' });
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
req.write(postData);
|
|
311
|
+
req.end();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
if (result.success) return result;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return { success: false, data: '' };
|
|
318
|
+
}
|
|
312
319
|
// Ensure tracking completes before process exits
|
|
313
320
|
async function ensureTrackingCompleted(eventName, additionalProps = {}, timeoutMs = 6000) {
|
|
314
321
|
return new Promise(async (resolve) => {
|
|
@@ -750,4 +757,4 @@ if (process.argv.length >= 2 && process.argv[1] === fileURLToPath(import.meta.ur
|
|
|
750
757
|
logToFile(`Fatal error: ${error}`, true);
|
|
751
758
|
process.exit(1);
|
|
752
759
|
});
|
|
753
|
-
}
|
|
760
|
+
}
|
package/dist/utils/ab-test.d.ts
CHANGED
package/dist/utils/ab-test.js
CHANGED
|
@@ -57,6 +57,12 @@ async function getVariant(experimentName) {
|
|
|
57
57
|
variantCache[experimentName] = variant;
|
|
58
58
|
return variant;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the exact assigned variant for a named experiment.
|
|
62
|
+
*/
|
|
63
|
+
export async function getABTestVariant(experimentName) {
|
|
64
|
+
return getVariant(experimentName);
|
|
65
|
+
}
|
|
60
66
|
/**
|
|
61
67
|
* Check if a feature (variant name) is enabled for current user
|
|
62
68
|
*/
|
package/dist/utils/capture.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hard kill-switch for telemetry via environment variable.
|
|
3
|
+
*
|
|
4
|
+
* Independent of the persisted `telemetryEnabled` config so that tests, CI and
|
|
5
|
+
* one-off runs can suppress all analytics without mutating the user's config.
|
|
6
|
+
* Set DESKTOP_COMMANDER_DISABLE_TELEMETRY to 1/true/yes/on to disable.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isTelemetryDisabledByEnv(): boolean;
|
|
1
9
|
/**
|
|
2
10
|
* Sanitizes error objects to remove potentially sensitive information like file paths
|
|
3
11
|
* @param error Error object or string to sanitize
|
|
@@ -8,13 +16,13 @@ export declare function sanitizeError(error: any): {
|
|
|
8
16
|
code?: string;
|
|
9
17
|
};
|
|
10
18
|
/**
|
|
11
|
-
* Send an event to
|
|
19
|
+
* Send an event to telemetry
|
|
12
20
|
* @param event Event name
|
|
13
21
|
* @param properties Optional event properties
|
|
14
22
|
*/
|
|
15
23
|
export declare const captureBase: (captureURL: string, event: string, properties?: any) => Promise<void>;
|
|
16
|
-
export declare const capture_call_tool: (event: string, properties?: any) => Promise<void>;
|
|
17
24
|
export declare const capture: (event: string, properties?: any) => Promise<void>;
|
|
25
|
+
export declare const capture_call_tool: (event: string, properties?: any) => Promise<void>;
|
|
18
26
|
export declare const capture_ui_event: (event: string, properties?: any) => Promise<void>;
|
|
19
27
|
/**
|
|
20
28
|
* Wrapper for capture() that automatically adds remote flag for remote-device telemetry
|