monomind 1.10.40 → 1.10.41

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 (24) hide show
  1. package/package.json +1 -1
  2. package/packages/@monomind/cli/dist/src/browser/actions.js +114 -22
  3. package/packages/@monomind/cli/dist/src/browser/browser.d.ts +1 -0
  4. package/packages/@monomind/cli/dist/src/browser/browser.js +51 -24
  5. package/packages/@monomind/cli/dist/src/browser/cdp.js +20 -4
  6. package/packages/@monomind/cli/dist/src/browser/console-log.d.ts +1 -0
  7. package/packages/@monomind/cli/dist/src/browser/console-log.js +19 -3
  8. package/packages/@monomind/cli/dist/src/browser/dialog.js +1 -1
  9. package/packages/@monomind/cli/dist/src/browser/find.js +28 -8
  10. package/packages/@monomind/cli/dist/src/browser/har.d.ts +1 -0
  11. package/packages/@monomind/cli/dist/src/browser/har.js +7 -5
  12. package/packages/@monomind/cli/dist/src/browser/network.d.ts +7 -4
  13. package/packages/@monomind/cli/dist/src/browser/network.js +59 -22
  14. package/packages/@monomind/cli/dist/src/browser/profiler.js +13 -2
  15. package/packages/@monomind/cli/dist/src/browser/screenshot.js +3 -2
  16. package/packages/@monomind/cli/dist/src/browser/session.js +49 -12
  17. package/packages/@monomind/cli/dist/src/browser/snapshot.js +26 -14
  18. package/packages/@monomind/cli/dist/src/browser/storage.d.ts +1 -0
  19. package/packages/@monomind/cli/dist/src/browser/storage.js +3 -0
  20. package/packages/@monomind/cli/dist/src/browser/tabs.d.ts +2 -2
  21. package/packages/@monomind/cli/dist/src/browser/tabs.js +8 -5
  22. package/packages/@monomind/cli/dist/src/browser/wait.js +23 -13
  23. package/packages/@monomind/cli/dist/src/commands/browse.js +265 -32
  24. package/packages/@monomind/cli/package.json +1 -1
