micro-models-agent 0.41.2 → 0.42.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 (171) hide show
  1. package/bin/mma.mjs +41 -41
  2. package/dist/cli/commands.js +19 -9
  3. package/dist/cli/completer.js +37 -36
  4. package/dist/cli/index.js +2 -2
  5. package/dist/cli/main.js +23 -48
  6. package/dist/cli/repl-commands.js +12 -40
  7. package/dist/cli/repl.js +87 -217
  8. package/dist/cli/security-commands.js +7 -5
  9. package/dist/cli/setup.js +26 -8
  10. package/dist/config/config.js +5 -52
  11. package/dist/config/defaults.js +5 -29
  12. package/dist/config/experts.js +1 -1
  13. package/dist/config/index.js +3 -3
  14. package/dist/config/security.js +10 -3
  15. package/dist/core/agent-moe.js +10 -2
  16. package/dist/core/agent.js +82 -273
  17. package/dist/core/bootstrap.js +13 -80
  18. package/dist/core/index.js +2 -2
  19. package/dist/core/prompt-builder.js +2 -23
  20. package/dist/core/session-logger.js +4 -46
  21. package/dist/i18n/en.json +2 -75
  22. package/dist/i18n/ru.json +1 -74
  23. package/dist/index.js +1 -1
  24. package/dist/llm/image-utils.js +5 -4
  25. package/dist/llm/index.js +4 -4
  26. package/dist/llm/model-loader.js +6 -6
  27. package/dist/llm/openai-compat.js +34 -40
  28. package/dist/llm/orchestrator.js +29 -33
  29. package/dist/llm/response.js +9 -9
  30. package/dist/logger/app-logger.js +1 -1
  31. package/dist/logger/index.js +1 -1
  32. package/dist/main.js +299 -55
  33. package/dist/migration/backup.js +13 -13
  34. package/dist/migration/detect.js +11 -11
  35. package/dist/migration/index.js +2 -2
  36. package/dist/modules/browser/actions.js +4 -34
  37. package/dist/modules/browser/bridge-server.mjs +202 -202
  38. package/dist/modules/browser/cookie-store.js +6 -6
  39. package/dist/modules/browser/index.js +5 -7
  40. package/dist/modules/browser/module.js +7 -8
  41. package/dist/modules/browser/session.js +84 -87
  42. package/dist/modules/browser/snapshot.js +58 -92
  43. package/dist/modules/browser/types.js +1 -4
  44. package/dist/modules/certification/cli.js +4 -2
  45. package/dist/modules/certification/fact-checker.js +3 -1
  46. package/dist/modules/certification/loader.js +9 -3
  47. package/dist/modules/certification/runner.js +4 -1
  48. package/dist/modules/context/index.js +1 -1
  49. package/dist/modules/context/manager.js +86 -160
  50. package/dist/modules/execution/auditor.js +25 -177
  51. package/dist/modules/execution/module.js +544 -201
  52. package/dist/modules/execution/moe-executor.js +0 -25
  53. package/dist/modules/execution/plan-store.js +3 -1
  54. package/dist/modules/execution/plan-validator.js +10 -10
  55. package/dist/modules/execution/planner.js +1 -6
  56. package/dist/modules/execution/stuck-detector.js +10 -173
  57. package/dist/modules/execution/verifier.js +42 -86
  58. package/dist/modules/hallucination/confidence.js +1 -8
  59. package/dist/modules/hallucination/detector.js +5 -2
  60. package/dist/modules/hallucination/factual.js +64 -3
  61. package/dist/modules/hallucination/index.js +1 -1
  62. package/dist/modules/hallucination/js-identifiers.js +0 -190
  63. package/dist/modules/hallucination/llm-judge.js +3 -1
  64. package/dist/modules/indexer/cache.js +7 -9
  65. package/dist/modules/indexer/index.js +3 -3
  66. package/dist/modules/indexer/module.js +42 -95
  67. package/dist/modules/indexer/walker.js +17 -17
  68. package/dist/modules/lsp/client.js +31 -74
  69. package/dist/modules/lsp/config.js +33 -87
  70. package/dist/modules/lsp/index.js +3 -3
  71. package/dist/modules/lsp/module.js +21 -185
  72. package/dist/modules/mcp/module.js +6 -2
  73. package/dist/modules/memory/index.js +1 -1
  74. package/dist/modules/memory/module.js +23 -71
  75. package/dist/modules/memory/search.js +9 -11
  76. package/dist/modules/memory/store.js +13 -13
  77. package/dist/modules/pipelines/engine.js +10 -10
  78. package/dist/modules/pipelines/index.js +3 -3
  79. package/dist/modules/pipelines/parser.js +14 -17
  80. package/dist/modules/pipelines/template.js +1 -1
  81. package/dist/modules/plugins/builtin/lint-on-write.js +16 -21
  82. package/dist/modules/plugins/builtin/notify.js +2 -3
  83. package/dist/modules/plugins/index.js +1 -1
  84. package/dist/modules/plugins/loader.js +17 -59
  85. package/dist/modules/plugins/manager.js +17 -73
  86. package/dist/modules/processes/index.js +1 -1
  87. package/dist/modules/processes/registry.js +46 -135
  88. package/dist/modules/registry.js +2 -4
  89. package/dist/modules/security/audit-notifier.js +39 -39
  90. package/dist/modules/security/command-validator.js +8 -2
  91. package/dist/modules/security/data-sanitizer.js +9 -1
  92. package/dist/modules/security/encryption.js +56 -58
  93. package/dist/modules/security/network-validator.js +9 -1
  94. package/dist/modules/security/path-validator.js +3 -1
  95. package/dist/modules/security/security-policies.js +19 -3
  96. package/dist/modules/security/session-encryption.js +1 -1
  97. package/dist/modules/security/session-isolation.js +8 -8
  98. package/dist/modules/session/index.js +3 -3
  99. package/dist/modules/session/module.js +5 -5
  100. package/dist/modules/session/store.js +9 -3
  101. package/dist/modules/skills/module.js +2 -1
  102. package/dist/modules/updater/checker.js +6 -70
  103. package/dist/modules/updater/index.js +1 -2
  104. package/dist/modules/user-profile/compressor.js +2 -2
  105. package/dist/modules/user-profile/index.js +1 -1
  106. package/dist/modules/user-profile/profile.js +9 -9
  107. package/dist/tools/attach-image.js +1 -1
  108. package/dist/tools/bash.js +19 -178
  109. package/dist/tools/browser.js +29 -46
  110. package/dist/tools/executor.js +5 -4
  111. package/dist/tools/file-info.js +12 -13
  112. package/dist/tools/filter-tools.js +2 -9
  113. package/dist/tools/glob-tool.js +11 -11
  114. package/dist/tools/grep-tool.js +3 -1
  115. package/dist/tools/index.js +2 -13
  116. package/dist/tools/list-dir.js +17 -18
  117. package/dist/tools/load-skill.js +3 -1
  118. package/dist/tools/path-utils.js +4 -4
  119. package/dist/tools/pipeline-run.js +25 -25
  120. package/dist/tools/process-kill.js +11 -11
  121. package/dist/tools/process-list.js +22 -20
  122. package/dist/tools/process-log.js +18 -22
  123. package/dist/tools/question.js +3 -1
  124. package/dist/tools/read-file.js +2 -10
  125. package/dist/tools/recall.js +37 -44
  126. package/dist/tools/registry.js +4 -15
  127. package/dist/tools/remember.js +29 -29
  128. package/dist/tools/scope-check.js +9 -9
  129. package/dist/tools/subagent.js +9 -54
  130. package/dist/tools/user-input.js +1 -1
  131. package/dist/tools/web-browse.js +3 -3
  132. package/dist/tools/web-fetch.js +3 -3
  133. package/dist/tools/web-search.js +3 -3
  134. package/dist/tools/write-file.js +3 -1
  135. package/dist/ui/box.js +5 -1
  136. package/dist/ui/index.js +6 -6
  137. package/dist/ui/md-formatter.js +33 -33
  138. package/dist/ui/output.js +5 -5
  139. package/dist/ui/renderer.js +10 -15
  140. package/dist/ui/table.js +1 -1
  141. package/package.json +48 -48
  142. package/dist/cli/plugin-commands.js +0 -36
  143. package/dist/cli/run-result.js +0 -22
  144. package/dist/core/version.js +0 -24
  145. package/dist/modules/artifacts/store.js +0 -61
  146. package/dist/modules/browser/bridge-client.js +0 -199
  147. package/dist/modules/browser/bridge-path.js +0 -10
  148. package/dist/modules/browser/driver.js +0 -136
  149. package/dist/modules/context/chunk-query.js +0 -100
  150. package/dist/modules/context/fact-extractor.js +0 -162
  151. package/dist/modules/context/history.js +0 -15
  152. package/dist/modules/execution/audit-runners.js +0 -152
  153. package/dist/modules/execution/execution-plugin.js +0 -272
  154. package/dist/modules/execution/plan-tool.js +0 -508
  155. package/dist/modules/execution/windows-commands.js +0 -41
  156. package/dist/modules/indexer/project-profile.js +0 -183
  157. package/dist/modules/lsp/check-tool.js +0 -58
  158. package/dist/modules/lsp/command.js +0 -60
  159. package/dist/modules/lsp/probe.js +0 -76
  160. package/dist/modules/lsp/project-root.js +0 -32
  161. package/dist/modules/lsp/startup-check.js +0 -141
  162. package/dist/modules/processes/detect.js +0 -34
  163. package/dist/modules/skills/matcher.js +0 -27
  164. package/dist/modules/updater/module.js +0 -116
  165. package/dist/tools/chunk-query.js +0 -99
  166. package/dist/tools/download-file.js +0 -116
  167. package/dist/tools/enable-tools.js +0 -58
  168. package/dist/tools/hidden-tools-block.js +0 -37
  169. package/dist/ui/line-editor.js +0 -703
  170. package/dist/ui/line-math.js +0 -69
  171. package/dist/ui/plan-view.js +0 -103
