newmark-agent 0.6.7 → 0.6.8

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.
@@ -605,11 +605,15 @@ function sceneGeneration(apps, elements) {
605
605
  async function observeUiAutomation(maxChars, rootHandle, timeoutMs = 30000, lane = 'uia') {
606
606
  const limit = Math.min(Math.max(Math.floor(maxChars || 30000), 1000), 200000);
607
607
  const handleHex = String(rootHandle || '').trim().replace(/^0x/i, '').replace(/[^0-9a-f]/gi, '');
608
+ if (!/[1-9a-f]/i.test(handleHex)) {
609
+ return { elements: [], visited: 0, error: 'Window-scoped UI Automation requires a non-zero native window handle.', elapsedMs: 0 };
610
+ }
611
+ const hostProcessId = Math.max(1, Math.floor(process.pid));
608
612
  const script = [
609
- handleHex
610
- ? `$nativeHandle = [System.IntPtr]::new([Convert]::ToInt64(${psQuote(handleHex)}, 16)); $root = [System.Windows.Automation.AutomationElement]::FromHandle($nativeHandle)`
611
- : '$root = [System.Windows.Automation.AutomationElement]::RootElement',
613
+ `$nativeHandle = [System.IntPtr]::new([Convert]::ToInt64(${psQuote(handleHex)}, 16)); $root = [System.Windows.Automation.AutomationElement]::FromHandle($nativeHandle)`,
612
614
  'if ($null -eq $root) { throw "UI Automation root is unavailable." }',
615
+ '$rootProcessId = 0; try { $rootProcessId = [int]$root.Current.ProcessId } catch {}',
616
+ `if ($rootProcessId -eq ${hostProcessId}) { throw "UI Automation of the Newmark host window is skipped to avoid Electron self-accessibility hangs." }`,
613
617
  '$cache = New-Object System.Windows.Automation.CacheRequest',
614
618
  '$cache.TreeScope = [System.Windows.Automation.TreeScope]::Element',
615
619
  '$cache.TreeFilter = [System.Windows.Automation.Automation]::ControlViewCondition',
@@ -663,6 +667,9 @@ async function observeUiAutomation(maxChars, rootHandle, timeoutMs = 30000, lane
663
667
  return { elements: parseJsonArray(result.output), visited: 0, error: 'UI Automation returned invalid JSON.', elapsedMs: result.elapsedMs };
664
668
  }
665
669
  }
670
+ function skippedUiAutomation(error) {
671
+ return { elements: [], visited: 0, ...(error ? { error } : {}), elapsedMs: 0 };
672
+ }
666
673
  async function observeAppWindows(maxChars, timeoutMs = 30000, lane = 'windows') {
667
674
  const limit = Math.min(Math.max(Math.floor(maxChars || 30000), 1000), 200000);
668
675
  const script = [
@@ -764,7 +771,162 @@ async function selectAppWindow(target, handle) {
764
771
  return { apps, error: `No visible application window matched: ${target || handle}.`, elapsedMs: observed.elapsedMs };
765
772
  return { app, apps, elapsedMs: observed.elapsedMs };
766
773
  }
767
- async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage, app, includeRawUi = false, captureMaxWidth, captureMaxHeight) {
774
+ const MAX_WAIT_FOR_TIMEOUT_MS = 30_000;
775
+ const DEFAULT_WAIT_FOR_TIMEOUT_MS = 5_000;
776
+ const WAIT_FOR_POLL_INTERVAL_MS = 250;
777
+ function boundedWaitForTimeout(value) {
778
+ const requested = Number(value);
779
+ if (!Number.isFinite(requested) || requested <= 0)
780
+ return DEFAULT_WAIT_FOR_TIMEOUT_MS;
781
+ return Math.min(MAX_WAIT_FOR_TIMEOUT_MS, Math.max(WAIT_FOR_POLL_INTERVAL_MS, Math.floor(requested)));
782
+ }
783
+ async function waitForUi(options, ownerId) {
784
+ const startedAt = Date.now();
785
+ const timeoutMs = boundedWaitForTimeout(options.timeoutMs);
786
+ const deadline = startedAt + timeoutMs;
787
+ const workspacePath = options.workspacePath;
788
+ const cache = observations.get(observationKey(workspacePath, ownerId));
789
+ const requestedText = String(options.text || '').trim();
790
+ const normalizedQuery = normalizeText(requestedText);
791
+ const targetId = String(options.targetId || '').trim();
792
+ const appTarget = String(options.appTarget || '').trim();
793
+ const requestedHandle = String(options.windowHandle || '').trim();
794
+ const cachedTarget = targetId ? cache?.objects.find(object => object.target_id === targetId) : undefined;
795
+ let app;
796
+ let lastWindowError;
797
+ let attempts = 0;
798
+ let nextWindowPollAt = 0;
799
+ if (!normalizedQuery && !targetId && !appTarget && !requestedHandle) {
800
+ return { ok: false, action: 'wait_for', error: 'Provide text, target_id, app_target, or window_handle to wait for.' };
801
+ }
802
+ if (targetId && !cachedTarget) {
803
+ return { ok: false, action: 'wait_for', error: `target_id not found in latest observation: ${targetId}. Call computer_use observe first.` };
804
+ }
805
+ const directHandle = requestedHandle || (!appTarget ? String(cache?.windowHandle || '') : '');
806
+ if (directHandle) {
807
+ const direct = await observeAppWindowByHandle(directHandle);
808
+ attempts += 1;
809
+ if (!direct.app) {
810
+ return { ok: false, action: 'wait_for', status: 'window_unavailable', error: direct.error || 'The selected window is no longer available.', elapsed_ms: Date.now() - startedAt, attempts };
811
+ }
812
+ app = direct.app;
813
+ }
814
+ if (!app && !appTarget) {
815
+ return { ok: false, action: 'wait_for', error: 'Call computer_use observe/app_observe first, or provide app_target/window_handle.' };
816
+ }
817
+ if (targetId && cache?.windowHandle && app && cache.windowHandle.toLowerCase() !== app.handle.toLowerCase()) {
818
+ return { ok: false, action: 'wait_for', error: `target_id=${targetId} belongs to a different window than ${app.handle}. Observe that window before waiting on the target.` };
819
+ }
820
+ while (Date.now() <= deadline) {
821
+ if (options.signal?.aborted) {
822
+ return { ok: false, action: 'wait_for', status: 'cancelled', error: 'wait_for was cancelled.', elapsed_ms: Date.now() - startedAt, attempts };
823
+ }
824
+ if (!app && appTarget) {
825
+ if (Date.now() >= nextWindowPollAt) {
826
+ const observed = await observeAppWindows(30000, 750, 'windows_advisory');
827
+ attempts += 1;
828
+ lastWindowError = observed.error;
829
+ app = observed.apps.find(candidate => appMatches(candidate, appTarget));
830
+ nextWindowPollAt = Date.now() + 1000;
831
+ }
832
+ if (!app) {
833
+ const remaining = deadline - Date.now();
834
+ if (remaining <= 0)
835
+ break;
836
+ await new Promise(resolve => setTimeout(resolve, Math.min(Math.max(1, nextWindowPollAt - Date.now()), remaining)));
837
+ continue;
838
+ }
839
+ }
840
+ if (targetId && cache?.windowHandle && app && cache.windowHandle.toLowerCase() !== app.handle.toLowerCase()) {
841
+ return { ok: false, action: 'wait_for', error: `target_id=${targetId} belongs to a different window than ${app.handle}. Observe that window before waiting on the target.`, elapsed_ms: Date.now() - startedAt, attempts };
842
+ }
843
+ if (app && !normalizedQuery && !targetId) {
844
+ return {
845
+ ok: true,
846
+ action: 'wait_for',
847
+ status: 'matched',
848
+ found: true,
849
+ condition: { app_target: appTarget || undefined, window_handle: app.handle },
850
+ app,
851
+ elapsed_ms: Date.now() - startedAt,
852
+ attempts,
853
+ };
854
+ }
855
+ if (app) {
856
+ const ui = await observeUiAutomation(Number(options.maxChars || 30000), app.handle, 750, 'uia_advisory');
857
+ attempts += 1;
858
+ if (ui.error) {
859
+ return { ok: false, action: 'wait_for', status: 'uia_unavailable', error: ui.error, app, elapsed_ms: Date.now() - startedAt, attempts };
860
+ }
861
+ const objects = semanticObjects(ui.elements, app.bbox.width, app.bbox.height, workspacePath, ownerId);
862
+ const match = objects.find(object => {
863
+ if (targetId && object.target_id !== targetId)
864
+ return false;
865
+ if (!normalizedQuery)
866
+ return true;
867
+ return normalizeText(object.name).includes(normalizedQuery) || normalizeText(object.automation_id).includes(normalizedQuery);
868
+ });
869
+ if (match) {
870
+ const generation = sceneGeneration([app], ui.elements);
871
+ observations.set(observationKey(workspacePath, ownerId), {
872
+ workspacePath,
873
+ objects,
874
+ windowHandle: app.handle,
875
+ width: Math.max(1, app.bbox.width),
876
+ height: Math.max(1, app.bbox.height),
877
+ at: new Date().toISOString(),
878
+ sceneGeneration: generation,
879
+ });
880
+ return {
881
+ ok: true,
882
+ action: 'wait_for',
883
+ status: 'matched',
884
+ found: true,
885
+ condition: {
886
+ ...(requestedText ? { text: requestedText } : {}),
887
+ ...(targetId ? { target_id: targetId } : {}),
888
+ ...(appTarget ? { app_target: appTarget } : {}),
889
+ },
890
+ app,
891
+ scene_generation: generation,
892
+ element: {
893
+ target_id: match.target_id,
894
+ label: match.label,
895
+ role: match.role,
896
+ control_type: match.control_type,
897
+ automation_id: match.automation_id,
898
+ bbox: match.bbox,
899
+ center: match.center,
900
+ },
901
+ elapsed_ms: Date.now() - startedAt,
902
+ attempts,
903
+ };
904
+ }
905
+ }
906
+ const remaining = deadline - Date.now();
907
+ if (remaining <= 0)
908
+ break;
909
+ await new Promise(resolve => setTimeout(resolve, Math.min(WAIT_FOR_POLL_INTERVAL_MS, remaining)));
910
+ }
911
+ return {
912
+ ok: true,
913
+ action: 'wait_for',
914
+ status: 'timed_out',
915
+ found: false,
916
+ condition: {
917
+ ...(requestedText ? { text: requestedText } : {}),
918
+ ...(targetId ? { target_id: targetId } : {}),
919
+ ...(appTarget ? { app_target: appTarget } : {}),
920
+ ...(requestedHandle ? { window_handle: requestedHandle } : {}),
921
+ },
922
+ ...(app ? { app } : {}),
923
+ ...(lastWindowError ? { warning: lastWindowError } : {}),
924
+ timeout_ms: timeoutMs,
925
+ elapsed_ms: Date.now() - startedAt,
926
+ attempts,
927
+ };
928
+ }
929
+ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage, app, includeRawUi = false, captureMaxWidth, captureMaxHeight, includeUiTree = true) {
768
930
  const totalStarted = Date.now();
769
931
  const outPath = ephemeralScreenshotPath('app');
770
932
  let retainedForVision = false;
@@ -775,7 +937,9 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
775
937
  ], captureMaxWidth, captureMaxHeight);
776
938
  const [result, ui] = await Promise.all([
777
939
  runPowerShell(script, 30000),
778
- observeUiAutomation(30000, app.handle, 750, 'uia_advisory'),
940
+ includeUiTree
941
+ ? observeUiAutomation(30000, app.handle, 750, 'uia_advisory')
942
+ : Promise.resolve(skippedUiAutomation()),
779
943
  ]);
780
944
  if (!result.ok)
781
945
  return captureFailure('app_observe', app);
@@ -798,7 +962,7 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
798
962
  }
799
963
  const cacheKey = observationKey(workspacePath, ownerId);
800
964
  const generation = sceneGeneration([app], elements);
801
- observations.set(cacheKey, { workspacePath, objects, width: Number(parsed.width || b.width || 1), height: Number(parsed.height || b.height || 1), at: new Date().toISOString(), sceneGeneration: generation });
965
+ observations.set(cacheKey, { workspacePath, objects, windowHandle: app.handle, width: Number(parsed.width || b.width || 1), height: Number(parsed.height || b.height || 1), at: new Date().toISOString(), sceneGeneration: generation });
802
966
  const payload = {
803
967
  ok: true,
804
968
  action: 'app_observe',
@@ -807,8 +971,10 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
807
971
  screenshot_retention: allowEphemeralVisionImage ? 'ephemeral-delete-after-vision-input' : 'ephemeral-deleted-before-tool-return',
808
972
  coordinate_system: 'virtual-screen-pixels',
809
973
  perception: {
810
- mode: 'native-application-screenshot-plus-windows-ui-automation',
974
+ mode: includeUiTree ? 'native-application-screenshot-plus-windows-ui-automation' : 'native-application-screenshot-only',
811
975
  application_scope: 'single-visible-window',
976
+ ui_tree_status: includeUiTree ? (ui.error ? 'unavailable' : 'captured') : 'skipped',
977
+ ui_tree_skipped: !includeUiTree,
812
978
  element_count: elements.length,
813
979
  visited_count: ui.visited,
814
980
  scene_generation: generation,
@@ -819,6 +985,7 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
819
985
  telemetry: {
820
986
  capture_ms: result.elapsedMs,
821
987
  uia_ms: ui.elapsedMs,
988
+ ui_tree_skipped: !includeUiTree,
822
989
  total_ms: Date.now() - totalStarted,
823
990
  element_count: elements.length,
824
991
  visited_count: ui.visited,
@@ -1068,7 +1235,7 @@ function resolveTarget(targetId, workspacePath, ownerId) {
1068
1235
  return { x: 0, y: 0, error: `target_id not found in latest observation: ${id}.` };
1069
1236
  return { x: target.center.x, y: target.center.y, target };
1070
1237
  }
1071
- async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, includeRawUi = false, captureMaxWidth, captureMaxHeight, sparseWaitMs, signal) {
1238
+ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, includeRawUi = false, captureMaxWidth, captureMaxHeight, sparseWaitMs, signal, includeUiTree = true) {
1072
1239
  const totalStarted = Date.now();
1073
1240
  const sparseDurationMs = boundedSparseWait(sparseWaitMs);
1074
1241
  const sparseStarted = Date.now();
@@ -1100,8 +1267,12 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
1100
1267
  const foreground = await foregroundWindowHandle();
1101
1268
  const [result, ui, apps] = await Promise.all([
1102
1269
  runPowerShell(script, 30000),
1103
- observeUiAutomation(30000, foreground.handle, 750, 'uia_advisory'),
1104
- observeAppWindows(30000, 750, 'windows_advisory'),
1270
+ includeUiTree && foreground.handle
1271
+ ? observeUiAutomation(30000, foreground.handle, 750, 'uia_advisory')
1272
+ : Promise.resolve(skippedUiAutomation(includeUiTree ? 'Foreground window handle is unavailable; UI Automation tree capture was skipped.' : undefined)),
1273
+ includeUiTree
1274
+ ? observeAppWindows(30000, 750, 'windows_advisory')
1275
+ : Promise.resolve({ apps: [], error: undefined, elapsedMs: 0 }),
1105
1276
  ]);
1106
1277
  if (signal?.aborted)
1107
1278
  throw sparseAbortError(signal);
@@ -1118,7 +1289,7 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
1118
1289
  const screenHeight = Number(parsed.height || 1);
1119
1290
  const objects = semanticObjects(ui.elements, screenWidth, screenHeight, workspacePath, ownerId);
1120
1291
  const generation = sceneGeneration(apps.apps, ui.elements);
1121
- observations.set(observationKey(workspacePath, ownerId), { workspacePath, objects, width: screenWidth, height: screenHeight, at: new Date().toISOString(), sceneGeneration: generation });
1292
+ observations.set(observationKey(workspacePath, ownerId), { workspacePath, objects, windowHandle: foreground.handle || undefined, width: screenWidth, height: screenHeight, at: new Date().toISOString(), sceneGeneration: generation });
1122
1293
  const payload = {
1123
1294
  ok: true,
1124
1295
  action: 'observe',
@@ -1126,10 +1297,16 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
1126
1297
  screenshot_retention: allowEphemeralVisionImage ? 'ephemeral-delete-after-vision-input' : 'ephemeral-deleted-before-tool-return',
1127
1298
  coordinate_system: 'virtual-screen-pixels',
1128
1299
  perception: {
1129
- mode: 'native-screenshot-plus-windows-ui-automation',
1300
+ mode: includeUiTree ? 'native-screenshot-plus-windows-ui-automation' : 'native-screenshot-only',
1301
+ ui_tree_status: includeUiTree ? (ui.error ? 'unavailable' : 'captured') : 'skipped',
1302
+ ui_tree_skipped: !includeUiTree,
1130
1303
  vision_assist: {
1131
- when_model_supports_vision: 'The Agent sends this screenshot as image input together with the UI Automation element tree, so visual recognition and control bounding boxes can be used in the same decision step.',
1132
- fallback_without_vision: 'Use the UI Automation element tree, bbox, center coordinates, and semantic target IDs only; no screenshot file path is retained or exposed.',
1304
+ when_model_supports_vision: includeUiTree
1305
+ ? 'The Agent sends this screenshot as image input together with the UI Automation element tree, so visual recognition and control bounding boxes can be used in the same decision step.'
1306
+ : 'The Agent receives this screenshot as image input without UI Automation traversal or semantic target IDs.',
1307
+ fallback_without_vision: includeUiTree
1308
+ ? 'Use the UI Automation element tree, bbox, center coordinates, and semantic target IDs only; no screenshot file path is retained or exposed.'
1309
+ : 'This result has no semantic target IDs; request include_ui_tree=true for a UI Automation element tree.',
1133
1310
  },
1134
1311
  element_count: ui.elements.length,
1135
1312
  visited_count: ui.visited,
@@ -1137,13 +1314,18 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
1137
1314
  scene_summary: buildSceneSummary(objects, screenWidth, screenHeight),
1138
1315
  objects: compactSemanticObjects(objects),
1139
1316
  ...(sparseDetails ? { sparse_measurement: sparseDetails } : {}),
1140
- object_usage: 'Prefer target_id from scene_summary.high_priority_objects or objects for click/move/scroll when available; it resolves to the latest observed center coordinate and keeps actions tied to a semantic UI object.',
1317
+ object_usage: includeUiTree
1318
+ ? 'Prefer target_id from scene_summary.high_priority_objects or objects for click/move/scroll when available; it resolves to the latest observed center coordinate and keeps actions tied to a semantic UI object.'
1319
+ : 'This screenshot-only observation has no semantic target IDs. Use visual coordinates or request another observe with include_ui_tree=true before using target_id.',
1141
1320
  warning: foreground.error || ui.error || apps.error || undefined,
1142
- note: 'Inspired by capture-parse-decide-act and MCP-style Computer Control agents: screenshot capture plus native Windows UI Automation text/control bounding boxes, semantic target objects, stable target IDs, high-priority object summaries, normalized bboxes, allowed actions, and risk hints. It does not copy third-party code.',
1321
+ note: includeUiTree
1322
+ ? 'Native screenshot plus a bounded, foreground-window-scoped Windows UI Automation tree. No third-party server or code is loaded.'
1323
+ : 'Fast native screenshot path; UI Automation traversal was intentionally skipped. No third-party server or code is loaded.',
1143
1324
  },
1144
1325
  telemetry: {
1145
1326
  capture_ms: result.elapsedMs,
1146
1327
  uia_ms: ui.elapsedMs,
1328
+ ui_tree_skipped: !includeUiTree,
1147
1329
  app_list_ms: apps.elapsedMs,
1148
1330
  foreground_ms: foreground.elapsedMs,
1149
1331
  total_ms: Date.now() - totalStarted,
@@ -1290,7 +1472,9 @@ async function foregroundSceneGeneration() {
1290
1472
  const handle = handleResult.handle;
1291
1473
  const [apps, ui] = await Promise.all([
1292
1474
  observeAppWindows(30000),
1293
- observeUiAutomation(30000, handle),
1475
+ handle
1476
+ ? observeUiAutomation(30000, handle)
1477
+ : Promise.resolve(skippedUiAutomation('Foreground window handle is unavailable; UI Automation tree capture was skipped.')),
1294
1478
  ]);
1295
1479
  return {
1296
1480
  generation: sceneGeneration(apps.apps, ui.elements),
@@ -1428,7 +1612,7 @@ async function runComputerUse(options) {
1428
1612
  result = { ok: true, action: 'takeover_stop', takeover: false };
1429
1613
  }
1430
1614
  else if (action === 'observe') {
1431
- result = await screenshot(options.workspacePath, ownerId, !!options.allowEphemeralVisionImage, !!options.includeRawUi, options.captureMaxWidth, options.captureMaxHeight, options.sparseWaitMs, options.signal);
1615
+ result = await screenshot(options.workspacePath, ownerId, !!options.allowEphemeralVisionImage, !!options.includeRawUi, options.captureMaxWidth, options.captureMaxHeight, options.sparseWaitMs, options.signal, options.includeUiTree !== false);
1432
1616
  }
1433
1617
  else if (action === 'app_list') {
1434
1618
  const first = await observeAppWindows(Number(options.maxChars || 60000));
@@ -1445,7 +1629,7 @@ async function runComputerUse(options) {
1445
1629
  else if (action === 'app_observe') {
1446
1630
  const selected = await selectAppWindow(options.appTarget, options.windowHandle);
1447
1631
  result = selected.app
1448
- ? await cropScreenshot(options.workspacePath, ownerId, !!options.allowEphemeralVisionImage, selected.app, !!options.includeRawUi, options.captureMaxWidth, options.captureMaxHeight)
1632
+ ? await cropScreenshot(options.workspacePath, ownerId, !!options.allowEphemeralVisionImage, selected.app, !!options.includeRawUi, options.captureMaxWidth, options.captureMaxHeight, options.includeUiTree !== false)
1449
1633
  : { ok: false, action, error: selected.error, applications: selected.apps.slice(0, 20) };
1450
1634
  }
1451
1635
  else if (action === 'app_activate') {
@@ -1455,6 +1639,9 @@ async function runComputerUse(options) {
1455
1639
  else if (action === 'wait') {
1456
1640
  result = await wait(Number(options.durationMs || 1000));
1457
1641
  }
1642
+ else if (action === 'wait_for') {
1643
+ result = await waitForUi(options, ownerId);
1644
+ }
1458
1645
  else if (action === 'sequence') {
1459
1646
  result = await executeSequence(options, ownerId);
1460
1647
  }
@@ -16,6 +16,7 @@ export interface ToolExecutionContext {
16
16
  actorId?: string;
17
17
  workspaceId?: string;
18
18
  runtimeKey?: string;
19
+ runId?: string;
19
20
  backend?: string;
20
21
  invocation?: 'agent' | 'cli';
21
22
  signal?: AbortSignal;
@@ -42,6 +43,7 @@ export declare class ToolExecutor {
42
43
  private readonly localOcr;
43
44
  private readonly argumentValidators;
44
45
  private readonly searchMcpPool;
46
+ private readonly fileWriteObservationGate;
45
47
  private hostProfile;
46
48
  constructor(root: string, config: ConfigManager, ssh?: SshManager | undefined, workspace?: WorkspaceManager | undefined);
47
49
  webSearch(query: string, signal?: AbortSignal): Promise<string>;
@@ -72,16 +74,13 @@ export declare class ToolExecutor {
72
74
  private decorateBrowserUseReceipt;
73
75
  private sshWorkspace;
74
76
  private checkWorkspaceAccess;
77
+ private fileWriteObservationScope;
75
78
  private checkBashWorkspaceAccess;
76
79
  private extractCommandPathRefs;
77
80
  private looksLikePath;
78
81
  private isReadonlyShellCommand;
79
82
  private resolveBashTimeout;
80
83
  private bash;
81
- private fread;
82
- private fwrite;
83
- private fedit;
84
- private fdelete;
85
84
  private glob;
86
85
  private grep;
87
86
  private createProxyAgent;