micro-models-agent 0.46.1 → 0.46.3

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 (218) hide show
  1. package/README.md +312 -312
  2. package/dist/main.js +724 -530
  3. package/dist/modules/browser/bridge-server.mjs +202 -202
  4. package/dist/skills/builtin/git.md +36 -36
  5. package/dist/skills/builtin/typescript.md +35 -35
  6. package/package.json +48 -48
  7. package/dist/cli/commands.js +0 -323
  8. package/dist/cli/completer.js +0 -167
  9. package/dist/cli/index.js +0 -2
  10. package/dist/cli/main.js +0 -165
  11. package/dist/cli/plugin-commands.js +0 -36
  12. package/dist/cli/repl-commands.js +0 -661
  13. package/dist/cli/repl.js +0 -616
  14. package/dist/cli/run-result.js +0 -22
  15. package/dist/cli/security-commands.js +0 -164
  16. package/dist/cli/setup.js +0 -231
  17. package/dist/config/config.js +0 -249
  18. package/dist/config/defaults.js +0 -124
  19. package/dist/config/experts.js +0 -15
  20. package/dist/config/index.js +0 -3
  21. package/dist/config/security.js +0 -193
  22. package/dist/config/types.js +0 -1
  23. package/dist/core/agent-moe.js +0 -102
  24. package/dist/core/agent.js +0 -886
  25. package/dist/core/bootstrap.js +0 -404
  26. package/dist/core/index.js +0 -2
  27. package/dist/core/prompt-builder.js +0 -76
  28. package/dist/core/session-logger.js +0 -197
  29. package/dist/core/types.js +0 -1
  30. package/dist/core/version.js +0 -24
  31. package/dist/core/workspace.js +0 -76
  32. package/dist/i18n/en.json +0 -598
  33. package/dist/i18n/index.js +0 -46
  34. package/dist/i18n/ru.json +0 -598
  35. package/dist/index.js +0 -22
  36. package/dist/llm/image-utils.js +0 -143
  37. package/dist/llm/index.js +0 -4
  38. package/dist/llm/model-loader.js +0 -78
  39. package/dist/llm/openai-compat.js +0 -359
  40. package/dist/llm/orchestrator.js +0 -198
  41. package/dist/llm/provider.js +0 -10
  42. package/dist/llm/response.js +0 -39
  43. package/dist/llm/token-counter.js +0 -39
  44. package/dist/llm/types.js +0 -1
  45. package/dist/logger/app-logger.js +0 -143
  46. package/dist/logger/file-log.js +0 -151
  47. package/dist/logger/index.js +0 -1
  48. package/dist/migration/backup.js +0 -45
  49. package/dist/migration/detect.js +0 -50
  50. package/dist/migration/index.js +0 -2
  51. package/dist/modules/artifacts/store.js +0 -61
  52. package/dist/modules/browser/actions.js +0 -76
  53. package/dist/modules/browser/bridge-client.js +0 -199
  54. package/dist/modules/browser/bridge-path.js +0 -10
  55. package/dist/modules/browser/cookie-store.js +0 -24
  56. package/dist/modules/browser/driver.js +0 -136
  57. package/dist/modules/browser/index.js +0 -7
  58. package/dist/modules/browser/module.js +0 -29
  59. package/dist/modules/browser/session.js +0 -338
  60. package/dist/modules/browser/snapshot.js +0 -148
  61. package/dist/modules/browser/types.js +0 -12
  62. package/dist/modules/certification/cli.js +0 -174
  63. package/dist/modules/certification/fact-checker.js +0 -82
  64. package/dist/modules/certification/loader.js +0 -105
  65. package/dist/modules/certification/manifest.js +0 -50
  66. package/dist/modules/certification/runner.js +0 -159
  67. package/dist/modules/certification/scenarios.js +0 -124
  68. package/dist/modules/certification/types.js +0 -1
  69. package/dist/modules/context/chunk-query.js +0 -100
  70. package/dist/modules/context/fact-extractor.js +0 -162
  71. package/dist/modules/context/history.js +0 -15
  72. package/dist/modules/context/index.js +0 -1
  73. package/dist/modules/context/manager.js +0 -423
  74. package/dist/modules/execution/audit-runners.js +0 -152
  75. package/dist/modules/execution/auditor.js +0 -218
  76. package/dist/modules/execution/execution-plugin.js +0 -272
  77. package/dist/modules/execution/index.js +0 -8
  78. package/dist/modules/execution/module.js +0 -436
  79. package/dist/modules/execution/moe-executor.js +0 -291
  80. package/dist/modules/execution/plan-coverage.js +0 -68
  81. package/dist/modules/execution/plan-persister.js +0 -46
  82. package/dist/modules/execution/plan-store.js +0 -157
  83. package/dist/modules/execution/plan-tool.js +0 -508
  84. package/dist/modules/execution/plan-validator.js +0 -153
  85. package/dist/modules/execution/planner.js +0 -90
  86. package/dist/modules/execution/stuck-detector.js +0 -510
  87. package/dist/modules/execution/tracker.js +0 -67
  88. package/dist/modules/execution/types.js +0 -1
  89. package/dist/modules/execution/verifier.js +0 -222
  90. package/dist/modules/execution/windows-commands.js +0 -41
  91. package/dist/modules/hallucination/confidence.js +0 -66
  92. package/dist/modules/hallucination/consistency.js +0 -26
  93. package/dist/modules/hallucination/detector.js +0 -43
  94. package/dist/modules/hallucination/factual.js +0 -129
  95. package/dist/modules/hallucination/index.js +0 -5
  96. package/dist/modules/hallucination/js-identifiers.js +0 -262
  97. package/dist/modules/hallucination/llm-judge.js +0 -101
  98. package/dist/modules/index.js +0 -5
  99. package/dist/modules/indexer/cache.js +0 -40
  100. package/dist/modules/indexer/index.js +0 -3
  101. package/dist/modules/indexer/module.js +0 -245
  102. package/dist/modules/indexer/project-profile.js +0 -183
  103. package/dist/modules/indexer/walker.js +0 -101
  104. package/dist/modules/lsp/check-tool.js +0 -58
  105. package/dist/modules/lsp/client.js +0 -278
  106. package/dist/modules/lsp/command.js +0 -60
  107. package/dist/modules/lsp/config.js +0 -135
  108. package/dist/modules/lsp/index.js +0 -3
  109. package/dist/modules/lsp/module.js +0 -232
  110. package/dist/modules/lsp/probe.js +0 -76
  111. package/dist/modules/lsp/project-root.js +0 -32
  112. package/dist/modules/lsp/startup-check.js +0 -141
  113. package/dist/modules/lsp/types.js +0 -1
  114. package/dist/modules/mcp/client.js +0 -399
  115. package/dist/modules/mcp/index.js +0 -3
  116. package/dist/modules/mcp/module.js +0 -142
  117. package/dist/modules/mcp/registry.js +0 -15
  118. package/dist/modules/memory/index.js +0 -1
  119. package/dist/modules/memory/module.js +0 -96
  120. package/dist/modules/memory/search.js +0 -42
  121. package/dist/modules/memory/store.js +0 -69
  122. package/dist/modules/pipelines/engine.js +0 -60
  123. package/dist/modules/pipelines/index.js +0 -3
  124. package/dist/modules/pipelines/parser.js +0 -56
  125. package/dist/modules/pipelines/template.js +0 -14
  126. package/dist/modules/plugins/builtin/lint-on-write.js +0 -231
  127. package/dist/modules/plugins/builtin/notify.js +0 -9
  128. package/dist/modules/plugins/index.js +0 -1
  129. package/dist/modules/plugins/loader.js +0 -70
  130. package/dist/modules/plugins/manager.js +0 -217
  131. package/dist/modules/plugins/types.js +0 -1
  132. package/dist/modules/processes/detect.js +0 -34
  133. package/dist/modules/processes/index.js +0 -2
  134. package/dist/modules/processes/registry.js +0 -327
  135. package/dist/modules/processes/runner.js +0 -23
  136. package/dist/modules/registry.js +0 -47
  137. package/dist/modules/security/audit-log.js +0 -136
  138. package/dist/modules/security/audit-notifier.js +0 -292
  139. package/dist/modules/security/command-validator.js +0 -205
  140. package/dist/modules/security/content-scanner.js +0 -53
  141. package/dist/modules/security/data-sanitizer.js +0 -89
  142. package/dist/modules/security/encryption.js +0 -242
  143. package/dist/modules/security/index.js +0 -14
  144. package/dist/modules/security/network-validator.js +0 -71
  145. package/dist/modules/security/path-validator.js +0 -207
  146. package/dist/modules/security/rate-limiter.js +0 -119
  147. package/dist/modules/security/security-policies.js +0 -531
  148. package/dist/modules/security/session-encryption.js +0 -210
  149. package/dist/modules/security/session-isolation.js +0 -95
  150. package/dist/modules/session/index.js +0 -3
  151. package/dist/modules/session/manager.js +0 -172
  152. package/dist/modules/session/module.js +0 -24
  153. package/dist/modules/session/store.js +0 -222
  154. package/dist/modules/session/types.js +0 -1
  155. package/dist/modules/skills/index.js +0 -2
  156. package/dist/modules/skills/loader.js +0 -72
  157. package/dist/modules/skills/matcher.js +0 -27
  158. package/dist/modules/skills/module.js +0 -129
  159. package/dist/modules/types.js +0 -1
  160. package/dist/modules/updater/checker.js +0 -96
  161. package/dist/modules/updater/index.js +0 -2
  162. package/dist/modules/updater/module.js +0 -116
  163. package/dist/modules/user-profile/compressor.js +0 -16
  164. package/dist/modules/user-profile/index.js +0 -1
  165. package/dist/modules/user-profile/profile.js +0 -68
  166. package/dist/tools/approve.js +0 -32
  167. package/dist/tools/attach-image.js +0 -89
  168. package/dist/tools/bash.js +0 -496
  169. package/dist/tools/browser.js +0 -114
  170. package/dist/tools/chunk-query.js +0 -99
  171. package/dist/tools/create-dir.js +0 -55
  172. package/dist/tools/delete-file.js +0 -62
  173. package/dist/tools/download-file.js +0 -116
  174. package/dist/tools/edit-file.js +0 -79
  175. package/dist/tools/enable-tools.js +0 -58
  176. package/dist/tools/executor.js +0 -144
  177. package/dist/tools/file-info.js +0 -46
  178. package/dist/tools/filter-tools.js +0 -17
  179. package/dist/tools/glob-tool.js +0 -26
  180. package/dist/tools/grep-tool.js +0 -84
  181. package/dist/tools/hidden-tools-block.js +0 -37
  182. package/dist/tools/index.js +0 -78
  183. package/dist/tools/list-dir.js +0 -48
  184. package/dist/tools/load-skill.js +0 -42
  185. package/dist/tools/mcp-call.js +0 -68
  186. package/dist/tools/move-file.js +0 -85
  187. package/dist/tools/path-utils.js +0 -51
  188. package/dist/tools/pipeline-run.js +0 -144
  189. package/dist/tools/preview.js +0 -2
  190. package/dist/tools/process-kill.js +0 -29
  191. package/dist/tools/process-list.js +0 -36
  192. package/dist/tools/process-log.js +0 -45
  193. package/dist/tools/question.js +0 -140
  194. package/dist/tools/read-file.js +0 -91
  195. package/dist/tools/recall.js +0 -117
  196. package/dist/tools/registry.js +0 -47
  197. package/dist/tools/remember.js +0 -67
  198. package/dist/tools/scope-check.js +0 -30
  199. package/dist/tools/search-history.js +0 -84
  200. package/dist/tools/subagent.js +0 -196
  201. package/dist/tools/types.js +0 -1
  202. package/dist/tools/user-input.js +0 -123
  203. package/dist/tools/web-browse.js +0 -86
  204. package/dist/tools/web-fetch.js +0 -98
  205. package/dist/tools/web-search.js +0 -78
  206. package/dist/tools/write-file.js +0 -81
  207. package/dist/ui/box.js +0 -77
  208. package/dist/ui/colors.js +0 -4
  209. package/dist/ui/diff.js +0 -178
  210. package/dist/ui/index.js +0 -6
  211. package/dist/ui/line-editor.js +0 -703
  212. package/dist/ui/line-math.js +0 -69
  213. package/dist/ui/md-formatter.js +0 -212
  214. package/dist/ui/output.js +0 -13
  215. package/dist/ui/plan-view.js +0 -103
  216. package/dist/ui/renderer.js +0 -209
  217. package/dist/ui/spinner.js +0 -70
  218. package/dist/ui/table.js +0 -144