@@ -15,6 +15,13 @@ async function getBrowser() {
15
15
  async function ensureConnected(port, targetId) {
16
16
  const browser = await getBrowser();
17
17
  if (!_client || !_client.isConnected()) {
18
+ if (_client && _sessionId) {
19
+ browser.teardownRouteInterception(_sessionId);
20
+ browser.stopRequestCapture(_sessionId);
21
+ browser.teardownDialogHandling(_sessionId);
22
+ browser.teardownConsoleCapture(_sessionId);
23
+ _client.close();
24
+ }
18
25
  _port = await browser.launchBrowser({ port, headless: false });
19
26
  const conn = await browser.connectToTarget(_port, targetId);
20
27
  _client = conn.client;
@@ -45,12 +52,46 @@ const openCommand = {
45
52
  throw new Error('URL required. Usage: monomind browse open <url>');
46
53
  const port = ctx.flags.port ?? 9222;
47
54
  const browser = await getBrowser();
55
+ if (_client) {
56
+ const prevSid = _sessionId;
57
+ const prevClient = _client;
58
+ if (browser.getHarStatus(prevSid).recording) {
59
+ try {
60
+ await browser.stopHarRecording(prevClient, prevSid);
61
+ }
62
+ catch { /* ignore */ }
63
+ }
64
+ if (browser.getTraceStatus(prevSid)) {
65
+ try {
66
+ await browser.stopTrace(prevClient, prevSid);
67
+ }
68
+ catch { /* ignore */ }
69
+ }
70
+ if (browser.isProfilingActive(prevSid)) {
71
+ try {
72
+ await browser.stopCpuProfile(prevClient, prevSid);
73
+ }
74
+ catch { /* ignore */ }
75
+ }
76
+ browser.teardownRouteInterception(prevSid);
77
+ browser.stopRequestCapture(prevSid);
78
+ browser.teardownDialogHandling(prevSid);
79
+ browser.teardownConsoleCapture(prevSid);
80
+ prevClient.close();
81
+ _client = null;
82
+ _sessionId = '';
83
+ _targetId = '';
84
+ _refs = new Map();
85
+ }
48
86
  _port = await browser.launchBrowser({ port, headless: ctx.flags.headless });
49
87
  const conn = await browser.connectToTarget(_port);
50
88
  _client = conn.client;
51
89
  _sessionId = conn.sessionId;
52
90
  _targetId = conn.target.id;
53
91
  _refs = new Map();
92
+ if (ctx.flags.state && ctx.flags.session) {
93
+ output.printWarning('Both --state and --session provided; --state takes precedence');
94
+ }
54
95
  if (ctx.flags.state) {
55
96
  await browser.loadStateFile(_client, _sessionId, ctx.flags.state);
56
97
  }
@@ -198,7 +239,7 @@ const waitCommand = {
198
239
  const interval = 200;
199
240
  const deadline = Date.now() + timeout;
200
241
  while (Date.now() < deadline) {
201
- const text = await browser.evaluateJs(client, sessionId, 'document.body.innerText');
242
+ const text = await browser.evaluateJs(client, sessionId, 'document.body?.innerText ?? ""');
202
243
  if (!text.includes(target)) {
203
244
  output.printSuccess('Text disappeared');
204
245
  return { success: true };
@@ -283,7 +324,7 @@ const getCommand = {
283
324
  value = result.result?.value ?? '';
284
325
  }
285
326
  else {
286
- value = (await browser.evaluateJs(client, sessionId, 'document.body.innerText'));
327
+ value = (await browser.evaluateJs(client, sessionId, 'document.body?.innerText ?? ""'));
287
328
  }
288
329
  break;
289
330
  }
@@ -410,21 +451,45 @@ const navigateCommand = {
410
451
  description: 'Navigate browser history. Usage: monomind browse navigate back|forward|reload',
411
452
  action: async (ctx) => {
412
453
  const { client, sessionId } = await ensureConnected(_port);
454
+ const browser = await getBrowser();
413
455
  const direction = ctx.args[0];
414
456
  if (!direction)
415
457
  throw new Error('Usage: monomind browse navigate back|forward|reload');
416
- switch (direction) {
417
- case 'back':
418
- await client.send('Runtime.evaluate', { expression: 'history.back()' }, sessionId);
419
- break;
420
- case 'forward':
421
- await client.send('Runtime.evaluate', { expression: 'history.forward()' }, sessionId);
422
- break;
423
- case 'reload':
424
- await client.send('Page.reload', {}, sessionId);
425
- break;
426
- default:
427
- throw new Error(`Unknown direction: ${direction}. Use: back|forward|reload`);
458
+ if (direction === 'back' || direction === 'forward') {
459
+ // Pre-register frame-start listener BEFORE JS navigation to avoid the race
460
+ // where history.back/forward() returns before the browser issues any requests
461
+ let offFrameStarted = () => { };
462
+ const frameStartedPromise = new Promise((resolve) => {
463
+ offFrameStarted = client.on('Page.frameStartedLoading', (_params, sid) => {
464
+ if (sid === sessionId) {
465
+ const off = offFrameStarted;
466
+ offFrameStarted = () => { };
467
+ off();
468
+ resolve();
469
+ }
470
+ });
471
+ });
472
+ try {
473
+ await client.send('Runtime.evaluate', {
474
+ expression: direction === 'back' ? 'history.back()' : 'history.forward()',
475
+ }, sessionId);
476
+ let fallbackHandle;
477
+ const fallbackPromise = new Promise((r) => { fallbackHandle = setTimeout(r, 2000); });
478
+ await Promise.race([frameStartedPromise, fallbackPromise]);
479
+ if (fallbackHandle !== undefined)
480
+ clearTimeout(fallbackHandle);
481
+ }
482
+ finally {
483
+ offFrameStarted();
484
+ }
485
+ await browser.waitForLoad(client, sessionId, 'networkidle');
486
+ }
487
+ else if (direction === 'reload') {
488
+ await client.send('Page.reload', {}, sessionId);
489
+ await browser.waitForLoad(client, sessionId, 'load');
490
+ }
491
+ else {
492
+ throw new Error(`Unknown direction: ${direction}. Use: back|forward|reload`);
428
493
  }
429
494
  output.printSuccess(`Navigated: ${direction}`);
430
495
  return { success: true };
@@ -474,7 +539,7 @@ const setCommand = {
474
539
  const offlineArg = ctx.args[1];
475
540
  if (offlineArg === undefined)
476
541
  throw new Error('Usage: set offline <true|false>');
477
- const enabled = offlineArg !== 'false';
542
+ const enabled = offlineArg === 'true';
478
543
  await browser.setOfflineMode(client, sessionId, enabled);
479
544
  output.printSuccess(`Offline mode: ${enabled}`);
480
545
  break;
@@ -701,7 +766,33 @@ const closeCommand = {
701
766
  description: 'Close the active browser session',
702
767
  action: async (_ctx) => {
703
768
  if (_client) {
704
- _client.close();
769
+ const browser = await getBrowser();
770
+ const sid = _sessionId;
771
+ const client = _client;
772
+ // Tear down per-session Maps and listeners before closing
773
+ if (browser.getHarStatus(sid).recording) {
774
+ try {
775
+ await browser.stopHarRecording(client, sid);
776
+ }
777
+ catch { /* ignore */ }
778
+ }
779
+ if (browser.getTraceStatus(sid)) {
780
+ try {
781
+ await browser.stopTrace(client, sid);
782
+ }
783
+ catch { /* ignore */ }
784
+ }
785
+ if (browser.isProfilingActive(sid)) {
786
+ try {
787
+ await browser.stopCpuProfile(client, sid);
788
+ }
789
+ catch { /* ignore */ }
790
+ }
791
+ browser.teardownRouteInterception(sid);
792
+ browser.stopRequestCapture(sid);
793
+ browser.teardownDialogHandling(sid);
794
+ browser.teardownConsoleCapture(sid);
795
+ client.close();
705
796
  _client = null;
706
797
  _sessionId = '';
707
798
  _targetId = '';
@@ -996,14 +1087,18 @@ const clipboardCommand = {
996
1087
  output.printSuccess('Clipboard written');
997
1088
  break;
998
1089
  }
999
- case 'copy':
1000
- await browser.pressKeyCombo(client, sessionId, 'c', 2); // Ctrl modifier
1090
+ case 'copy': {
1091
+ const mod = process.platform === 'darwin' ? 4 : 2; // Meta/Cmd on macOS, Ctrl elsewhere
1092
+ await browser.pressKeyCombo(client, sessionId, 'c', mod);
1001
1093
  output.printSuccess('Copy sent');
1002
1094
  break;
1003
- case 'paste':
1004
- await browser.pressKeyCombo(client, sessionId, 'v', 2); // Ctrl modifier
1095
+ }
1096
+ case 'paste': {
1097
+ const mod = process.platform === 'darwin' ? 4 : 2;
1098
+ await browser.pressKeyCombo(client, sessionId, 'v', mod);
1005
1099
  output.printSuccess('Paste sent');
1006
1100
  break;
1101
+ }
1007
1102
  default:
1008
1103
  throw new Error('Usage: monomind browse clipboard read|write|copy|paste');
1009
1104
  }
@@ -1090,13 +1185,74 @@ const tabCommand = {
1090
1185
  const tabId = ctx.args[1];
1091
1186
  if (!tabId)
1092
1187
  throw new Error('Usage: monomind browse tab close <tabId>');
1093
- await browser.closeTab(client, sessionId, tabId);
1188
+ if (tabId === _targetId) {
1189
+ const sid = _sessionId;
1190
+ // Stop profiling before closing the tab so CDP commands still reach the live session
1191
+ if (browser.getHarStatus(sid).recording) {
1192
+ try {
1193
+ await browser.stopHarRecording(client, sid);
1194
+ }
1195
+ catch { /* ignore */ }
1196
+ }
1197
+ if (browser.getTraceStatus(sid)) {
1198
+ try {
1199
+ await browser.stopTrace(client, sid);
1200
+ }
1201
+ catch { /* ignore */ }
1202
+ }
1203
+ if (browser.isProfilingActive(sid)) {
1204
+ try {
1205
+ await browser.stopCpuProfile(client, sid);
1206
+ }
1207
+ catch { /* ignore */ }
1208
+ }
1209
+ browser.teardownRouteInterception(sid);
1210
+ browser.stopRequestCapture(sid);
1211
+ browser.teardownDialogHandling(sid);
1212
+ browser.teardownConsoleCapture(sid);
1213
+ await browser.closeTab(client, sessionId, tabId);
1214
+ client.close();
1215
+ _client = null;
1216
+ _sessionId = '';
1217
+ _targetId = '';
1218
+ _refs = new Map();
1219
+ }
1220
+ else {
1221
+ await browser.closeTab(client, sessionId, tabId);
1222
+ }
1094
1223
  output.printSuccess(`Closed tab: ${tabId}`);
1095
1224
  break;
1096
1225
  }
1097
1226
  default: {
1098
- // Try to treat arg as tab ID to switch to
1099
- _sessionId = await browser.activateTab(client, sessionId, action);
1227
+ // Attach to new tab FIRST only tear down old session if that succeeds
1228
+ const newSid = await browser.activateTab(client, sessionId, action);
1229
+ const oldSid = _sessionId;
1230
+ if (browser.getHarStatus(oldSid).recording) {
1231
+ try {
1232
+ await browser.stopHarRecording(client, oldSid);
1233
+ }
1234
+ catch { /* ignore */ }
1235
+ }
1236
+ if (browser.getTraceStatus(oldSid)) {
1237
+ try {
1238
+ await browser.stopTrace(client, oldSid);
1239
+ }
1240
+ catch { /* ignore */ }
1241
+ }
1242
+ if (browser.isProfilingActive(oldSid)) {
1243
+ try {
1244
+ await browser.stopCpuProfile(client, oldSid);
1245
+ }
1246
+ catch { /* ignore */ }
1247
+ }
1248
+ await browser.disableInterception(client, oldSid).catch(() => { });
1249
+ browser.stopRequestCapture(oldSid);
1250
+ browser.teardownDialogHandling(oldSid);
1251
+ browser.teardownConsoleCapture(oldSid);
1252
+ _sessionId = newSid;
1253
+ _targetId = action;
1254
+ _refs = new Map();
1255
+ await browser.enableSessionDomains(client, _sessionId);
1100
1256
  output.printSuccess(`Switched to tab: ${action}`);
1101
1257
  }
1102
1258
  }
@@ -1114,11 +1270,12 @@ const consoleLogCommand = {
1114
1270
  action: async (ctx) => {
1115
1271
  const browser = await getBrowser();
1116
1272
  if (ctx.flags.clear) {
1117
- browser.clearConsoleMessages();
1273
+ browser.clearConsoleMessages(_sessionId);
1118
1274
  output.printSuccess('Console cleared');
1119
1275
  return { success: true };
1120
1276
  }
1121
- const msgs = browser.getConsoleMessages(_sessionId);
1277
+ const allMsgs = browser.getConsoleMessages(_sessionId);
1278
+ const msgs = ctx.flags['errors-only'] ? allMsgs.filter((m) => m.type === 'error') : allMsgs;
1122
1279
  if (ctx.flags.json) {
1123
1280
  print(JSON.stringify(msgs));
1124
1281
  }
@@ -1141,7 +1298,7 @@ const errorsCommand = {
1141
1298
  action: async (ctx) => {
1142
1299
  const browser = await getBrowser();
1143
1300
  if (ctx.flags.clear) {
1144
- browser.clearPageErrors();
1301
+ browser.clearPageErrors(_sessionId);
1145
1302
  output.printSuccess('Errors cleared');
1146
1303
  return { success: true };
1147
1304
  }
@@ -1195,6 +1352,8 @@ const storageCommand = {
1195
1352
  if (key && ctx.flags.remove) {
1196
1353
  if (isLocal)
1197
1354
  await browser.removeLocalStorageKey(client, sessionId, key);
1355
+ else
1356
+ await browser.removeSessionStorageKey(client, sessionId, key);
1198
1357
  output.printSuccess(`Removed ${key}`);
1199
1358
  return { success: true };
1200
1359
  }
@@ -1507,28 +1666,71 @@ const batchCommand = {
1507
1666
  }
1508
1667
  try {
1509
1668
  const parsedFlags = { _: [] };
1510
- // Parse --flags from subArgs
1669
+ const consumedIndices = new Set();
1670
+ // Parse --flags from subArgs, tracking which indices are flag names/values
1511
1671
  for (let i = 0; i < subArgs.length; i++) {
1512
1672
  if (subArgs[i].startsWith('--')) {
1673
+ consumedIndices.add(i);
1513
1674
  const key = subArgs[i].slice(2);
1514
1675
  const next = subArgs[i + 1];
1515
- if (next && !next.startsWith('--')) {
1516
- parsedFlags[key] = next;
1676
+ const optDef = subCmd.options?.find((o) => o.name === key);
1677
+ const isBooleanFlag = optDef?.type === 'boolean';
1678
+ if (next && (!next.startsWith('-') || /^-\d/.test(next)) && !isBooleanFlag) {
1679
+ // Non-boolean flags consume the next token as their value (allow negative numbers like -1)
1680
+ consumedIndices.add(i + 1);
1681
+ if (optDef?.type === 'number') {
1682
+ parsedFlags[key] = Number(next);
1683
+ }
1684
+ else {
1685
+ parsedFlags[key] = next;
1686
+ }
1687
+ i++;
1688
+ }
1689
+ else if (isBooleanFlag && (next === 'true' || next === 'false')) {
1690
+ // Explicit boolean value token
1691
+ consumedIndices.add(i + 1);
1692
+ parsedFlags[key] = next !== 'false';
1517
1693
  i++;
1518
1694
  }
1519
1695
  else {
1520
1696
  parsedFlags[key] = true;
1521
1697
  }
1522
1698
  }
1699
+ else if (subArgs[i].startsWith('-') && subArgs[i].length === 2 && /[a-zA-Z]/.test(subArgs[i][1])) {
1700
+ consumedIndices.add(i);
1701
+ const shortKey = subArgs[i][1];
1702
+ const optDef = subCmd.options?.find((o) => o.short === shortKey);
1703
+ if (optDef) {
1704
+ const key = optDef.name;
1705
+ const next = subArgs[i + 1];
1706
+ const isBooleanFlag = optDef.type === 'boolean';
1707
+ if (next && (!next.startsWith('-') || /^-\d/.test(next)) && !isBooleanFlag) {
1708
+ consumedIndices.add(i + 1);
1709
+ parsedFlags[key] = optDef.type === 'number' ? Number(next) : next;
1710
+ i++;
1711
+ }
1712
+ else if (isBooleanFlag && (next === 'true' || next === 'false')) {
1713
+ consumedIndices.add(i + 1);
1714
+ parsedFlags[key] = next !== 'false';
1715
+ i++;
1716
+ }
1717
+ else {
1718
+ parsedFlags[key] = true;
1719
+ }
1720
+ }
1721
+ }
1523
1722
  }
1524
1723
  const fakeCtx = {
1525
- args: subArgs.filter((a) => !a.startsWith('--')),
1724
+ args: subArgs.filter((_, i) => !consumedIndices.has(i)),
1526
1725
  flags: parsedFlags,
1527
1726
  cwd: ctx.cwd,
1528
1727
  interactive: false,
1529
1728
  };
1530
- await subCmd.action(fakeCtx);
1531
- results.push({ command: cmdStr, success: true });
1729
+ const cmdResult = await subCmd.action(fakeCtx);
1730
+ const succeeded = cmdResult?.success !== false;
1731
+ results.push({ command: cmdStr, success: succeeded, error: succeeded ? undefined : 'Command returned failure' });
1732
+ if (!succeeded && ctx.flags.bail)
1733
+ break;
1532
1734
  }
1533
1735
  catch (e) {
1534
1736
  const err = e instanceof Error ? e.message : String(e);
@@ -1581,6 +1783,37 @@ const connectCommand = {
1581
1783
  action: async (ctx) => {
1582
1784
  const port = ctx.flags.port ?? 9222;
1583
1785
  const browser = await getBrowser();
1786
+ if (_client) {
1787
+ const prevSid = _sessionId;
1788
+ const prevClient = _client;
1789
+ if (browser.getHarStatus(prevSid).recording) {
1790
+ try {
1791
+ await browser.stopHarRecording(prevClient, prevSid);
1792
+ }
1793
+ catch { /* ignore */ }
1794
+ }
1795
+ if (browser.getTraceStatus(prevSid)) {
1796
+ try {
1797
+ await browser.stopTrace(prevClient, prevSid);
1798
+ }
1799
+ catch { /* ignore */ }
1800
+ }
1801
+ if (browser.isProfilingActive(prevSid)) {
1802
+ try {
1803
+ await browser.stopCpuProfile(prevClient, prevSid);
1804
+ }
1805
+ catch { /* ignore */ }
1806
+ }
1807
+ browser.teardownRouteInterception(prevSid);
1808
+ browser.stopRequestCapture(prevSid);
1809
+ browser.teardownDialogHandling(prevSid);
1810
+ browser.teardownConsoleCapture(prevSid);
1811
+ prevClient.close();
1812
+ _client = null;
1813
+ _sessionId = '';
1814
+ _targetId = '';
1815
+ _refs = new Map();
1816
+ }
1584
1817
  const conn = await browser.connectToTarget(port, ctx.flags.target);
1585
1818
  _client = conn.client;
1586
1819
  _sessionId = conn.sessionId;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "1.10.40",
3
+ "version": "1.10.41",
4
4
  "type": "module",
5
5
  "description": "Monomind CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",