@monoes/monobrowse 1.0.1 → 1.0.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.
- package/dist/src/__tests__/output.test.d.ts +2 -0
- package/dist/src/__tests__/output.test.d.ts.map +1 -0
- package/dist/src/__tests__/output.test.js +54 -0
- package/dist/src/__tests__/output.test.js.map +1 -0
- package/dist/src/__tests__/ref-cache.test.d.ts +2 -0
- package/dist/src/__tests__/ref-cache.test.d.ts.map +1 -0
- package/dist/src/__tests__/ref-cache.test.js +109 -0
- package/dist/src/__tests__/ref-cache.test.js.map +1 -0
- package/dist/src/browser/action-builder/analyzer.d.ts +0 -1
- package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -1
- package/dist/src/browser/action-builder/analyzer.js +43 -21
- package/dist/src/browser/action-builder/analyzer.js.map +1 -1
- package/dist/src/browser/action-builder/types.d.ts +0 -0
- package/dist/src/browser/action-builder/types.d.ts.map +0 -0
- package/dist/src/browser/action-builder/types.js +0 -0
- package/dist/src/browser/action-builder/types.js.map +0 -0
- package/dist/src/browser/actions.d.ts +0 -0
- package/dist/src/browser/actions.d.ts.map +0 -0
- package/dist/src/browser/actions.js +0 -0
- package/dist/src/browser/actions.js.map +0 -0
- package/dist/src/browser/adapters/gemini.d.ts +0 -0
- package/dist/src/browser/adapters/gemini.d.ts.map +0 -0
- package/dist/src/browser/adapters/gemini.js +0 -0
- package/dist/src/browser/adapters/gemini.js.map +0 -0
- package/dist/src/browser/adapters/google.d.ts +0 -0
- package/dist/src/browser/adapters/google.d.ts.map +0 -0
- package/dist/src/browser/adapters/google.js +0 -0
- package/dist/src/browser/adapters/google.js.map +0 -0
- package/dist/src/browser/adapters/index.d.ts +0 -0
- package/dist/src/browser/adapters/index.d.ts.map +0 -0
- package/dist/src/browser/adapters/index.js +0 -0
- package/dist/src/browser/adapters/index.js.map +0 -0
- package/dist/src/browser/adapters/instagram.d.ts +0 -0
- package/dist/src/browser/adapters/instagram.d.ts.map +0 -0
- package/dist/src/browser/adapters/instagram.js +0 -0
- package/dist/src/browser/adapters/instagram.js.map +0 -0
- package/dist/src/browser/adapters/linkedin.d.ts +0 -0
- package/dist/src/browser/adapters/linkedin.d.ts.map +0 -0
- package/dist/src/browser/adapters/linkedin.js +0 -0
- package/dist/src/browser/adapters/linkedin.js.map +0 -0
- package/dist/src/browser/adapters/microsoft.d.ts +0 -0
- package/dist/src/browser/adapters/microsoft.d.ts.map +0 -0
- package/dist/src/browser/adapters/microsoft.js +0 -0
- package/dist/src/browser/adapters/microsoft.js.map +0 -0
- package/dist/src/browser/adapters/x.d.ts +0 -0
- package/dist/src/browser/adapters/x.d.ts.map +0 -0
- package/dist/src/browser/adapters/x.js +0 -0
- package/dist/src/browser/adapters/x.js.map +0 -0
- package/dist/src/browser/batch.d.ts +0 -0
- package/dist/src/browser/batch.d.ts.map +0 -0
- package/dist/src/browser/batch.js +0 -0
- package/dist/src/browser/batch.js.map +0 -0
- package/dist/src/browser/browser.d.ts +0 -0
- package/dist/src/browser/browser.d.ts.map +0 -0
- package/dist/src/browser/browser.js +0 -0
- package/dist/src/browser/browser.js.map +0 -0
- package/dist/src/browser/cdp.d.ts +0 -0
- package/dist/src/browser/cdp.d.ts.map +0 -0
- package/dist/src/browser/cdp.js +0 -0
- package/dist/src/browser/cdp.js.map +0 -0
- package/dist/src/browser/console-log.d.ts +0 -0
- package/dist/src/browser/console-log.d.ts.map +0 -0
- package/dist/src/browser/console-log.js +0 -0
- package/dist/src/browser/console-log.js.map +0 -0
- package/dist/src/browser/dashboard/api-types.d.ts +0 -0
- package/dist/src/browser/dashboard/api-types.d.ts.map +0 -0
- package/dist/src/browser/dashboard/api-types.js +0 -0
- package/dist/src/browser/dashboard/api-types.js.map +0 -0
- package/dist/src/browser/dashboard/server.d.ts +12 -1
- package/dist/src/browser/dashboard/server.d.ts.map +1 -1
- package/dist/src/browser/dashboard/server.js +4 -198
- package/dist/src/browser/dashboard/server.js.map +1 -1
- package/dist/src/browser/dashboard/ui.html +0 -0
- package/dist/src/browser/dialog.d.ts +0 -0
- package/dist/src/browser/dialog.d.ts.map +0 -0
- package/dist/src/browser/dialog.js +0 -0
- package/dist/src/browser/dialog.js.map +0 -0
- package/dist/src/browser/emulation.d.ts +0 -0
- package/dist/src/browser/emulation.d.ts.map +0 -0
- package/dist/src/browser/emulation.js +0 -0
- package/dist/src/browser/emulation.js.map +0 -0
- package/dist/src/browser/find.d.ts +0 -0
- package/dist/src/browser/find.d.ts.map +0 -0
- package/dist/src/browser/find.js +0 -0
- package/dist/src/browser/find.js.map +0 -0
- package/dist/src/browser/har.d.ts +0 -0
- package/dist/src/browser/har.d.ts.map +0 -0
- package/dist/src/browser/har.js +0 -0
- package/dist/src/browser/har.js.map +0 -0
- package/dist/src/browser/index.d.ts +1 -0
- package/dist/src/browser/index.d.ts.map +1 -1
- package/dist/src/browser/index.js +1 -0
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/browser/network.d.ts +0 -0
- package/dist/src/browser/network.d.ts.map +0 -0
- package/dist/src/browser/network.js +0 -0
- package/dist/src/browser/network.js.map +0 -0
- package/dist/src/browser/pdf.d.ts +0 -0
- package/dist/src/browser/pdf.d.ts.map +0 -0
- package/dist/src/browser/pdf.js +0 -0
- package/dist/src/browser/pdf.js.map +0 -0
- package/dist/src/browser/profiler.d.ts +0 -0
- package/dist/src/browser/profiler.d.ts.map +0 -0
- package/dist/src/browser/profiler.js +0 -0
- package/dist/src/browser/profiler.js.map +0 -0
- package/dist/src/browser/record.d.ts +0 -0
- package/dist/src/browser/record.d.ts.map +0 -0
- package/dist/src/browser/record.js +0 -0
- package/dist/src/browser/record.js.map +0 -0
- package/dist/src/browser/ref-cache.d.ts +21 -0
- package/dist/src/browser/ref-cache.d.ts.map +1 -0
- package/dist/src/browser/ref-cache.js +70 -0
- package/dist/src/browser/ref-cache.js.map +1 -0
- package/dist/src/browser/screenshot.d.ts +0 -0
- package/dist/src/browser/screenshot.d.ts.map +0 -0
- package/dist/src/browser/screenshot.js +0 -0
- package/dist/src/browser/screenshot.js.map +0 -0
- package/dist/src/browser/session.d.ts +0 -0
- package/dist/src/browser/session.d.ts.map +0 -0
- package/dist/src/browser/session.js +0 -0
- package/dist/src/browser/session.js.map +0 -0
- package/dist/src/browser/snapshot.d.ts +0 -0
- package/dist/src/browser/snapshot.d.ts.map +0 -0
- package/dist/src/browser/snapshot.js +0 -0
- package/dist/src/browser/snapshot.js.map +0 -0
- package/dist/src/browser/storage.d.ts +0 -0
- package/dist/src/browser/storage.d.ts.map +0 -0
- package/dist/src/browser/storage.js +0 -0
- package/dist/src/browser/storage.js.map +0 -0
- package/dist/src/browser/tabs.d.ts +1 -1
- package/dist/src/browser/tabs.d.ts.map +1 -1
- package/dist/src/browser/tabs.js +20 -4
- package/dist/src/browser/tabs.js.map +1 -1
- package/dist/src/browser/trace.d.ts +0 -0
- package/dist/src/browser/trace.d.ts.map +0 -0
- package/dist/src/browser/trace.js +0 -0
- package/dist/src/browser/trace.js.map +0 -0
- package/dist/src/browser/types.d.ts +0 -0
- package/dist/src/browser/types.d.ts.map +0 -0
- package/dist/src/browser/types.js +0 -0
- package/dist/src/browser/types.js.map +0 -0
- package/dist/src/browser/vitals.d.ts +0 -0
- package/dist/src/browser/vitals.d.ts.map +0 -0
- package/dist/src/browser/vitals.js +0 -0
- package/dist/src/browser/vitals.js.map +0 -0
- package/dist/src/browser/wait.d.ts +0 -0
- package/dist/src/browser/wait.d.ts.map +0 -0
- package/dist/src/browser/wait.js +0 -0
- package/dist/src/browser/wait.js.map +0 -0
- package/dist/src/cli/action.d.ts +0 -0
- package/dist/src/cli/action.d.ts.map +1 -1
- package/dist/src/cli/action.js +92 -4
- package/dist/src/cli/action.js.map +1 -1
- package/dist/src/cli/commands.d.ts +0 -0
- package/dist/src/cli/commands.d.ts.map +1 -1
- package/dist/src/cli/commands.js +32 -6
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/output.d.ts +0 -0
- package/dist/src/cli/output.d.ts.map +0 -0
- package/dist/src/cli/output.js +0 -0
- package/dist/src/cli/output.js.map +0 -0
- package/dist/src/cli/platform.d.ts +0 -0
- package/dist/src/cli/platform.d.ts.map +0 -0
- package/dist/src/cli/platform.js +0 -0
- package/dist/src/cli/platform.js.map +0 -0
- package/dist/src/cli/types.d.ts +0 -0
- package/dist/src/cli/types.d.ts.map +0 -0
- package/dist/src/cli/types.js +0 -0
- package/dist/src/cli/types.js.map +0 -0
- package/dist/src/cli.d.ts +0 -0
- package/dist/src/cli.d.ts.map +0 -0
- package/dist/src/cli.js.map +0 -0
- package/dist/src/index.d.ts +0 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +0 -16
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -15
- package/src/__tests__/output.test.ts +62 -0
- package/src/__tests__/ref-cache.test.ts +134 -0
- package/src/browser/action-builder/analyzer.ts +41 -23
- package/src/browser/action-builder/types.ts +0 -0
- package/src/browser/actions.ts +0 -0
- package/src/browser/adapters/gemini.ts +0 -0
- package/src/browser/adapters/google.ts +0 -0
- package/src/browser/adapters/index.ts +0 -0
- package/src/browser/adapters/instagram.ts +0 -0
- package/src/browser/adapters/linkedin.ts +0 -0
- package/src/browser/adapters/microsoft.ts +0 -0
- package/src/browser/adapters/x.ts +0 -0
- package/src/browser/batch.ts +0 -0
- package/src/browser/browser.ts +0 -0
- package/src/browser/cdp.ts +0 -0
- package/src/browser/console-log.ts +0 -0
- package/src/browser/dashboard/api-types.ts +0 -0
- package/src/browser/dashboard/server.ts +16 -198
- package/src/browser/dashboard/ui.html +0 -0
- package/src/browser/dialog.ts +0 -0
- package/src/browser/emulation.ts +0 -0
- package/src/browser/find.ts +0 -0
- package/src/browser/har.ts +0 -0
- package/src/browser/index.ts +1 -0
- package/src/browser/network.ts +0 -0
- package/src/browser/pdf.ts +0 -0
- package/src/browser/profiler.ts +0 -0
- package/src/browser/record.ts +0 -0
- package/src/browser/ref-cache.ts +90 -0
- package/src/browser/screenshot.ts +0 -0
- package/src/browser/session.ts +0 -0
- package/src/browser/snapshot.ts +0 -0
- package/src/browser/storage.ts +0 -0
- package/src/browser/tabs.ts +36 -7
- package/src/browser/trace.ts +0 -0
- package/src/browser/types.ts +0 -0
- package/src/browser/vitals.ts +0 -0
- package/src/browser/wait.ts +0 -0
- package/src/cli/action.ts +91 -5
- package/src/cli/commands.ts +35 -6
- package/src/cli/output.ts +0 -0
- package/src/cli/platform.ts +0 -0
- package/src/cli/types.ts +0 -0
- package/src/cli.ts +0 -0
- package/src/index.ts +0 -20
- package/dist/package.json +0 -75
- package/dist/src/browser/playbook/browser-handlers.d.ts +0 -3
- package/dist/src/browser/playbook/browser-handlers.d.ts.map +0 -1
- package/dist/src/browser/playbook/browser-handlers.js +0 -377
- package/dist/src/browser/playbook/browser-handlers.js.map +0 -1
- package/dist/src/browser/playbook/builtin-handlers.d.ts +0 -3
- package/dist/src/browser/playbook/builtin-handlers.d.ts.map +0 -1
- package/dist/src/browser/playbook/builtin-handlers.js +0 -364
- package/dist/src/browser/playbook/builtin-handlers.js.map +0 -1
- package/dist/src/browser/playbook/index.d.ts +0 -3
- package/dist/src/browser/playbook/index.d.ts.map +0 -1
- package/dist/src/browser/playbook/index.js +0 -3
- package/dist/src/browser/playbook/index.js.map +0 -1
- package/dist/src/browser/playbook/store.d.ts +0 -8
- package/dist/src/browser/playbook/store.d.ts.map +0 -1
- package/dist/src/browser/playbook/store.js +0 -87
- package/dist/src/browser/playbook/store.js.map +0 -1
- package/dist/src/browser/workflow/builtin-handlers.d.ts +0 -3
- package/dist/src/browser/workflow/builtin-handlers.d.ts.map +0 -1
- package/dist/src/browser/workflow/builtin-handlers.js +0 -343
- package/dist/src/browser/workflow/builtin-handlers.js.map +0 -1
- package/dist/src/browser/workflow/engine.d.ts +0 -10
- package/dist/src/browser/workflow/engine.d.ts.map +0 -1
- package/dist/src/browser/workflow/engine.js +0 -174
- package/dist/src/browser/workflow/engine.js.map +0 -1
- package/dist/src/browser/workflow/expression.d.ts +0 -4
- package/dist/src/browser/workflow/expression.d.ts.map +0 -1
- package/dist/src/browser/workflow/expression.js +0 -89
- package/dist/src/browser/workflow/expression.js.map +0 -1
- package/dist/src/browser/workflow/index.d.ts +0 -6
- package/dist/src/browser/workflow/index.d.ts.map +0 -1
- package/dist/src/browser/workflow/index.js +0 -6
- package/dist/src/browser/workflow/index.js.map +0 -1
- package/dist/src/browser/workflow/store.d.ts +0 -8
- package/dist/src/browser/workflow/store.d.ts.map +0 -1
- package/dist/src/browser/workflow/store.js +0 -87
- package/dist/src/browser/workflow/store.js.map +0 -1
- package/dist/src/browser/workflow/types.d.ts +0 -62
- package/dist/src/browser/workflow/types.d.ts.map +0 -1
- package/dist/src/browser/workflow/types.js +0 -2
- package/dist/src/browser/workflow/types.js.map +0 -1
- package/dist/src/cli/playbook.d.ts +0 -3
- package/dist/src/cli/playbook.d.ts.map +0 -1
- package/dist/src/cli/playbook.js +0 -265
- package/dist/src/cli/playbook.js.map +0 -1
- package/dist/src/cli/workflow.d.ts +0 -2
- package/dist/src/cli/workflow.d.ts.map +0 -1
- package/dist/src/cli/workflow.js +0 -3
- package/dist/src/cli/workflow.js.map +0 -1
- package/src/browser/playbook/browser-handlers.ts +0 -449
- package/src/browser/playbook/builtin-handlers.ts +0 -400
- package/src/browser/playbook/index.ts +0 -2
- package/src/browser/playbook/store.ts +0 -87
- package/src/cli/playbook.ts +0 -280
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { mkdtemp, readFile, rm, stat } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
|
|
6
|
+
// We need to mock process.cwd() before importing ref-cache so CACHE_DIR
|
|
7
|
+
// resolves to our temp directory.
|
|
8
|
+
let tempDir: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
tempDir = await mkdtemp(join(tmpdir(), 'monobrowse-test-'));
|
|
12
|
+
vi.spyOn(process, 'cwd').mockReturnValue(tempDir);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
vi.restoreAllMocks();
|
|
17
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Dynamic import so each test picks up the mocked cwd.
|
|
21
|
+
// We must bust the module cache between tests.
|
|
22
|
+
async function importRefCache() {
|
|
23
|
+
// Vitest module cache means re-import returns the same module.
|
|
24
|
+
// Since CACHE_DIR is set at module-load time, we need resetModules.
|
|
25
|
+
vi.resetModules();
|
|
26
|
+
return import('../browser/ref-cache.js');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function makeRefs(count = 2): Map<string, { ref: string; role: string; name: string; nodeId: number }> {
|
|
30
|
+
const map = new Map();
|
|
31
|
+
for (let i = 1; i <= count; i++) {
|
|
32
|
+
map.set(`ref${i}`, { ref: `ref${i}`, role: 'button', name: `Button ${i}`, nodeId: i });
|
|
33
|
+
}
|
|
34
|
+
return map;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('ref-cache', () => {
|
|
38
|
+
it('round-trip: save then load returns equivalent data', async () => {
|
|
39
|
+
const mod = await importRefCache();
|
|
40
|
+
const refs = makeRefs(2);
|
|
41
|
+
|
|
42
|
+
await mod.saveRefCache('target-1', 'https://example.com', refs as any);
|
|
43
|
+
const loaded = await mod.loadRefCache('target-1');
|
|
44
|
+
|
|
45
|
+
expect(loaded).not.toBeNull();
|
|
46
|
+
expect(loaded!.url).toBe('https://example.com');
|
|
47
|
+
expect(loaded!.refs.size).toBe(2);
|
|
48
|
+
expect(loaded!.refs.get('ref1')!.role).toBe('button');
|
|
49
|
+
expect(loaded!.refs.get('ref2')!.name).toBe('Button 2');
|
|
50
|
+
expect(typeof loaded!.savedAt).toBe('number');
|
|
51
|
+
expect(typeof loaded!.ageMs).toBe('number');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('targetId mismatch returns null', async () => {
|
|
55
|
+
const mod = await importRefCache();
|
|
56
|
+
const refs = makeRefs(1);
|
|
57
|
+
|
|
58
|
+
await mod.saveRefCache('target-A', 'https://a.com', refs as any);
|
|
59
|
+
const loaded = await mod.loadRefCache('target-B');
|
|
60
|
+
|
|
61
|
+
expect(loaded).toBeNull();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('corrupt JSON file returns null', async () => {
|
|
65
|
+
const mod = await importRefCache();
|
|
66
|
+
// First save a valid cache to create the directory structure
|
|
67
|
+
await mod.saveRefCache('t', 'https://x.com', makeRefs(1) as any);
|
|
68
|
+
|
|
69
|
+
// Now overwrite the file with invalid JSON
|
|
70
|
+
const cacheFile = join(tempDir, '.monomind', 'monobrowse', 'ax-snapshot.json');
|
|
71
|
+
const { writeFile: wf } = await import('fs/promises');
|
|
72
|
+
await wf(cacheFile, '{{{not valid json!!!');
|
|
73
|
+
|
|
74
|
+
const loaded = await mod.loadRefCache('t');
|
|
75
|
+
expect(loaded).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('clearRefCache removes the cache file', async () => {
|
|
79
|
+
const mod = await importRefCache();
|
|
80
|
+
await mod.saveRefCache('t1', 'https://x.com', makeRefs(1) as any);
|
|
81
|
+
|
|
82
|
+
const cacheFile = join(tempDir, '.monomind', 'monobrowse', 'ax-snapshot.json');
|
|
83
|
+
// Verify file exists
|
|
84
|
+
const before = await stat(cacheFile).catch(() => null);
|
|
85
|
+
expect(before).not.toBeNull();
|
|
86
|
+
|
|
87
|
+
await mod.clearRefCache();
|
|
88
|
+
|
|
89
|
+
const after = await stat(cacheFile).catch(() => null);
|
|
90
|
+
expect(after).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('load after clear returns null', async () => {
|
|
94
|
+
const mod = await importRefCache();
|
|
95
|
+
await mod.saveRefCache('t1', 'https://x.com', makeRefs(1) as any);
|
|
96
|
+
await mod.clearRefCache();
|
|
97
|
+
|
|
98
|
+
const loaded = await mod.loadRefCache('t1');
|
|
99
|
+
expect(loaded).toBeNull();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('freshly saved cache is not stale', async () => {
|
|
103
|
+
const mod = await importRefCache();
|
|
104
|
+
await mod.saveRefCache('t1', 'https://x.com', makeRefs(1) as any);
|
|
105
|
+
|
|
106
|
+
const loaded = await mod.loadRefCache('t1');
|
|
107
|
+
expect(loaded).not.toBeNull();
|
|
108
|
+
expect(loaded!.stale).toBe(false);
|
|
109
|
+
expect(loaded!.ageMs).toBeLessThan(mod.REF_CACHE_STALE_MS);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('cache older than REF_CACHE_STALE_MS is flagged stale', async () => {
|
|
113
|
+
const mod = await importRefCache();
|
|
114
|
+
await mod.saveRefCache('t1', 'https://x.com', makeRefs(1) as any);
|
|
115
|
+
|
|
116
|
+
// Manually backdate the savedAt timestamp
|
|
117
|
+
const cacheFile = join(tempDir, '.monomind', 'monobrowse', 'ax-snapshot.json');
|
|
118
|
+
const raw = JSON.parse(await readFile(cacheFile, 'utf8'));
|
|
119
|
+
raw.savedAt = Date.now() - mod.REF_CACHE_STALE_MS - 5000;
|
|
120
|
+
const { writeFile: wf } = await import('fs/promises');
|
|
121
|
+
await wf(cacheFile, JSON.stringify(raw));
|
|
122
|
+
|
|
123
|
+
const loaded = await mod.loadRefCache('t1');
|
|
124
|
+
expect(loaded).not.toBeNull();
|
|
125
|
+
expect(loaded!.stale).toBe(true);
|
|
126
|
+
expect(loaded!.ageMs).toBeGreaterThan(mod.REF_CACHE_STALE_MS);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('loading with no cache file returns null (no crash)', async () => {
|
|
130
|
+
const mod = await importRefCache();
|
|
131
|
+
const loaded = await mod.loadRefCache('nonexistent');
|
|
132
|
+
expect(loaded).toBeNull();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
// src/browser/action-builder/analyzer.ts
|
|
2
|
-
import
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
3
|
import type { ActionDef } from './types.js';
|
|
4
|
-
const MODEL_DEFAULTS = { sonnet: 'claude-sonnet-4-6' } as const;
|
|
5
4
|
|
|
6
5
|
export interface AnalyzerPage {
|
|
7
6
|
evaluate<T>(expression: string): Promise<T>;
|
|
8
7
|
url(): Promise<string>;
|
|
9
8
|
}
|
|
10
9
|
|
|
10
|
+
// monolean: no options needed — routes through claude --print, no API key required
|
|
11
11
|
export interface AnalyzerOptions {
|
|
12
|
-
apiKey?: string;
|
|
13
12
|
model?: string;
|
|
14
13
|
}
|
|
15
14
|
|
|
@@ -42,6 +41,40 @@ JSON.stringify(
|
|
|
42
41
|
}))
|
|
43
42
|
)`;
|
|
44
43
|
|
|
44
|
+
function claudeCliCall(prompt: string): Promise<string> {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const child = spawn(
|
|
47
|
+
'claude',
|
|
48
|
+
['--print', '--model', 'haiku', '--strict-mcp-config', '--no-session-persistence', '--', prompt],
|
|
49
|
+
{ stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true },
|
|
50
|
+
);
|
|
51
|
+
let stdout = '';
|
|
52
|
+
let stderr = '';
|
|
53
|
+
let settled = false;
|
|
54
|
+
const timer = setTimeout(() => {
|
|
55
|
+
if (settled) return;
|
|
56
|
+
settled = true;
|
|
57
|
+
try { child.kill('SIGTERM'); } catch { /* ignore */ }
|
|
58
|
+
reject(new Error('claude --print timed out after 60s'));
|
|
59
|
+
}, 60_000);
|
|
60
|
+
child.stdout.on('data', (d: Buffer) => { stdout += d.toString(); });
|
|
61
|
+
child.stderr.on('data', (d: Buffer) => { stderr += d.toString(); });
|
|
62
|
+
child.on('close', (code) => {
|
|
63
|
+
if (settled) return;
|
|
64
|
+
settled = true;
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
if (code === 0) resolve(stdout.trim());
|
|
67
|
+
else reject(new Error(stderr.trim() || `claude exited with code ${code}`));
|
|
68
|
+
});
|
|
69
|
+
child.on('error', (err) => {
|
|
70
|
+
if (settled) return;
|
|
71
|
+
settled = true;
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
reject(new Error(`claude CLI not found — is Claude Code installed? (${(err as NodeJS.ErrnoException).code})`));
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
45
78
|
const SYSTEM_PROMPT = `You are an expert browser automation engineer. Given a webpage's interactive elements and a task description, generate a browser automation ActionDef JSON.
|
|
46
79
|
|
|
47
80
|
Rules:
|
|
@@ -94,33 +127,18 @@ ${elements.map((el, i) => {
|
|
|
94
127
|
return `${i + 1}. <${el.tag}${attrs ? ' ' + attrs : ''}>${el.text}</${el.tag}>`;
|
|
95
128
|
}).join('\n')}`;
|
|
96
129
|
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const message = await client.messages.create({
|
|
101
|
-
model,
|
|
102
|
-
max_tokens: 2048,
|
|
103
|
-
system: SYSTEM_PROMPT,
|
|
104
|
-
messages: [
|
|
105
|
-
{
|
|
106
|
-
role: 'user',
|
|
107
|
-
content: `Task: ${task}\n\nPage context:\n${domContext}`,
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const content = message.content[0];
|
|
113
|
-
if (content.type !== 'text') throw new Error('Claude returned non-text response');
|
|
130
|
+
const fullPrompt = `${SYSTEM_PROMPT}\n\nTask: ${task}\n\nPage context:\n${domContext}`;
|
|
131
|
+
const responseText = await claudeCliCall(fullPrompt);
|
|
114
132
|
|
|
115
133
|
let actionDef: ActionDef;
|
|
116
134
|
try {
|
|
117
|
-
actionDef = JSON.parse(
|
|
135
|
+
actionDef = JSON.parse(responseText) as ActionDef;
|
|
118
136
|
} catch {
|
|
119
|
-
throw new Error(`Claude returned invalid JSON:\n${
|
|
137
|
+
throw new Error(`Claude returned invalid JSON:\n${responseText.slice(0, 500)}`);
|
|
120
138
|
}
|
|
121
139
|
|
|
122
140
|
if (!actionDef.id || !actionDef.steps || !Array.isArray(actionDef.steps)) {
|
|
123
|
-
throw new Error(`Claude returned invalid ActionDef: missing id or steps\n${
|
|
141
|
+
throw new Error(`Claude returned invalid ActionDef: missing id or steps\n${responseText.slice(0, 500)}`);
|
|
124
142
|
}
|
|
125
143
|
|
|
126
144
|
return actionDef;
|
|
File without changes
|
package/src/browser/actions.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/browser/batch.ts
CHANGED
|
File without changes
|
package/src/browser/browser.ts
CHANGED
|
File without changes
|
package/src/browser/cdp.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -5,23 +5,23 @@ import { join, dirname } from 'node:path';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
|
-
import type { StepEvent, RunRecord, PlaybookDef } from '@monoes/monoplaybook';
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (!existsSync(PLAYBOOKS_FILE)) return [];
|
|
15
|
-
try {
|
|
16
|
-
return JSON.parse(await readFile(PLAYBOOKS_FILE, 'utf-8')) as PlaybookDef[];
|
|
17
|
-
} catch { return []; }
|
|
9
|
+
export interface StepEvent {
|
|
10
|
+
type: string;
|
|
11
|
+
projectDir?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
18
13
|
}
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
export interface RunRecord {
|
|
16
|
+
id: string;
|
|
17
|
+
startedAt: number;
|
|
18
|
+
status: string;
|
|
19
|
+
itemsProcessed?: number;
|
|
20
|
+
[key: string]: unknown;
|
|
23
21
|
}
|
|
24
22
|
|
|
23
|
+
const RUNS_FILE = join(homedir(), '.monomind', 'browse-runs.json');
|
|
24
|
+
|
|
25
25
|
function readBody(req: IncomingMessage): Promise<string> {
|
|
26
26
|
return new Promise((resolve, reject) => {
|
|
27
27
|
let body = '';
|
|
@@ -90,7 +90,6 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
if (url === '/runs' && req.method === 'GET') {
|
|
93
|
-
// Merge in-memory runs with persisted runs so the UI shows history even across restarts
|
|
94
93
|
const persisted = await loadPersistedRuns().catch(() => [] as RunRecord[]);
|
|
95
94
|
const seen = new Set(runHistory.map(r => r.id));
|
|
96
95
|
const merged = [...runHistory, ...persisted.filter(r => !seen.has(r.id))]
|
|
@@ -109,12 +108,9 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
109
108
|
return;
|
|
110
109
|
}
|
|
111
110
|
|
|
112
|
-
// Parse the URL to correctly handle query parameters such as ?dir=
|
|
113
111
|
const parsed = new URL(url, 'http://localhost');
|
|
114
112
|
if (parsed.pathname === '/events' && (req.method === 'GET' || req.method === 'HEAD') && !WebSocketServer) {
|
|
115
|
-
// SSE
|
|
116
|
-
// No CORS header — the dashboard is served from 127.0.0.1:4242 and no cross-origin
|
|
117
|
-
// access is needed. A wildcard ACAO would let any web page subscribe to playbook events.
|
|
113
|
+
// SSE fallback when ws not available
|
|
118
114
|
const subscribedDir = parsed.searchParams.get('dir') ?? null;
|
|
119
115
|
res.writeHead(200, {
|
|
120
116
|
'Content-Type': 'text/event-stream',
|
|
@@ -123,7 +119,6 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
123
119
|
});
|
|
124
120
|
res.write(`data: ${JSON.stringify({ type: 'connected' })}\n\n`);
|
|
125
121
|
clientDirs.set(res, subscribedDir);
|
|
126
|
-
// 30-second keep-alive heartbeat prevents proxy idle-timeout drops.
|
|
127
122
|
const heartbeat = setInterval(() => {
|
|
128
123
|
try { res.write(': keep-alive\n\n'); } catch { clearInterval(heartbeat); }
|
|
129
124
|
}, 30_000);
|
|
@@ -134,176 +129,6 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
134
129
|
return;
|
|
135
130
|
}
|
|
136
131
|
|
|
137
|
-
// ── Playbook CRUD ────────────────────────────────────────────────────
|
|
138
|
-
// GET /api/playbooks → list all saved playbooks
|
|
139
|
-
// POST /api/playbooks → create/update a playbook (body: PlaybookDef)
|
|
140
|
-
// GET /api/playbooks/:id → get single playbook
|
|
141
|
-
// DELETE /api/playbooks/:id → delete a playbook
|
|
142
|
-
// POST /api/playbooks/:id/run → run a playbook (delegates to engine)
|
|
143
|
-
if (parsed.pathname === '/api/playbooks' && req.method === 'GET') {
|
|
144
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
145
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
146
|
-
res.end(JSON.stringify(list));
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (parsed.pathname === '/api/playbooks' && req.method === 'POST') {
|
|
151
|
-
try {
|
|
152
|
-
const raw = await readBody(req);
|
|
153
|
-
const pb = JSON.parse(raw) as PlaybookDef;
|
|
154
|
-
if (!pb.id || !pb.name) {
|
|
155
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
156
|
-
res.end(JSON.stringify({ error: 'id and name are required' }));
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
160
|
-
const idx = list.findIndex(w => w.id === pb.id);
|
|
161
|
-
if (idx >= 0) list[idx] = pb; else list.push(pb);
|
|
162
|
-
await savePlaybooks(list);
|
|
163
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
164
|
-
res.end(JSON.stringify(pb));
|
|
165
|
-
} catch (e) {
|
|
166
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
167
|
-
res.end(JSON.stringify({ error: String(e) }));
|
|
168
|
-
}
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const pbIdMatch = parsed.pathname.match(/^\/api\/playbooks\/([^/]+)$/);
|
|
173
|
-
const pbRunMatch = parsed.pathname.match(/^\/api\/playbooks\/([^/]+)\/run$/);
|
|
174
|
-
|
|
175
|
-
if (pbRunMatch && req.method === 'POST') {
|
|
176
|
-
const pbId = pbRunMatch[1];
|
|
177
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
178
|
-
const pb = list.find(w => w.id === pbId);
|
|
179
|
-
if (!pb) {
|
|
180
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
181
|
-
res.end(JSON.stringify({ error: `Playbook ${pbId} not found` }));
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
// Import engine and builtin handlers dynamically to avoid circular deps at startup
|
|
185
|
-
res.writeHead(202, { 'Content-Type': 'application/json' });
|
|
186
|
-
res.end(JSON.stringify({ ok: true, playbookId: pbId, message: 'Playbook run started' }));
|
|
187
|
-
// Run async, broadcast events via dashboard
|
|
188
|
-
Promise.resolve().then(async () => {
|
|
189
|
-
try {
|
|
190
|
-
const { runPlaybook, createDefaultHandlers } = await import('../../index.js');
|
|
191
|
-
const handlers = createDefaultHandlers();
|
|
192
|
-
const record = await runPlaybook(pb, {
|
|
193
|
-
handlers,
|
|
194
|
-
onEvent: (evt) => { instance?.broadcast(evt); },
|
|
195
|
-
isStopRequested: (id) => instance?.isStopRequested(id) ?? false,
|
|
196
|
-
});
|
|
197
|
-
instance?.addRunRecord(record);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
console.error('[dashboard] playbook run error:', err);
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (pbIdMatch && req.method === 'GET') {
|
|
206
|
-
const pbId = pbIdMatch[1];
|
|
207
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
208
|
-
const pb = list.find(w => w.id === pbId);
|
|
209
|
-
if (!pb) { res.writeHead(404); res.end('Not found'); return; }
|
|
210
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
211
|
-
res.end(JSON.stringify(pb));
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (pbIdMatch && req.method === 'DELETE') {
|
|
216
|
-
const pbId = pbIdMatch[1];
|
|
217
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
218
|
-
const newList = list.filter(w => w.id !== pbId);
|
|
219
|
-
await savePlaybooks(newList);
|
|
220
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
221
|
-
res.end(JSON.stringify({ ok: true, deleted: pbId }));
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// ── Webhook trigger ──────────────────────────────────────────────────────
|
|
226
|
-
// POST /webhooks/:playbookId
|
|
227
|
-
// Body (optional): JSON object or array — merged as `params` into the playbook run.
|
|
228
|
-
// Header (optional): X-Webhook-Signature: sha256=<hex> for HMAC verification.
|
|
229
|
-
// Response: 202 { ok, runId, playbookId } — run executes asynchronously.
|
|
230
|
-
const webhookMatch = parsed.pathname.match(/^\/webhooks\/([^/]+)$/);
|
|
231
|
-
if (webhookMatch && req.method === 'POST') {
|
|
232
|
-
const pbId = webhookMatch[1];
|
|
233
|
-
const list = await loadPlaybooks().catch(() => [] as PlaybookDef[]);
|
|
234
|
-
const pb = list.find(w => w.id === pbId);
|
|
235
|
-
if (!pb) {
|
|
236
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
237
|
-
res.end(JSON.stringify({ error: `Playbook ${pbId} not found` }));
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const rawBody = await readBody(req).catch(() => '');
|
|
242
|
-
|
|
243
|
-
// Optional HMAC-SHA256 signature verification
|
|
244
|
-
const sigHeader = (req.headers['x-webhook-signature'] as string | undefined) ?? '';
|
|
245
|
-
if (sigHeader) {
|
|
246
|
-
try {
|
|
247
|
-
const { createHmac } = await import('node:crypto');
|
|
248
|
-
// Secret is stored in the playbook's trigger config (first schedule/webhook trigger)
|
|
249
|
-
const triggers = (pb as unknown as Record<string, unknown>)['triggers'];
|
|
250
|
-
const secret: string | undefined =
|
|
251
|
-
Array.isArray(triggers)
|
|
252
|
-
? (triggers as Array<Record<string, unknown>>).find(t => t['secret'])?.[
|
|
253
|
-
'secret'
|
|
254
|
-
] as string | undefined
|
|
255
|
-
: undefined;
|
|
256
|
-
if (secret) {
|
|
257
|
-
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
|
|
258
|
-
if (sigHeader !== expected) {
|
|
259
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
260
|
-
res.end(JSON.stringify({ error: 'Invalid webhook signature' }));
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
} catch {
|
|
265
|
-
// crypto not available or other error — skip verification
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Parse body into params (string values only, per engine contract)
|
|
270
|
-
const params: Record<string, string> = {};
|
|
271
|
-
try {
|
|
272
|
-
if (rawBody.trim()) {
|
|
273
|
-
const parsed2 = JSON.parse(rawBody);
|
|
274
|
-
if (parsed2 && typeof parsed2 === 'object' && !Array.isArray(parsed2)) {
|
|
275
|
-
for (const [k, v] of Object.entries(parsed2 as Record<string, unknown>)) {
|
|
276
|
-
params[k] = typeof v === 'string' ? v : JSON.stringify(v);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
params['__body'] = rawBody;
|
|
280
|
-
}
|
|
281
|
-
} catch {
|
|
282
|
-
params['__body'] = rawBody;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const runId = `webhook-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
286
|
-
res.writeHead(202, { 'Content-Type': 'application/json' });
|
|
287
|
-
res.end(JSON.stringify({ ok: true, runId, playbookId: pbId }));
|
|
288
|
-
|
|
289
|
-
Promise.resolve().then(async () => {
|
|
290
|
-
try {
|
|
291
|
-
const { runPlaybook, createDefaultHandlers } = await import('../../index.js');
|
|
292
|
-
const handlers = createDefaultHandlers();
|
|
293
|
-
const record = await runPlaybook(pb, {
|
|
294
|
-
handlers,
|
|
295
|
-
params,
|
|
296
|
-
onEvent: (evt) => { instance?.broadcast(evt); },
|
|
297
|
-
isStopRequested: (id) => instance?.isStopRequested(id) ?? false,
|
|
298
|
-
});
|
|
299
|
-
instance?.addRunRecord(record);
|
|
300
|
-
} catch (err) {
|
|
301
|
-
console.error('[dashboard] webhook run error:', err);
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
132
|
res.writeHead(404);
|
|
308
133
|
res.end('Not found');
|
|
309
134
|
});
|
|
@@ -311,12 +136,10 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
311
136
|
if (WebSocketServer) {
|
|
312
137
|
const wss = new WebSocketServer({ server });
|
|
313
138
|
wss.on('connection', async (ws: any, req: any) => {
|
|
314
|
-
// Extract optional project-dir filter from the WebSocket upgrade URL
|
|
315
139
|
const upgradeUrl = req?.url ?? '/ws';
|
|
316
140
|
const upgradeParsed = new URL(upgradeUrl, 'http://localhost');
|
|
317
141
|
const subscribedDir = upgradeParsed.searchParams.get('dir') ?? null;
|
|
318
142
|
clientDirs.set(ws, subscribedDir);
|
|
319
|
-
// Merge in-memory and persisted runs so history survives server restarts
|
|
320
143
|
const persisted = await loadPersistedRuns().catch(() => [] as RunRecord[]);
|
|
321
144
|
const seen = new Set(runHistory.map(r => r.id));
|
|
322
145
|
const merged = [...runHistory, ...persisted.filter(r => !seen.has(r.id))]
|
|
@@ -329,11 +152,10 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
329
152
|
|
|
330
153
|
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
331
154
|
if (err.code === 'EADDRINUSE') {
|
|
332
|
-
console.log(`Dashboard port ${port} in use —
|
|
155
|
+
console.log(`Dashboard port ${port} in use — continuing without live dashboard.`);
|
|
333
156
|
} else {
|
|
334
157
|
console.error(`Dashboard server error: ${err.message}`);
|
|
335
158
|
}
|
|
336
|
-
// Replace with a no-op instance so the engine doesn't crash
|
|
337
159
|
instance = {
|
|
338
160
|
broadcast: () => {},
|
|
339
161
|
addRunRecord: () => {},
|
|
@@ -348,17 +170,14 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
348
170
|
function broadcast(event: StepEvent): void {
|
|
349
171
|
const msg = JSON.stringify(event);
|
|
350
172
|
for (const [client, subscribedDir] of clientDirs) {
|
|
351
|
-
// Server-side project filtering: if the client subscribed with ?dir=<path>, only
|
|
352
|
-
// deliver events whose projectDir matches. Clients without a dir filter (subscribedDir
|
|
353
|
-
// === null) receive all events, preserving backward-compatibility with ui.html.
|
|
354
173
|
if (subscribedDir !== null && event.projectDir !== undefined && subscribedDir !== event.projectDir) {
|
|
355
174
|
continue;
|
|
356
175
|
}
|
|
357
176
|
try {
|
|
358
177
|
if (typeof client.send === 'function') {
|
|
359
|
-
client.send(msg);
|
|
178
|
+
client.send(msg);
|
|
360
179
|
} else {
|
|
361
|
-
client.write(`data: ${msg}\n\n`);
|
|
180
|
+
client.write(`data: ${msg}\n\n`);
|
|
362
181
|
}
|
|
363
182
|
} catch {
|
|
364
183
|
clientDirs.delete(client);
|
|
@@ -374,7 +193,6 @@ export function startDashboard(port = DEFAULT_PORT): DashboardServer {
|
|
|
374
193
|
runHistory.unshift(record);
|
|
375
194
|
if (runHistory.length > MAX_RUN_HISTORY) runHistory.pop();
|
|
376
195
|
}
|
|
377
|
-
// Persist to disk so the CLI dashboard (/api/workflow-runs) can read run history
|
|
378
196
|
const dir = join(homedir(), '.monomind');
|
|
379
197
|
mkdir(dir, { recursive: true }).then(() =>
|
|
380
198
|
writeFile(RUNS_FILE, JSON.stringify(runHistory, null, 2))
|
|
File without changes
|
package/src/browser/dialog.ts
CHANGED
|
File without changes
|
package/src/browser/emulation.ts
CHANGED
|
File without changes
|
package/src/browser/find.ts
CHANGED
|
File without changes
|
package/src/browser/har.ts
CHANGED
|
File without changes
|
package/src/browser/index.ts
CHANGED
package/src/browser/network.ts
CHANGED
|
File without changes
|
package/src/browser/pdf.ts
CHANGED
|
File without changes
|
package/src/browser/profiler.ts
CHANGED
|
File without changes
|
package/src/browser/record.ts
CHANGED
|
File without changes
|