@mind-harness/mh-browser 0.1.4 → 0.1.5
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/extension/manifest.json
CHANGED
package/lib/index.js
CHANGED
|
@@ -2964,7 +2964,7 @@ async function launchBrowser(options) {
|
|
|
2964
2964
|
acceptDownloads: true,
|
|
2965
2965
|
...options.headless ? {} : {
|
|
2966
2966
|
ignoreDefaultArgs: ["--disable-extensions"],
|
|
2967
|
-
args: ["--enable-unsafe-extension-debugging"]
|
|
2967
|
+
args: ["--enable-unsafe-extension-debugging", "--keep-alive-for-test"]
|
|
2968
2968
|
},
|
|
2969
2969
|
...options.browser === "chromium" ? {} : { channel: options.browser },
|
|
2970
2970
|
...options.executablePath !== void 0 ? { executablePath: options.executablePath } : {}
|
|
@@ -4297,8 +4297,9 @@ var Windows = class {
|
|
|
4297
4297
|
if (browser === null) throw new Error("persistent context has no browser");
|
|
4298
4298
|
this.browserSession = await browser.newBrowserCDPSession();
|
|
4299
4299
|
if (options.headed) this.extension = await Extension.load(context, this.browserSession);
|
|
4300
|
-
|
|
4300
|
+
const launched = context.pages().filter((page) => page.url() === "about:blank");
|
|
4301
4301
|
context.on("page", (page) => {
|
|
4302
|
+
for (const blank of launched.splice(0)) blank.close().catch(() => void 0);
|
|
4302
4303
|
this.ingest(page);
|
|
4303
4304
|
});
|
|
4304
4305
|
}
|
|
@@ -4995,7 +4996,7 @@ var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializ
|
|
|
4995
4996
|
const NAVIGATION_MS = 1e4;
|
|
4996
4997
|
/** How long an open that started a download waits for the download event. */
|
|
4997
4998
|
const DOWNLOAD_EVENT_MS = 3e3;
|
|
4998
|
-
/** More
|
|
4999
|
+
/** More exits than this within the window and the browser is not started again until one ages out. */
|
|
4999
5000
|
const RELAUNCH_LIMIT = 3;
|
|
5000
5001
|
const RELAUNCH_WINDOW_MS = 6e4;
|
|
5001
5002
|
const STOP_WAIT_MS = 5e3;
|
|
@@ -5043,8 +5044,9 @@ let MhBrowserService = (() => {
|
|
|
5043
5044
|
context;
|
|
5044
5045
|
ready;
|
|
5045
5046
|
closing = false;
|
|
5046
|
-
|
|
5047
|
-
|
|
5047
|
+
/** The browser exited. The next call that needs a page starts it again. */
|
|
5048
|
+
exited = false;
|
|
5049
|
+
exits = [];
|
|
5048
5050
|
/** One queue per owner: a slow wait in one Session does not hold up another. */
|
|
5049
5051
|
queues = /* @__PURE__ */ new Map();
|
|
5050
5052
|
shown = /* @__PURE__ */ new Map();
|
|
@@ -5099,7 +5101,7 @@ let MhBrowserService = (() => {
|
|
|
5099
5101
|
}
|
|
5100
5102
|
async open(owner, items, signal) {
|
|
5101
5103
|
return this.exclusive(owner, async () => {
|
|
5102
|
-
await this.
|
|
5104
|
+
await this.up(owner === PERSON_OWNER);
|
|
5103
5105
|
const views = [];
|
|
5104
5106
|
for (const item of items) {
|
|
5105
5107
|
if (signal?.aborted) {
|
|
@@ -5159,7 +5161,7 @@ let MhBrowserService = (() => {
|
|
|
5159
5161
|
async presenceCheck(owner, which, opts) {
|
|
5160
5162
|
if (this.book.entries(owner).length === 0 && this.book.peekRemovals(owner).length === 0) return;
|
|
5161
5163
|
return this.exclusive(owner, async () => {
|
|
5162
|
-
const down = await this.
|
|
5164
|
+
const down = await this.up().then(() => void 0, (error) => error instanceof Error ? error.message : String(error));
|
|
5163
5165
|
const pages = this.book.entries(owner);
|
|
5164
5166
|
const removals = this.book.peekRemovals(owner);
|
|
5165
5167
|
const wanted = new Set(which === "all" ? pages.map((page) => page.url) : which.map(canonical));
|
|
@@ -5215,7 +5217,7 @@ let MhBrowserService = (() => {
|
|
|
5215
5217
|
}
|
|
5216
5218
|
async fetch(url, opts) {
|
|
5217
5219
|
try {
|
|
5218
|
-
await this.
|
|
5220
|
+
await this.up();
|
|
5219
5221
|
const context = this.context;
|
|
5220
5222
|
if (context === void 0) return {
|
|
5221
5223
|
kind: "error",
|
|
@@ -5261,14 +5263,18 @@ let MhBrowserService = (() => {
|
|
|
5261
5263
|
async started() {
|
|
5262
5264
|
await this.ready;
|
|
5263
5265
|
}
|
|
5266
|
+
/** The person asked for the browser: start it if it exited, past the exit limit. */
|
|
5267
|
+
startForPerson() {
|
|
5268
|
+
return this.up(true);
|
|
5269
|
+
}
|
|
5264
5270
|
[testHooks]() {
|
|
5265
5271
|
return {
|
|
5266
5272
|
crash: async () => {
|
|
5267
5273
|
this.windows.suppressPersonInput();
|
|
5268
5274
|
await (this.context?.browser())?.close();
|
|
5269
|
-
await this.relaunching;
|
|
5270
5275
|
},
|
|
5271
|
-
page: (owner, tab) => this.windows.get(owner, tab)?.page
|
|
5276
|
+
page: (owner, tab) => this.windows.get(owner, tab)?.page,
|
|
5277
|
+
running: () => this.context !== void 0
|
|
5272
5278
|
};
|
|
5273
5279
|
}
|
|
5274
5280
|
async act(owner, tab, op, signal) {
|
|
@@ -5356,7 +5362,7 @@ let MhBrowserService = (() => {
|
|
|
5356
5362
|
}
|
|
5357
5363
|
invoke(owner, _page, call, opts) {
|
|
5358
5364
|
return this.exclusive(owner, async () => {
|
|
5359
|
-
await this.
|
|
5365
|
+
await this.up();
|
|
5360
5366
|
if (opts?.signal?.aborted) throw new MhBrowserError(INTERRUPTED, WAITING_STOPPED);
|
|
5361
5367
|
const signal = opts?.signal;
|
|
5362
5368
|
const sent = await runInvoke({
|
|
@@ -5376,7 +5382,7 @@ let MhBrowserService = (() => {
|
|
|
5376
5382
|
* Session's other calls. `root` is the directory the session may write.
|
|
5377
5383
|
*/
|
|
5378
5384
|
async download(_owner, url, root, signal) {
|
|
5379
|
-
await this.
|
|
5385
|
+
await this.up();
|
|
5380
5386
|
return this.saveResponse(url, root, signal);
|
|
5381
5387
|
}
|
|
5382
5388
|
controlOf(owner) {
|
|
@@ -5395,7 +5401,7 @@ let MhBrowserService = (() => {
|
|
|
5395
5401
|
*/
|
|
5396
5402
|
handover(owner, req, signal) {
|
|
5397
5403
|
return this.exclusive(owner, async () => {
|
|
5398
|
-
await this.
|
|
5404
|
+
await this.up();
|
|
5399
5405
|
if (signal?.aborted === true) throw new MhBrowserError(INTERRUPTED, WAITING_STOPPED);
|
|
5400
5406
|
if (req.tab !== void 0) {
|
|
5401
5407
|
const live = this.windows.get(owner, req.tab);
|
|
@@ -6320,27 +6326,39 @@ let MhBrowserService = (() => {
|
|
|
6320
6326
|
this.context = context;
|
|
6321
6327
|
await this.windows.attach(context, this.launchOptions.profileDir, { headed: !this.launchOptions.headless });
|
|
6322
6328
|
context.browser()?.on("disconnected", () => {
|
|
6323
|
-
if (this.closing) return;
|
|
6324
|
-
this.
|
|
6329
|
+
if (this.closing || this.context !== context) return;
|
|
6330
|
+
this.onExit();
|
|
6325
6331
|
});
|
|
6326
6332
|
}
|
|
6327
|
-
|
|
6333
|
+
/** No window opens on exit: the person may have quit it. Calls on old tabs find none. */
|
|
6334
|
+
onExit() {
|
|
6328
6335
|
this.windows.suppressPersonInput();
|
|
6329
6336
|
this.windows.dropPages();
|
|
6330
6337
|
this.context = void 0;
|
|
6338
|
+
this.exits.push(Date.now());
|
|
6339
|
+
this.exited = true;
|
|
6340
|
+
this.ready = Promise.resolve();
|
|
6341
|
+
}
|
|
6342
|
+
/**
|
|
6343
|
+
* The browser for a call that needs a page. One that exited starts again
|
|
6344
|
+
* here. The exit limit stops unattended restarts; a person's request is
|
|
6345
|
+
* one start per click and skips it.
|
|
6346
|
+
*/
|
|
6347
|
+
up(byPerson = false) {
|
|
6348
|
+
if (!this.exited || this.closing) return this.ready;
|
|
6331
6349
|
const now = Date.now();
|
|
6332
|
-
while (this.
|
|
6333
|
-
this.
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
down.catch(() => void 0);
|
|
6337
|
-
this.ready = down;
|
|
6338
|
-
this.ctx.logger.warn("browser keeps exiting; not restarting it");
|
|
6339
|
-
return;
|
|
6350
|
+
while (this.exits.length > 0 && now - (this.exits[0] ?? now) > RELAUNCH_WINDOW_MS) this.exits.shift();
|
|
6351
|
+
if (!byPerson && this.exits.length > RELAUNCH_LIMIT) {
|
|
6352
|
+
this.ctx.logger.warn("browser keeps exiting; not starting it again yet");
|
|
6353
|
+
return Promise.reject(new MhBrowserError(BROWSER_UNAVAILABLE, `the browser exited ${String(this.exits.length)} times within ${String(RELAUNCH_WINDOW_MS / 1e3)} s; try again in a minute`));
|
|
6340
6354
|
}
|
|
6341
|
-
this.
|
|
6342
|
-
|
|
6343
|
-
this.windows.resumePersonInput()
|
|
6355
|
+
this.exited = false;
|
|
6356
|
+
const started = this.launch();
|
|
6357
|
+
started.then(() => this.windows.resumePersonInput(), () => {
|
|
6358
|
+
this.exited = true;
|
|
6359
|
+
});
|
|
6360
|
+
this.ready = started;
|
|
6361
|
+
return started;
|
|
6344
6362
|
}
|
|
6345
6363
|
async stop() {
|
|
6346
6364
|
this.windows.suppressPersonInput();
|
package/lib/typert.host.js
CHANGED
|
@@ -44,7 +44,7 @@ const $remote = createTypertRemoteInfo({
|
|
|
44
44
|
create: _mind_harness_mh_browser_mhBrowser_revealHandover_result$schema,
|
|
45
45
|
jsonSchema: {"type":"object","properties":{"status":{"type":"string","enum":["revealed","not-handing-over","no-tab"]}},"required":["status"],"additionalProperties":true},
|
|
46
46
|
},
|
|
47
|
-
sourceLocation: {"file":"packages/mh-browser/src/service/service.ts","line":
|
|
47
|
+
sourceLocation: {"file":"packages/mh-browser/src/service/service.ts","line":616,"column":9},
|
|
48
48
|
},
|
|
49
49
|
},
|
|
50
50
|
}, z)
|
|
@@ -160,6 +160,13 @@ export const TYPERT = {
|
|
|
160
160
|
"name": "started",
|
|
161
161
|
"signature": "async started(): Promise<void>"
|
|
162
162
|
},
|
|
163
|
+
{
|
|
164
|
+
"kind": "method",
|
|
165
|
+
"name": "startForPerson",
|
|
166
|
+
"signature": "startForPerson(): Promise<void>",
|
|
167
|
+
"summary": "The person asked for the browser: start it if it exited, past the exit limit.",
|
|
168
|
+
"jsDoc": "/** The person asked for the browser: start it if it exited, past the exit limit. */"
|
|
169
|
+
},
|
|
163
170
|
{
|
|
164
171
|
"kind": "method",
|
|
165
172
|
"name": "[testHooks]",
|
|
@@ -352,7 +359,7 @@ export const TYPERT = {
|
|
|
352
359
|
},
|
|
353
360
|
{
|
|
354
361
|
"name": "TestHooks",
|
|
355
|
-
"declaration": "export interface TestHooks {\n crash(): Promise<void>;\n page(owner: string, tab: string): unknown;\n}"
|
|
362
|
+
"declaration": "export interface TestHooks {\n crash(): Promise<void>;\n page(owner: string, tab: string): unknown;\n running(): boolean;\n}"
|
|
356
363
|
},
|
|
357
364
|
{
|
|
358
365
|
"name": "WaitFor",
|
|
@@ -44,7 +44,7 @@ export const TYPERT_REMOTE = createTypertRemoteInfo({
|
|
|
44
44
|
create: _mind_harness_mh_browser_mhBrowser_revealHandover_result$schema,
|
|
45
45
|
jsonSchema: {"type":"object","properties":{"status":{"type":"string","enum":["revealed","not-handing-over","no-tab"]}},"required":["status"],"additionalProperties":true},
|
|
46
46
|
},
|
|
47
|
-
sourceLocation: {"file":"packages/mh-browser/src/service/service.ts","line":
|
|
47
|
+
sourceLocation: {"file":"packages/mh-browser/src/service/service.ts","line":616,"column":9},
|
|
48
48
|
},
|
|
49
49
|
},
|
|
50
50
|
}, z)
|
|
@@ -36,8 +36,9 @@ export declare class MhBrowserService extends TypertRemoteService {
|
|
|
36
36
|
private context;
|
|
37
37
|
private ready;
|
|
38
38
|
private closing;
|
|
39
|
-
|
|
40
|
-
private
|
|
39
|
+
/** The browser exited. The next call that needs a page starts it again. */
|
|
40
|
+
private exited;
|
|
41
|
+
private readonly exits;
|
|
41
42
|
/** One queue per owner: a slow wait in one Session does not hold up another. */
|
|
42
43
|
private readonly queues;
|
|
43
44
|
private readonly shown;
|
|
@@ -105,6 +106,8 @@ export declare class MhBrowserService extends TypertRemoteService {
|
|
|
105
106
|
}>;
|
|
106
107
|
pushPath(owner: string, tab: string, path: string): Promise<void>;
|
|
107
108
|
started(): Promise<void>;
|
|
109
|
+
/** The person asked for the browser: start it if it exited, past the exit limit. */
|
|
110
|
+
startForPerson(): Promise<void>;
|
|
108
111
|
[testHooks](): TestHooks;
|
|
109
112
|
act(owner: string, tab: string, op: PageOp, signal?: AbortSignal): Promise<PageView>;
|
|
110
113
|
holdHandover(owner: string, tab: string): void;
|
|
@@ -219,7 +222,14 @@ export declare class MhBrowserService extends TypertRemoteService {
|
|
|
219
222
|
/** A failed launch is logged here; calls that need the browser see it as `BROWSER_UNAVAILABLE`. */
|
|
220
223
|
private launch;
|
|
221
224
|
private start;
|
|
222
|
-
|
|
225
|
+
/** No window opens on exit: the person may have quit it. Calls on old tabs find none. */
|
|
226
|
+
private onExit;
|
|
227
|
+
/**
|
|
228
|
+
* The browser for a call that needs a page. One that exited starts again
|
|
229
|
+
* here. The exit limit stops unattended restarts; a person's request is
|
|
230
|
+
* one start per click and skips it.
|
|
231
|
+
*/
|
|
232
|
+
private up;
|
|
223
233
|
private stop;
|
|
224
234
|
private refuseIfPerson;
|
|
225
235
|
private takenOver;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mind-harness/mh-browser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Opens pages in a real browser for the agent, keeps chosen Agent Views in context, and hands the browser to you when a person is needed.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"files": [
|