@@ -1,338 +0,0 @@
1
- import { createBrowserDriver } from "./driver";
2
- import { extractInteractiveElements, formatSnapshot } from "./snapshot";
3
- import { buildClickScript, buildTypeScript, buildScrollScript, buildIndexInjectionScript, buildTextExtractionScript, } from "./actions";
4
- import { CookieStore } from "./cookie-store";
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
- export class BrowserActionTracker {
34
- history = [];
35
- threshold;
36
- constructor(threshold = 3) {
37
- this.threshold = threshold;
38
- }
39
- record(action, args, url) {
40
- this.history.push({ action, args, url });
41
- if (this.history.length < this.threshold)
42
- return null;
43
- const recent = this.history.slice(-this.threshold);
44
- const allSame = recent.every((h) => h.action === recent[0].action &&
45
- h.url === recent[0].url &&
46
- JSON.stringify(h.args) === JSON.stringify(recent[0].args));
47
- if (allSame) {
48
- return t("browser.repeated_action", {
49
- action,
50
- threshold: this.threshold,
51
- });
52
- }
53
- return null;
54
- }
55
- reset() {
56
- this.history = [];
57
- }
58
- }
59
- export class BrowserSession {
60
- driver = null;
61
- config;
62
- cookieStore;
63
- state = {
64
- isOpen: false,
65
- url: null,
66
- title: null,
67
- elementCount: 0,
68
- };
69
- actionTracker = new BrowserActionTracker(3);
70
- constructor(config) {
71
- this.config = config;
72
- this.cookieStore = new CookieStore(config.cookieDir);
73
- }
74
- getState() {
75
- return { ...this.state };
76
- }
77
- async execute(action, args) {
78
- try {
79
- let result;
80
- switch (action) {
81
- case "open":
82
- result = await this.open(String(args.url || ""));
83
- break;
84
- case "click":
85
- result = await this.click(Number(args.target));
86
- break;
87
- case "type":
88
- result = await this.type(Number(args.target), String(args.text || ""));
89
- break;
90
- case "scroll":
91
- result = await this.scroll(String(args.direction || "down"));
92
- break;
93
- case "back":
94
- result = await this.back();
95
- break;
96
- case "forward":
97
- result = await this.forward();
98
- break;
99
- case "screenshot":
100
- result = await this.screenshot();
101
- break;
102
- case "snapshot":
103
- result = await this.snapshot();
104
- break;
105
- case "close":
106
- result = await this.close();
107
- break;
108
- case "wait":
109
- result = await this.wait(Number(args.ms || 1000));
110
- break;
111
- default:
112
- return {
113
- success: false,
114
- output: t("browser.unknown_action", { action }),
115
- };
116
- }
117
- if (action !== "open") {
118
- const warning = this.actionTracker.record(action, args, this.driver?.url() || "");
119
- if (warning && result.success) {
120
- result.output = result.output + "\n\n⚠️ " + warning;
121
- }
122
- }
123
- return result;
124
- }
125
- catch (err) {
126
- return {
127
- success: false,
128
- output: t("browser.error", { message: err.message }),
129
- };
130
- }
131
- }
132
- async ensureDriver() {
133
- if (!this.driver) {
134
- return { success: false, output: t("browser.no_page") };
135
- }
136
- return null;
137
- }
138
- async launch() {
139
- if (this.driver)
140
- return;
141
- this.driver = await createBrowserDriver(this.config);
142
- const savedCookies = await this.cookieStore.load();
143
- if (savedCookies.length > 0) {
144
- await this.driver.addCookies(savedCookies);
145
- }
146
- }
147
- async saveCookies() {
148
- if (!this.driver)
149
- return;
150
- const cookies = await this.driver.cookies();
151
- await this.cookieStore.save(cookies);
152
- }
153
- async takeSnapshot() {
154
- if (!this.driver)
155
- 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
- }
167
- const allElements = extractInteractiveElements(html, this.config.maxElements + 5);
168
- const truncated = allElements.length > this.config.maxElements;
169
- const elements = allElements.slice(0, this.config.maxElements);
170
- this.state = {
171
- isOpen: true,
172
- url,
173
- title,
174
- elementCount: elements.length,
175
- };
176
- return formatSnapshot({
177
- url,
178
- title,
179
- elements,
180
- content,
181
- console: this.driver.getConsole(),
182
- networkErrors: this.driver.getNetworkErrors(),
183
- truncated,
184
- });
185
- }
186
- async open(url) {
187
- if (!url)
188
- return { success: false, output: t("browser.url_required") };
189
- if (!url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("file://")) {
190
- const isLocalhost = /^(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?$/i.test(url);
191
- url = isLocalhost ? "http://" + url : "https://" + url;
192
- }
193
- await this.launch();
194
- if (!this.driver)
195
- return { success: false, output: t("browser.create_page_failed") };
196
- this.actionTracker.reset();
197
- this.driver.resetEventLog();
198
- try {
199
- await this.driver.goto(url, this.config.navigationTimeout);
200
- }
201
- catch (err) {
202
- return {
203
- success: false,
204
- output: t("browser.nav_failed", { message: err.message }),
205
- };
206
- }
207
- const snapshot = await this.takeSnapshot();
208
- await this.saveCookies();
209
- return { success: true, output: snapshot };
210
- }
211
- async click(target) {
212
- const err = await this.ensureDriver();
213
- if (err)
214
- return err;
215
- if (!this.driver)
216
- return { success: false, output: "No page" };
217
- if (!target || target < 1) {
218
- return { success: false, output: t("browser.invalid_target") };
219
- }
220
- try {
221
- await this.driver.evaluate(buildClickScript(target));
222
- await this.driver.waitForLoad(5000);
223
- await new Promise((r) => setTimeout(r, 500));
224
- const snapshot = await this.takeSnapshot();
225
- await this.saveCookies();
226
- return { success: true, output: snapshot };
227
- }
228
- catch (err) {
229
- return {
230
- success: false,
231
- output: t("browser.click_failed", { message: err.message }),
232
- };
233
- }
234
- }
235
- async type(target, text) {
236
- const err = await this.ensureDriver();
237
- if (err)
238
- return err;
239
- if (!this.driver)
240
- return { success: false, output: "No page" };
241
- if (!target || target < 1) {
242
- return { success: false, output: t("browser.invalid_target_short") };
243
- }
244
- if (!text) {
245
- return { success: false, output: t("browser.text_required") };
246
- }
247
- try {
248
- await this.driver.evaluate(buildTypeScript(target, text));
249
- const snapshot = await this.takeSnapshot();
250
- return { success: true, output: snapshot };
251
- }
252
- catch (err) {
253
- return {
254
- success: false,
255
- output: t("browser.type_failed", { message: err.message }),
256
- };
257
- }
258
- }
259
- async scroll(direction) {
260
- const err = await this.ensureDriver();
261
- if (err)
262
- return err;
263
- if (!this.driver)
264
- return { success: false, output: "No page" };
265
- const dir = direction;
266
- if (!["up", "down", "top", "bottom"].includes(dir)) {
267
- return { success: false, output: t("browser.direction_invalid") };
268
- }
269
- try {
270
- await this.driver.evaluate(buildScrollScript(dir));
271
- await new Promise((r) => setTimeout(r, 300));
272
- const snapshot = await this.takeSnapshot();
273
- return { success: true, output: snapshot };
274
- }
275
- catch (err) {
276
- return {
277
- success: false,
278
- output: t("browser.scroll_failed", { message: err.message }),
279
- };
280
- }
281
- }
282
- async back() {
283
- const err = await this.ensureDriver();
284
- if (err)
285
- return err;
286
- if (!this.driver)
287
- return { success: false, output: "No page" };
288
- await this.driver.goBack(this.config.navigationTimeout);
289
- await new Promise((r) => setTimeout(r, 300));
290
- const snapshot = await this.takeSnapshot();
291
- return { success: true, output: snapshot };
292
- }
293
- async forward() {
294
- const err = await this.ensureDriver();
295
- if (err)
296
- return err;
297
- if (!this.driver)
298
- return { success: false, output: "No page" };
299
- await this.driver.goForward(this.config.navigationTimeout);
300
- await new Promise((r) => setTimeout(r, 300));
301
- const snapshot = await this.takeSnapshot();
302
- return { success: true, output: snapshot };
303
- }
304
- async screenshot() {
305
- const err = await this.ensureDriver();
306
- if (err)
307
- return err;
308
- if (!this.driver)
309
- return { success: false, output: "No page" };
310
- const buffer = await this.driver.screenshot();
311
- const snapshot = await this.takeSnapshot();
312
- return { success: true, output: snapshot, screenshot: buffer };
313
- }
314
- async snapshot() {
315
- const err = await this.ensureDriver();
316
- if (err)
317
- return err;
318
- const snap = await this.takeSnapshot();
319
- return { success: true, output: snap };
320
- }
321
- async wait(ms) {
322
- const err = await this.ensureDriver();
323
- if (err)
324
- return err;
325
- await new Promise((r) => setTimeout(r, Math.min(ms, 10000)));
326
- const snapshot = await this.takeSnapshot();
327
- return { success: true, output: snapshot };
328
- }
329
- async close() {
330
- await this.saveCookies();
331
- if (this.driver) {
332
- await this.driver.close();
333
- this.driver = null;
334
- }
335
- this.state = { isOpen: false, url: null, title: null, elementCount: 0 };
336
- return { success: true, output: t("browser.closed") };
337
- }
338
- }
@@ -1,148 +0,0 @@
1
- import { t } from "../../i18n/index";
2
- import { DEFAULT_BROWSER_CONFIG } from "./types";
3
- function inferRole(tag, type) {
4
- const t = tag.toLowerCase();
5
- if (t === "a")
6
- return "link";
7
- if (t === "button")
8
- return "button";
9
- if (t === "input") {
10
- 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";
18
- }
19
- if (t === "textarea")
20
- return "textbox";
21
- if (t === "select")
22
- return "combobox";
23
- return "widget";
24
- }
25
- 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
- "";
33
- if (fromAttr)
34
- return fromAttr;
35
- const afterTag = html.slice(matchIndex);
36
- const closeIdx = afterTag.indexOf(">");
37
- if (closeIdx === -1)
38
- return "";
39
- const afterOpen = afterTag.slice(closeIdx + 1);
40
- const endTag = `</${tag}>`;
41
- const endIdx = afterOpen.indexOf(endTag);
42
- if (endIdx === -1)
43
- return "";
44
- const text = afterOpen
45
- .slice(0, endIdx)
46
- .replace(/<[^>]+>/g, "")
47
- .trim();
48
- return text.slice(0, 50);
49
- }
50
- export function extractInteractiveElements(html, maxElements = 30) {
51
- const elements = [];
52
- const tagRegex = /<(a|button|input|textarea|select|div|span)([^>]*)>/gi;
53
- let match;
54
- while ((match = tagRegex.exec(html)) !== null) {
55
- if (elements.length >= maxElements)
56
- break;
57
- const tag = match[1].toLowerCase();
58
- const attrStr = match[2];
59
- const attrs = {};
60
- const attrRegex = /(\w[\w-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|(\S+)))?/g;
61
- let attrMatch;
62
- while ((attrMatch = attrRegex.exec(attrStr)) !== null) {
63
- attrs[attrMatch[1].toLowerCase()] = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
64
- }
65
- if (attrs["style"]?.includes("display:none") || attrs["style"]?.includes("display: none"))
66
- continue;
67
- if (attrs["hidden"] !== undefined)
68
- continue;
69
- if (attrs["aria-hidden"] === "true")
70
- continue;
71
- if (tag === "input" && attrs["type"] === "hidden")
72
- continue;
73
- const role = attrs["role"] || inferRole(tag, attrs["type"]);
74
- const isInteractive = ["a", "button", "input", "textarea", "select"].includes(tag) || attrs["role"] !== undefined;
75
- if (!isInteractive)
76
- continue;
77
- if (tag === "a" && !attrs["href"])
78
- continue;
79
- const name = getName(tag, attrs, html, match.index);
80
- elements.push({
81
- index: elements.length + 1,
82
- tag,
83
- role,
84
- name,
85
- value: attrs["value"],
86
- href: attrs["href"],
87
- visible: true,
88
- });
89
- }
90
- return elements;
91
- }
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) {
103
- const lines = [];
104
- lines.push(`Page: "${snapshot.title}"`);
105
- lines.push(`URL: ${snapshot.url}`);
106
- lines.push("");
107
- 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
- }
125
- }
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
- }
139
- }
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
- }
146
- }
147
- return lines.join("\n");
148
- }
@@ -1,12 +0,0 @@
1
- export const DEFAULT_BROWSER_CONFIG = {
2
- headless: true,
3
- maxElements: 30,
4
- maxContentChars: 2500,
5
- maxConsoleEntries: 40,
6
- maxConsoleLineChars: 400,
7
- screenshotMaxWidth: 1280,
8
- cookieDir: "",
9
- viewportWidth: 1280,
10
- viewportHeight: 720,
11
- navigationTimeout: 15000,
12
- };
@@ -1,174 +0,0 @@
1
- import { rmSync } from "fs";
2
- import { homedir } from "os";
3
- import { join, dirname } from "path";
4
- import { fileURLToPath } from "url";
5
- import { existsSync, readFileSync } from "fs";
6
- import { t } from "../../i18n/index";
7
- import { pc } from "../../ui/colors";
8
- import { loadScenarios, filterByTags } from "./loader";
9
- import { runScenario, findMmaRoot } from "./runner";
10
- import { readManifest, upsertCertification, removeCertification } from "./manifest";
11
- const HERE = dirname(fileURLToPath(import.meta.url));
12
- const MMA_ROOT = findMmaRoot(HERE);
13
- const USER_SCENARIO_DIR = join(homedir(), ".mma", "certification", "scenarios");
14
- function readVersion() {
15
- const candidates = [join(MMA_ROOT, "package.json")];
16
- for (const p of candidates) {
17
- if (existsSync(p)) {
18
- try {
19
- const raw = JSON.parse(readFileSync(p, "utf-8"));
20
- if (raw.version)
21
- return raw.version;
22
- }
23
- catch {
24
- // Broken package.json — fall back to the default version
25
- }
26
- }
27
- }
28
- return "0.0.0";
29
- }
30
- export function parseTags(s) {
31
- return s
32
- .split(",")
33
- .map((x) => x.trim())
34
- .filter(Boolean);
35
- }
36
- export async function certify(opts) {
37
- const providerUrl = opts.providerUrl || opts.config.provider.baseUrl;
38
- if (opts.tags.includes("security") && !opts.config.security?.enabled) {
39
- console.error(pc.red(t("cli.cert_security_required")));
40
- process.exitCode = 1;
41
- return;
42
- }
43
- const { scenarios, errors } = loadScenarios(USER_SCENARIO_DIR);
44
- for (const e of errors)
45
- console.error(pc.yellow(` ${e}`));
46
- const selected = filterByTags(scenarios, opts.tags);
47
- if (selected.length === 0) {
48
- console.error(pc.red(t("cli.cert_no_scenarios", { tags: opts.tags.join(",") })));
49
- process.exitCode = 1;
50
- return;
51
- }
52
- const manifest = readManifest();
53
- const existing = manifest.certifications.find((e) => e.model === opts.name && e.providerUrl === providerUrl);
54
- if (existing && !opts.force) {
55
- console.error(pc.yellow(t("cli.cert_exists", { model: opts.name })));
56
- console.error(pc.yellow(t("cli.cert_exists_hint")));
57
- process.exitCode = 1;
58
- return;
59
- }
60
- console.log(t("cli.cert_started", { model: opts.name, provider: providerUrl }));
61
- const sandboxBase = join(process.cwd(), ".mma", "certification");
62
- const results = [];
63
- const total = selected.length;
64
- let idx = 0;
65
- for (const scenario of selected) {
66
- idx++;
67
- if (scenario.mode === "skip") {
68
- console.log(pc.dim(`[${idx}/${total}] ${scenario.id} ... skipped`));
69
- results.push({
70
- id: scenario.id,
71
- title: scenario.title,
72
- status: "skipped",
73
- passed: 0,
74
- of: 0,
75
- });
76
- continue;
77
- }
78
- const res = await runScenario(scenario, {
79
- model: opts.name,
80
- providerUrl,
81
- providerKey: opts.providerKey,
82
- contextWindow: opts.contextWindow,
83
- mmaRoot: MMA_ROOT,
84
- sandboxBase,
85
- defaultReps: opts.reps,
86
- defaultThreshold: 2,
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"));
89
- console.log(`[${idx}/${total}] ${id} (${rep}/${reps})... ${word}`);
90
- if (!passed)
91
- console.log(` ${pc.dim(failures.join("; "))}`);
92
- },
93
- });
94
- results.push(res);
95
- }
96
- if (opts.clean) {
97
- try {
98
- rmSync(sandboxBase, { recursive: true, force: true });
99
- }
100
- catch {
101
- /* ignore */
102
- }
103
- }
104
- const suite = summarize(results);
105
- const entry = {
106
- model: opts.name,
107
- providerUrl,
108
- mmaVersion: readVersion(),
109
- certifiedAt: new Date().toISOString(),
110
- suite,
111
- results,
112
- };
113
- upsertCertification(entry);
114
- console.log(t("cli.cert_done", {
115
- passed: String(suite.passed),
116
- failed: String(suite.failed),
117
- skipped: String(suite.skipped),
118
- total: String(suite.total),
119
- }));
120
- printResults(results);
121
- }
122
- export async function certStatus(name, config) {
123
- const m = readManifest();
124
- const providerUrl = config.provider.baseUrl;
125
- const entry = m.certifications.find((e) => e.model === name && e.providerUrl === providerUrl);
126
- if (!entry) {
127
- console.log(t("cli.cert_not_found", { model: name }));
128
- return;
129
- }
130
- console.log(`${t("cli.cert_provider_col")}: ${entry.providerUrl}`);
131
- console.log(`${t("cli.cert_version_col")}: ${entry.mmaVersion} ${t("cli.cert_date_col")}: ${entry.certifiedAt.slice(0, 10)}`);
132
- console.log(`${t("cli.cert_suite_col")}: ${entry.suite.passed} pass / ${entry.suite.failed} fail / ${entry.suite.skipped} skipped`);
133
- printResults(entry.results);
134
- }
135
- export async function certList() {
136
- const m = readManifest();
137
- if (m.certifications.length === 0) {
138
- console.log(t("cli.cert_empty"));
139
- return;
140
- }
141
- for (const e of m.certifications) {
142
- console.log(` ${pc.green("✔")} ${e.model} ${pc.dim(e.providerUrl)} ${e.mmaVersion} ${e.certifiedAt.slice(0, 10)} ${e.suite.passed}/${e.suite.total} pass`);
143
- }
144
- }
145
- export async function uncertify(name, config) {
146
- const removed = removeCertification(name, config.provider.baseUrl);
147
- if (removed)
148
- console.log(t("cli.cert_uncertified", { model: name }));
149
- else
150
- console.log(t("cli.cert_not_found", { model: name }));
151
- }
152
- function summarize(results) {
153
- return {
154
- passed: results.filter((r) => r.status === "pass").length,
155
- failed: results.filter((r) => r.status === "fail").length,
156
- skipped: results.filter((r) => r.status === "skipped").length,
157
- total: results.length,
158
- };
159
- }
160
- function printResults(results) {
161
- for (const r of results) {
162
- const icon = r.status === "pass"
163
- ? pc.green("✔")
164
- : r.status === "fail"
165
- ? pc.red("✘")
166
- : r.status === "skipped"
167
- ? pc.dim("–")
168
- : pc.yellow("!");
169
- const detail = r.status === "skipped" ? pc.dim(r.title) : `${r.passed}/${r.of}`;
170
- console.log(` ${icon} ${r.id} ${detail}`);
171
- if (r.error)
172
- console.log(` ${pc.dim(r.error)}`);
173
- }
174
- }