aipeek 0.1.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.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # aipeek
2
+
3
+ Gives AI a peek into your running browser app. Captures UI tree (React fiber), console logs, network requests, errors, and store state — served as plain text via Vite dev server.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i aipeek
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```ts
14
+ // vite.config.ts
15
+ import { aipeekPlugin } from 'aipeek'
16
+
17
+ export default defineConfig({
18
+ plugins: [aipeekPlugin()],
19
+ })
20
+ ```
21
+
22
+ ## API Endpoints
23
+
24
+ All endpoints are available on your Vite dev server:
25
+
26
+ | Endpoint | Description |
27
+ |----------|-------------|
28
+ | `GET /__aipeek` | Summary of all sections (UI, console, network, errors, state) |
29
+ | `GET /__aipeek/{section}` | Detail for a section (`ui`, `console`, `network`, `errors`, `state`) |
30
+ | `GET /__aipeek/{section}/{index}` | Detail for a specific item in a section |
31
+ | `GET /__aipeek/{section}?full` | Full detail (no truncation) |
32
+
33
+ ## CLI
34
+
35
+ ```bash
36
+ npx aipeek # fetch from localhost:5195
37
+ npx aipeek --port=3000 # custom port
38
+ ```
39
+
40
+ ## Store Registration (optional)
41
+
42
+ Register MobX/other stores for state inspection:
43
+
44
+ ```ts
45
+ window.__AIPEEK_STORES__ = { myStore, anotherStore }
46
+ ```
47
+
48
+ State is snapshotted on demand (depth-limited, bounded) and included in the `<state>` section.
@@ -0,0 +1,529 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/plugin.ts
2
+ var _path = require('path');
3
+ var _url = require('url');
4
+
5
+ // src/compact.ts
6
+ var SLOW_THRESHOLD = 1e3;
7
+ var MAX_UI_DEPTH = 6;
8
+ var UI_PRIMITIVES = /* @__PURE__ */ new Set([
9
+ "Button",
10
+ "Input",
11
+ "Label",
12
+ "Badge",
13
+ "Checkbox",
14
+ "Skeleton",
15
+ "Spinner",
16
+ "Switch",
17
+ "Tabs",
18
+ "Tooltip",
19
+ "Popover",
20
+ "Dialog",
21
+ "Select",
22
+ "Card",
23
+ "Table",
24
+ "Slider",
25
+ "Progress",
26
+ "RadioGroup",
27
+ "HoverCard",
28
+ "DropdownMenu",
29
+ "ContextMenu",
30
+ "Command",
31
+ "Form",
32
+ "Alert",
33
+ "Pagination",
34
+ "Textarea",
35
+ "TooltipProvider",
36
+ "DialogPortal",
37
+ "Router",
38
+ "RenderErrorBoundary",
39
+ "RouterProvider",
40
+ "RouterProvider2",
41
+ "PanelGroup",
42
+ "Panel"
43
+ ]);
44
+ function compactUI(tree) {
45
+ if (!tree) return "";
46
+ const lines = tree.split("\n");
47
+ const result = [];
48
+ const repeatTracker = /* @__PURE__ */ new Map();
49
+ for (let i = 0; i < lines.length; i++) {
50
+ const line = lines[i];
51
+ const trimmed = line.trimStart();
52
+ if (!trimmed) continue;
53
+ const indent = line.length - trimmed.length;
54
+ const depth = Math.floor(indent / 2);
55
+ if (depth > MAX_UI_DEPTH) continue;
56
+ const componentName = trimmed.split(/[\s\[—]/)[0];
57
+ if (UI_PRIMITIVES.has(componentName)) continue;
58
+ const key = `${depth}:${componentName}`;
59
+ const tracker = repeatTracker.get(key);
60
+ if (tracker && i - tracker.lastIndex <= 2) {
61
+ tracker.count++;
62
+ tracker.lastIndex = i;
63
+ continue;
64
+ }
65
+ for (const [k, t] of repeatTracker) {
66
+ if (t.count > 1) {
67
+ const d = parseInt(k.split(":")[0]);
68
+ const name = k.split(":").slice(1).join(":");
69
+ result.push(`${" ".repeat(d)}${name} \xD7${t.count}`);
70
+ }
71
+ if (t.count > 1 || i - t.lastIndex > 2) {
72
+ repeatTracker.delete(k);
73
+ }
74
+ }
75
+ repeatTracker.set(key, { count: 1, lastIndex: i });
76
+ result.push(line);
77
+ }
78
+ for (const [k, t] of repeatTracker) {
79
+ if (t.count > 1) {
80
+ const d = parseInt(k.split(":")[0]);
81
+ const name = k.split(":").slice(1).join(":");
82
+ result.push(`${" ".repeat(d)}${name} \xD7${t.count}`);
83
+ }
84
+ }
85
+ return result.join("\n");
86
+ }
87
+ var NOISE_PATTERNS = [
88
+ /\[HMR\]/i,
89
+ /\[vite\]/i,
90
+ /hot module/i,
91
+ /react-devtools/i,
92
+ /download the react devtools/i,
93
+ /warning: react does not recognize/i,
94
+ /source map/i,
95
+ /favicon\.ico/,
96
+ /webpack/i,
97
+ /\[webpack\]/i
98
+ ];
99
+ function compactConsole(logs) {
100
+ if (!logs.length) return "";
101
+ const filtered = logs.filter((l) => !NOISE_PATTERNS.some((p) => p.test(l.text)));
102
+ if (!filtered.length) return "";
103
+ const deduped = [];
104
+ for (const log of filtered) {
105
+ const last = deduped[deduped.length - 1];
106
+ if (last && last.entry.text === log.text && last.entry.level === log.level) {
107
+ last.count++;
108
+ } else {
109
+ deduped.push({ entry: log, count: 1 });
110
+ }
111
+ }
112
+ const errors = deduped.filter((d) => d.entry.level === "error");
113
+ const warns = deduped.filter((d) => d.entry.level === "warn");
114
+ const rest = deduped.filter((d) => d.entry.level !== "error" && d.entry.level !== "warn");
115
+ const recentRest = rest.slice(-10);
116
+ const lines = [];
117
+ for (const group of [...errors, ...warns, ...recentRest]) {
118
+ const prefix = `[${group.entry.level}]`;
119
+ const count = group.count > 1 ? ` \xD7${group.count}` : "";
120
+ const source = group.entry.source ? ` (${group.entry.source})` : "";
121
+ const text = truncate(group.entry.text, 200);
122
+ lines.push(`${prefix}${count} ${text}${source}`);
123
+ }
124
+ return lines.join("\n");
125
+ }
126
+ function compactNetwork(requests) {
127
+ if (!requests.length) return "";
128
+ const relevant = requests.filter(
129
+ (r) => r.resourceType === "fetch" || r.resourceType === "xhr" || r.resourceType === "websocket" || isApiUrl(r.url)
130
+ );
131
+ if (!relevant.length) return "";
132
+ const lines = [];
133
+ for (const req of relevant) {
134
+ const duration = req.duration > 0 ? ` ${formatDuration(req.duration)}` : "";
135
+ const slow = req.duration >= SLOW_THRESHOLD ? " [SLOW]" : "";
136
+ const url = compactUrl(req.url);
137
+ if (req.failed || req.status >= 400) {
138
+ const body = req.responseBody ? ` "${truncate(req.responseBody, 100)}"` : "";
139
+ const failure = req.failureText ? ` (${req.failureText})` : "";
140
+ lines.push(`${req.method} ${url} ${req.status}${failure}${body}${duration}${slow}`);
141
+ } else {
142
+ lines.push(`${req.method} ${url} ${req.status}${duration}${slow}`);
143
+ }
144
+ }
145
+ return lines.join("\n");
146
+ }
147
+ function isApiUrl(url) {
148
+ try {
149
+ const u = new URL(url);
150
+ return u.pathname.startsWith("/api") || u.pathname.includes("/graphql");
151
+ } catch (e) {
152
+ return false;
153
+ }
154
+ }
155
+ function compactUrl(url) {
156
+ try {
157
+ const u = new URL(url);
158
+ const path = u.pathname + (u.search ? "?" + truncate(u.search.slice(1), 50) : "");
159
+ return path;
160
+ } catch (e2) {
161
+ return truncate(url, 80);
162
+ }
163
+ }
164
+ function formatDuration(ms) {
165
+ return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${Math.round(ms)}ms`;
166
+ }
167
+ function compactErrors(errors) {
168
+ if (!errors.length) return "";
169
+ const seen = /* @__PURE__ */ new Map();
170
+ for (const err of errors) {
171
+ if (!seen.has(err.message)) {
172
+ seen.set(err.message, err);
173
+ }
174
+ }
175
+ const lines = [];
176
+ for (const err of seen.values()) {
177
+ lines.push(err.message);
178
+ if (err.stack) {
179
+ const frames = filterStack(err.stack);
180
+ for (const frame of frames) {
181
+ lines.push(` at ${frame}`);
182
+ }
183
+ }
184
+ }
185
+ return lines.join("\n");
186
+ }
187
+ function filterStack(stack) {
188
+ return stack.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ")).map((l) => l.slice(3)).filter((l) => !l.includes("node_modules") && !l.includes("<anonymous>")).slice(0, 5);
189
+ }
190
+ function compactState(state) {
191
+ if (!state || !Object.keys(state).length) return "";
192
+ const lines = [];
193
+ for (const [name, value] of Object.entries(state)) {
194
+ lines.push(`${name}:`);
195
+ if (typeof value === "object" && value !== null) {
196
+ for (const [k, v] of Object.entries(value)) {
197
+ lines.push(` ${k}: ${formatValue(v)}`);
198
+ }
199
+ } else {
200
+ lines.push(` ${String(value)}`);
201
+ }
202
+ }
203
+ return lines.join("\n");
204
+ }
205
+ function formatValue(v) {
206
+ if (v === null || v === void 0) return String(v);
207
+ if (typeof v === "string") return v;
208
+ if (typeof v === "number" || typeof v === "boolean") return String(v);
209
+ if (typeof v === "object") {
210
+ const s = JSON.stringify(v);
211
+ return s.length > 120 ? s.slice(0, 120) + "\u2026" : s;
212
+ }
213
+ return String(v);
214
+ }
215
+ function compact(raw) {
216
+ return {
217
+ url: raw.url,
218
+ ui: compactUI(raw.ui),
219
+ console: compactConsole(raw.console),
220
+ network: compactNetwork(raw.network),
221
+ errors: compactErrors(raw.errors),
222
+ state: compactState(raw.state),
223
+ timestamp: raw.timestamp,
224
+ counts: {
225
+ console: raw.console.length,
226
+ network: raw.network.length,
227
+ errors: raw.errors.length,
228
+ state: Object.keys(raw.state).length
229
+ }
230
+ };
231
+ }
232
+ function truncate(s, max) {
233
+ return s.length > max ? s.slice(0, max) + "\u2026" : s;
234
+ }
235
+
236
+ // src/emit.ts
237
+ var SECTIONS = ["ui", "console", "network", "errors", "state"];
238
+ var COUNTED_SECTIONS = {
239
+ console: "console",
240
+ network: "network",
241
+ errors: "errors",
242
+ state: "state"
243
+ };
244
+ function emit(state) {
245
+ const sections = [];
246
+ for (const key of SECTIONS) {
247
+ if (state[key]) {
248
+ const countKey = COUNTED_SECTIONS[key];
249
+ const count = countKey ? _nullishCoalesce(_optionalChain([state, 'access', _ => _.counts, 'optionalAccess', _2 => _2[countKey]]), () => ( 0)) : 0;
250
+ const attr = count ? ` count="${count}"` : "";
251
+ sections.push(`<${key}${attr}>
252
+ ${state[key]}
253
+ </${key}>`);
254
+ }
255
+ }
256
+ if (!sections.length) {
257
+ sections.push("<empty/>");
258
+ }
259
+ return `<aipeek url="${state.url}">
260
+
261
+ ${sections.join("\n\n")}
262
+
263
+ </aipeek>
264
+
265
+ detail: GET /__aipeek/{ui|console|network|errors|state}/{index}`;
266
+ }
267
+
268
+ // src/detail.ts
269
+ async function detail(raw, section, index, full) {
270
+ switch (section) {
271
+ case "ui":
272
+ return full ? raw.ui || null : compactUI(raw.ui) || null;
273
+ case "console":
274
+ return detailConsole(raw.console, index, full);
275
+ case "network":
276
+ return detailNetwork(raw.network, index, full);
277
+ case "errors":
278
+ return detailError(raw.errors, index, full);
279
+ case "state":
280
+ return detailState(raw.state, index, full);
281
+ default:
282
+ return null;
283
+ }
284
+ }
285
+ function detailConsole(logs, index, full) {
286
+ const i = parseInt(_nullishCoalesce(index, () => ( "")));
287
+ if (isNaN(i) || i < 0 || i >= logs.length) return null;
288
+ const log = logs[i];
289
+ if (full) {
290
+ const parts = [`[${log.level}] ${log.text}`];
291
+ if (log.timestamp) parts.push(`timestamp: ${new Date(log.timestamp).toISOString()}`);
292
+ if (log.source) parts.push(`source: ${log.source}`);
293
+ return parts.join("\n");
294
+ }
295
+ return `[${log.level}] ${truncate2(log.text, 200)}`;
296
+ }
297
+ async function detailNetwork(requests, index, full) {
298
+ const i = parseInt(_nullishCoalesce(index, () => ( "")));
299
+ if (isNaN(i) || i < 0 || i >= requests.length) return null;
300
+ const req = requests[i];
301
+ const lines = [
302
+ `${req.method} ${req.url}`,
303
+ `status: ${req.status}`,
304
+ `duration: ${req.duration}ms`,
305
+ `type: ${req.resourceType}`
306
+ ];
307
+ if (req.failed) lines.push(`failed: ${req.failureText || "true"}`);
308
+ if (full) {
309
+ if (req.requestHeaders && Object.keys(req.requestHeaders).length) {
310
+ lines.push("request-headers:");
311
+ for (const [k, v] of Object.entries(req.requestHeaders)) lines.push(` ${k}: ${v}`);
312
+ }
313
+ if (req.requestBody) lines.push(`request-body:
314
+ ${req.requestBody}`);
315
+ if (req.responseHeaders && Object.keys(req.responseHeaders).length) {
316
+ lines.push("response-headers:");
317
+ for (const [k, v] of Object.entries(req.responseHeaders)) lines.push(` ${k}: ${v}`);
318
+ }
319
+ if (req.responseBody) lines.push(`response-body:
320
+ ${req.responseBody}`);
321
+ } else {
322
+ if (req.requestBody) {
323
+ lines.push(`request-body: ${byteSize(req.requestBody)}`);
324
+ if (req.requestSample) {
325
+ const ts = await quickTypeOrFallback(req.requestSample, "RequestBody");
326
+ if (ts) lines.push(ts);
327
+ }
328
+ }
329
+ if (req.responseBody) {
330
+ if (req.status >= 400) {
331
+ lines.push(`response-body: ${byteSize(req.responseBody)} "${truncate2(req.responseBody, 100)}"`);
332
+ } else {
333
+ if (req.responseSample) {
334
+ const ts = await quickTypeOrFallback(req.responseSample, "ResponseBody");
335
+ lines.push(`response-body: ${byteSize(req.responseBody)}`);
336
+ if (ts) lines.push(ts);
337
+ } else {
338
+ lines.push(`response-body: ${byteSize(req.responseBody)} ${truncate2(req.responseBody, 100)}`);
339
+ }
340
+ }
341
+ }
342
+ }
343
+ return lines.join("\n");
344
+ }
345
+ async function quickTypeOrFallback(sample, typeName) {
346
+ try {
347
+ const { InputData, jsonInputForTargetLanguage, quicktype } = await Promise.resolve().then(() => _interopRequireWildcard(require("quicktype-core")));
348
+ const jsonInput = jsonInputForTargetLanguage("typescript");
349
+ await jsonInput.addSource({ name: typeName, samples: [sample] });
350
+ const inputData = new InputData();
351
+ inputData.addInput(jsonInput);
352
+ const { lines } = await quicktype({
353
+ inputData,
354
+ lang: "typescript",
355
+ rendererOptions: { "just-types": "true", "acronym-style": "original" }
356
+ });
357
+ return lines.join("\n");
358
+ } catch (e3) {
359
+ return jsonSchema(sample);
360
+ }
361
+ }
362
+ function detailError(errors, index, full) {
363
+ const i = parseInt(_nullishCoalesce(index, () => ( "")));
364
+ if (isNaN(i) || i < 0 || i >= errors.length) return null;
365
+ const err = errors[i];
366
+ if (full) {
367
+ const lines2 = [err.message];
368
+ if (err.stack) lines2.push(err.stack);
369
+ if (err.source) lines2.push(`source: ${err.source}`);
370
+ if (err.line != null) lines2.push(`location: ${err.source || ""}:${err.line}:${_nullishCoalesce(err.column, () => ( 0))}`);
371
+ return lines2.join("\n");
372
+ }
373
+ const lines = [err.message];
374
+ if (err.stack) {
375
+ const appFrames = err.stack.split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at ") && !l.includes("node_modules")).slice(0, 3);
376
+ if (appFrames.length) lines.push(...appFrames);
377
+ const totalApp = err.stack.split("\n").filter((l) => l.trim().startsWith("at ") && !l.includes("node_modules")).length;
378
+ if (totalApp > 3) lines.push(` ... ${totalApp - 3} more app frames`);
379
+ }
380
+ if (err.line != null) lines.push(`location: ${err.source || ""}:${err.line}:${_nullishCoalesce(err.column, () => ( 0))}`);
381
+ return lines.join("\n");
382
+ }
383
+ function detailState(state, name, full) {
384
+ if (!name || !(name in state)) return null;
385
+ const value = state[name];
386
+ if (full) {
387
+ try {
388
+ return JSON.stringify(value, null, 2);
389
+ } catch (e4) {
390
+ return String(value);
391
+ }
392
+ }
393
+ if (typeof value !== "object" || value === null) return `${name}: ${typeof value}`;
394
+ const lines = [];
395
+ for (const [k, v] of Object.entries(value)) {
396
+ lines.push(`${k}: ${formatSummaryValue(v)}`);
397
+ }
398
+ return lines.join("\n");
399
+ }
400
+ function formatSummaryValue(v) {
401
+ if (v === null || v === void 0) return String(v);
402
+ if (typeof v === "string") {
403
+ if (/^Array\(\d+\)$/.test(v)) return v;
404
+ return v.length > 80 ? v.slice(0, 80) + "\u2026" : v;
405
+ }
406
+ if (typeof v === "number" || typeof v === "boolean") return String(v);
407
+ if (typeof v === "object") {
408
+ const s = JSON.stringify(v);
409
+ return s.length > 80 ? s.slice(0, 80) + "\u2026" : s;
410
+ }
411
+ return String(v);
412
+ }
413
+ function jsonSchema(sample) {
414
+ try {
415
+ return schemaOf(JSON.parse(sample), 0);
416
+ } catch (e5) {
417
+ return null;
418
+ }
419
+ }
420
+ function schemaOf(v, d) {
421
+ if (v === null) return "null";
422
+ if (typeof v === "string") return "string";
423
+ if (typeof v === "number") return "number";
424
+ if (typeof v === "boolean") return "boolean";
425
+ if (Array.isArray(v)) {
426
+ if (!v.length) return "[]";
427
+ if (d >= 3) return "[\u2026]";
428
+ return `${schemaOf(v[0], d + 1)}[]`;
429
+ }
430
+ if (typeof v === "object") {
431
+ if (d >= 3) return "{\u2026}";
432
+ const entries = Object.entries(v);
433
+ if (!entries.length) return "{}";
434
+ const max = d === 0 ? 12 : 6;
435
+ const fields = entries.slice(0, max).map(([k, val]) => `${k}: ${schemaOf(val, d + 1)}`);
436
+ if (entries.length > max) fields.push(`\u2026 ${entries.length - max} more`);
437
+ return `{ ${fields.join(", ")} }`;
438
+ }
439
+ return typeof v;
440
+ }
441
+ function truncate2(s, max) {
442
+ return s.length > max ? s.slice(0, max) + "\u2026" : s;
443
+ }
444
+ function byteSize(s) {
445
+ const bytes = new TextEncoder().encode(s).length;
446
+ if (bytes < 1024) return `${bytes}B`;
447
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
448
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
449
+ }
450
+
451
+ // src/plugin.ts
452
+ var __dirname = _path.dirname.call(void 0, _url.fileURLToPath.call(void 0, import.meta.url));
453
+ var clientPath = _path.resolve.call(void 0, __dirname, "client.ts");
454
+ function aipeekPlugin() {
455
+ let pendingResolve = null;
456
+ let server;
457
+ let lastRaw = null;
458
+ function collectFromClient() {
459
+ return new Promise((resolve2, reject) => {
460
+ pendingResolve = resolve2;
461
+ setTimeout(() => {
462
+ pendingResolve = null;
463
+ reject(new Error("timeout: no client response within 3s"));
464
+ }, 3e3);
465
+ server.hot.send("aipeek:collect", {});
466
+ });
467
+ }
468
+ return {
469
+ name: "aipeek",
470
+ apply: "serve",
471
+ transformIndexHtml() {
472
+ return [{
473
+ tag: "script",
474
+ attrs: { type: "module" },
475
+ children: `import '/@fs/${clientPath}'`,
476
+ injectTo: "body"
477
+ }];
478
+ },
479
+ configureServer(_server) {
480
+ server = _server;
481
+ server.hot.on("aipeek:state", (data) => {
482
+ if (pendingResolve) {
483
+ pendingResolve(data);
484
+ pendingResolve = null;
485
+ }
486
+ });
487
+ server.middlewares.use("/__aipeek", async (req, res) => {
488
+ const url = new URL(req.url || "/", "http://localhost");
489
+ const parts = url.pathname.split("/").filter(Boolean);
490
+ const full = url.searchParams.has("full");
491
+ try {
492
+ if (parts.length >= 1) {
493
+ if (!lastRaw) lastRaw = await collectFromClient();
494
+ const result = await detail(lastRaw, parts[0], parts[1], full);
495
+ if (result !== null) {
496
+ res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
497
+ res.end(result);
498
+ return;
499
+ }
500
+ res.writeHead(404, { "Content-Type": "text/plain" });
501
+ res.end(`not found: ${parts.join("/")}`);
502
+ return;
503
+ }
504
+ const raw = await collectFromClient();
505
+ lastRaw = raw;
506
+ const compacted = compact(raw);
507
+ const output = emit(compacted);
508
+ res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
509
+ res.end(output);
510
+ } catch (err) {
511
+ res.writeHead(504, { "Content-Type": "text/plain" });
512
+ res.end(err instanceof Error ? err.message : "unknown error");
513
+ }
514
+ });
515
+ }
516
+ };
517
+ }
518
+
519
+
520
+
521
+
522
+
523
+
524
+
525
+
526
+
527
+
528
+
529
+ exports.compactUI = compactUI; exports.compactConsole = compactConsole; exports.compactNetwork = compactNetwork; exports.compactErrors = compactErrors; exports.compactState = compactState; exports.compact = compact; exports.emit = emit; exports.detail = detail; exports.aipeekPlugin = aipeekPlugin;