@@ -1,35 +1,8 @@
1
- import { createBrowserDriver } from "./driver";
1
+ import { chromium, } from "playwright";
2
2
  import { extractInteractiveElements, formatSnapshot } from "./snapshot";
3
- import { buildClickScript, buildTypeScript, buildScrollScript, buildIndexInjectionScript, buildTextExtractionScript, } from "./actions";
3
+ import { buildClickScript, buildTypeScript, buildScrollScript, buildIndexInjectionScript, } from "./actions";
4
4
  import { CookieStore } from "./cookie-store";
5
5
  import { t } from "../../i18n/index";
6
- const CONSOLE_MAX_LINE = 400;
7
- export class ConsoleBuffer {
8
- entries = [];
9
- maxEntries;
10
- constructor(maxEntries = 20) {
11
- this.maxEntries = maxEntries;
12
- }
13
- add(type, text) {
14
- const clean = String(text).replace(/\s+/g, " ").trim();
15
- if (!clean)
16
- return;
17
- const line = clean.length > CONSOLE_MAX_LINE ? clean.slice(0, CONSOLE_MAX_LINE) + "…" : clean;
18
- this.entries.push({ type, text: line });
19
- if (this.entries.length > this.maxEntries) {
20
- this.entries.splice(0, this.entries.length - this.maxEntries);
21
- }
22
- }
23
- clear() {
24
- this.entries = [];
25
- }
26
- getAll() {
27
- return [...this.entries];
28
- }
29
- get size() {
30
- return this.entries.length;
31
- }
32
- }
33
6
  export class BrowserActionTracker {
34
7
  history = [];
35
8
  threshold;
@@ -57,7 +30,9 @@ export class BrowserActionTracker {
57
30
  }
58
31
  }
