loop-sdk 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 +591 -0
- package/dist/agent.d.ts +31 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +48 -0
- package/dist/agent.js.map +1 -0
- package/dist/checkpoint.d.ts +16 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint.js +20 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/claude-cli.d.ts +35 -0
- package/dist/claude-cli.d.ts.map +1 -0
- package/dist/claude-cli.js +135 -0
- package/dist/claude-cli.js.map +1 -0
- package/dist/context.d.ts +53 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +95 -0
- package/dist/context.js.map +1 -0
- package/dist/events.d.ts +111 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +53 -0
- package/dist/events.js.map +1 -0
- package/dist/flow.d.ts +36 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/flow.js +62 -0
- package/dist/flow.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +37 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +64 -0
- package/dist/logger.js.map +1 -0
- package/dist/loop.d.ts +199 -0
- package/dist/loop.d.ts.map +1 -0
- package/dist/loop.js +403 -0
- package/dist/loop.js.map +1 -0
- package/dist/loopfile.d.ts +82 -0
- package/dist/loopfile.d.ts.map +1 -0
- package/dist/loopfile.js +235 -0
- package/dist/loopfile.js.map +1 -0
- package/dist/mcp/server.d.ts +26 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +160 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/notify.d.ts +43 -0
- package/dist/notify.d.ts.map +1 -0
- package/dist/notify.js +68 -0
- package/dist/notify.js.map +1 -0
- package/dist/plugins/retry.d.ts +41 -0
- package/dist/plugins/retry.d.ts.map +1 -0
- package/dist/plugins/retry.js +53 -0
- package/dist/plugins/retry.js.map +1 -0
- package/dist/providers/playwright.d.ts +38 -0
- package/dist/providers/playwright.d.ts.map +1 -0
- package/dist/providers/playwright.js +155 -0
- package/dist/providers/playwright.js.map +1 -0
- package/dist/session.d.ts +43 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +26 -0
- package/dist/session.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global retry plugin — retries every failing step up to `attempts` times.
|
|
3
|
+
*
|
|
4
|
+
* For per-step retry control, use the `retries` / `retryDelay` / `retryBackoff`
|
|
5
|
+
* options on `loop.step()` instead. Both can coexist: step-level retries run
|
|
6
|
+
* first, then the plugin gets a chance if the step is still failing.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { RetryPlugin } from 'loop-sdk/plugins'
|
|
10
|
+
*
|
|
11
|
+
* loop.use(RetryPlugin({ attempts: 3, delay: 1000, backoff: 'exponential' }))
|
|
12
|
+
*
|
|
13
|
+
* // Only retry network errors:
|
|
14
|
+
* loop.use(RetryPlugin({
|
|
15
|
+
* attempts: 5,
|
|
16
|
+
* delay: 2000,
|
|
17
|
+
* retryIf: (err) => err.message.includes('fetch failed'),
|
|
18
|
+
* }))
|
|
19
|
+
*/
|
|
20
|
+
export function RetryPlugin(opts = {}) {
|
|
21
|
+
const maxAttempts = opts.attempts ?? 3;
|
|
22
|
+
const delay = opts.delay ?? 1000;
|
|
23
|
+
const backoff = opts.backoff ?? 'flat';
|
|
24
|
+
const retryIf = opts.retryIf ?? (() => true);
|
|
25
|
+
// Track per-step attempt counts across retries
|
|
26
|
+
const counts = new Map();
|
|
27
|
+
return {
|
|
28
|
+
name: 'retry',
|
|
29
|
+
hooks: {
|
|
30
|
+
onStepError: async (err, step, ctx) => {
|
|
31
|
+
if (!retryIf(err))
|
|
32
|
+
return false;
|
|
33
|
+
const attempt = counts.get(step.name) ?? 0;
|
|
34
|
+
if (attempt >= maxAttempts - 1) {
|
|
35
|
+
counts.delete(step.name);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
counts.set(step.name, attempt + 1);
|
|
39
|
+
const wait = backoff === 'exponential' ? delay * Math.pow(2, attempt) :
|
|
40
|
+
backoff === 'linear' ? delay * (attempt + 1) :
|
|
41
|
+
delay;
|
|
42
|
+
const label = `attempt ${attempt + 2}/${maxAttempts}`;
|
|
43
|
+
ctx.log(wait > 0
|
|
44
|
+
? `[retry] "${step.name}" — retrying in ${wait}ms (${label})`
|
|
45
|
+
: `[retry] "${step.name}" — retrying (${label})`);
|
|
46
|
+
if (wait > 0)
|
|
47
|
+
await new Promise(r => setTimeout(r, wait));
|
|
48
|
+
return true;
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/plugins/retry.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B,EAAE;IACvD,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAA;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAA;IAChC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,MAAM,CAAA;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IAE5C,+CAA+C;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IAExC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,KAAK,EAAE;YACL,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;gBACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;oBAAE,OAAO,KAAK,CAAA;gBAE/B,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC1C,IAAI,OAAO,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;oBAC/B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACxB,OAAO,KAAK,CAAA;gBACd,CAAC;gBAED,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,CAAC,CAAA;gBAElC,MAAM,IAAI,GACR,OAAO,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;oBAC1D,OAAO,KAAK,QAAQ,CAAM,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;wBACvB,KAAK,CAAA;gBAEnC,MAAM,KAAK,GAAG,WAAW,OAAO,GAAG,CAAC,IAAI,WAAW,EAAE,CAAA;gBACrD,GAAG,CAAC,GAAG,CACL,IAAI,GAAG,CAAC;oBACN,CAAC,CAAC,YAAY,IAAI,CAAC,IAAI,mBAAmB,IAAI,OAAO,KAAK,GAAG;oBAC7D,CAAC,CAAC,YAAY,IAAI,CAAC,IAAI,iBAAiB,KAAK,GAAG,CACnD,CAAA;gBACD,IAAI,IAAI,GAAG,CAAC;oBAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;gBAEzD,OAAO,IAAI,CAAA;YACb,CAAC;SACF;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Session, type ClickOptions, type ScrollOptions } from '../session.js';
|
|
2
|
+
/**
|
|
3
|
+
* PlaywrightSession — Session adapter for the aria-playwright daemon.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the daemon's HTTP API (http://localhost:4848 by default).
|
|
6
|
+
* Exposes mcpUrl so agent() can give AI models live browser tool access.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { PlaywrightSession } from 'loop-sdk/playwright'
|
|
10
|
+
*
|
|
11
|
+
* const session = new PlaywrightSession('my-session')
|
|
12
|
+
* await session.ensure()
|
|
13
|
+
* await loop.run({ session })
|
|
14
|
+
* await session.destroy()
|
|
15
|
+
*/
|
|
16
|
+
export declare class PlaywrightSession extends Session {
|
|
17
|
+
readonly daemon: string;
|
|
18
|
+
constructor(id: string, { daemon }?: {
|
|
19
|
+
daemon?: string;
|
|
20
|
+
});
|
|
21
|
+
ensure(): Promise<boolean>;
|
|
22
|
+
destroy(): Promise<void>;
|
|
23
|
+
recreate(): Promise<void>;
|
|
24
|
+
navigate(url: string): Promise<void>;
|
|
25
|
+
click({ x, y, selector, text, button }: ClickOptions): Promise<void>;
|
|
26
|
+
type(text: string): Promise<void>;
|
|
27
|
+
key(key: string): Promise<void>;
|
|
28
|
+
scroll({ x, y, deltaX, deltaY }?: ScrollOptions): Promise<void>;
|
|
29
|
+
screenshot(): Promise<Buffer>;
|
|
30
|
+
get mcpUrl(): string;
|
|
31
|
+
mcp(toolName: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
32
|
+
evaluate(fn: string | (() => unknown)): Promise<string | null>;
|
|
33
|
+
currentUrl(): Promise<string | null>;
|
|
34
|
+
private _mcpRunCode;
|
|
35
|
+
private _fetch;
|
|
36
|
+
private _post;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=playwright.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/providers/playwright.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAE9E;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAkB,SAAQ,OAAO;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAEX,EAAE,EAAE,MAAM,EAAE,EAAE,MAAgC,EAAE,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAOhF,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAY1B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAQzB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAYpE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/B,MAAM,CAAC,EAAE,CAAO,EAAE,CAAO,EAAE,MAAU,EAAE,MAAY,EAAE,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzF,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAQnC,IAAa,MAAM,IAAI,MAAM,CAE5B;IAEK,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS3E,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAU9D,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAM5B,WAAW;YAIX,MAAM;YAYN,KAAK;CASpB"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Session } from '../session.js';
|
|
2
|
+
/**
|
|
3
|
+
* PlaywrightSession — Session adapter for the aria-playwright daemon.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the daemon's HTTP API (http://localhost:4848 by default).
|
|
6
|
+
* Exposes mcpUrl so agent() can give AI models live browser tool access.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* import { PlaywrightSession } from 'loop-sdk/playwright'
|
|
10
|
+
*
|
|
11
|
+
* const session = new PlaywrightSession('my-session')
|
|
12
|
+
* await session.ensure()
|
|
13
|
+
* await loop.run({ session })
|
|
14
|
+
* await session.destroy()
|
|
15
|
+
*/
|
|
16
|
+
export class PlaywrightSession extends Session {
|
|
17
|
+
daemon;
|
|
18
|
+
constructor(id, { daemon = 'http://localhost:4848' } = {}) {
|
|
19
|
+
super(id);
|
|
20
|
+
this.daemon = daemon;
|
|
21
|
+
}
|
|
22
|
+
// ── lifecycle ────────────────────────────────────────────────────────────────
|
|
23
|
+
async ensure() {
|
|
24
|
+
const list = await this._fetch('/sessions').then(r => r.json());
|
|
25
|
+
if (list.some(s => s.id === this.id))
|
|
26
|
+
return false;
|
|
27
|
+
await this._fetch('/sessions', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
body: JSON.stringify({ id: this.id }),
|
|
31
|
+
});
|
|
32
|
+
await sleep(3000);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
async destroy() {
|
|
36
|
+
await this._fetch(`/sessions/${this.id}`, { method: 'DELETE' }).catch(() => { });
|
|
37
|
+
}
|
|
38
|
+
async recreate() {
|
|
39
|
+
await this.destroy();
|
|
40
|
+
await sleep(1000);
|
|
41
|
+
await this.ensure();
|
|
42
|
+
}
|
|
43
|
+
// ── Session interface ────────────────────────────────────────────────────────
|
|
44
|
+
async navigate(url) {
|
|
45
|
+
await this._post('/navigate', { url });
|
|
46
|
+
}
|
|
47
|
+
async click({ x, y, selector, text, button }) {
|
|
48
|
+
if (x != null && y != null) {
|
|
49
|
+
await this._post('/click', { x, y, button });
|
|
50
|
+
}
|
|
51
|
+
else if (selector) {
|
|
52
|
+
await this._mcpRunCode(selectorClickCode(selector));
|
|
53
|
+
}
|
|
54
|
+
else if (text) {
|
|
55
|
+
await this._mcpRunCode(textClickCode(text));
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw new Error('click() requires x+y, selector, or text');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async type(text) {
|
|
62
|
+
await this._post('/type', { text });
|
|
63
|
+
}
|
|
64
|
+
async key(key) {
|
|
65
|
+
await this._post('/key', { key });
|
|
66
|
+
}
|
|
67
|
+
async scroll({ x = 760, y = 400, deltaX = 0, deltaY = 300 } = {}) {
|
|
68
|
+
await this._post('/scroll', { x, y, deltaX, deltaY });
|
|
69
|
+
}
|
|
70
|
+
async screenshot() {
|
|
71
|
+
const res = await this._fetch(`/sessions/${this.id}/screenshot`);
|
|
72
|
+
if (!res.ok)
|
|
73
|
+
throw new Error(`screenshot failed: HTTP ${res.status}`);
|
|
74
|
+
return Buffer.from(await res.arrayBuffer());
|
|
75
|
+
}
|
|
76
|
+
// ── MCP integration ──────────────────────────────────────────────────────────
|
|
77
|
+
get mcpUrl() {
|
|
78
|
+
return `${this.daemon}/sessions/${this.id}/mcp`;
|
|
79
|
+
}
|
|
80
|
+
async mcp(toolName, args = {}) {
|
|
81
|
+
return this._post('/mcp', {
|
|
82
|
+
jsonrpc: '2.0',
|
|
83
|
+
id: Date.now(),
|
|
84
|
+
method: 'tools/call',
|
|
85
|
+
params: { name: toolName, arguments: args },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async evaluate(fn) {
|
|
89
|
+
const fnStr = typeof fn === 'function' ? fn.toString() : fn;
|
|
90
|
+
const r = await this.mcp('browser_evaluate', { function: fnStr });
|
|
91
|
+
const text = r?.result?.content?.[0]?.text ?? '';
|
|
92
|
+
const m = text.match(/### Result\s*\n"([^"]+)"/);
|
|
93
|
+
return m?.[1] ?? null;
|
|
94
|
+
}
|
|
95
|
+
async currentUrl() {
|
|
96
|
+
return this.evaluate('() => window.location.href');
|
|
97
|
+
}
|
|
98
|
+
// ── internal ─────────────────────────────────────────────────────────────────
|
|
99
|
+
async _mcpRunCode(code) {
|
|
100
|
+
return this.mcp('browser_run_code_unsafe', { code });
|
|
101
|
+
}
|
|
102
|
+
async _fetch(path, opts = {}) {
|
|
103
|
+
const url = `${this.daemon}${path}`;
|
|
104
|
+
let last = new Error('fetch failed');
|
|
105
|
+
for (let i = 0; i < 4; i++) {
|
|
106
|
+
try {
|
|
107
|
+
return await fetch(url, opts);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
last = e instanceof Error ? e : new Error(String(e));
|
|
111
|
+
await sleep(800 * (i + 1));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw last;
|
|
115
|
+
}
|
|
116
|
+
async _post(endpoint, body) {
|
|
117
|
+
const res = await this._fetch(`/sessions/${this.id}${endpoint}`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: { 'Content-Type': 'application/json' },
|
|
120
|
+
body: JSON.stringify(body),
|
|
121
|
+
});
|
|
122
|
+
if (!res.ok)
|
|
123
|
+
throw new Error(`${endpoint} failed: HTTP ${res.status}: ${await res.text()}`);
|
|
124
|
+
return res.json();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// ── click helpers ─────────────────────────────────────────────────────────────
|
|
128
|
+
function selectorClickCode(selector) {
|
|
129
|
+
const sel = selector.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
130
|
+
return `async (page) => {
|
|
131
|
+
for (const f of page.frames()) {
|
|
132
|
+
try {
|
|
133
|
+
const loc = f.locator('${sel}')
|
|
134
|
+
if (await loc.count() > 0) { await loc.first().click(); return 'ok' }
|
|
135
|
+
} catch(e) {}
|
|
136
|
+
}
|
|
137
|
+
throw new Error('No element matching: ${sel}')
|
|
138
|
+
}`;
|
|
139
|
+
}
|
|
140
|
+
function textClickCode(text) {
|
|
141
|
+
const txt = text.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
142
|
+
return `async (page) => {
|
|
143
|
+
for (const f of page.frames()) {
|
|
144
|
+
try {
|
|
145
|
+
const loc = f.locator('button,a,[role=button],input[type=submit]').filter({ hasText: '${txt}' })
|
|
146
|
+
if (await loc.count() > 0) { await loc.first().click(); return 'ok' }
|
|
147
|
+
} catch(e) {}
|
|
148
|
+
}
|
|
149
|
+
throw new Error('No element with text: ${txt}')
|
|
150
|
+
}`;
|
|
151
|
+
}
|
|
152
|
+
function sleep(ms) {
|
|
153
|
+
return new Promise(r => setTimeout(r, ms));
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=playwright.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.js","sourceRoot":"","sources":["../../src/providers/playwright.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session — abstract base class every provider must extend.
|
|
3
|
+
*
|
|
4
|
+
* Extend this class to add support for any browser automation tool:
|
|
5
|
+
*
|
|
6
|
+
* import { Session } from 'loop-sdk'
|
|
7
|
+
* export class MySession extends Session {
|
|
8
|
+
* async navigate(url: string) { ... }
|
|
9
|
+
* // etc.
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
export interface ClickOptions {
|
|
13
|
+
x?: number;
|
|
14
|
+
y?: number;
|
|
15
|
+
selector?: string;
|
|
16
|
+
text?: string;
|
|
17
|
+
button?: 'left' | 'right' | 'middle';
|
|
18
|
+
}
|
|
19
|
+
export interface ScrollOptions {
|
|
20
|
+
x?: number;
|
|
21
|
+
y?: number;
|
|
22
|
+
deltaX?: number;
|
|
23
|
+
deltaY?: number;
|
|
24
|
+
}
|
|
25
|
+
export declare abstract class Session {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
constructor(id: string);
|
|
28
|
+
abstract navigate(url: string): Promise<void>;
|
|
29
|
+
abstract click(opts: ClickOptions): Promise<void>;
|
|
30
|
+
abstract type(text: string): Promise<void>;
|
|
31
|
+
abstract key(key: string): Promise<void>;
|
|
32
|
+
abstract scroll(opts: ScrollOptions): Promise<void>;
|
|
33
|
+
/** Capture a screenshot. Returns JPEG bytes. */
|
|
34
|
+
abstract screenshot(): Promise<Buffer>;
|
|
35
|
+
/** Release all resources held by this session. */
|
|
36
|
+
abstract destroy(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* MCP server URL for AI agent tool access, or null if not supported.
|
|
39
|
+
* When set, agent() wires up an MCP client so the AI can call browser tools.
|
|
40
|
+
*/
|
|
41
|
+
get mcpUrl(): string | null;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,YAAY;IAC3B,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,8BAAsB,OAAO;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;gBAEP,EAAE,EAAE,MAAM;IAMtB,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7C,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IACjD,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1C,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACxC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnD,gDAAgD;IAChD,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAEtC,kDAAkD;IAClD,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC;;;OAGG;IACH,IAAI,MAAM,IAAI,MAAM,GAAG,IAAI,CAE1B;CACF"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session — abstract base class every provider must extend.
|
|
3
|
+
*
|
|
4
|
+
* Extend this class to add support for any browser automation tool:
|
|
5
|
+
*
|
|
6
|
+
* import { Session } from 'loop-sdk'
|
|
7
|
+
* export class MySession extends Session {
|
|
8
|
+
* async navigate(url: string) { ... }
|
|
9
|
+
* // etc.
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
export class Session {
|
|
13
|
+
id;
|
|
14
|
+
constructor(id) {
|
|
15
|
+
this.id = id;
|
|
16
|
+
}
|
|
17
|
+
// ── Optional — providers implement for AI agent tool access ──────────────────
|
|
18
|
+
/**
|
|
19
|
+
* MCP server URL for AI agent tool access, or null if not supported.
|
|
20
|
+
* When set, agent() wires up an MCP client so the AI can call browser tools.
|
|
21
|
+
*/
|
|
22
|
+
get mcpUrl() {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiBH,MAAM,OAAgB,OAAO;IAClB,EAAE,CAAQ;IAEnB,YAAY,EAAU;QACpB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;IACd,CAAC;IAgBD,gFAAgF;IAEhF;;;OAGG;IACH,IAAI,MAAM;QACR,OAAO,IAAI,CAAA;IACb,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "loop-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Framework for building long-running agentic loops — browser, AI, or both",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./playwright": {
|
|
14
|
+
"types": "./dist/providers/playwright.d.ts",
|
|
15
|
+
"default": "./dist/providers/playwright.js"
|
|
16
|
+
},
|
|
17
|
+
"./flow": {
|
|
18
|
+
"types": "./dist/flow.d.ts",
|
|
19
|
+
"default": "./dist/flow.js"
|
|
20
|
+
},
|
|
21
|
+
"./agent": {
|
|
22
|
+
"types": "./dist/agent.d.ts",
|
|
23
|
+
"default": "./dist/agent.js"
|
|
24
|
+
},
|
|
25
|
+
"./claude-cli": {
|
|
26
|
+
"types": "./dist/claude-cli.d.ts",
|
|
27
|
+
"default": "./dist/claude-cli.js"
|
|
28
|
+
},
|
|
29
|
+
"./plugins": {
|
|
30
|
+
"types": "./dist/plugins/retry.d.ts",
|
|
31
|
+
"default": "./dist/plugins/retry.js"
|
|
32
|
+
},
|
|
33
|
+
"./mcp": {
|
|
34
|
+
"default": "./dist/mcp/server.js"
|
|
35
|
+
},
|
|
36
|
+
"./notify": {
|
|
37
|
+
"types": "./dist/notify.d.ts",
|
|
38
|
+
"default": "./dist/notify.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"bin": {
|
|
42
|
+
"loop-mcp": "dist/mcp/server.js"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc",
|
|
49
|
+
"typecheck": "tsc --noEmit",
|
|
50
|
+
"prepublishOnly": "npm run build",
|
|
51
|
+
"dev": "tsc --watch",
|
|
52
|
+
"demo": "node examples/demo.js",
|
|
53
|
+
"demo:loopfile": "node examples/run-loopfile.js",
|
|
54
|
+
"mcp": "node dist/mcp/server.js"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
58
|
+
"@types/js-yaml": "^4.0.9",
|
|
59
|
+
"ai": "^4.0.0",
|
|
60
|
+
"js-yaml": "^4.1.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^22.0.0",
|
|
64
|
+
"typescript": "^5.0.0"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@ai-sdk/anthropic": ">=1.0.0",
|
|
68
|
+
"@ai-sdk/openai": ">=1.0.0"
|
|
69
|
+
},
|
|
70
|
+
"peerDependenciesMeta": {
|
|
71
|
+
"@ai-sdk/anthropic": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"@ai-sdk/openai": {
|
|
75
|
+
"optional": true
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|