starmux 0.12.0 → 0.13.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 (93) hide show
  1. package/dist/src/cli/mac-mcp-gateway.js +4 -3
  2. package/dist/src/cli/mac-mcp-gateway.js.map +1 -1
  3. package/dist/src/cli/starmux.js +1 -1
  4. package/dist/src/cli/starmux.js.map +1 -1
  5. package/dist/src/runtime-paths.js +3 -3
  6. package/dist/src/runtime-paths.js.map +1 -1
  7. package/dist/vendor/desktop-commander/LICENSE +21 -0
  8. package/dist/vendor/desktop-commander/dist/bootstrap.js +22 -0
  9. package/dist/vendor/desktop-commander/dist/command-manager.js +246 -0
  10. package/dist/vendor/desktop-commander/dist/config-field-definitions.js +37 -0
  11. package/dist/vendor/desktop-commander/dist/config-manager.js +634 -0
  12. package/dist/vendor/desktop-commander/dist/config-recovery.js +81 -0
  13. package/dist/vendor/desktop-commander/dist/config.js +19 -0
  14. package/dist/vendor/desktop-commander/dist/custom-stdio.js +358 -0
  15. package/dist/vendor/desktop-commander/dist/error-handlers.js +15 -0
  16. package/dist/vendor/desktop-commander/dist/handlers/edit-search-handlers.js +6 -0
  17. package/dist/vendor/desktop-commander/dist/handlers/filesystem-handlers.js +450 -0
  18. package/dist/vendor/desktop-commander/dist/handlers/history-handlers.js +67 -0
  19. package/dist/vendor/desktop-commander/dist/handlers/index.js +7 -0
  20. package/dist/vendor/desktop-commander/dist/handlers/process-handlers.js +15 -0
  21. package/dist/vendor/desktop-commander/dist/handlers/search-answers.js +172 -0
  22. package/dist/vendor/desktop-commander/dist/handlers/search-handlers.js +136 -0
  23. package/dist/vendor/desktop-commander/dist/handlers/terminal-handlers.js +35 -0
  24. package/dist/vendor/desktop-commander/dist/index.js +88 -0
  25. package/dist/vendor/desktop-commander/dist/search-manager.js +1262 -0
  26. package/dist/vendor/desktop-commander/dist/server.js +1414 -0
  27. package/dist/vendor/desktop-commander/dist/terminal-manager.js +757 -0
  28. package/dist/vendor/desktop-commander/dist/tools/config.js +211 -0
  29. package/dist/vendor/desktop-commander/dist/tools/edit.js +455 -0
  30. package/dist/vendor/desktop-commander/dist/tools/feedback.js +10 -0
  31. package/dist/vendor/desktop-commander/dist/tools/filesystem.js +1047 -0
  32. package/dist/vendor/desktop-commander/dist/tools/fuzzySearch.js +74 -0
  33. package/dist/vendor/desktop-commander/dist/tools/fuzzySearchCore.js +125 -0
  34. package/dist/vendor/desktop-commander/dist/tools/improved-process-tools.js +726 -0
  35. package/dist/vendor/desktop-commander/dist/tools/mime-types.js +28 -0
  36. package/dist/vendor/desktop-commander/dist/tools/pdf/extract-images.js +131 -0
  37. package/dist/vendor/desktop-commander/dist/tools/pdf/index.js +4 -0
  38. package/dist/vendor/desktop-commander/dist/tools/pdf/lib/pdf2md.js +78 -0
  39. package/dist/vendor/desktop-commander/dist/tools/pdf/manipulations.js +112 -0
  40. package/dist/vendor/desktop-commander/dist/tools/pdf/markdown.js +30 -0
  41. package/dist/vendor/desktop-commander/dist/tools/pdf/utils.js +45 -0
  42. package/dist/vendor/desktop-commander/dist/tools/process.js +90 -0
  43. package/dist/vendor/desktop-commander/dist/tools/prompts.js +259 -0
  44. package/dist/vendor/desktop-commander/dist/tools/schemas.js +242 -0
  45. package/dist/vendor/desktop-commander/dist/tools/usage.js +24 -0
  46. package/dist/vendor/desktop-commander/dist/types.js +1 -0
  47. package/dist/vendor/desktop-commander/dist/ui/config-editor/config-editor-runtime.js +150 -0
  48. package/dist/vendor/desktop-commander/dist/ui/config-editor/index.html +13 -0
  49. package/dist/vendor/desktop-commander/dist/ui/config-editor/styles.css +587 -0
  50. package/dist/vendor/desktop-commander/dist/ui/contracts.js +57 -0
  51. package/dist/vendor/desktop-commander/dist/ui/file-preview/index.html +16 -0
  52. package/dist/vendor/desktop-commander/dist/ui/file-preview/preview-runtime.js +471 -0
  53. package/dist/vendor/desktop-commander/dist/ui/file-preview/shared/preview-file-types.js +59 -0
  54. package/dist/vendor/desktop-commander/dist/ui/file-preview/styles.css +1631 -0
  55. package/dist/vendor/desktop-commander/dist/ui/resources.js +93 -0
  56. package/dist/vendor/desktop-commander/dist/utils/ab-test.js +122 -0
  57. package/dist/vendor/desktop-commander/dist/utils/atomic-write.js +80 -0
  58. package/dist/vendor/desktop-commander/dist/utils/capture.js +40 -0
  59. package/dist/vendor/desktop-commander/dist/utils/dockerPrompt.js +9 -0
  60. package/dist/vendor/desktop-commander/dist/utils/exit-process.js +31 -0
  61. package/dist/vendor/desktop-commander/dist/utils/feature-flags.js +18 -0
  62. package/dist/vendor/desktop-commander/dist/utils/files/base.js +5 -0
  63. package/dist/vendor/desktop-commander/dist/utils/files/binary.js +65 -0
  64. package/dist/vendor/desktop-commander/dist/utils/files/docx.js +694 -0
  65. package/dist/vendor/desktop-commander/dist/utils/files/excel.js +497 -0
  66. package/dist/vendor/desktop-commander/dist/utils/files/factory.js +168 -0
  67. package/dist/vendor/desktop-commander/dist/utils/files/image.js +89 -0
  68. package/dist/vendor/desktop-commander/dist/utils/files/index.js +14 -0
  69. package/dist/vendor/desktop-commander/dist/utils/files/pdf.js +124 -0
  70. package/dist/vendor/desktop-commander/dist/utils/files/text.js +423 -0
  71. package/dist/vendor/desktop-commander/dist/utils/fuzzySearchLogger.js +126 -0
  72. package/dist/vendor/desktop-commander/dist/utils/internal-facts.js +23 -0
  73. package/dist/vendor/desktop-commander/dist/utils/lazy-package.js +44 -0
  74. package/dist/vendor/desktop-commander/dist/utils/lineEndingHandler.js +77 -0
  75. package/dist/vendor/desktop-commander/dist/utils/logger.js +72 -0
  76. package/dist/vendor/desktop-commander/dist/utils/mcp-ui-ab-test.js +62 -0
  77. package/dist/vendor/desktop-commander/dist/utils/open-browser.js +2 -0
  78. package/dist/vendor/desktop-commander/dist/utils/process-detection.js +260 -0
  79. package/dist/vendor/desktop-commander/dist/utils/process-tree.js +223 -0
  80. package/dist/vendor/desktop-commander/dist/utils/rename.js +74 -0
  81. package/dist/vendor/desktop-commander/dist/utils/ripgrep-resolver.js +69 -0
  82. package/dist/vendor/desktop-commander/dist/utils/shell.js +187 -0
  83. package/dist/vendor/desktop-commander/dist/utils/system-info.js +727 -0
  84. package/dist/vendor/desktop-commander/dist/utils/terminal-timestamps.js +18 -0
  85. package/dist/vendor/desktop-commander/dist/utils/toolHistory.js +309 -0
  86. package/dist/vendor/desktop-commander/dist/utils/trackTools.js +55 -0
  87. package/dist/vendor/desktop-commander/dist/utils/unsupportedParams.js +66 -0
  88. package/dist/vendor/desktop-commander/dist/utils/usageTracker.js +225 -0
  89. package/dist/vendor/desktop-commander/dist/utils/welcome-onboarding.js +2 -0
  90. package/dist/vendor/desktop-commander/dist/utils/withTimeout.js +85 -0
  91. package/dist/vendor/desktop-commander/dist/version.js +1 -0
  92. package/dist/vendor/desktop-commander/package.json +15 -0
  93. package/package.json +14 -2
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Server-side UI resource loading helpers for MCP responses. It resolves packaged UI assets, reads files safely, and exposes structured resource payloads to clients.
3
+ */
4
+ import fs from 'fs/promises';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { buildUiContentSecurityPolicy, CONFIG_EDITOR_RESOURCE_URI, FILE_PREVIEW_RESOURCE_URI, UI_FONT_ORIGINS, UI_RESOURCE_CSP } from './contracts.js';
8
+ const UI_RESOURCE_MIME_TYPE = 'text/html;profile=mcp-app';
9
+ export const FILE_PREVIEW_RESOURCE = {
10
+ uri: FILE_PREVIEW_RESOURCE_URI,
11
+ name: 'Desktop Commander File Preview',
12
+ description: 'Markdown-first preview surface for read_file structured content.',
13
+ mimeType: UI_RESOURCE_MIME_TYPE
14
+ };
15
+ export const CONFIG_EDITOR_RESOURCE = {
16
+ uri: CONFIG_EDITOR_RESOURCE_URI,
17
+ name: 'Desktop Commander Config Editor',
18
+ description: 'Interactive editor for Desktop Commander configuration values.',
19
+ mimeType: UI_RESOURCE_MIME_TYPE
20
+ };
21
+ const __filename = fileURLToPath(import.meta.url);
22
+ const __dirname = path.dirname(__filename);
23
+ const DIST_FILE_PREVIEW_DIR = path.resolve(__dirname, 'file-preview');
24
+ const DIST_CONFIG_EDITOR_DIR = path.resolve(__dirname, 'config-editor');
25
+ function replaceOrThrow(source, pattern, replacement, context) {
26
+ if (!pattern.test(source)) {
27
+ throw new Error(`UI template is missing expected ${context}`);
28
+ }
29
+ return source.replace(pattern, () => replacement);
30
+ }
31
+ function inlineTemplateAssets(templateHtml, css, runtime) {
32
+ const safeCss = css.replace(/<\/style/gi, '<\\/style');
33
+ const safeRuntime = runtime.replace(/<\/script/gi, '<\\/script');
34
+ // First in <head>, so it covers everything the page loads, previews included
35
+ const policyAdded = replaceOrThrow(templateHtml, /<head>/i, `<head><meta http-equiv="Content-Security-Policy" content="${buildUiContentSecurityPolicy(UI_RESOURCE_CSP, UI_FONT_ORIGINS)}">`, '<head> tag');
36
+ const cssInlined = replaceOrThrow(policyAdded, /<link[^>]*href=["']\.\/styles\.css["'][^>]*>/i, `<style>${safeCss}</style>`, 'styles.css link tag');
37
+ const runtimeInlined = replaceOrThrow(cssInlined, /<script[^>]*src=["']\.\/[^"']+["'][^>]*>\s*<\/script>/i, `<script>${safeRuntime}</script>`, 'runtime script tag');
38
+ if (/href=["']\.\/styles\.css["']/i.test(runtimeInlined) ||
39
+ /<script[^>]*src=["']\.\/[^"']+["']/i.test(runtimeInlined)) {
40
+ throw new Error('UI template still contains external static asset references after inlining');
41
+ }
42
+ return runtimeInlined;
43
+ }
44
+ async function readInlinedResourceHtml(distDir, runtimeFileName) {
45
+ const [templateHtml, css, runtime] = await Promise.all([
46
+ fs.readFile(path.join(distDir, 'index.html'), 'utf8'),
47
+ fs.readFile(path.join(distDir, 'styles.css'), 'utf8'),
48
+ fs.readFile(path.join(distDir, runtimeFileName), 'utf8')
49
+ ]);
50
+ return inlineTemplateAssets(templateHtml, css, runtime);
51
+ }
52
+ export async function getFilePreviewResourceText() {
53
+ return readInlinedResourceHtml(DIST_FILE_PREVIEW_DIR, 'preview-runtime.js');
54
+ }
55
+ export async function getConfigEditorResourceText() {
56
+ return readInlinedResourceHtml(DIST_CONFIG_EDITOR_DIR, 'config-editor-runtime.js');
57
+ }
58
+ function getUiResourceMeta() {
59
+ return { ui: { csp: UI_RESOURCE_CSP } };
60
+ }
61
+ const READABLE_UI_RESOURCES = {
62
+ [FILE_PREVIEW_RESOURCE_URI]: {
63
+ mimeType: FILE_PREVIEW_RESOURCE.mimeType,
64
+ getText: getFilePreviewResourceText,
65
+ getMeta: getUiResourceMeta
66
+ },
67
+ [CONFIG_EDITOR_RESOURCE_URI]: {
68
+ mimeType: CONFIG_EDITOR_RESOURCE.mimeType,
69
+ getText: getConfigEditorResourceText,
70
+ getMeta: getUiResourceMeta
71
+ }
72
+ };
73
+ export function listUiResources() {
74
+ return [FILE_PREVIEW_RESOURCE, CONFIG_EDITOR_RESOURCE];
75
+ }
76
+ export async function readUiResource(uri) {
77
+ const resource = READABLE_UI_RESOURCES[uri];
78
+ if (!resource) {
79
+ return null;
80
+ }
81
+ const resourceText = await resource.getText();
82
+ const resourceMeta = resource.getMeta?.();
83
+ return {
84
+ contents: [
85
+ {
86
+ uri,
87
+ mimeType: resource.mimeType,
88
+ text: resourceText,
89
+ ...(resourceMeta ? { _meta: resourceMeta } : {})
90
+ }
91
+ ]
92
+ };
93
+ }
@@ -0,0 +1,122 @@
1
+ import { configManager } from '../config-manager.js';
2
+ import { featureFlagManager } from './feature-flags.js';
3
+ // Cache for variant assignments (loaded once per session). This map and the
4
+ // experiments map are keyed by experiment names from remote JSON, so they have
5
+ // no prototype: a name like __proto__ or constructor is an entry like any other.
6
+ const variantCache = Object.create(null);
7
+ /**
8
+ * Get experiments config from feature flags.
9
+ * The config is remote JSON, so it is validated here once: every experiment
10
+ * comes back as { variants: WeightedVariant[] }, keeping only variants with a
11
+ * string name (an invalid or negative weight counts as 0). One malformed entry
12
+ * must not break the lookups for all the others.
13
+ */
14
+ function getExperiments() {
15
+ const raw = featureFlagManager.get('experiments', {});
16
+ const experiments = Object.create(null);
17
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
18
+ return experiments;
19
+ for (const [name, experiment] of Object.entries(raw)) {
20
+ const rawVariants = Array.isArray(experiment?.variants) ? experiment.variants : [];
21
+ const variants = [];
22
+ for (const v of rawVariants) {
23
+ if (typeof v?.name !== 'string' || !v.name)
24
+ continue;
25
+ const weight = typeof v.weight === 'number' && Number.isFinite(v.weight) && v.weight > 0 ? v.weight : 0;
26
+ variants.push({ name: v.name, weight });
27
+ }
28
+ experiments[name] = { variants };
29
+ }
30
+ return experiments;
31
+ }
32
+ /**
33
+ * Get user's variant for an experiment (cached, deterministic)
34
+ * Supports weighted variants for unequal splits
35
+ */
36
+ async function getVariant(experimentName) {
37
+ const experiments = getExperiments();
38
+ const experiment = experiments[experimentName];
39
+ if (!experiment?.variants.length)
40
+ return null;
41
+ // Check cache
42
+ if (variantCache[experimentName]) {
43
+ return variantCache[experimentName];
44
+ }
45
+ // Check persisted assignment
46
+ const configKey = `abTest_${experimentName}`;
47
+ const existing = await configManager.getValue(configKey);
48
+ // Validate existing assignment is still a valid variant
49
+ const variantNames = experiment.variants.map(v => v.name);
50
+ if (existing && variantNames.includes(existing)) {
51
+ variantCache[experimentName] = existing;
52
+ return existing;
53
+ }
54
+ // New assignment based on clientId with weighted selection
55
+ const clientId = await configManager.getOrCreateClientId();
56
+ const hash = hashCode(clientId + experimentName);
57
+ // Calculate total weight and select variant
58
+ const totalWeight = experiment.variants.reduce((sum, v) => sum + v.weight, 0);
59
+ let variant;
60
+ if (totalWeight > 0) {
61
+ const roll = hash % totalWeight;
62
+ let cumulative = 0;
63
+ variant = experiment.variants[0].name; // fallback
64
+ for (const v of experiment.variants) {
65
+ cumulative += v.weight;
66
+ if (roll < cumulative) {
67
+ variant = v.name;
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ else {
73
+ // Fallback to equal split when weights are misconfigured (all zero)
74
+ const index = hash % experiment.variants.length;
75
+ variant = experiment.variants[index].name;
76
+ }
77
+ await configManager.setValue(configKey, variant);
78
+ variantCache[experimentName] = variant;
79
+ return variant;
80
+ }
81
+ /**
82
+ * Get the exact assigned variant for a named experiment.
83
+ */
84
+ export async function getABTestVariant(experimentName) {
85
+ return getVariant(experimentName);
86
+ }
87
+ /**
88
+ * Check if a feature (variant name) is enabled for current user
89
+ */
90
+ export async function hasFeature(featureName) {
91
+ const experiments = getExperiments();
92
+ for (const [expName, experiment] of Object.entries(experiments)) {
93
+ if (experiment.variants.some(v => v.name === featureName)) {
94
+ const variant = await getVariant(expName);
95
+ return variant === featureName;
96
+ }
97
+ }
98
+ return false;
99
+ }
100
+ /**
101
+ * Get all A/B test assignments for analytics (reads from config)
102
+ */
103
+ export async function getABTestAssignments() {
104
+ const experiments = getExperiments();
105
+ const assignments = {};
106
+ for (const expName of Object.keys(experiments)) {
107
+ const configKey = `abTest_${expName}`;
108
+ const variant = await configManager.getValue(configKey);
109
+ if (variant) {
110
+ assignments[`ab_${expName}`] = variant;
111
+ }
112
+ }
113
+ return assignments;
114
+ }
115
+ function hashCode(str) {
116
+ let hash = 0;
117
+ for (let i = 0; i < str.length; i++) {
118
+ hash = ((hash << 5) - hash) + str.charCodeAt(i);
119
+ hash |= 0;
120
+ }
121
+ return Math.abs(hash);
122
+ }
@@ -0,0 +1,80 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { renameWithRetry } from './rename.js';
4
+ import { logger } from './logger.js';
5
+ /**
6
+ * Single place for crash-safe file writes. Data goes to a temp file next to
7
+ * the target and a rename swaps it in, so readers only ever see the previous
8
+ * or the new complete file — never a truncated one.
9
+ *
10
+ * - The temp file's data is flushed to disk before the rename: otherwise the
11
+ * new name can reach the disk before the data, and a crash or power loss
12
+ * leaves the file empty or zero-filled (#697, #692). On macOS/Linux the
13
+ * folder is flushed after the rename, so the rename itself survives.
14
+ * - Writes to the same file from this process run one after another, so the
15
+ * per-process temp name (`<file>.<pid>.tmp`) can't collide; the pid keeps
16
+ * separate processes off each other's temp file.
17
+ * - The swap goes through renameWithRetry, which rides out Windows' brief
18
+ * locks on files other processes have open.
19
+ */
20
+ const pendingWrites = new Map();
21
+ async function writeThroughTempFile(filePath, data, options) {
22
+ const tempPath = `${filePath}.${process.pid}.tmp`;
23
+ try {
24
+ const handle = await fs.open(tempPath, 'w', options.mode);
25
+ try {
26
+ await handle.writeFile(data, { encoding: options.encoding ?? 'utf8' });
27
+ await handle.sync();
28
+ }
29
+ finally {
30
+ await handle.close();
31
+ }
32
+ await options.beforeCommit?.();
33
+ await renameWithRetry(tempPath, filePath);
34
+ }
35
+ finally {
36
+ // After a successful rename the temp file is gone (ENOENT); after a failed
37
+ // write, removing it is best effort and the write's own error is what's reported
38
+ await fs.unlink(tempPath).catch(() => { });
39
+ }
40
+ await syncFolder(path.dirname(filePath));
41
+ }
42
+ /**
43
+ * macOS/Linux: flushes the folder, so a rename in it reaches the disk. Windows
44
+ * can't open a folder for flushing, and NTFS journals the rename itself. The
45
+ * new content is already committed when this runs, so a failure is logged,
46
+ * not reported as a failed write.
47
+ */
48
+ async function syncFolder(folder) {
49
+ if (process.platform === 'win32')
50
+ return;
51
+ try {
52
+ const handle = await fs.open(folder, 'r');
53
+ try {
54
+ await handle.sync();
55
+ }
56
+ finally {
57
+ await handle.close();
58
+ }
59
+ }
60
+ catch (error) {
61
+ logger.error(`Could not flush ${folder} after replacing a file in it: ${error instanceof Error ? error.message : String(error)}`);
62
+ }
63
+ }
64
+ /**
65
+ * Atomically replaces `filePath` with `data`. Resolves once the new content is
66
+ * committed; rejects (leaving the previous file intact) if it can't be.
67
+ */
68
+ export function writeFileAtomic(filePath, data, options = {}) {
69
+ const key = path.resolve(filePath);
70
+ const previous = pendingWrites.get(key) ?? Promise.resolve();
71
+ // The previous write's failure was already reported to its own caller; this one runs regardless
72
+ const write = previous.catch(() => { }).then(() => writeThroughTempFile(key, data, options));
73
+ pendingWrites.set(key, write);
74
+ // Bookkeeping only: the caller gets `write` itself, with its rejection
75
+ write.finally(() => {
76
+ if (pendingWrites.get(key) === write)
77
+ pendingWrites.delete(key);
78
+ }).catch(() => { });
79
+ return write;
80
+ }
@@ -0,0 +1,40 @@
1
+ import { AsyncLocalStorage } from 'async_hooks';
2
+
3
+ const uiOriginCallContext = new AsyncLocalStorage();
4
+ export function runInUiOriginCallContext(fn) {
5
+ return uiOriginCallContext.run(true, fn);
6
+ }
7
+ export function isInsideUiOriginCall() {
8
+ return uiOriginCallContext.getStore() === true;
9
+ }
10
+
11
+ const toolCallPaths = new AsyncLocalStorage();
12
+ export function runWithToolCallPaths(fn) {
13
+ return toolCallPaths.run(new Set(), fn);
14
+ }
15
+ export function addToolCallPaths() {}
16
+
17
+ export function isTelemetryDisabledByEnv() {
18
+ return true;
19
+ }
20
+
21
+ export function sanitizeError(error) {
22
+ if (error instanceof Error) {
23
+ return {
24
+ message: `${error.name}: ${error.message}`,
25
+ code: 'code' in error ? error.code : undefined,
26
+ };
27
+ }
28
+ return {
29
+ message: typeof error === 'string' ? error : 'Unknown error',
30
+ code: undefined,
31
+ };
32
+ }
33
+
34
+ export const captureBase = async () => {};
35
+ export const buildEventProperties = async () => ({});
36
+ export const capture = async () => {};
37
+ export const captureTransport = async () => {};
38
+ export const capture_call_tool = async () => {};
39
+ export const capture_ui_event = async () => {};
40
+ export const captureRemote = async () => {};
@@ -0,0 +1,9 @@
1
+ export async function shouldPromptForDockerInfo() {
2
+ return false;
3
+ }
4
+ export function getDockerInfoMessage() {
5
+ return '';
6
+ }
7
+ export async function processDockerPrompt(result) {
8
+ return result;
9
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Single place for ending the process on purpose. Use this instead of process.exit().
3
+ *
4
+ * process.exit() tears Node down at once, without waiting for work V8 still
5
+ * runs on background threads. On Windows that aborts the process with
6
+ * "Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c"
7
+ * and exit code 0xC0000409 instead of the one asked for, whenever V8 is still
8
+ * compiling WebAssembly, e.g. the HTTP parser fetch() compiles right after a
9
+ * download (nodejs/node#56645). An exit that happens because nothing is left
10
+ * to do waits for that work.
11
+ *
12
+ * So exitProcess() sets the exit code and lets Node exit on its own. If
13
+ * something keeps the process alive (stdin, a socket, a timer), process.exit()
14
+ * ends it after EXIT_GRACE_MS, by which time that background work is done.
15
+ * Either way 'exit' listeners run and pending stdout/stderr writes get the
16
+ * grace period to flush.
17
+ *
18
+ * Unlike process.exit() this returns, and the process keeps running for up to
19
+ * EXIT_GRACE_MS: callers return right after calling it. Only the first call
20
+ * counts; later calls (a second Ctrl+C, a timeout firing) keep its exit code.
21
+ */
22
+ /** Longest the process keeps running after exitProcess() */
23
+ export const EXIT_GRACE_MS = 1000;
24
+ let exitCode;
25
+ export function exitProcess(code) {
26
+ if (exitCode !== undefined)
27
+ return;
28
+ exitCode = code;
29
+ process.exitCode = code;
30
+ setTimeout(() => process.exit(code), EXIT_GRACE_MS).unref();
31
+ }
@@ -0,0 +1,18 @@
1
+ class FeatureFlagManager {
2
+ async initialize() {}
3
+ get(_flagName, defaultValue = false) {
4
+ return defaultValue;
5
+ }
6
+ getAll() {
7
+ return {};
8
+ }
9
+ async refresh() {
10
+ return false;
11
+ }
12
+ wasLoadedFromCache() {
13
+ return true;
14
+ }
15
+ async waitForFreshFlags() {}
16
+ destroy() {}
17
+ }
18
+ export const featureFlagManager = new FeatureFlagManager();
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Base interfaces and types for file handling system
3
+ * All file handlers implement the FileHandler interface
4
+ */
5
+ export {};
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Binary file handler
3
+ * Handles binary files that aren't supported by other handlers (Excel, Image)
4
+ * Uses isBinaryFile for content-based detection
5
+ * Returns instructions to use start_process with appropriate tools
6
+ */
7
+ import fs from "fs/promises";
8
+ import path from "path";
9
+ import { isBinaryFile } from 'isbinaryfile';
10
+ /**
11
+ * Binary file handler implementation
12
+ * Uses content-based detection via isBinaryFile
13
+ */
14
+ export class BinaryFileHandler {
15
+ async canHandle(filePath) {
16
+ // Content-based binary detection using isBinaryFile
17
+ try {
18
+ return await isBinaryFile(filePath);
19
+ }
20
+ catch (error) {
21
+ // If we can't check (file doesn't exist, etc.), don't handle it
22
+ return false;
23
+ }
24
+ }
25
+ async read(filePath, options) {
26
+ const instructions = this.getBinaryInstructions(filePath);
27
+ return {
28
+ content: instructions,
29
+ mimeType: 'text/plain',
30
+ metadata: {
31
+ isBinary: true
32
+ }
33
+ };
34
+ }
35
+ async write(path, content) {
36
+ throw new Error('Cannot write binary files directly. Use start_process with appropriate tools (Python, Node.js libraries, command-line utilities).');
37
+ }
38
+ async getInfo(path) {
39
+ const stats = await fs.stat(path);
40
+ return {
41
+ size: stats.size,
42
+ created: stats.birthtime,
43
+ modified: stats.mtime,
44
+ accessed: stats.atime,
45
+ isDirectory: stats.isDirectory(),
46
+ isFile: stats.isFile(),
47
+ permissions: stats.mode.toString(8).slice(-3),
48
+ fileType: 'binary',
49
+ metadata: {
50
+ isBinary: true
51
+ }
52
+ };
53
+ }
54
+ /**
55
+ * Generate instructions for handling binary files
56
+ */
57
+ getBinaryInstructions(filePath) {
58
+ const fileName = path.basename(filePath);
59
+ return `Cannot read binary file as text: ${fileName}
60
+
61
+ Use start_process + interact_with_process to analyze binary files with appropriate tools (Node.js or Python libraries, command-line utilities, etc.).
62
+
63
+ The read_file tool only handles text files, images, and Excel files.`;
64
+ }
65
+ }