@praveen-palanisamy/agent-browser-runtime 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/CHANGELOG.md +23 -0
- package/LICENSE +23 -0
- package/README.md +261 -0
- package/action.yml +185 -0
- package/dist/attribution.d.ts +22 -0
- package/dist/attribution.d.ts.map +1 -0
- package/dist/attribution.js +57 -0
- package/dist/attribution.js.map +1 -0
- package/dist/cli.d.ts +21 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +242 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/index.d.ts +9 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +26 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/runner-client.d.ts +41 -0
- package/dist/client/runner-client.d.ts.map +1 -0
- package/dist/client/runner-client.js +98 -0
- package/dist/client/runner-client.js.map +1 -0
- package/dist/core/capture.d.ts +50 -0
- package/dist/core/capture.d.ts.map +1 -0
- package/dist/core/capture.js +129 -0
- package/dist/core/capture.js.map +1 -0
- package/dist/core/poster.d.ts +45 -0
- package/dist/core/poster.d.ts.map +1 -0
- package/dist/core/poster.js +132 -0
- package/dist/core/poster.js.map +1 -0
- package/dist/core/stores.d.ts +35 -0
- package/dist/core/stores.d.ts.map +1 -0
- package/dist/core/stores.js +32 -0
- package/dist/core/stores.js.map +1 -0
- package/dist/core/types.d.ts +138 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +13 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +6 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +3 -0
- package/dist/platform.js.map +1 -0
- package/dist/protocol.d.ts +84 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +24 -0
- package/dist/protocol.js.map +1 -0
- package/dist/providers/kernel.d.ts +53 -0
- package/dist/providers/kernel.d.ts.map +1 -0
- package/dist/providers/kernel.js +82 -0
- package/dist/providers/kernel.js.map +1 -0
- package/dist/providers/playwright.d.ts +88 -0
- package/dist/providers/playwright.d.ts.map +1 -0
- package/dist/providers/playwright.js +114 -0
- package/dist/providers/playwright.js.map +1 -0
- package/dist/providers/steel.d.ts +48 -0
- package/dist/providers/steel.d.ts.map +1 -0
- package/dist/providers/steel.js +79 -0
- package/dist/providers/steel.js.map +1 -0
- package/dist/service/config.d.ts +33 -0
- package/dist/service/config.d.ts.map +1 -0
- package/dist/service/config.js +59 -0
- package/dist/service/config.js.map +1 -0
- package/dist/service/index.d.ts +30 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +53 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/live-proxy.d.ts +27 -0
- package/dist/service/live-proxy.d.ts.map +1 -0
- package/dist/service/live-proxy.js +146 -0
- package/dist/service/live-proxy.js.map +1 -0
- package/dist/service/runner.d.ts +35 -0
- package/dist/service/runner.d.ts.map +1 -0
- package/dist/service/runner.js +139 -0
- package/dist/service/runner.js.map +1 -0
- package/dist/service/serialized-provider.d.ts +20 -0
- package/dist/service/serialized-provider.d.ts.map +1 -0
- package/dist/service/serialized-provider.js +68 -0
- package/dist/service/serialized-provider.js.map +1 -0
- package/dist/service/server.d.ts +17 -0
- package/dist/service/server.d.ts.map +1 -0
- package/dist/service/server.js +159 -0
- package/dist/service/server.js.map +1 -0
- package/docs/AGENTS.md +46 -0
- package/docs/ARCHITECTURE.md +73 -0
- package/docs/DEPLOY.md +61 -0
- package/docs/GITHUB_ACTION.md +92 -0
- package/examples/demo-strategy/index.js +53 -0
- package/examples/demo-strategy/session.json +5 -0
- package/package.json +96 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- **CLI**: new `post` command; `--local` runs `probe`/`post` in-process with local Chromium (no service needed); `--session-out`, `--out`, `--content`, `version`.
|
|
6
|
+
- **GitHub Action**: `praveen-palanisamy/agent-browser-runtime@v0` composite action (`command: probe|post`) with `ok` / `authenticated` / `needs-reauth` / `uncertain` outputs; floating major tag maintained by `release-on-green`.
|
|
7
|
+
- **Attribution**: `/healthz` reports `name`, `version`, `repository`; `Server` header on all JSON responses.
|
|
8
|
+
- **Toolchain**: Node 24 everywhere (`.node-version`, CI, Pages, Action default); `engines.node >= 22`. Biome 2, TypeScript 7 (`module`/`moduleResolution: node16`, still CommonJS output), Vitest 5, `@types/node` 26. GitHub Actions bumped to Node 24-compatible majors (checkout v7, setup-node v7, docker/* v4/v7, pages v5/v6); grouped Dependabot updates for actions.
|
|
9
|
+
- **Fix**: container build keeps optional dependencies (TypeScript 7 ships its compiler as platform-specific optional packages).
|
|
10
|
+
- **Fix**: `action.yml` input description contained a `${{ … }}` expression, which made the runner reject the manifest.
|
|
11
|
+
- **Docs & site**: project website on GitHub Pages (auto-deployed on releases), `docs/GITHUB_ACTION.md`, distribution-channel table in `docs/DEPLOY.md`; README use cases and examples reworked around an expense-portal assistant.
|
|
12
|
+
|
|
13
|
+
## v0.1.0 — 2026-09-06
|
|
14
|
+
|
|
15
|
+
First public release.
|
|
16
|
+
|
|
17
|
+
- **Contracts**: `SessionStore`, `SessionProvider`, `WebPostStrategy`; rich `AgentPostResult` (`verification`, `uncertain`, `needsReauth`, `failureStage`, redacted screenshot).
|
|
18
|
+
- **Orchestration**: `AgentPoster` (load → acquire → auth probe → pacing → post → persist rotated cookies), `SessionCaptureManager` (interactive sign-in with TTL and unguessable ids).
|
|
19
|
+
- **Providers**: local Chromium, any CDP endpoint, self-hosted/cloud Steel, Kernel microVMs; `SerializedProvider` for single-session backends.
|
|
20
|
+
- **Runner service**: bearer-protected HTTP API (`/v1/post`, `/v1/probe`, `/v1/capture/*`), Steel live-view proxy (HTTP + WebSocket, iframe-safe), `/healthz`.
|
|
21
|
+
- **Client**: Playwright-free `AgentRunnerClient` with transient/permanent error classification.
|
|
22
|
+
- **CLI**: `serve --strategies <module>`, `health`, `probe`.
|
|
23
|
+
- **Packaging**: `.`, `./client`, `./service` entry points; container image on GHCR; npm Trusted Publishing with provenance; GitHub Packages mirror.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Praveen Palanisamy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# ABR: Agent Browser Runtime
|
|
2
|
+
|
|
3
|
+
**Let your agent act as the user - in their own logged-in browser session - on infrastructure you control.**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/praveen-palanisamy/agent-browser-runtime/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/@praveen-palanisamy/agent-browser-runtime)
|
|
7
|
+
[](https://github.com/praveen-palanisamy/agent-browser-runtime/pkgs/container/agent-browser-runtime)
|
|
8
|
+
[](docs/GITHUB_ACTION.md)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
**Website:** [praveen-palanisamy.github.io/agent-browser-runtime](https://praveen-palanisamy.github.io/agent-browser-runtime/) · **Source:** [github.com/praveen-palanisamy/agent-browser-runtime](https://github.com/praveen-palanisamy/agent-browser-runtime)
|
|
12
|
+
|
|
13
|
+
`agent-browser-runtime` is a small, self-hostable runtime for **stateful, secure browser-session execution**. You capture a user's authenticated session once (they sign in inside a live view; you never see a password), store it encrypted in *your* database, and from then on any scheduled or event-driven job can open a fresh isolated browser, inject that session, drive the site's real UI with verification, and hand back the rotated cookies — without the user's laptop being online and without a developer API.
|
|
14
|
+
|
|
15
|
+
It is the missing layer between "I have Playwright" and "I can run unattended, per-user, authenticated browser jobs in production":
|
|
16
|
+
|
|
17
|
+
| Problem | What ABR gives you |
|
|
18
|
+
|---------|----------------------------|
|
|
19
|
+
| Platforms without a (usable/affordable) API | Ability to drive the real web UI with the user's own session |
|
|
20
|
+
| Sessions live on the user's machine | Portable `storageState` you persist encrypted; runs on your servers |
|
|
21
|
+
| Cookies rotate, sessions expire | Every job returns the refreshed session; `needsReauth` is a first-class outcome |
|
|
22
|
+
| "Did the action actually happen?" | Strategies must verify; `uncertain` results are separated from failures so you never double-act |
|
|
23
|
+
| Browser infra is painful | One `SessionProvider` contract over local Chromium, any CDP endpoint, self-hosted [Steel](https://github.com/steel-dev/steel-browser), or [Kernel](https://www.kernel.sh) microVMs |
|
|
24
|
+
| Serverless can't run Chromium | An HTTP runner service + a Playwright-free client for functions, queues, and cron |
|
|
25
|
+
| Users need to sign in somewhere safe | Interactive capture with an embeddable, proxied live view and unguessable, expiring capture ids |
|
|
26
|
+
|
|
27
|
+
No browser engine is reinvented here: the runtime orchestrates proven engines (Playwright, Steel, Kernel) and owns the parts they leave to you — session lifecycle, verification contracts, pacing, isolation per job, and a stable wire protocol.
|
|
28
|
+
|
|
29
|
+
## Use cases
|
|
30
|
+
|
|
31
|
+
- **Autonomous assistants** that file expense reports, submit timesheets, update CRMs, or fetch statements from portals that have no API.
|
|
32
|
+
- **Back-office automation for SaaS** — act inside each customer's vendor, bank, marketplace, or government portal with that customer's own session, isolated and encrypted under your keys.
|
|
33
|
+
- **Agent frameworks / MCP tools** that need a durable "act on behalf of this user" primitive with re-auth signalling instead of silent failures.
|
|
34
|
+
- **Scheduled, user-authorized publishing** to systems that expose no automation surface — where the user has explicitly authorized each action and the platform's terms permit it; fall back to an official API when one exists and the session is gone.
|
|
35
|
+
- **Health probes** that tell users *before* a scheduled job fails that they need to sign in again.
|
|
36
|
+
|
|
37
|
+
ABR gives you the execution primitive; you remain responsible for using it only with the user's explicit authorization and within each target site's terms of service.
|
|
38
|
+
|
|
39
|
+
## How it fits together
|
|
40
|
+
|
|
41
|
+
```mermaid
|
|
42
|
+
flowchart LR
|
|
43
|
+
subgraph app [Your application]
|
|
44
|
+
Orch[Orchestrator<br/>cron · queue · serverless]
|
|
45
|
+
Vault[(Encrypted session store<br/>your database)]
|
|
46
|
+
Strat[WebPostStrategy implementations]
|
|
47
|
+
end
|
|
48
|
+
subgraph rt [agent-browser-runtime]
|
|
49
|
+
HTTP[HTTP runner<br/>/v1/post · /v1/probe · /v1/capture/*]
|
|
50
|
+
Core[AgentPoster · SessionCaptureManager]
|
|
51
|
+
Prov[SessionProvider<br/>local · cdp · steel · kernel]
|
|
52
|
+
end
|
|
53
|
+
Orch -->|client, bearer token,<br/>decrypted session in / refreshed out| HTTP
|
|
54
|
+
Vault <--> Orch
|
|
55
|
+
Strat --> Core
|
|
56
|
+
HTTP --> Core --> Prov
|
|
57
|
+
Prov --> Steel[Steel — self-hosted]
|
|
58
|
+
Prov --> Kernel[Kernel — microVMs]
|
|
59
|
+
Prov --> Local[Local Chromium]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The runtime is **stateless between requests**: sessions are never persisted by it, every job runs in a fresh browser context that is closed afterwards, and the only in-memory state is an in-flight interactive capture.
|
|
63
|
+
|
|
64
|
+
## Quick start
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm i @praveen-palanisamy/agent-browser-runtime
|
|
68
|
+
npx playwright-core install chromium # local provider
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 1. Describe a platform (`WebPostStrategy`)
|
|
72
|
+
|
|
73
|
+
Example: an AI finance assistant files expense reports in a corporate portal that has no API. The user signed in once; every month the assistant submits the report with receipts attached and only reports success when the portal shows a claim number.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import type { WebPostStrategy } from '@praveen-palanisamy/agent-browser-runtime';
|
|
77
|
+
|
|
78
|
+
export const expensePortal: WebPostStrategy = {
|
|
79
|
+
platform: 'expense-portal',
|
|
80
|
+
loginUrl: 'https://expenses.corp.example/login',
|
|
81
|
+
|
|
82
|
+
validate: (c) =>
|
|
83
|
+
!c.text ? 'Description required'
|
|
84
|
+
: (c.mediaUrls?.length ?? 0) === 0 ? 'At least one receipt required'
|
|
85
|
+
: null,
|
|
86
|
+
|
|
87
|
+
async isAuthenticated(ctx) {
|
|
88
|
+
const page = await ctx.newPage();
|
|
89
|
+
await page.goto('https://expenses.corp.example/');
|
|
90
|
+
return page.getByRole('button', { name: 'New claim' }).isVisible();
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
async post(ctx, content) {
|
|
94
|
+
const page = await ctx.newPage();
|
|
95
|
+
await page.goto('https://expenses.corp.example/claims/new');
|
|
96
|
+
await page.getByLabel('Description').fill(content.text);
|
|
97
|
+
for (const receipt of content.mediaUrls ?? []) {
|
|
98
|
+
await page.getByLabel('Receipt').setInputFiles(await download(receipt));
|
|
99
|
+
}
|
|
100
|
+
// Pre-submit assert: the form shows exactly what we intend to file.
|
|
101
|
+
if ((await page.getByLabel('Description').inputValue()) !== content.text) {
|
|
102
|
+
return { ok: false, failureStage: 'compose', error: 'Description mismatch' };
|
|
103
|
+
}
|
|
104
|
+
await page.getByRole('button', { name: 'Submit claim' }).click();
|
|
105
|
+
const claim = await page.getByTestId('claim-number').textContent({ timeout: 15_000 }).catch(() => null);
|
|
106
|
+
return claim
|
|
107
|
+
? { ok: true, platformPostId: claim, platformPostUrl: page.url(), verification: 'toast' }
|
|
108
|
+
: { ok: false, uncertain: true, failureStage: 'verify', error: 'No claim number shown' };
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 2. Run it as a service
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { startRunner } from '@praveen-palanisamy/agent-browser-runtime/service';
|
|
117
|
+
startRunner({ strategies: [expensePortal] });
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
or with the CLI and a module exporting `WebPostStrategy[]` (see [`examples/demo-strategy`](examples/demo-strategy/index.js)):
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
AGENT_RUNNER_TOKEN=$(openssl rand -base64 32) \
|
|
124
|
+
npx agent-browser-runtime serve --strategies ./strategies.js
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
No service at all? The CLI can run a single job in-process with local Chromium — the same path the [GitHub Action](docs/GITHUB_ACTION.md) uses:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx agent-browser-runtime post --local --strategies ./strategies.js \
|
|
131
|
+
--platform expense-portal --session ./session.json \
|
|
132
|
+
--text "March travel" --session-out ./session.json # refreshed cookies written back
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 3. Call it from anywhere (no Playwright needed)
|
|
136
|
+
|
|
137
|
+
The orchestrator can be a cron job, a queue worker, or a serverless function — it never loads Playwright.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { AgentRunnerClient } from '@praveen-palanisamy/agent-browser-runtime/client';
|
|
141
|
+
|
|
142
|
+
const runner = new AgentRunnerClient({ baseUrl, token });
|
|
143
|
+
|
|
144
|
+
// One-time: the employee signs in inside the live view you embed in your app.
|
|
145
|
+
const { captureId, liveViewUrl } = await runner.captureStart({ platform: 'expense-portal' });
|
|
146
|
+
// ... show liveViewUrl in an iframe; the user completes login (and MFA) ...
|
|
147
|
+
const done = await runner.captureFinish({ captureId });
|
|
148
|
+
if (done.ok) await vault.save(userId, encrypt(done.state)); // your encrypted store
|
|
149
|
+
|
|
150
|
+
// Monthly, unattended: file the report the assistant prepared.
|
|
151
|
+
const res = await runner.post({
|
|
152
|
+
workspaceId: orgId, accountId: userId, platform: 'expense-portal', jobId,
|
|
153
|
+
session: decrypt(await vault.load(userId)),
|
|
154
|
+
content: {
|
|
155
|
+
text: 'March travel — client onsite, 3 nights',
|
|
156
|
+
mediaUrls: receipts.map((r) => r.signedUrl),
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (res.refreshedSession) await vault.save(userId, encrypt(res.refreshedSession)); // rotated cookies
|
|
161
|
+
if (res.ok) ledger.record({ claim: res.platformPostId, url: res.platformPostUrl });
|
|
162
|
+
if (res.needsReauth) notify(userId, 'Please sign in to the expense portal again');
|
|
163
|
+
if (res.uncertain) queueManualReview(jobId); // submitted but unconfirmed — never retry blindly
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Library-only (no HTTP)
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { AgentPoster, SteelSessionProvider } from '@praveen-palanisamy/agent-browser-runtime';
|
|
170
|
+
|
|
171
|
+
const poster = new AgentPoster(
|
|
172
|
+
new SteelSessionProvider({ baseUrl: 'http://steel:3000' }),
|
|
173
|
+
myEncryptedStore
|
|
174
|
+
).register(expensePortal);
|
|
175
|
+
await poster.post({ workspaceId, accountId, platform: 'expense-portal', content: { text, mediaUrls } });
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Contracts
|
|
179
|
+
|
|
180
|
+
| Contract | Responsibility | Provided implementations |
|
|
181
|
+
|----------|----------------|--------------------------|
|
|
182
|
+
| `SessionStore` | Persist/load `AgentSessionState` (Playwright `storageState` + UA + timestamps) | `InMemorySessionStore`; you implement the encrypted one |
|
|
183
|
+
| `SessionProvider` | Turn a stored state into a live `BrowserContext` | `LocalBrowserSessionProvider`, `CdpSessionProvider`, `SteelSessionProvider`, `KernelSessionProvider` |
|
|
184
|
+
| `WebPostStrategy` | Drive one platform: `validate`, `isAuthenticated`, `post` | yours (`examples/demo-strategy`) |
|
|
185
|
+
|
|
186
|
+
`AgentPostResult` is deliberately rich: `ok`, `platformPostId/Url`, `verification: 'toast' | 'timeline'`, `needsReauth`, `uncertain`, `failureStage: 'precheck' | 'auth' | 'compose' | 'media' | 'submit' | 'verify'`, and an optional redacted `screenshotBase64` for your private artifact storage.
|
|
187
|
+
|
|
188
|
+
## Runner service
|
|
189
|
+
|
|
190
|
+
| Route | Purpose |
|
|
191
|
+
|-------|---------|
|
|
192
|
+
| `POST /v1/post` | Publish with an injected session → result + `refreshedSession` |
|
|
193
|
+
| `POST /v1/probe` | Cheap auth check; refreshes cookies |
|
|
194
|
+
| `POST /v1/capture/start` | Open an interactive browser → `captureId`, `liveViewUrl`, `expiresAt` |
|
|
195
|
+
| `POST /v1/capture/finish` · `/cancel` | Export the session / discard |
|
|
196
|
+
| `GET /live/{captureId}/…` | Proxied Steel live view (HTTP + WebSocket, iframe-safe) |
|
|
197
|
+
| `GET /healthz` | Liveness |
|
|
198
|
+
|
|
199
|
+
All `/v1/*` routes require `Authorization: Bearer $AGENT_RUNNER_TOKEN`. Live-view paths are guarded by the unguessable capture id and TTL.
|
|
200
|
+
|
|
201
|
+
Configuration is environment-driven ([`.env.example`](.env.example)). Job and capture providers are independent: run headless jobs in parallel on local Chromium while captures use Steel's live view, or route everything to Kernel for burst capacity. Steel OSS serves one session at a time; `SerializedProvider` queues instead of failing.
|
|
202
|
+
|
|
203
|
+
## Deploy
|
|
204
|
+
|
|
205
|
+
Pick the channel that fits your stack — all three are published from every release:
|
|
206
|
+
|
|
207
|
+
| Channel | Use it when |
|
|
208
|
+
|---------|-------------|
|
|
209
|
+
| **npm** `@praveen-palanisamy/agent-browser-runtime` | you embed the library or the Playwright-free client in your app |
|
|
210
|
+
| **GHCR** `ghcr.io/praveen-palanisamy/agent-browser-runtime` | you run the HTTP runner service (Chromium included) |
|
|
211
|
+
| **GitHub Action** `praveen-palanisamy/agent-browser-runtime@v0` | you want session probes or jobs straight from CI — see [`docs/GITHUB_ACTION.md`](docs/GITHUB_ACTION.md) |
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
- uses: praveen-palanisamy/agent-browser-runtime@v0
|
|
215
|
+
with:
|
|
216
|
+
command: probe
|
|
217
|
+
platform: expense-portal
|
|
218
|
+
strategies: ./automation/strategies.js
|
|
219
|
+
session: ${{ secrets.EXPENSE_PORTAL_SESSION }}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- **Local**: `docker compose up` (Steel + runtime; mount strategies via `STRATEGIES_DIR`).
|
|
223
|
+
- **Container**: `ghcr.io/praveen-palanisamy/agent-browser-runtime` — extend it with your strategies module (see [`Dockerfile`](Dockerfile)). The Playwright base image and `playwright-core` are pinned to the same version so no browser downloads happen at runtime.
|
|
224
|
+
- **Cloud Run / ECS / Nomad**: run the runtime with Steel as a sidecar; pin to one instance per active interactive capture (captures are stateful), scale headless jobs freely. See [`docs/DEPLOY.md`](docs/DEPLOY.md).
|
|
225
|
+
|
|
226
|
+
## Security posture
|
|
227
|
+
|
|
228
|
+
- The runtime never persists sessions; your encrypted store is the source of truth. Steel/Kernel profiles are disposable caches.
|
|
229
|
+
- Fresh browser context per job, disposed after use; no cross-tenant state.
|
|
230
|
+
- Bearer-token auth on every API route; live view requires an active capture id and expires.
|
|
231
|
+
- Failure screenshots are returned to the caller for private storage — never served to end users by the runtime.
|
|
232
|
+
- Treat `AgentSessionState` with password-equivalent sensitivity. See [`SECURITY.md`](SECURITY.md).
|
|
233
|
+
|
|
234
|
+
## Documentation
|
|
235
|
+
|
|
236
|
+
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — contracts, lifecycle, verification model
|
|
237
|
+
- [`docs/DEPLOY.md`](docs/DEPLOY.md) — providers, environment, Cloud Run / compose patterns, distribution channels
|
|
238
|
+
- [`docs/GITHUB_ACTION.md`](docs/GITHUB_ACTION.md) — run probes and jobs from GitHub Actions
|
|
239
|
+
- [`docs/AGENTS.md`](docs/AGENTS.md) — notes for coding agents and LLM tool builders
|
|
240
|
+
- [`CHANGELOG.md`](CHANGELOG.md) · [`CONTRIBUTING.md`](CONTRIBUTING.md)
|
|
241
|
+
|
|
242
|
+
## Development
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npm install
|
|
246
|
+
npm run ci:check # lint + typecheck + unit tests + pin check (what CI runs)
|
|
247
|
+
npm run test:browser # real Chromium smoke (npm run browsers:install first)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## License & attribution
|
|
251
|
+
|
|
252
|
+
MIT © [Praveen Palanisamy](https://github.com/praveen-palanisamy). If ABR is useful to you, keep the link back to the [source repository](https://github.com/praveen-palanisamy/agent-browser-runtime) in your forks and derived work, and cite it as:
|
|
253
|
+
|
|
254
|
+
```bibtex
|
|
255
|
+
@software{palanisamy_agent_browser_runtime,
|
|
256
|
+
author = {Palanisamy, Praveen},
|
|
257
|
+
title = {Agent Browser Runtime (ABR): stateful, secure browser-session execution for agents},
|
|
258
|
+
url = {https://github.com/praveen-palanisamy/agent-browser-runtime},
|
|
259
|
+
license = {MIT}
|
|
260
|
+
}
|
|
261
|
+
```
|
package/action.yml
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
name: "Agent Browser Runtime (ABR)"
|
|
2
|
+
description: >-
|
|
3
|
+
Run authenticated browser jobs as the user from CI: probe a stored session or
|
|
4
|
+
execute a WebPostStrategy with it. Self-hosted.
|
|
5
|
+
author: "Praveen Palanisamy"
|
|
6
|
+
branding:
|
|
7
|
+
icon: "globe"
|
|
8
|
+
color: "purple"
|
|
9
|
+
|
|
10
|
+
inputs:
|
|
11
|
+
command:
|
|
12
|
+
description: "`probe` (is the session still authenticated?) or `post` (run a job)"
|
|
13
|
+
required: false
|
|
14
|
+
default: "probe"
|
|
15
|
+
platform:
|
|
16
|
+
description: "Platform id handled by one of the strategies in `strategies`"
|
|
17
|
+
required: true
|
|
18
|
+
session:
|
|
19
|
+
description: >-
|
|
20
|
+
Serialized AgentSessionState JSON. Store it as a repository secret and
|
|
21
|
+
pass the secret here; alternatively set `session-file`.
|
|
22
|
+
required: false
|
|
23
|
+
default: ""
|
|
24
|
+
session-file:
|
|
25
|
+
description: "Path to a file containing AgentSessionState JSON (alternative to `session`)"
|
|
26
|
+
required: false
|
|
27
|
+
default: ""
|
|
28
|
+
strategies:
|
|
29
|
+
description: >-
|
|
30
|
+
Path (relative to the workspace) of a CommonJS/ESM module exporting
|
|
31
|
+
WebPostStrategy[]. Defaults to the bundled example strategy.
|
|
32
|
+
required: false
|
|
33
|
+
default: ""
|
|
34
|
+
text:
|
|
35
|
+
description: "Post text (`post` only)"
|
|
36
|
+
required: false
|
|
37
|
+
default: ""
|
|
38
|
+
media:
|
|
39
|
+
description: "Comma-separated media URLs (`post` only)"
|
|
40
|
+
required: false
|
|
41
|
+
default: ""
|
|
42
|
+
link:
|
|
43
|
+
description: "Link to attach (`post` only)"
|
|
44
|
+
required: false
|
|
45
|
+
default: ""
|
|
46
|
+
content-file:
|
|
47
|
+
description: "Path to an AgentPostContent JSON file (overrides text/media/link)"
|
|
48
|
+
required: false
|
|
49
|
+
default: ""
|
|
50
|
+
workspace:
|
|
51
|
+
description: "Workspace id used for the job identity"
|
|
52
|
+
required: false
|
|
53
|
+
default: "github-actions"
|
|
54
|
+
account:
|
|
55
|
+
description: "Account id used for the job identity"
|
|
56
|
+
required: false
|
|
57
|
+
default: "default"
|
|
58
|
+
session-out:
|
|
59
|
+
description: "Where to write the refreshed session state (persist it back to your vault)"
|
|
60
|
+
required: false
|
|
61
|
+
default: ".abr/session.refreshed.json"
|
|
62
|
+
result-out:
|
|
63
|
+
description: "Where to write the JSON result"
|
|
64
|
+
required: false
|
|
65
|
+
default: ".abr/result.json"
|
|
66
|
+
fail-on-reauth:
|
|
67
|
+
description: "Fail the step when the session needs re-authentication"
|
|
68
|
+
required: false
|
|
69
|
+
default: "true"
|
|
70
|
+
pacing:
|
|
71
|
+
description: "Apply human-pace jitter before acting (`true`/`false`)"
|
|
72
|
+
required: false
|
|
73
|
+
default: "true"
|
|
74
|
+
node-version:
|
|
75
|
+
description: "Node.js version to run the runtime with"
|
|
76
|
+
required: false
|
|
77
|
+
default: "24"
|
|
78
|
+
|
|
79
|
+
outputs:
|
|
80
|
+
result-file:
|
|
81
|
+
description: "Path of the JSON result"
|
|
82
|
+
value: ${{ steps.run.outputs.result-file }}
|
|
83
|
+
session-file:
|
|
84
|
+
description: "Path of the refreshed session (may not exist if nothing changed)"
|
|
85
|
+
value: ${{ steps.run.outputs.session-file }}
|
|
86
|
+
ok:
|
|
87
|
+
description: "`true` when a `post` job verified success"
|
|
88
|
+
value: ${{ steps.run.outputs.ok }}
|
|
89
|
+
authenticated:
|
|
90
|
+
description: "`true` when the session is still signed in (`probe`, or auth stage of `post`)"
|
|
91
|
+
value: ${{ steps.run.outputs.authenticated }}
|
|
92
|
+
needs-reauth:
|
|
93
|
+
description: "`true` when the user must sign in again"
|
|
94
|
+
value: ${{ steps.run.outputs.needs-reauth }}
|
|
95
|
+
uncertain:
|
|
96
|
+
description: "`true` when the action ran but could not be verified — never retry blindly"
|
|
97
|
+
value: ${{ steps.run.outputs.uncertain }}
|
|
98
|
+
|
|
99
|
+
runs:
|
|
100
|
+
using: "composite"
|
|
101
|
+
steps:
|
|
102
|
+
- name: Setup Node.js
|
|
103
|
+
uses: actions/setup-node@v7
|
|
104
|
+
with:
|
|
105
|
+
node-version: ${{ inputs.node-version }}
|
|
106
|
+
|
|
107
|
+
- name: Install Agent Browser Runtime (from this action's ref)
|
|
108
|
+
shell: bash
|
|
109
|
+
run: |
|
|
110
|
+
set -euo pipefail
|
|
111
|
+
cd "${GITHUB_ACTION_PATH}"
|
|
112
|
+
npm ci --no-audit --no-fund --ignore-scripts
|
|
113
|
+
npm run build
|
|
114
|
+
npx playwright-core install --with-deps chromium
|
|
115
|
+
|
|
116
|
+
- name: Run ABR job
|
|
117
|
+
id: run
|
|
118
|
+
shell: bash
|
|
119
|
+
env:
|
|
120
|
+
ABR_SESSION_JSON: ${{ inputs.session }}
|
|
121
|
+
AGENT_RUNNER_PACING: ${{ inputs.pacing }}
|
|
122
|
+
run: |
|
|
123
|
+
set -euo pipefail
|
|
124
|
+
ABR="node ${GITHUB_ACTION_PATH}/dist/cli.js"
|
|
125
|
+
mkdir -p "$(dirname "${{ inputs.session-out }}")" "$(dirname "${{ inputs.result-out }}")"
|
|
126
|
+
|
|
127
|
+
if [ -n "${{ inputs.session-file }}" ]; then
|
|
128
|
+
SESSION_FILE="${{ inputs.session-file }}"
|
|
129
|
+
elif [ -n "$ABR_SESSION_JSON" ]; then
|
|
130
|
+
SESSION_FILE="$(mktemp)"
|
|
131
|
+
printf '%s' "$ABR_SESSION_JSON" > "$SESSION_FILE"
|
|
132
|
+
else
|
|
133
|
+
echo "::error::Provide either 'session' or 'session-file'"; exit 1
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
STRATEGIES="${{ inputs.strategies }}"
|
|
137
|
+
if [ -z "$STRATEGIES" ]; then
|
|
138
|
+
STRATEGIES="${GITHUB_ACTION_PATH}/examples/demo-strategy/index.js"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
CONTENT_FLAGS=()
|
|
142
|
+
if [ "${{ inputs.command }}" = "post" ]; then
|
|
143
|
+
if [ -n "${{ inputs.content-file }}" ]; then
|
|
144
|
+
CONTENT_FLAGS=(--content "${{ inputs.content-file }}")
|
|
145
|
+
else
|
|
146
|
+
CONTENT_FLAGS=(--text "${{ inputs.text }}")
|
|
147
|
+
[ -n "${{ inputs.media }}" ] && CONTENT_FLAGS+=(--media "${{ inputs.media }}")
|
|
148
|
+
[ -n "${{ inputs.link }}" ] && CONTENT_FLAGS+=(--link "${{ inputs.link }}")
|
|
149
|
+
fi
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
set +e
|
|
153
|
+
$ABR "${{ inputs.command }}" --local \
|
|
154
|
+
--strategies "$STRATEGIES" \
|
|
155
|
+
--platform "${{ inputs.platform }}" \
|
|
156
|
+
--session "$SESSION_FILE" \
|
|
157
|
+
--workspace "${{ inputs.workspace }}" --account "${{ inputs.account }}" \
|
|
158
|
+
--session-out "${{ inputs.session-out }}" \
|
|
159
|
+
--out "${{ inputs.result-out }}" \
|
|
160
|
+
"${CONTENT_FLAGS[@]}"
|
|
161
|
+
CODE=$?
|
|
162
|
+
set -e
|
|
163
|
+
|
|
164
|
+
[ -n "$ABR_SESSION_JSON" ] && rm -f "$SESSION_FILE"
|
|
165
|
+
|
|
166
|
+
RESULT="${{ inputs.result-out }}"
|
|
167
|
+
echo "result-file=$RESULT" >> "$GITHUB_OUTPUT"
|
|
168
|
+
echo "session-file=${{ inputs.session-out }}" >> "$GITHUB_OUTPUT"
|
|
169
|
+
if [ -f "$RESULT" ]; then
|
|
170
|
+
node -e '
|
|
171
|
+
const r = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
|
|
172
|
+
const out = [
|
|
173
|
+
["ok", r.ok === true],
|
|
174
|
+
["authenticated", r.authenticated === true || (r.ok === true) || (r.ok === false && r.failureStage && r.failureStage !== "auth" && r.failureStage !== "precheck")],
|
|
175
|
+
["needs-reauth", r.needsReauth === true || (r.authenticated === false)],
|
|
176
|
+
["uncertain", r.uncertain === true],
|
|
177
|
+
];
|
|
178
|
+
require("fs").appendFileSync(process.env.GITHUB_OUTPUT, out.map(([k, v]) => `${k}=${v}\n`).join(""));
|
|
179
|
+
' "$RESULT"
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
if [ "$CODE" -eq 2 ] && [ "${{ inputs.fail-on-reauth }}" != "true" ] && grep -q '"needsReauth": true\|"authenticated": false' "$RESULT" 2>/dev/null; then
|
|
183
|
+
echo "::warning::Session needs re-authentication (fail-on-reauth=false)"; exit 0
|
|
184
|
+
fi
|
|
185
|
+
exit $CODE
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project identity. Surfaced in the CLI banner, the runner's `Server` header
|
|
3
|
+
* and `/healthz` so deployments (and forks) always point back to the source.
|
|
4
|
+
*/
|
|
5
|
+
export declare const PROJECT: {
|
|
6
|
+
readonly name: 'agent-browser-runtime';
|
|
7
|
+
readonly shortName: 'ABR';
|
|
8
|
+
readonly npm: '@praveen-palanisamy/agent-browser-runtime';
|
|
9
|
+
readonly author: 'Praveen Palanisamy';
|
|
10
|
+
readonly repository: 'https://github.com/praveen-palanisamy/agent-browser-runtime';
|
|
11
|
+
readonly homepage: 'https://praveen-palanisamy.github.io/agent-browser-runtime/';
|
|
12
|
+
readonly image: 'ghcr.io/praveen-palanisamy/agent-browser-runtime';
|
|
13
|
+
readonly license: 'MIT';
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Version from the nearest package.json (works from src/ and dist/, and from
|
|
17
|
+
* vendored/forked copies where the package name differs).
|
|
18
|
+
*/
|
|
19
|
+
export declare function packageVersion(): string;
|
|
20
|
+
/** e.g. `agent-browser-runtime/0.1.0 (+https://github.com/...)` */
|
|
21
|
+
export declare function userAgentString(): string;
|
|
22
|
+
//# sourceMappingURL=attribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,eAAO,MAAM,OAAO;aAClB,IAAI,EAAE,uBAAuB;aAC7B,SAAS,EAAE,KAAK;aAChB,GAAG,EAAE,2CAA2C;aAChD,MAAM,EAAE,oBAAoB;aAC5B,UAAU,EAAE,6DAA6D;aACzE,QAAQ,EAAE,6DAA6D;aACvE,KAAK,EAAE,kDAAkD;aACzD,OAAO,EAAE,KAAK;CACN,CAAC;AAIX;;;GAGG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAmBvC;AAED,mEAAmE;AACnE,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Project identity. Surfaced in the CLI banner, the runner's `Server` header
|
|
4
|
+
* and `/healthz` so deployments (and forks) always point back to the source.
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.PROJECT = void 0;
|
|
11
|
+
exports.packageVersion = packageVersion;
|
|
12
|
+
exports.userAgentString = userAgentString;
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
exports.PROJECT = {
|
|
16
|
+
name: 'agent-browser-runtime',
|
|
17
|
+
shortName: 'ABR',
|
|
18
|
+
npm: '@praveen-palanisamy/agent-browser-runtime',
|
|
19
|
+
author: 'Praveen Palanisamy',
|
|
20
|
+
repository: 'https://github.com/praveen-palanisamy/agent-browser-runtime',
|
|
21
|
+
homepage: 'https://praveen-palanisamy.github.io/agent-browser-runtime/',
|
|
22
|
+
image: 'ghcr.io/praveen-palanisamy/agent-browser-runtime',
|
|
23
|
+
license: 'MIT',
|
|
24
|
+
};
|
|
25
|
+
let cachedVersion;
|
|
26
|
+
/**
|
|
27
|
+
* Version from the nearest package.json (works from src/ and dist/, and from
|
|
28
|
+
* vendored/forked copies where the package name differs).
|
|
29
|
+
*/
|
|
30
|
+
function packageVersion() {
|
|
31
|
+
if (cachedVersion)
|
|
32
|
+
return cachedVersion;
|
|
33
|
+
let fallback;
|
|
34
|
+
for (const rel of ['../package.json', '../../package.json']) {
|
|
35
|
+
try {
|
|
36
|
+
const raw = (0, node_fs_1.readFileSync)(node_path_1.default.join(__dirname, rel), 'utf8');
|
|
37
|
+
const parsed = JSON.parse(raw);
|
|
38
|
+
if (!parsed.version)
|
|
39
|
+
continue;
|
|
40
|
+
if (parsed.name === exports.PROJECT.npm) {
|
|
41
|
+
cachedVersion = parsed.version;
|
|
42
|
+
return cachedVersion;
|
|
43
|
+
}
|
|
44
|
+
fallback ??= parsed.version;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// try next candidate
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
cachedVersion = fallback ?? '0.0.0';
|
|
51
|
+
return cachedVersion;
|
|
52
|
+
}
|
|
53
|
+
/** e.g. `agent-browser-runtime/0.1.0 (+https://github.com/...)` */
|
|
54
|
+
function userAgentString() {
|
|
55
|
+
return `${exports.PROJECT.name}/${packageVersion()} (+${exports.PROJECT.repository})`;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=attribution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.js","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;AAEH,qCAAuC;AACvC,0DAA6B;AAEhB,QAAA,OAAO,GAAG;IACrB,IAAI,EAAE,uBAAuB;IAC7B,SAAS,EAAE,KAAK;IAChB,GAAG,EAAE,2CAA2C;IAChD,MAAM,EAAE,oBAAoB;IAC5B,UAAU,EAAE,6DAA6D;IACzE,QAAQ,EAAE,6DAA6D;IACvE,KAAK,EAAE,kDAAkD;IACzD,OAAO,EAAE,KAAK;CACN,CAAC;AAEX,IAAI,aAAiC,CAAC;AAEtC;;;GAGG;AACH;IACE,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IACxC,IAAI,QAA4B,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAA,sBAAY,EAAC,mBAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;YAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAwC,CAAC;YACtE,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,SAAS;YAC9B,IAAI,MAAM,CAAC,IAAI,KAAK,QAAA,OAAO,CAAC,GAAG,EAAE,CAAC;gBAChC,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC;gBAC/B,OAAO,aAAa,CAAC;YACvB,CAAC;YACD,QAAQ,KAAK,MAAM,CAAC,OAAO,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IACD,aAAa,GAAG,QAAQ,IAAI,OAAO,CAAC;IACpC,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,mEAAmE;AACnE;IACE,OAAO,GAAG,QAAA,OAAO,CAAC,IAAI,IAAI,cAAc,EAAE,MAAM,QAAA,OAAO,CAAC,UAAU,GAAG,CAAC;AACxE,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* agent-browser-runtime (ABR) CLI
|
|
4
|
+
*
|
|
5
|
+
* agent-browser-runtime serve --strategies ./dist/strategies.js
|
|
6
|
+
* agent-browser-runtime health [--url http://localhost:8080]
|
|
7
|
+
* agent-browser-runtime probe --platform <id> --session ./session.json [--url … --token … | --local --strategies …]
|
|
8
|
+
* agent-browser-runtime post --platform <id> --session ./session.json --text "…" [--media url,…] [--link url]
|
|
9
|
+
* [--url … --token … | --local --strategies …] [--session-out file] [--out file]
|
|
10
|
+
*
|
|
11
|
+
* `serve` loads a CommonJS/ESM module whose default export (or `strategies`
|
|
12
|
+
* export) is an array of WebPostStrategy instances, then starts the runner
|
|
13
|
+
* using environment configuration (see service/config.ts).
|
|
14
|
+
*
|
|
15
|
+
* `probe` / `post` talk to a running runner by default. With `--local` they
|
|
16
|
+
* run in-process against local Chromium instead (used by the GitHub Action
|
|
17
|
+
* and for one-off jobs), which requires `--strategies`.
|
|
18
|
+
*/
|
|
19
|
+
import type { WebPostStrategy } from './core/types';
|
|
20
|
+
export declare function loadStrategies(spec: string): Promise<WebPostStrategy[]>;
|
|
21
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AASH,OAAO,KAAK,EAIV,eAAe,EAChB,MAAM,cAAc,CAAC;AA8BtB,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAiB7E"}
|