59
32
  export class BrowserSession {
60
- driver = null;
33
+ browser = null;
34
+ context = null;
35
+ page = null;
61
36
  config;
62
37
  cookieStore;
63
38
  state = {
@@ -115,7 +90,7 @@ export class BrowserSession {
115
90
  };
116
91
  }
117
92
  if (action !== "open") {
118
- const warning = this.actionTracker.record(action, args, this.driver?.url() || "");
93
+ const warning = this.actionTracker.record(action, args, this.page?.url() || "");
119
94
  if (warning && result.success) {
120
95
  result.output = result.output + "\n\n⚠️ " + warning;
121
96
  }
@@ -129,41 +104,45 @@ export class BrowserSession {
129
104
  };
130
105
  }
131
106
  }
132
- async ensureDriver() {
133
- if (!this.driver) {
107
+ async ensurePage() {
108
+ if (!this.page) {
134
109
  return { success: false, output: t("browser.no_page") };
135
110
  }
136
111
  return null;
137
112
  }
138
113
  async launch() {
139
- if (this.driver)
114
+ if (this.browser)
140
115
  return;
141
- this.driver = await createBrowserDriver(this.config);
116
+ this.browser = await chromium.launch({ headless: this.config.headless });
117
+ this.context = await this.browser.newContext({
118
+ viewport: {
119
+ width: this.config.viewportWidth,
120
+ height: this.config.viewportHeight,
121
+ },
122
+ });
142
123
  const savedCookies = await this.cookieStore.load();
143
124
  if (savedCookies.length > 0) {
144
- await this.driver.addCookies(savedCookies);
125
+ await this.context.addCookies(savedCookies.map((c) => ({
126
+ ...c,
127
+ sameSite: c.sameSite,
128
+ })));
145
129
  }
130
+ this.page = await this.context.newPage();
131
+ this.page.setDefaultTimeout(this.config.navigationTimeout);
146
132
  }
147
133
  async saveCookies() {
148
- if (!this.driver)
134
+ if (!this.context)
149
135
  return;
150
- const cookies = await this.driver.cookies();
136
+ const cookies = await this.context.cookies();
151
137
  await this.cookieStore.save(cookies);
152
138
  }
153
139
  async takeSnapshot() {
154
- if (!this.driver)
140
+ if (!this.page)
155
141
  return "No page open.";
156
- await this.driver.evaluate(buildIndexInjectionScript());
157
- const html = await this.driver.content();
158
- const url = this.driver.url();
159
- const title = await this.driver.title();
160
- let content = "";
161
- try {
162
- content = String(await this.driver.evaluate(buildTextExtractionScript()));
163
- }
164
- catch {
165
- content = "";
166
- }
142
+ await this.page.evaluate(buildIndexInjectionScript());
143
+ const html = await this.page.content();
144
+ const url = this.page.url();
145
+ const title = await this.page.title();
167
146
  const allElements = extractInteractiveElements(html, this.config.maxElements + 5);
168
147
  const truncated = allElements.length > this.config.maxElements;
169
148
  const elements = allElements.slice(0, this.config.maxElements);
@@ -173,30 +152,26 @@ export class BrowserSession {
173
152
  title,
174
153
  elementCount: elements.length,
175
154
  };
176
- return formatSnapshot({
177
- url,
178
- title,
179
- elements,
180
- content,
181
- console: this.driver.getConsole(),
182
- networkErrors: this.driver.getNetworkErrors(),
183
- truncated,
184
- });
155
+ return formatSnapshot({ url, title, elements, truncated });
185
156
  }
186
157
  async open(url) {
187
158
  if (!url)
188
159
  return { success: false, output: t("browser.url_required") };
189
- if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("file://")) {
160
+ if (!url.startsWith("http://") &&
161
+ !url.startsWith("https://") &&
162
+ !url.startsWith("file://")) {
190
163
  const isLocalhost = /^(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?$/i.test(url);
191
164
  url = isLocalhost ? "http://" + url : "https://" + url;
192
165
  }
193
166
  await this.launch();
194
- if (!this.driver)
167
+ if (!this.page)
195
168
  return { success: false, output: t("browser.create_page_failed") };
196
169
  this.actionTracker.reset();
197
- this.driver.resetEventLog();
198
170
  try {
199
- await this.driver.goto(url, this.config.navigationTimeout);
171
+ await this.page.goto(url, {
172
+ waitUntil: "domcontentloaded",
173
+ timeout: this.config.navigationTimeout,
174
+ });
200
175
  }
201
176
  catch (err) {
202
177
  return {
@@ -209,17 +184,20 @@ export class BrowserSession {
209
184
  return { success: true, output: snapshot };
210
185
  }
211
186
  async click(target) {
212
- const err = await this.ensureDriver();
187
+ const err = await this.ensurePage();
213
188
  if (err)
214
189
  return err;
215
- if (!this.driver)
190
+ if (!this.page)
216
191
  return { success: false, output: "No page" };
217
192
  if (!target || target < 1) {
218
193
  return { success: false, output: t("browser.invalid_target") };
219
194
  }
220
195
  try {
221
- await this.driver.evaluate(buildClickScript(target));
222
- await this.driver.waitForLoad(5000);
196
+ const script = buildClickScript(target);
197
+ await this.page.evaluate(script);
198
+ await this.page
199
+ .waitForLoadState("domcontentloaded", { timeout: 5000 })
200
+ .catch(() => { });
223
201
  await new Promise((r) => setTimeout(r, 500));
224
202
  const snapshot = await this.takeSnapshot();
225
203
  await this.saveCookies();
@@ -233,10 +211,10 @@ export class BrowserSession {
233
211
  }
234
212
  }
235
213
  async type(target, text) {
236
- const err = await this.ensureDriver();
214
+ const err = await this.ensurePage();
237
215
  if (err)
238
216
  return err;
239
- if (!this.driver)
217
+ if (!this.page)
240
218
  return { success: false, output: "No page" };
241
219
  if (!target || target < 1) {
242
220
  return { success: false, output: t("browser.invalid_target_short") };
@@ -245,7 +223,8 @@ export class BrowserSession {
245
223
  return { success: false, output: t("browser.text_required") };
246
224
  }
247
225
  try {
248
- await this.driver.evaluate(buildTypeScript(target, text));
226
+ const script = buildTypeScript(target, text);
227
+ await this.page.evaluate(script);
249
228
  const snapshot = await this.takeSnapshot();
250
229
  return { success: true, output: snapshot };
251
230
  }
@@ -257,17 +236,17 @@ export class BrowserSession {
257
236
  }
258
237
  }
259
238
  async scroll(direction) {
260
- const err = await this.ensureDriver();
239
+ const err = await this.ensurePage();
261
240
  if (err)
262
241
  return err;
263
- if (!this.driver)
242
+ if (!this.page)
264
243
  return { success: false, output: "No page" };
265
244
  const dir = direction;
266
245
  if (!["up", "down", "top", "bottom"].includes(dir)) {
267
246
  return { success: false, output: t("browser.direction_invalid") };
268
247
  }
269
248
  try {
270
- await this.driver.evaluate(buildScrollScript(dir));
249
+ await this.page.evaluate(buildScrollScript(dir));
271
250
  await new Promise((r) => setTimeout(r, 300));
272
251
  const snapshot = await this.takeSnapshot();
273
252
  return { success: true, output: snapshot };
@@ -280,46 +259,56 @@ export class BrowserSession {
280
259
  }
281
260
  }
282
261
  async back() {
283
- const err = await this.ensureDriver();
262
+ const err = await this.ensurePage();
284
263
  if (err)
285
264
  return err;
286
- if (!this.driver)
265
+ if (!this.page)
287
266
  return { success: false, output: "No page" };
288
- await this.driver.goBack(this.config.navigationTimeout);
267
+ await this.page
268
+ .goBack({
269
+ waitUntil: "domcontentloaded",
270
+ timeout: this.config.navigationTimeout,
271
+ })
272
+ .catch(() => { });
289
273
  await new Promise((r) => setTimeout(r, 300));
290
274
  const snapshot = await this.takeSnapshot();
291
275
  return { success: true, output: snapshot };
292
276
  }
293
277
  async forward() {
294
- const err = await this.ensureDriver();
278
+ const err = await this.ensurePage();
295
279
  if (err)
296
280
  return err;
297
- if (!this.driver)
281
+ if (!this.page)
298
282
  return { success: false, output: "No page" };
299
- await this.driver.goForward(this.config.navigationTimeout);
283
+ await this.page
284
+ .goForward({
285
+ waitUntil: "domcontentloaded",
286
+ timeout: this.config.navigationTimeout,
287
+ })
288
+ .catch(() => { });
300
289
  await new Promise((r) => setTimeout(r, 300));
301
290
  const snapshot = await this.takeSnapshot();
302
291
  return { success: true, output: snapshot };
303
292
  }
304
293
  async screenshot() {
305
- const err = await this.ensureDriver();
294
+ const err = await this.ensurePage();
306
295
  if (err)
307
296
  return err;
308
- if (!this.driver)
297
+ if (!this.page)
309
298
  return { success: false, output: "No page" };
310
- const buffer = await this.driver.screenshot();
299
+ const buffer = await this.page.screenshot({ type: "png", fullPage: false });
311
300
  const snapshot = await this.takeSnapshot();
312
301
  return { success: true, output: snapshot, screenshot: buffer };
313
302
  }
314
303
  async snapshot() {
315
- const err = await this.ensureDriver();
304
+ const err = await this.ensurePage();
316
305
  if (err)
317
306
  return err;
318
307
  const snap = await this.takeSnapshot();
319
308
  return { success: true, output: snap };
320
309
  }
321
310
  async wait(ms) {
322
- const err = await this.ensureDriver();
311
+ const err = await this.ensurePage();
323
312
  if (err)
324
313
  return err;
325
314
  await new Promise((r) => setTimeout(r, Math.min(ms, 10000)));
@@ -328,9 +317,17 @@ export class BrowserSession {
328
317
  }
329
318
  async close() {
330
319
  await this.saveCookies();
331
- if (this.driver) {
332
- await this.driver.close();
333
- this.driver = null;
320
+ if (this.page) {
321
+ await this.page.close().catch(() => { });
322
+ this.page = null;
323
+ }
324
+ if (this.context) {
325
+ await this.context.close().catch(() => { });
326
+ this.context = null;
327
+ }
328
+ if (this.browser) {
329
+ await this.browser.close().catch(() => { });
330
+ this.browser = null;
334
331
  }
335
332
  this.state = { isOpen: false, url: null, title: null, elementCount: 0 };
336
333
  return { success: true, output: t("browser.closed") };
@@ -1,50 +1,46 @@
1
- import { t } from "../../i18n/index";
2
- import { DEFAULT_BROWSER_CONFIG } from "./types";
1
+ import { t } from '../../i18n/index';
3
2
  function inferRole(tag, type) {
4
3
  const t = tag.toLowerCase();
5
- if (t === "a")
6
- return "link";
7
- if (t === "button")
8
- return "button";
9
- if (t === "input") {
4
+ if (t === 'a')
5
+ return 'link';
6
+ if (t === 'button')
7
+ return 'button';
8
+ if (t === 'input') {
10
9
  const inputType = type?.toLowerCase();
11
- if (inputType === "submit" || inputType === "button")
12
- return "button";
13
- if (inputType === "checkbox")
14
- return "checkbox";
15
- if (inputType === "radio")
16
- return "radio";
17
- return "textbox";
10
+ if (inputType === 'submit' || inputType === 'button')
11
+ return 'button';
12
+ if (inputType === 'checkbox')
13
+ return 'checkbox';
14
+ if (inputType === 'radio')
15
+ return 'radio';
16
+ return 'textbox';
18
17
  }
19
- if (t === "textarea")
20
- return "textbox";
21
- if (t === "select")
22
- return "combobox";
23
- return "widget";
18
+ if (t === 'textarea')
19
+ return 'textbox';
20
+ if (t === 'select')
21
+ return 'combobox';
22
+ return 'widget';
24
23
  }
25
24
  function getName(tag, attrs, html, matchIndex) {
26
- const fromAttr = attrs["aria-label"] ||
27
- attrs["placeholder"] ||
28
- attrs["title"] ||
29
- attrs["alt"] ||
30
- attrs["value"] ||
31
- attrs["name"] ||
32
- "";
25
+ const fromAttr = (attrs['aria-label'] ||
26
+ attrs['placeholder'] ||
27
+ attrs['title'] ||
28
+ attrs['alt'] ||
29
+ attrs['value'] ||
30
+ attrs['name'] ||
31
+ '');
33
32
  if (fromAttr)
34
33
  return fromAttr;
35
34
  const afterTag = html.slice(matchIndex);
36
- const closeIdx = afterTag.indexOf(">");
35
+ const closeIdx = afterTag.indexOf('>');
37
36
  if (closeIdx === -1)
38
- return "";
37
+ return '';
39
38
  const afterOpen = afterTag.slice(closeIdx + 1);
40
39
  const endTag = `</${tag}>`;
41
40
  const endIdx = afterOpen.indexOf(endTag);
42
41
  if (endIdx === -1)
43
- return "";
44
- const text = afterOpen
45
- .slice(0, endIdx)
46
- .replace(/<[^>]+>/g, "")
47
- .trim();
42
+ return '';
43
+ const text = afterOpen.slice(0, endIdx).replace(/<[^>]+>/g, '').trim();
48
44
  return text.slice(0, 50);
49
45
  }
50
46
  export function extractInteractiveElements(html, maxElements = 30) {
@@ -60,21 +56,23 @@ export function extractInteractiveElements(html, maxElements = 30) {
60
56
  const attrRegex = /(\w[\w-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
61
57
  let attrMatch;
62
58
  while ((attrMatch = attrRegex.exec(attrStr)) !== null) {
63
- attrs[attrMatch[1].toLowerCase()] = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
59
+ attrs[attrMatch[1].toLowerCase()] = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? '';
64
60
  }
65
- if (attrs["style"]?.includes("display:none") || attrs["style"]?.includes("display: none"))
61
+ if (attrs['style']?.includes('display:none') || attrs['style']?.includes('display: none'))
66
62
  continue;
67
- if (attrs["hidden"] !== undefined)
63
+ if (attrs['hidden'] !== undefined)
68
64
  continue;
69
- if (attrs["aria-hidden"] === "true")
65
+ if (attrs['aria-hidden'] === 'true')
70
66
  continue;
71
- if (tag === "input" && attrs["type"] === "hidden")
67
+ if (tag === 'input' && attrs['type'] === 'hidden')
72
68
  continue;
73
- const role = attrs["role"] || inferRole(tag, attrs["type"]);
74
- const isInteractive = ["a", "button", "input", "textarea", "select"].includes(tag) || attrs["role"] !== undefined;
69
+ const role = attrs['role'] || inferRole(tag, attrs['type']);
70
+ const isInteractive = [
71
+ 'a', 'button', 'input', 'textarea', 'select',
72
+ ].includes(tag) || attrs['role'] !== undefined;
75
73
  if (!isInteractive)
76
74
  continue;
77
- if (tag === "a" && !attrs["href"])
75
+ if (tag === 'a' && !attrs['href'])
78
76
  continue;
79
77
  const name = getName(tag, attrs, html, match.index);
80
78
  elements.push({
@@ -82,67 +80,35 @@ export function extractInteractiveElements(html, maxElements = 30) {
82
80
  tag,
83
81
  role,
84
82
  name,
85
- value: attrs["value"],
86
- href: attrs["href"],
83
+ value: attrs['value'],
84
+ href: attrs['href'],
87
85
  visible: true,
88
86
  });
89
87
  }
90
88
  return elements;
91
89
  }
92
- export function truncateText(text, maxChars) {
93
- if (maxChars <= 0)
94
- return "";
95
- if (text.length <= maxChars)
96
- return text;
97
- const cut = text.slice(0, maxChars);
98
- const lastNewline = cut.lastIndexOf("\n");
99
- const safe = lastNewline > maxChars * 0.6 ? cut.slice(0, lastNewline) : cut;
100
- return `${safe}\n${t("browser.truncated")}`;
101
- }
102
- export function formatSnapshot(snapshot, maxContentChars = DEFAULT_BROWSER_CONFIG.maxContentChars) {
90
+ export function formatSnapshot(snapshot) {
103
91
  const lines = [];
104
92
  lines.push(`Page: "${snapshot.title}"`);
105
93
  lines.push(`URL: ${snapshot.url}`);
106
- lines.push("");
94
+ lines.push('');
107
95
  if (snapshot.elements.length === 0 && !snapshot.truncated) {
108
- lines.push(t("browser.no_elements"));
109
- }
110
- else {
111
- for (const el of snapshot.elements) {
112
- let line = `[${el.index}] ${el.role}`;
113
- if (el.name)
114
- line += ` "${el.name}"`;
115
- if (el.href)
116
- line += ` → ${el.href}`;
117
- if (el.value)
118
- line += ` = "${el.value}"`;
119
- lines.push(line);
120
- }
121
- if (snapshot.truncated) {
122
- lines.push("");
123
- lines.push(t("browser.more_elements"));
124
- }
96
+ lines.push(t('browser.no_elements'));
97
+ return lines.join('\n');
125
98
  }
126
- if (snapshot.content) {
127
- lines.push("");
128
- lines.push(t("browser.content_header"));
129
- for (const line of truncateText(snapshot.content, maxContentChars).split("\n")) {
130
- lines.push(`- ${line}`);
131
- }
132
- }
133
- if (snapshot.console.length > 0) {
134
- lines.push("");
135
- lines.push(t("browser.console_header"));
136
- for (const entry of snapshot.console) {
137
- lines.push(`[${entry.type}] ${entry.text}`);
138
- }
99
+ for (const el of snapshot.elements) {
100
+ let line = `[${el.index}] ${el.role}`;
101
+ if (el.name)
102
+ line += ` "${el.name}"`;
103
+ if (el.href)
104
+ line += ` → ${el.href}`;
105
+ if (el.value)
106
+ line += ` = "${el.value}"`;
107
+ lines.push(line);
139
108
  }
140
- if (snapshot.networkErrors.length > 0) {
141
- lines.push("");
142
- lines.push(t("browser.network_errors_header"));
143
- for (const err of snapshot.networkErrors) {
144
- lines.push(`${err.method} ${err.url} → ${err.error}`);
145
- }
109
+ if (snapshot.truncated) {
110
+ lines.push('');
111
+ lines.push(t('browser.more_elements'));
146
112
  }
147
- return lines.join("\n");
113
+ return lines.join('\n');
148
114
  }
@@ -1,11 +1,8 @@
1
1
  export const DEFAULT_BROWSER_CONFIG = {
2
2
  headless: true,
3
3
  maxElements: 30,
4
- maxContentChars: 2500,
5
- maxConsoleEntries: 40,
6
- maxConsoleLineChars: 400,
7
4
  screenshotMaxWidth: 1280,
8
- cookieDir: "",
5
+ cookieDir: '',
9
6
  viewportWidth: 1280,
10
7
  viewportHeight: 720,
11
8
  navigationTimeout: 15000,
@@ -7,7 +7,7 @@ import { t } from "../../i18n/index";
7
7
  import { pc } from "../../ui/colors";
8
8
  import { loadScenarios, filterByTags } from "./loader";
9
9
  import { runScenario, findMmaRoot } from "./runner";
10
- import { readManifest, upsertCertification, removeCertification } from "./manifest";
10
+ import { readManifest, upsertCertification, removeCertification, } from "./manifest";
11
11
  const HERE = dirname(fileURLToPath(import.meta.url));
12
12
  const MMA_ROOT = findMmaRoot(HERE);
13
13
  const USER_SCENARIO_DIR = join(homedir(), ".mma", "certification", "scenarios");
@@ -85,7 +85,9 @@ export async function certify(opts) {
85
85
  defaultReps: opts.reps,
86
86
  defaultThreshold: 2,
87
87
  onRep: (id, rep, reps, passed, failures) => {
88
- const word = passed ? pc.green(t("cli.cert_rep_pass")) : pc.red(t("cli.cert_rep_fail"));
88
+ const word = passed
89
+ ? pc.green(t("cli.cert_rep_pass"))
90
+ : pc.red(t("cli.cert_rep_fail"));
89
91
  console.log(`[${idx}/${total}] ${id} (${rep}/${reps})... ${word}`);
90
92
  if (!passed)
91
93
  console.log(` ${pc.dim(failures.join("; "))}`);
@@ -71,7 +71,9 @@ function describe(check) {
71
71
  case "dirExists":
72
72
  return `directory missing: ${check.path}`;
73
73
  case "fileContent": {
74
- const what = check.contains !== undefined ? `contains "${check.contains}"` : `equals "${check.equals}"`;
74
+ const what = check.contains !== undefined
75
+ ? `contains "${check.contains}"`
76
+ : `equals "${check.equals}"`;
75
77
  return `${check.path} does not ${what}`;
76
78
  }
77
79
  case "fileRegex":
@@ -35,7 +35,9 @@ export function validateScenario(s) {
35
35
  if (!CHECK_TYPES.includes(c.type)) {
36
36
  errors.push(`[${s.id}] unknown check type: ${c.type}`);
37
37
  }
38
- if (c.type === "fileContent" && c.contains === undefined && c.equals === undefined) {
38
+ if (c.type === "fileContent" &&
39
+ c.contains === undefined &&
40
+ c.equals === undefined) {
39
41
  errors.push(`[${s.id}] fileContent needs contains or equals`);
40
42
  }
41
43
  }
@@ -46,7 +48,9 @@ export function validateScenario(s) {
46
48
  (!Number.isInteger(s.passThreshold) || s.passThreshold < 1)) {
47
49
  errors.push(`[${s.id}] passThreshold must be a positive integer`);
48
50
  }
49
- if (s.passThreshold !== undefined && s.reps !== undefined && s.passThreshold > s.reps) {
51
+ if (s.passThreshold !== undefined &&
52
+ s.reps !== undefined &&
53
+ s.passThreshold > s.reps) {
50
54
  errors.push(`[${s.id}] passThreshold > reps (must be <= reps)`);
51
55
  }
52
56
  return errors;
@@ -92,7 +96,9 @@ function normalizeScenario(data, file) {
92
96
  prompt: String(d.prompt ?? ""),
93
97
  reps: typeof d.reps === "number" ? d.reps : undefined,
94
98
  passThreshold: typeof d.passThreshold === "number" ? d.passThreshold : undefined,
95
- fixtures: Array.isArray(d.fixtures) ? d.fixtures : undefined,
99
+ fixtures: Array.isArray(d.fixtures)
100
+ ? d.fixtures
101
+ : undefined,
96
102
  checks: Array.isArray(d.checks) ? d.checks : [],
97
103
  skipReason: typeof d.skipReason === "string" ? d.skipReason : undefined,
98
104
  };
@@ -98,7 +98,10 @@ export function resolveMmaEntry(mmaRoot) {
98
98
  return join(mmaRoot, "dist", "main.js");
99
99
  }
100
100
  export function findMmaRoot(fromDir) {
101
- const candidates = [resolve(fromDir, "..", "..", ".."), resolve(fromDir, "..")];
101
+ const candidates = [
102
+ resolve(fromDir, "..", "..", ".."),
103
+ resolve(fromDir, ".."),
104
+ ];
102
105
  for (const c of candidates) {
103
106
  if (existsSync(join(c, "package.json")))
104
107
  return c;
@@ -1 +1 @@
1
- export { ContextManager } from "./manager";
1
+ export { ContextManager } from './manager';