pi-lean-dimension 0.2.4 → 0.3.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 +1 -1
- package/node_modules/pi-lean-portal/AGENTS.md +15 -23
- package/node_modules/pi-lean-portal/README.md +31 -1
- package/node_modules/pi-lean-portal/__tests__/browser-toggle-profile.test.ts +12 -1
- package/node_modules/pi-lean-portal/__tests__/browser-toggle.test.ts +263 -1126
- package/node_modules/pi-lean-portal/__tests__/fetch-backend.test.ts +6 -32
- package/node_modules/pi-lean-portal/__tests__/helpers/mock-python-bridge.py +1 -1
- package/node_modules/pi-lean-portal/__tests__/helpers/probe-user-backend.ts +1 -2
- package/node_modules/pi-lean-portal/__tests__/helpers/setup-temp-dir.ts +26 -0
- package/node_modules/pi-lean-portal/__tests__/plugin-config-browser.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/plugin-contract.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/plugin-loading.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/probe-user-backend.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/python-adapter.test.ts +65 -69
- package/node_modules/pi-lean-portal/__tests__/router-dispatch.test.ts +1 -14
- package/node_modules/pi-lean-portal/__tests__/run-contributed-suites.test.ts +31 -16
- package/node_modules/pi-lean-portal/__tests__/snapshot-cache.test.ts +1 -1
- package/node_modules/pi-lean-portal/__tests__/web-guides.test.ts +146 -19
- package/node_modules/pi-lean-portal/backends/playwright-base/playwright-plugin.ts +1 -1
- package/node_modules/pi-lean-portal/backends/python-adapter.ts +3 -3
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__init__.py +2 -4
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-313.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/playwright_base.py +572 -81
- package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/node_modules/pi-lean-portal/backends/python-base/tests/test_playwright_base_quirks.py +13 -43
- package/node_modules/pi-lean-portal/browser-profile.ts +2 -3
- package/node_modules/pi-lean-portal/browser-toggle.ts +168 -385
- package/node_modules/pi-lean-portal/contributed/CHOOSING.md +2 -2
- package/node_modules/pi-lean-portal/contributed/README.md +65 -6
- package/node_modules/pi-lean-portal/contributed/camoufox-py/__pycache__/bridge.cpython-312.pyc +0 -0
- package/node_modules/pi-lean-portal/contributed/camoufox-py/bridge.py +108 -14
- package/node_modules/pi-lean-portal/contributed/camoufox-py/pin.json +4 -0
- package/node_modules/pi-lean-portal/core/fetch-backend.ts +10 -30
- package/node_modules/pi-lean-portal/core/guides.ts +14 -18
- package/node_modules/pi-lean-portal/core/router.ts +2 -20
- package/node_modules/pi-lean-portal/core/shared/bot-detection.ts +19 -30
- package/node_modules/pi-lean-portal/core/shared/browser-data.ts +4 -4
- package/node_modules/pi-lean-portal/core/shared/nav-settle.ts +2 -2
- package/node_modules/pi-lean-portal/core/shared/paths.ts +16 -2
- package/node_modules/pi-lean-portal/core/shared/settings-reader.ts +2 -7
- package/node_modules/pi-lean-portal/core/shared/snapshot-cache.ts +1 -1
- package/node_modules/pi-lean-portal/index.ts +8 -1
- package/node_modules/pi-lean-portal/package.json +2 -1
- package/node_modules/pi-lean-portal/tools/web-learn.ts +1 -4
- package/node_modules/pi-lean-search/__tests__/web-search.test.ts +87 -0
- package/node_modules/pi-lean-search/index.ts +134 -64
- package/node_modules/pi-lean-search/package.json +2 -1
- package/node_modules/playwright/README.md +3 -3
- package/node_modules/playwright/lib/agents/copilot-setup-steps.yml +2 -2
- package/node_modules/playwright/lib/cli/reportActions.js +2 -2
- package/node_modules/playwright/lib/common/index.js +77 -52
- package/node_modules/playwright/lib/common/index.js.txt +10 -11
- package/node_modules/playwright/lib/errorContext.js +2 -3
- package/node_modules/playwright/lib/index.js +30 -8
- package/node_modules/playwright/lib/matchers/expect.js +4811 -4379
- package/node_modules/playwright/lib/matchers/expect.js.LICENSE +87 -140
- package/node_modules/playwright/lib/matchers/expect.js.txt +30 -35
- package/node_modules/playwright/lib/program.js +19 -0
- package/node_modules/playwright/lib/runner/index.js +309 -153
- package/node_modules/playwright/lib/runner/index.js.txt +13 -12
- package/node_modules/playwright/lib/transform/babelBundle.js +12899 -11654
- package/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +379 -354
- package/node_modules/playwright/lib/transform/babelBundle.js.txt +39 -39
- package/node_modules/playwright/lib/transform/esmLoader.js +3129 -1116
- package/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +81 -61
- package/node_modules/playwright/lib/transform/esmLoader.js.txt +21 -18
- package/node_modules/playwright/lib/util.js +2 -29
- package/node_modules/playwright/lib/worker/workerProcessEntry.js +14 -32
- package/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +7 -8
- package/node_modules/playwright/package.json +3 -6
- package/node_modules/playwright/types/test.d.ts +391 -4
- package/node_modules/playwright/types/testReporter.d.ts +79 -4
- package/node_modules/playwright-core/browsers.json +12 -18
- package/node_modules/playwright-core/lib/bootstrap.js +1 -1
- package/node_modules/playwright-core/lib/coreBundle.js +11344 -9900
- package/node_modules/playwright-core/lib/server/electron/loader.js +5 -2
- package/node_modules/playwright-core/lib/serverRegistry.js +5 -1
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +21 -6
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +1 -1
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +4 -0
- package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/SKILL.md +21 -5
- package/node_modules/playwright-core/lib/tools/{cli-client/skill/references/spec-driven-testing.md → skills/playwright-cli/references/test-generation.md} +135 -7
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +143 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +85 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +67 -0
- package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +75 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +31 -8
- package/node_modules/playwright-core/lib/utilsBundle.js +37698 -33670
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +391 -268
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +2 -2
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -1
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +15 -55
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CwYYHbcZ.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-DYjdXIbE.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +2 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-By56iMx7.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-B34OrIms.js +181 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-COQkjINf.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.Dl36UVQT.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +5 -5
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +2 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +3 -4
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.D962mr9b.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +5 -5
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +338 -137
- package/node_modules/playwright-core/package.json +2 -2
- package/node_modules/playwright-core/types/protocol.d.ts +249 -36
- package/node_modules/playwright-core/types/structs.d.ts +3 -1
- package/node_modules/playwright-core/types/types.d.ts +2009 -266
- package/package.json +3 -3
- package/node_modules/pi-lean-portal/__tests__/url-safety.test.ts +0 -188
- package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/bridge.py +0 -525
- package/node_modules/pi-lean-portal/core/shared/url-safety.ts +0 -164
- package/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json +0 -2739
- package/node_modules/playwright-core/lib/tools/cli-client/skill/references/test-generation.md +0 -134
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-BY2S1tHT.css +0 -1
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-C_5TMfeg.js +0 -52
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DYBRYzYX.css +0 -1
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DeBYQozu.js +0 -32
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-4ZiSSCmn.css +0 -1
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-Bq-mQf8S.js +0 -193
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-LEHpjmcn.js +0 -32
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-BNmKHKpQ.js +0 -264
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-BYQrIQwR.js +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-CsJ4vdCR.js +0 -9
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.DYBRYzYX.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.CjdS-WJx.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/index.CzXZzn5A.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/index.DMMX1gXU.js +0 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.v8KI4P3m.js +0 -2
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.BZQ54Kgt.css +0 -1
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.Ut8wwJNp.js +0 -6
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.DYP7pi_n.css +0 -32
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/element-attributes.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/playwright-tests.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/request-mocking.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/running-code.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/session-management.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/storage-state.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/tracing.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/video-recording.md +0 -0
- /package/node_modules/playwright-core/lib/tools/{trace → skills/playwright-trace}/SKILL.md +0 -0
|
@@ -2,457 +2,251 @@ import type {
|
|
|
2
2
|
ExtensionAPI,
|
|
3
3
|
ExtensionCommandContext,
|
|
4
4
|
ExtensionContext,
|
|
5
|
+
ThemeColor,
|
|
5
6
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* browser automation tools (browsing tools) and learn tools (web-learn) in the system prompt.
|
|
13
|
-
*
|
|
14
|
-
* Three states: on (browsing only), learn (browsing + web-learn), off (all disabled).
|
|
15
|
-
* Internally stores two independent booleans (browserToolsEnabled, learnToolsEnabled).
|
|
16
|
-
*
|
|
17
|
-
* When browser tools are disabled, the LLM cannot see them — no descriptions,
|
|
18
|
-
* parameter schemas, prompt snippets, or guidelines are included in the context,
|
|
19
|
-
* saving roughly 1500–2000 tokens per turn.
|
|
20
|
-
*
|
|
21
|
-
* ── Stateless fallback ─────────────────────────────────────────
|
|
22
|
-
*
|
|
23
|
-
* web-fetch is a stateless HTTP tool (no session, no interactivity). If you
|
|
24
|
-
* only need quick page content and never use the interactive browser tools,
|
|
25
|
-
* you can leave browser tools disabled and rely on web-fetch alone.
|
|
26
|
-
*/
|
|
27
|
-
|
|
7
|
+
import {
|
|
8
|
+
defineToolset,
|
|
9
|
+
TOOLSET_EVENTS,
|
|
10
|
+
getDefaultResolutionMode,
|
|
11
|
+
} from "pi-tool-masking";
|
|
12
|
+
import type { ToolsetSpec } from "pi-tool-masking";
|
|
28
13
|
import { readMergedSettings } from "./core/shared/settings-reader.js";
|
|
29
14
|
|
|
30
|
-
// ----
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"web
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/** Names of learn tools (web-learn) that require /web learn to be active. */
|
|
51
|
-
const LEARN_TOOL_NAMES = new Set(["web-learn"]);
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Names of sibling-package tools that are toggled alongside browser tools
|
|
55
|
-
* by /web on|off|learn.
|
|
56
|
-
*
|
|
57
|
-
* - "web-search": SearXNG search from pi-lean-search (Sprint 4).
|
|
58
|
-
*
|
|
59
|
-
* Exact-name `Set.has()` membership — NO regex (avoids false positives
|
|
60
|
-
* on third-party web-* tools).
|
|
61
|
-
*/
|
|
62
|
-
const SIBLING_TOOL_NAMES = new Set<string>(["web-search"]);
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Update the search status bar glyph if sibling tools are installed.
|
|
66
|
-
* Used by ``/web on|learn|off`` to keep the ``search`` slot in sync.
|
|
67
|
-
*/
|
|
68
|
-
function setSearchSlot(
|
|
69
|
-
pi: ExtensionAPI,
|
|
70
|
-
ctx: ExtensionCommandContext,
|
|
71
|
-
glyph: string,
|
|
72
|
-
): void {
|
|
73
|
-
const hasSiblingTools = getRegisteredIn(pi, SIBLING_TOOL_NAMES).length > 0;
|
|
74
|
-
if (hasSiblingTools) {
|
|
75
|
-
ctx.ui.setStatus("search", glyph);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
15
|
+
// ---- Toolset specs -----------------------------------------------
|
|
16
|
+
|
|
17
|
+
const PORTAL_WEB_SPEC: ToolsetSpec = {
|
|
18
|
+
id: "portal.web",
|
|
19
|
+
names: new Set([
|
|
20
|
+
"web-fetch",
|
|
21
|
+
"browser-navigate",
|
|
22
|
+
"browser-snapshot",
|
|
23
|
+
"browser-click",
|
|
24
|
+
"browser-type",
|
|
25
|
+
"browser-scroll",
|
|
26
|
+
"browser-back",
|
|
27
|
+
"browser-press",
|
|
28
|
+
"browser-console",
|
|
29
|
+
"browser-inspect",
|
|
30
|
+
"web-guide",
|
|
31
|
+
]),
|
|
32
|
+
persistKey: "toolset-state:portal.web",
|
|
33
|
+
};
|
|
78
34
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
35
|
+
const PORTAL_LEARN_SPEC: ToolsetSpec = {
|
|
36
|
+
id: "portal.learn",
|
|
37
|
+
names: new Set(["web-learn"]),
|
|
38
|
+
persistKey: "toolset-state:portal.learn",
|
|
39
|
+
defaultEnabled: false,
|
|
40
|
+
requires: ["portal.web"],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// ---- Status bar cached state (derived from library events) ------
|
|
86
44
|
|
|
87
|
-
/** Last known toggle state
|
|
45
|
+
/** @internal Last known web-toggle state for status bar rendering. */
|
|
88
46
|
let _lastToggleState = true;
|
|
89
47
|
|
|
90
|
-
/** Last known learn state
|
|
48
|
+
/** @internal Last known learn state for status bar coloring. */
|
|
91
49
|
let _lastLearnState = false;
|
|
92
50
|
|
|
93
|
-
/**
|
|
94
|
-
let
|
|
51
|
+
/** @internal Last captured ExtensionContext for event-driven glyph rendering. */
|
|
52
|
+
let _lastCtx: ExtensionContext | null = null;
|
|
95
53
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* if set, otherwise falls back to "none" (the router reads browser.defaultProfile
|
|
99
|
-
* from settings.json as its own fallback).
|
|
100
|
-
*/
|
|
101
|
-
export function getConversationDefaultProfile(): string | undefined {
|
|
102
|
-
return _conversationDefaultProfile;
|
|
54
|
+
export function getToggleState(): boolean {
|
|
55
|
+
return _lastToggleState;
|
|
103
56
|
}
|
|
104
57
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
/** Return the subset of a tool-name set that is actually registered. */
|
|
108
|
-
function getRegisteredIn(pi: ExtensionAPI, names: Set<string>): string[] {
|
|
109
|
-
return pi
|
|
110
|
-
.getAllTools()
|
|
111
|
-
.map((t) => t.name)
|
|
112
|
-
.filter((n) => names.has(n));
|
|
58
|
+
export function getLearnState(): boolean {
|
|
59
|
+
return _lastLearnState;
|
|
113
60
|
}
|
|
114
61
|
|
|
115
|
-
|
|
116
|
-
* Check whether browser tools are currently active in the system prompt.
|
|
117
|
-
*/
|
|
118
|
-
function isBrowserEnabled(pi: ExtensionAPI): boolean {
|
|
119
|
-
const registered = getRegisteredIn(pi, BROWSER_TOOL_NAMES);
|
|
62
|
+
// ---- Conversation-scoped default profile -------------------------
|
|
120
63
|
|
|
121
|
-
|
|
122
|
-
return registered.some((name) => active.has(name));
|
|
123
|
-
}
|
|
64
|
+
const PROFILE_PERSIST_KEY = "portal-conversation-state";
|
|
124
65
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
* @param pi ExtensionAPI
|
|
128
|
-
* @param enable true = add browser tools to the active set, false = remove them
|
|
129
|
-
*/
|
|
130
|
-
function applyBrowserState(pi: ExtensionAPI, enable: boolean): void {
|
|
131
|
-
// Combine browser tools + sibling tools into one toggle set.
|
|
132
|
-
// (/web on enables both; /web off disables both.)
|
|
133
|
-
const registered = new Set([
|
|
134
|
-
...getRegisteredIn(pi, BROWSER_TOOL_NAMES),
|
|
135
|
-
...getRegisteredIn(pi, SIBLING_TOOL_NAMES),
|
|
136
|
-
]);
|
|
137
|
-
if (registered.size === 0) return;
|
|
138
|
-
_lastToggleState = enable;
|
|
139
|
-
|
|
140
|
-
if (enable) {
|
|
141
|
-
// Merge browser+sibling tools back into whatever is currently active
|
|
142
|
-
const current = pi.getActiveTools();
|
|
143
|
-
pi.setActiveTools([...new Set([...current, ...registered])]);
|
|
144
|
-
} else {
|
|
145
|
-
// Keep everything that is NOT a browser+sibling tool — but only
|
|
146
|
-
// within the *currently active* set. Using getActiveTools() (not
|
|
147
|
-
// getAllTools()) is critical for peer-toggle composition: /web off
|
|
148
|
-
// must not re-enable tools that another toggle (e.g. host's /api off)
|
|
149
|
-
// has already disabled. getAllTools() would silently re-activate
|
|
150
|
-
// every registered tool outside our own set.
|
|
151
|
-
const current = pi.getActiveTools();
|
|
152
|
-
pi.setActiveTools(current.filter((name) => !registered.has(name)));
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Persist the current toggle state into the session for branch-aware
|
|
158
|
-
* restoration across /reload, /resume, /fork, and /tree navigation.
|
|
159
|
-
*/
|
|
160
|
-
function persistState(pi: ExtensionAPI, state: BrowserToggleState): void {
|
|
161
|
-
pi.appendEntry<BrowserToggleState>("web-toggle-state", state);
|
|
66
|
+
interface ProfileState {
|
|
67
|
+
defaultProfile: string;
|
|
162
68
|
}
|
|
163
69
|
|
|
164
|
-
|
|
70
|
+
let _conversationDefaultProfile: string | undefined;
|
|
165
71
|
|
|
166
|
-
function
|
|
167
|
-
|
|
168
|
-
|
|
72
|
+
export function getConversationDefaultProfile(): string | undefined {
|
|
73
|
+
return _conversationDefaultProfile;
|
|
74
|
+
}
|
|
169
75
|
|
|
170
|
-
|
|
76
|
+
function persistProfile(pi: ExtensionAPI, profile: string): void {
|
|
77
|
+
pi.appendEntry<ProfileState>(PROFILE_PERSIST_KEY, {
|
|
78
|
+
defaultProfile: profile,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
171
81
|
|
|
82
|
+
function restoreProfile(_pi: ExtensionAPI, ctx: ExtensionContext): void {
|
|
172
83
|
for (const entry of ctx.sessionManager.getBranch()) {
|
|
173
|
-
if (entry.type === "custom" && entry.customType ===
|
|
174
|
-
const data = entry.data as Record<string, unknown
|
|
175
|
-
if (data && typeof data.
|
|
176
|
-
|
|
84
|
+
if (entry.type === "custom" && entry.customType === PROFILE_PERSIST_KEY) {
|
|
85
|
+
const data = entry.data as Record<string, unknown> | undefined;
|
|
86
|
+
if (data && typeof data.defaultProfile === "string") {
|
|
87
|
+
_conversationDefaultProfile = data.defaultProfile;
|
|
88
|
+
return;
|
|
177
89
|
}
|
|
178
90
|
}
|
|
179
91
|
}
|
|
180
|
-
|
|
181
|
-
if (savedState !== undefined) {
|
|
182
|
-
applyBrowserState(pi, savedState.browserToolsEnabled);
|
|
183
|
-
applyLearnState(pi, savedState.learnToolsEnabled);
|
|
184
|
-
// Restore conversation-scoped default profile
|
|
185
|
-
if (savedState.defaultProfile && savedState.defaultProfile !== "none") {
|
|
186
|
-
_conversationDefaultProfile = savedState.defaultProfile;
|
|
187
|
-
}
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Apply the config-file default on a fresh session (no branch state found).
|
|
196
|
-
* Reads `browserToggle.defaultEnabled` from pi's settings.json files.
|
|
197
|
-
* Learn always starts disabled by default.
|
|
198
|
-
*/
|
|
199
|
-
function applyConfigDefault(pi: ExtensionAPI): void {
|
|
200
|
-
const enabled = readBrowserToggleConfig();
|
|
201
|
-
applyBrowserState(pi, enabled);
|
|
202
|
-
applyLearnState(pi, false);
|
|
203
|
-
// Persist so subsequent branch navigation sees this as the initial state
|
|
204
|
-
persistState(pi, {
|
|
205
|
-
browserToolsEnabled: enabled,
|
|
206
|
-
learnToolsEnabled: false,
|
|
207
|
-
defaultProfile: "none",
|
|
208
|
-
});
|
|
209
92
|
}
|
|
210
93
|
|
|
211
|
-
// ----
|
|
212
|
-
// Helper functions are exported for testing while the default export
|
|
213
|
-
// wires the toggle into pi-lean-portal's runtime.
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Return the last known toggle state (true = enabled, false = disabled).
|
|
217
|
-
* Returns true initially at startup (browser tools start enabled).
|
|
218
|
-
*/
|
|
219
|
-
export function getToggleState(): boolean {
|
|
220
|
-
return _lastToggleState;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Return the last known learn state (true = learn mode enabled).
|
|
225
|
-
* Used by the status bar to color the glyph differently.
|
|
226
|
-
*/
|
|
227
|
-
export function getLearnState(): boolean {
|
|
228
|
-
return _lastLearnState;
|
|
229
|
-
}
|
|
94
|
+
// ---- Test helpers -------------------------------------------------
|
|
230
95
|
|
|
231
|
-
/** @internal Reset
|
|
96
|
+
/** @internal Reset cached state to defaults (test helper). */
|
|
232
97
|
function _resetToggleStateForTest(): void {
|
|
233
98
|
_lastToggleState = true;
|
|
234
99
|
_lastLearnState = false;
|
|
100
|
+
_lastCtx = null;
|
|
101
|
+
_conversationDefaultProfile = undefined;
|
|
235
102
|
}
|
|
236
103
|
|
|
237
|
-
/**
|
|
238
|
-
|
|
239
|
-
*
|
|
240
|
-
* Called at the start of the extension entry function to ensure safe
|
|
241
|
-
* re-invocation when pi reuses the cached module factory (e.g.
|
|
242
|
-
* during /resume to the same working directory).
|
|
243
|
-
* Resets both the toggle status and conversation-scoped profile.
|
|
244
|
-
*/
|
|
245
|
-
export function resetToggleModuleState(): void {
|
|
104
|
+
/** @internal Reset cached state (called from index.ts on re-entry). */
|
|
105
|
+
function resetToggleModuleState(): void {
|
|
246
106
|
_lastToggleState = true;
|
|
247
107
|
_lastLearnState = false;
|
|
108
|
+
_lastCtx = null;
|
|
248
109
|
_conversationDefaultProfile = undefined;
|
|
249
110
|
}
|
|
250
111
|
|
|
251
|
-
// ----
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Read the browser toggle default from pi's settings.json files.
|
|
255
|
-
*
|
|
256
|
-
* Looks for `browserToggle.defaultEnabled` in:
|
|
257
|
-
* 1. `~/.pi/agent/settings.json` (global)
|
|
258
|
-
* 2. `.pi/settings.json` (project-local, overrides global)
|
|
259
|
-
*
|
|
260
|
-
* Returns the config value, or `true` if unset (backward-compatible default).
|
|
261
|
-
* Silently returns `true` on missing files, parse errors, or type mismatches.
|
|
262
|
-
*/
|
|
263
|
-
function readBrowserToggleConfig(): boolean {
|
|
264
|
-
const merged = readMergedSettings();
|
|
265
|
-
const segment = merged[CONFIG_KEY];
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
segment &&
|
|
269
|
-
typeof segment === "object" &&
|
|
270
|
-
!Array.isArray(segment) &&
|
|
271
|
-
typeof (segment as Record<string, unknown>).defaultEnabled === "boolean"
|
|
272
|
-
) {
|
|
273
|
-
return (segment as Record<string, unknown>).defaultEnabled as boolean;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return true; // default: enabled
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Check whether learn tools are currently active.
|
|
281
|
-
*/
|
|
282
|
-
function isLearnEnabled(pi: ExtensionAPI): boolean {
|
|
283
|
-
const registered = getRegisteredIn(pi, LEARN_TOOL_NAMES);
|
|
284
|
-
const active = new Set(pi.getActiveTools());
|
|
285
|
-
return registered.some((name) => active.has(name));
|
|
286
|
-
}
|
|
112
|
+
// ---- Exports for testing -----------------------------------------
|
|
287
113
|
|
|
288
|
-
|
|
289
|
-
* Apply a learn-tool state without persisting.
|
|
290
|
-
* Uses getActiveTools() (not getAllTools()) to avoid accidentally activating
|
|
291
|
-
* learn tools that are not currently registered.
|
|
292
|
-
*/
|
|
293
|
-
function applyLearnState(pi: ExtensionAPI, enable: boolean): void {
|
|
294
|
-
_lastLearnState = enable;
|
|
295
|
-
const registered = new Set(getRegisteredIn(pi, LEARN_TOOL_NAMES));
|
|
296
|
-
if (registered.size === 0) return;
|
|
297
|
-
if (enable) {
|
|
298
|
-
const current = pi.getActiveTools();
|
|
299
|
-
pi.setActiveTools([...new Set([...current, ...registered])]);
|
|
300
|
-
} else {
|
|
301
|
-
// Remove learn tools from the *currently active* set
|
|
302
|
-
const current = pi.getActiveTools();
|
|
303
|
-
pi.setActiveTools(current.filter((name) => !registered.has(name)));
|
|
304
|
-
}
|
|
305
|
-
}
|
|
114
|
+
export { _resetToggleStateForTest, resetToggleModuleState };
|
|
306
115
|
|
|
307
|
-
//
|
|
308
|
-
// These are exported solely for unit testing.
|
|
309
|
-
// Do not import them directly from production code.
|
|
310
|
-
/** @internal */
|
|
311
|
-
export {
|
|
312
|
-
getRegisteredIn,
|
|
313
|
-
isBrowserEnabled,
|
|
314
|
-
applyBrowserState,
|
|
315
|
-
persistState,
|
|
316
|
-
restoreFromBranch,
|
|
317
|
-
readBrowserToggleConfig,
|
|
318
|
-
applyConfigDefault,
|
|
319
|
-
isLearnEnabled,
|
|
320
|
-
applyLearnState,
|
|
321
|
-
_resetToggleStateForTest,
|
|
322
|
-
};
|
|
323
|
-
export type { BrowserToggleState };
|
|
116
|
+
// ---- Glyph helpers -----------------------------------------------
|
|
324
117
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
* Set the conversation-scoped default profile.
|
|
335
|
-
* Affects this conversation only; survives /reload, /resume, /fork.
|
|
336
|
-
* "none" resets to the config-file default.
|
|
337
|
-
*/
|
|
338
|
-
function setConversationDefaultProfile(
|
|
339
|
-
pi: ExtensionAPI,
|
|
340
|
-
profile: string,
|
|
118
|
+
function renderBrowserGlyph(
|
|
119
|
+
ctx: {
|
|
120
|
+
ui: {
|
|
121
|
+
setStatus: (key: string, label: string) => void;
|
|
122
|
+
theme: { fg: (c: ThemeColor, t: string) => string };
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
webEnabled: boolean,
|
|
126
|
+
learnEnabled: boolean,
|
|
341
127
|
): void {
|
|
342
|
-
if (
|
|
343
|
-
|
|
128
|
+
if (!webEnabled) {
|
|
129
|
+
ctx.ui.setStatus("browser", "○ web off");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (learnEnabled) {
|
|
133
|
+
ctx.ui.setStatus("browser", ctx.ui.theme.fg("success", "●") + " idle");
|
|
344
134
|
} else {
|
|
345
|
-
|
|
135
|
+
ctx.ui.setStatus("browser", ctx.ui.theme.fg("accent", "●") + " idle");
|
|
346
136
|
}
|
|
347
|
-
|
|
348
|
-
// Persist so it survives /reload, /resume, /fork
|
|
349
|
-
const currentState = getCurrentState(pi);
|
|
350
|
-
persistState(pi, {
|
|
351
|
-
...currentState,
|
|
352
|
-
defaultProfile: profile,
|
|
353
|
-
});
|
|
354
137
|
}
|
|
355
138
|
|
|
356
|
-
|
|
357
|
-
function getCurrentState(
|
|
358
|
-
pi: ExtensionAPI,
|
|
359
|
-
): Pick<BrowserToggleState, "browserToolsEnabled" | "learnToolsEnabled"> {
|
|
360
|
-
return {
|
|
361
|
-
browserToolsEnabled: isBrowserEnabled(pi),
|
|
362
|
-
learnToolsEnabled: isLearnEnabled(pi),
|
|
363
|
-
};
|
|
364
|
-
}
|
|
139
|
+
// ---- Toggle initializer ------------------------------------------
|
|
365
140
|
|
|
366
|
-
// ---- Toggle initializer ----------------------------------------
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Register the /web command and session-restoration hooks.
|
|
370
|
-
* Called from pi-lean-portal's index.ts entry point.
|
|
371
|
-
*/
|
|
372
141
|
export default function initBrowserToggle(pi: ExtensionAPI) {
|
|
373
|
-
|
|
142
|
+
const merged = readMergedSettings();
|
|
143
|
+
const browserToggleSegment = (merged as Record<string, unknown>)[
|
|
144
|
+
"browserToggle"
|
|
145
|
+
] as Record<string, unknown> | undefined;
|
|
146
|
+
const webDefault =
|
|
147
|
+
browserToggleSegment &&
|
|
148
|
+
typeof browserToggleSegment["defaultEnabled"] === "boolean"
|
|
149
|
+
? (browserToggleSegment["defaultEnabled"] as boolean)
|
|
150
|
+
: true;
|
|
151
|
+
|
|
152
|
+
const webSpec: ToolsetSpec = {
|
|
153
|
+
...PORTAL_WEB_SPEC,
|
|
154
|
+
defaultEnabled: webDefault,
|
|
155
|
+
};
|
|
156
|
+
const webToolset = defineToolset(pi, webSpec);
|
|
157
|
+
const learnToolset = defineToolset(pi, PORTAL_LEARN_SPEC);
|
|
158
|
+
|
|
159
|
+
// ── Keep cached state in sync with library events ─────────
|
|
160
|
+
// Re-render the glyph on every change/restore so external callers
|
|
161
|
+
// (e.g. pi-tbox's `/tbox all off`) keep the slot in sync — the cached
|
|
162
|
+
// flags alone don't update the status bar.
|
|
163
|
+
const syncCachedState = () => {
|
|
164
|
+
_lastToggleState = webToolset.isEnabled(pi);
|
|
165
|
+
_lastLearnState = learnToolset.isEnabled(pi);
|
|
166
|
+
if (_lastCtx) {
|
|
167
|
+
renderBrowserGlyph(_lastCtx, _lastToggleState, _lastLearnState);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
374
170
|
|
|
171
|
+
pi.events.on(TOOLSET_EVENTS.changed, syncCachedState);
|
|
172
|
+
pi.events.on(TOOLSET_EVENTS.restored, syncCachedState);
|
|
173
|
+
|
|
174
|
+
// ── /web command ──────────────────────────────────────────
|
|
375
175
|
pi.registerCommand("web", {
|
|
376
176
|
description:
|
|
377
177
|
"Enable/disable browser automation tools. " +
|
|
378
178
|
"Usage: /web on | off | learn | status",
|
|
379
179
|
handler: async (args, ctx) => {
|
|
380
180
|
const cmd = args.trim().toLowerCase();
|
|
381
|
-
const hasBrowserTools = getRegisteredIn(pi, BROWSER_TOOL_NAMES).length > 0;
|
|
382
181
|
|
|
383
|
-
|
|
182
|
+
// Focus-mode guard (§13.2): refuse actuating subcommands while the
|
|
183
|
+
// library holds the line in inclusion mode, so a sibling toggle
|
|
184
|
+
// can't write a focus-indistinguishable {enabled} entry. Read-only
|
|
185
|
+
// subcommands (status/profile/cookies/bare /web) stay unguarded,
|
|
186
|
+
// matching tbox's treatment of its own read-only commands.
|
|
187
|
+
if (
|
|
188
|
+
["on", "off", "learn"].includes(cmd) &&
|
|
189
|
+
getDefaultResolutionMode() === "inclusion"
|
|
190
|
+
) {
|
|
384
191
|
ctx.ui.notify(
|
|
385
|
-
"
|
|
192
|
+
"Another plugin has active inclusion mode — this toolset can't be toggled while inclusion is holding the line. Deactivate it there first.",
|
|
386
193
|
"warning",
|
|
387
194
|
);
|
|
388
195
|
return;
|
|
389
196
|
}
|
|
390
197
|
|
|
391
198
|
if (cmd === "on") {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
persistState(pi, {
|
|
395
|
-
browserToolsEnabled: true,
|
|
396
|
-
learnToolsEnabled: false,
|
|
397
|
-
defaultProfile: _conversationDefaultProfile ?? "none",
|
|
398
|
-
});
|
|
399
|
-
ctx.ui.setStatus("browser", ctx.ui.theme.fg("accent", "●") + " idle");
|
|
400
|
-
|
|
401
|
-
setSearchSlot(pi, ctx, ctx.ui.theme.fg("accent", "●") + " searxng");
|
|
402
|
-
|
|
199
|
+
webToolset.enable(pi);
|
|
200
|
+
learnToolset.disable(pi);
|
|
403
201
|
ctx.ui.notify(
|
|
404
202
|
"🌐 Browser tools enabled. /web learn to make web-learn available.",
|
|
405
203
|
"info",
|
|
406
204
|
);
|
|
407
205
|
} else if (cmd === "learn") {
|
|
408
|
-
|
|
409
|
-
applyLearnState(pi, true);
|
|
410
|
-
persistState(pi, {
|
|
411
|
-
browserToolsEnabled: true,
|
|
412
|
-
learnToolsEnabled: true,
|
|
413
|
-
defaultProfile: _conversationDefaultProfile ?? "none",
|
|
414
|
-
});
|
|
415
|
-
ctx.ui.setStatus("browser", ctx.ui.theme.fg("success", "●") + " idle");
|
|
416
|
-
|
|
417
|
-
setSearchSlot(pi, ctx, ctx.ui.theme.fg("accent", "●") + " searxng");
|
|
418
|
-
|
|
206
|
+
learnToolset.enable(pi); // cascades web on via requires
|
|
419
207
|
ctx.ui.notify(
|
|
420
208
|
"📖 web-learn tool is now available. Agent will save/update guides when asked.",
|
|
421
209
|
"info",
|
|
422
210
|
);
|
|
423
211
|
} else if (cmd === "off") {
|
|
424
|
-
|
|
425
|
-
applyLearnState(pi, false);
|
|
426
|
-
persistState(pi, {
|
|
427
|
-
browserToolsEnabled: false,
|
|
428
|
-
learnToolsEnabled: false,
|
|
429
|
-
defaultProfile: _conversationDefaultProfile ?? "none",
|
|
430
|
-
});
|
|
431
|
-
ctx.ui.setStatus("browser", "○ web off");
|
|
432
|
-
|
|
433
|
-
setSearchSlot(pi, ctx, "○ searxng");
|
|
434
|
-
|
|
212
|
+
webToolset.disable(pi); // cascades learn off via requires
|
|
435
213
|
ctx.ui.notify(
|
|
436
214
|
"🌐 Browser tools disabled. /web on to re-enable.",
|
|
437
215
|
"info",
|
|
438
216
|
);
|
|
439
217
|
} else if (cmd === "profile" || cmd.startsWith("profile ")) {
|
|
440
218
|
const sub = cmd.slice("profile".length).trim();
|
|
219
|
+
const { handleProfileSubcommand } = await import(
|
|
220
|
+
"./browser-profile.js"
|
|
221
|
+
);
|
|
441
222
|
await handleProfileSubcommand(sub, ctx, pi, (profile: string) => {
|
|
442
|
-
|
|
223
|
+
if (profile === "none") {
|
|
224
|
+
_conversationDefaultProfile = undefined;
|
|
225
|
+
} else {
|
|
226
|
+
_conversationDefaultProfile = profile;
|
|
227
|
+
}
|
|
228
|
+
persistProfile(pi, profile);
|
|
443
229
|
});
|
|
444
230
|
} else if (cmd === "cookies" || cmd.startsWith("cookies ")) {
|
|
445
231
|
const sub = cmd.slice("cookies".length).trim();
|
|
232
|
+
const { handleCookiesSubcommand } = await import(
|
|
233
|
+
"./browser-cookies.js"
|
|
234
|
+
);
|
|
446
235
|
await handleCookiesSubcommand(sub, ctx);
|
|
447
236
|
} else if (cmd === "status") {
|
|
448
|
-
handleStatusSubcommand
|
|
237
|
+
const { handleStatusSubcommand } = await import("./browser-status.js");
|
|
238
|
+
handleStatusSubcommand(
|
|
239
|
+
ctx,
|
|
240
|
+
webToolset.isEnabled(pi),
|
|
241
|
+
learnToolset.isEnabled(pi),
|
|
242
|
+
);
|
|
449
243
|
} else {
|
|
450
244
|
// Default: show status
|
|
451
|
-
const
|
|
452
|
-
const
|
|
245
|
+
const webOn = webToolset.isEnabled(pi) ? "✅ on" : "❌ off";
|
|
246
|
+
const learnOn = learnToolset.isEnabled(pi) ? "✅ on" : "❌ off";
|
|
453
247
|
ctx.ui.notify(
|
|
454
|
-
`🌐 Browser tools: ${
|
|
455
|
-
`📖 Learn mode: ${
|
|
248
|
+
`🌐 Browser tools: ${webOn}\n` +
|
|
249
|
+
`📖 Learn mode: ${learnOn}\n` +
|
|
456
250
|
` /web profile manage browser profiles\n` +
|
|
457
251
|
` /web cookies inspect or clear session cookies\n` +
|
|
458
252
|
` /web off disable all browser tools\n` +
|
|
@@ -466,31 +260,20 @@ export default function initBrowserToggle(pi: ExtensionAPI) {
|
|
|
466
260
|
},
|
|
467
261
|
});
|
|
468
262
|
|
|
469
|
-
// ──
|
|
470
|
-
// Ensures the toggle survives /reload, /resume, /fork, and
|
|
471
|
-
// /tree navigation without "forgetting" whether we're on or off.
|
|
472
|
-
//
|
|
473
|
-
// For fresh sessions (no branch state), falls back to the config
|
|
474
|
-
// default from settings.json (`browserToggle.defaultEnabled`).
|
|
475
|
-
|
|
263
|
+
// ── Session handlers: restore profile + render glyph ─────
|
|
476
264
|
pi.on("session_start", async (_event, ctx) => {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
applyConfigDefault(pi);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// After state restoration, ensure the search slot reflects the actual
|
|
484
|
-
// toggle state. Without this, a race between portal's session_start
|
|
485
|
-
// (async — restores/applies state) and search's session_start (probes
|
|
486
|
-
// health) can leave the search glyph showing blue when tools are off.
|
|
487
|
-
const hasSiblingTools = getRegisteredIn(pi, SIBLING_TOOL_NAMES).length > 0;
|
|
488
|
-
if (hasSiblingTools && !isBrowserEnabled(pi)) {
|
|
489
|
-
ctx.ui.setStatus("search", "○ searxng");
|
|
490
|
-
}
|
|
265
|
+
restoreProfile(pi, ctx);
|
|
266
|
+
_lastCtx = ctx;
|
|
267
|
+
syncCachedState();
|
|
491
268
|
});
|
|
492
269
|
|
|
493
270
|
pi.on("session_tree", async (_event, ctx) => {
|
|
494
|
-
|
|
271
|
+
restoreProfile(pi, ctx);
|
|
272
|
+
_lastCtx = ctx;
|
|
273
|
+
syncCachedState();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
pi.on("session_shutdown", async () => {
|
|
277
|
+
_lastCtx = null;
|
|
495
278
|
});
|
|
496
279
|
}
|
|
@@ -118,8 +118,8 @@ rather than failing opaquely.
|
|
|
118
118
|
executes stealth backends automatically. `user-backends/` is trusted
|
|
119
119
|
user code — you wrote or audited it.
|
|
120
120
|
- **Not shipped in the npm tarball.** The templates live in the source
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
repo under `contributed/`; `contributed/` is not included in the
|
|
122
|
+
`package.json` `files` allow-list. You need the git repo (or a copy of the
|
|
123
123
|
files) to install a stealth backend.
|
|
124
124
|
- **Not in the default fallback list.** Stealth backends are loaded
|
|
125
125
|
only when explicitly listed in `browser.plugins`. A fresh install
|