Browser Context (upcoming)
Available in version 1.1.72 (not yet live).
Launch, quit, manage, and inspect the browser context and pages your TaskBot uses.
// @zw-run-locally
// Quick start (abridged)
await zw.browserContext.launch({
launchOptions: { headless: true },
/* More options available */
});
await zw.log("Context info", await zw.browserContext.getContextInfo());
await zw.browserContext.quit();
1. Core Concepts
Launching the browser context
Launch with zw.browserContext.launch()
(ZeroWork-managed). This ensures:
No-code blocks use the same context.
Lifecycle is managed for you. To opt out of auto-quit, pass
keepAlive: true
.(Re)launches reapply your launch arguments.
Note: If you need a custom launch flow or a self-managed context, see Advanced: Self-managed contexts below.
Active page
A TaskBot has one active page at a time. No-code web-interaction blocks act on the active page. If you open a page in code and want no-code blocks to use it, set it with zw.browserContext.setActivePage(page)
.
Context (re)launch
A TaskBot automatically (re)launches a context in these cases:
Open Link building block — launches one if none exists, otherwise reuses.
Write JS set to run in the browser — launches one if none exists, otherwise reuses.
A call to
zw.browserContext.launch()
.Launch Browser building block — launches one and replaces the existing context.
Recovery after staleness or a crash.
Performance optimizations in long-running TaskBots.
💡 Common gotcha: Closing the last tab (e.g., with Switch or Close Tab) ends the context. The next Open Link block creates a fresh context from defaults.
Avoid surprises (e.g., “I launched a headless browser and it suddenly became headful mid-run”) with these best practices:
Use
zw.browserContext.launch()
to launch. (If you need a custom launch flow, you can passcontextProvider
.)Add cookies, scripts, and permissions via
zw.browserContext.launch()
/zw.browserContext.setDefaults()
so that they reapply on (re)launches. Don't use ad hoc calls likecontext.addCookies()
.
Persistent (sticky) profiles
Sticky profiles follow special rules.
One browser instance per profile. Same
userDataDir
⇒ the same live browser instance across TaskBots (multiple isolated tabs).Later launches attach to the live browser instance and ignore conflicting browser-level args (e.g.,
headless
). Other arguments likecookies
,scripts
andonContextReady
still apply on relaunch (deduplicated).Quitting from one run closes that run’s tabs, but if other TaskBots are still using the browser instance it remains running and only quits when the last user leaves.
Lifecycle
Contexts are closed and cleared automatically.
When a run ends, the context and browser quit unless
keepAlive
istrue
or a shared sticky profile is still in use by other TaskBots.If
keepAlive
istrue
but there are no open or eligible tabs, the context and browser still quit.If a kept-alive browser remains open, the Desktop Agent cleans up when it detects manual closure (e.g., you close the window or the last tab).
⚠️ Caution:
headless: true
+keepAlive: true
can leave an invisible instance consuming resources or blocking a browser profile. The UI blocks this combo, but the API allows it—use with care.
Advanced: Self-managed contexts
Custom launch flow Provide a callback via
contextProvider
inzw.browserContext.launch()
when you need options the standardzw
API doesn’t cover (e.g., launching with Firefox).Self-managed context Pass
makeMain: false
inzw.browserContext.launch()
to opt out of lifecycle and relaunch policy. No-code blocks keep using the main context. To rejoin the managed flow later, usezw.browserContext.adoptContext()
.
2. Launch the Browser — launch()
launch()
At a Glance
async
await zw.browserContext.launch(args: CustomLaunchArgs)
→ launches a new browser context and returns it.
Launch Arguments — CustomLaunchArgs
CustomLaunchArgs
// Type reference — not runnable in Write JS
type CustomLaunchArgs = {
contextControls?: {
maximize?: boolean; // default: true
powerMode?: boolean; // default: true
persistent?: boolean; // default: false
persistentProfile?: { // required if persistent is true
userDataDir: string; // absolute path
};
keepAlive?: boolean; // default: false
bringToFront?: boolean; // default: true
};
onContextReady?: (ctx: BrowserContext) => void | Promise<void>;
scripts?: Array<{ path: string } | { content: string }>;
cookies?: Array<CookieObject>;
launchOptions?: LaunchOptions;
contextOptions?: ContextOptions;
config?: {
makeMain?: boolean; // default: true
inheritDefaults?: boolean; // default: true
saveAsDefaults?: boolean; // default: true if makeMain=true, else false
};
contextProvider?: () => Promise<BrowserContext> | BrowserContext; // advanced: callback that returns a custom-launched context
};
Defaults used when you pass no arguments
All options are optional. If you call zw.browserContext.launch()
with no arguments, it inherits your TaskBot Run Settings and any values previously saved via zw.browserContext.setDefaults()
.
// @zw-run-locally
// ✅ Works: launch without passing any arguments
await zw.browserContext.launch();
Context Controls — contextControls
contextControls
maximize
(default:true
) Maximizes the window. Ignored inheadless
mode (headless
must use an explicitviewport
, see Context Options further below).powerMode
(default:true
) Hardens anti-detection and keeps TaskBots indistinguishable from human users to Cloudflare and similar anti-bot systems.ℹ️ Trade-offs: In
powerMode
, some launch and context options are unavailable (see Launch Options and Context Options further below) and file uploads larger than ~50 MB are blocked (downloads are unaffected).
persistent
(default:false
) Launches in a sticky browser profile. RequirespersistentProfile.userDataDir
(absolute path). Multiple TaskBots with the sameuserDataDir
share the same browser instance in parallel tabs (see Persistent Profiles further below).keepAlive
(default:false
) Keeps the browser open after the run ends.💡
keepAlive: true
is ignored if, at run end, no pages remain. The context then closes. Tabs on about:blank or ZeroWork launch pages don’t count unless they are tied to Write JS in-browser code execution.ℹ️ In sticky profiles,
keepAlive
applies only to this run’s tabs. Iftrue
, your tabs remain open; iffalse
, your tabs close. The browser instance stays running as long as another TaskBot in the same profile is using it. It quits only when the last user leaves and no kept-alive tabs remain.⚠️ With sticky profiles, prefer leaving
keepAlive
false. After a long device sleep, the connection can drop while the browser stays open, blocking the profile until you restart the Desktop Agent or quit that browser. Only one browser instance can use a profile at a time.
bringToFront
(default:true
) Brings newly opened tabs to the front (applies to no-code blocks that open tabs).
Cookies — cookies
cookies
Provide cookies up front so they are reapplied on every context (re)launch.
Each cookie must include at least name
, value
, and domain
(and path
, where relevant).
💡 Tip! Avoid adding cookies in code later via
context.addCookies()
— those won’t automatically reapply on context relaunch.
Scripts — scripts
scripts
Scripts are injected before any page loads and reinjected on context (re)launch.
Each script item is either { path }
or { content }
(when using path
, provide an absolute path).
💡 Tip! Avoid adding scripts in code later via
context.addInitScript()
— those won’t automatically reapply on context relaunch.
Example
// @zw-run-locally
await zw.browserContext.launch({
scripts: [
{ content: `window._usedByTaskBot = ${(await zw.getTaskbotInfo()).id};` },
{ path: "/Users/me/scripts/guard.js" },
]
});
Callback when Ready — onContextReady
onContextReady
Runs whenever the context is (re)launched.
Example
// @zw-run-locally
import * as crypto from "crypto";
await zw.browserContext.launch({
onContextReady: async (context) => {
// Expose a function from an imported package
await context.exposeFunction(
"sha256",
(text) => crypto.createHash("sha256").update(text).digest("hex")
);
// Save bandwidth by blocking images
await context.route('**/*.{png,jpg,jpeg}', r => r.abort());
},
});
Launch Options — launchOptions
launchOptions
// Type reference — not runnable in Write JS
type LaunchOptions = {
args?: string[]; // Chrome/Chromium flags; use only for advanced use cases
executablePath?: string; // Chromium-based browser path; defaults to Chrome
headless?: boolean; // default: false
proxy?: {
server: string; // "host:port" or "socks5://host:port"
bypass?: string; // comma-separated domains
username?: string;
password?: string;
};
// In non-Power Mode, more options are available (e.g., channel).
};
args
Use with care: custom flags can break anti-detection. Note:--user-data-dir
and--profile-directory
are reserved. In Power Mode,--remote-debugging-port
is also reserved. If you pass arguments for reserved keys, they will be ignored.executablePath
By default, your Chrome path is auto-detected and used. You can override it with any Chromium-based browser (e.g., Chrome, Brave, Vivaldi, Chromium). You can find the executable path by opening chrome://version in your browser. Note: Some Chromium forks (e.g., Opera) diverge too much and may not work.headless
Runs in the background and uses fewer resources.proxy
For SOCKS5, prefix withsocks5://
. SOCKS5 auth isn’t supported —username
andpassword
apply to HTTP proxies only.More options when Power Mode is disabled When
powerMode
is disabled, Playwright’sBrowserType.launch
options are available. For the full list, see launch → Arguments here.
Example
// @zw-run-locally
await zw.browserContext.launch({
launchOptions: {
headless: true,
proxy: {
server: "123.45.67.89:3128",
username: "username",
password: await zw.deviceStorage.get("proxy_password"),
},
},
});
Context Options — contextOptions
contextOptions
// Type reference — not runnable in Write JS
type ContextOptions = {
permissions?: string[]; // will include "clipboard-read" and "clipboard-write" by default
userAgent?: string; // dangerous! Changing can harm anti-detection
viewport?: { width: number; height: number } | null;
// In non-Power Mode, more options are available (e.g., recordVideo).
};
permissions
ZeroWork always grants"clipboard-read"
and"clipboard-write"
so that the Save From Clipboard building block works.⚠️ Avoid adding permissions in code later via
context.grantPermissions()
— those won’t automatically reapply on context relaunch.
userAgent
Unless you know exactly what you’re doing, prefer not to change it. If you changeuserAgent
, anti-detection may no longer be fully guaranteed.viewport
(default:{ width: 1440, height: 900 }
) Takes effect whenmaximize
is disabled or in headless mode.More options when Power Mode is disabled When
powerMode
is disabled, Playwright’sBrowser.newContext
options are available. For the full list, see newContext → Arguments here.⚠️ Warning! Using Playwright API to set options like
geolocation
,extraHTTPHeaders
, etc. can harm the built-in anti-detection measures.
Examples
Headless with explicit viewport
// @zw-run-locally
await zw.browserContext.launch({
launchOptions: { headless: true },
contextOptions: {
viewport: { width: 1000, height: 600 }
},
});
Record a video
// @zw-run-locally
await zw.browserContext.launch({
contextControls: { powerMode: false }, // disable powerMode to unlock recordVideo
contextOptions: {
recordVideo: {
dir: "/Users/me/zw-runs/videos",
size: { width: 800, height: 450 },
},
}
});
Config Options — config
config
inheritDefaults
(default:true
) Inherits values from Run Settings and any values previously saved viazw.browserContext.setDefaults()
for options you don’t specify. Example: if Run Settings have Run in background on and you don’t setheadless
inlaunchOptions
,headless
will be inherited astrue
.makeMain
(default:true
) — advanced Iftrue
, the launched context becomes the main context. The previous main context is closed (unless itskeepAlive
setting istrue
). If you set tofalse
, you partially lose automatic lifecycle, retries, and no-code blocks will keep using the old context.⚠️ Leave
makeMain
attrue
unless you’re deliberately running a self-managed context for an advanced use case. If you do, see Adopt Self-Managed Contexts for more details further below.💡
makeMain: false
is ignored if you launch a context with a persistent (sticky) browser profile that is already launched and has already been made main. This is because launches of the same sticky browser profile all share the same browser instance, see Sticky Profiles for more details further below.
saveAsDefaults
(default:true
ifmakeMain
istrue
, otherwisefalse
) — advanced Persists these options for future launches as well as automatic relaunches (e.g., after a staleness or crash recovery).⚠️ If you set
saveAsDefaults
tofalse
, a later relaunch may revert to older defaults. Likewise, if you setsaveAsDefaults
totrue
whilemakeMain
isfalse
, the main context may relaunch with unexpected, unrelated settings. Prefer keeping the default.
Context Provider — contextProvider
— Advanced
contextProvider
— AdvancedProvide a custom launch callback. Use it when you need a custom-launched context but still want no-code blocks to use it and benefit from lifecycle management, relaunch policy, and browser profile session sharing.
Use cases
You must use a browser the ZeroWork launch API doesn’t support out of the box (for example, Firefox instead of Chromium).
You need a 100% pristine context without ZeroWork’s built-in anti-detection measures or defaults.
Example
Launching Firefox
// @zw-run-locally
import { firefox } from "playwright";
const launchFirefoxContext = async () => {
const firefoxBrowser = await firefox.launch();
const context = await firefoxBrowser.newContext();
return context;
};
await zw.browserContext.launch({
contextProvider: launchFirefoxContext,
cookies: [],
scripts: [],
onContextReady: () => zw.log("My Firefox context is ready."),
});
Because you supply the context, you control launchOptions
(headless, proxy, etc.), contextOptions
(viewport, permissions, etc.), window size, and any anti-detection choices. ZeroWork adopts your context, applies cookies
and scripts
, then runs onContextReady
. Use this only if you need full control over browser creation and understand the trade-offs.
These arguments are ignored when contextProvider
is set
launchOptions
contextOptions
contextControls.maximize
,contextControls.powerMode
The rest of the arguments apply — including their corresponding defaults if config.inheritDefaults
is true or left undefined (default is true).
⚠️ Exception: When
contextProvider
is used,persistent
andpersistentProfile.userDataDir
are not inherited from defaults. Pass them explicitly if needed. This guardrail prevents accidental reuse or exposure of a browser profile that wasn’t launched by your context provider.💡Sticky profiles: If you pass
persistent: true
withpersistentProfile.userDataDir
, the context becomes discoverable to other TaskBots using the sameuserDataDir
.
3. Defaults — setDefaults()
, getDefaults()
setDefaults()
, getDefaults()
Defaults are the settings a TaskBot uses when launching or relaunching contexts. You can ensure that any subsequent launch or relaunch uses the settings you want by setting defaults.
See Core Concepts → Context (re)launch for a list of cases when a browser context is (re)launched.
At a Glance
async
await zw.browserContext.getDefaults()
→ returnsCustomLaunchArgs
.async
await zw.browserContext.setDefaults(args:
CustomLaunchArgs
)
→ updates TaskBot-level default launch settings.
Notes
zw.browserContext.getDefaults()
excludesconfig
, which is only meaningful when launching or setting defaults.In
setDefaults()
, onlyconfig.inheritDefaults
is accepted (makeMain
andsaveAsDefaults
are ignored — they have no effect when setting defaults).
Examples
Set all subsequent (re)launches to headless
// @zw-run-locally
await zw.browserContext.setDefaults({
launchOptions: { headless: true }
});
await zw.browserContext.launch(); // now guaranteed to launch headless
Discover a sticky profile’s userDataDir
const defaults = await zw.browserContext.getDefaults();
const userDataDir = defaults?.contextControls?.persistent
? defaults?.contextControls?.persistentProfile?.userDataDir
: null;
await zw.log("Sticky profile userDataDir", userDataDir);
4. Context — getContextInfo()
, getContext()
getContextInfo()
, getContext()
At a Glance
zw.browserContext.getContext()
→ returns the current main browser context.async/sync*
await zw.browserContext.getContextInfo()
→ returns:{ id: string, contextProps: { maximize?: boolean, headless?: boolean, persistent?: boolean, powerMode?: boolean, userDataDir?: string, // when persistent foreignContext?: boolean, // advanced: true if launched with contextProvider }, usedInTaskbots: number[], // TaskBot IDs currently sharing this context if persistent currentRunControls: { keepAlive?: boolean, bringToFront?: boolean, }, } | null
*async
in browser, sync
locally (see Local vs. Browser Execution).
Working with the returned context
You can call any Playwright BrowserContext
API. For the full list of available methods, properties and events, see here.
⚠️ Avoid adding cookies, scripts, and permissions via the context API (e.g.,
context.addCookies()
). Instead, pass them tozw.browserContext.launch()
orzw.browserContext.setDefaults()
(e.g.,zw.browserContext.launch({ cookies: [] })
) so that they reapply on relaunch.
⚠️ Avoid changing user agent, timezone, locale, or geolocation (e.g.,
context.setGeolocation()
), as this can harm the built-in anti-detection measures.
Examples
Add a listener
// @zw-run-locally
const context = zw.browserContext.getContext();
context.on("close", async () => {
await zw.log("Context is being closed.");
});
Clear cookies
// @zw-run-locally
const context = zw.browserContext.getContext();
await context.clearCookies();
Relaunch non-headless if the current context is headless
// @zw-run-locally
const contextInfo = await zw.browserContext.getContextInfo();
if (contextInfo?.contextProps?.headless) {
// Relaunch because a headful browser is required
await zw.browserContext.quit();
await zw.browserContext.launch({
launchOptions: { headless: false }
});
await zw.log("Browser is relaunched.");
const updatedInfo = await zw.browserContext.getContextInfo();
await zw.log("Confirm headless is now false", updatedInfo.contextProps.headless);
}
5. Persistent (Sticky) Browser Profiles — clearProfile()
, cloneProfile()
clearProfile()
, cloneProfile()
Sticky profiles let multiple TaskBots share one browser session (same cookies/storage/fingerprint) via a persistent, shared userDataDir
.
⚠️ Due to recent Chrome updates, you can no longer use the userDataDir
of the browser profile you use for regular browsing. Instead, clone your profile with zw.browserContext.cloneProfile()
or create an empty folder.
At a Glance
async
await zw.browserContext.clearProfile({ userDataDir: string })
→ clears the profile, or refuses to clear if the profile is in use.async
await zw.browserContext.cloneProfile({ userDataDir: string, cloneFrom: { userDataDir: string, profile: string } })
→ clones the profile, or refuses to clone if the target profile is in use.
Behavior
Parallel TaskBots, one instance. Same
userDataDir
⇒ a shared browser instance (multiple isolated tabs).In-use lock. If a profile is in use, both clear and clone will fail (to avoid breaking active runs). Check via
zw.browserContext.getContextInfo().usedInTaskbots
.Create & discover. ZeroWork can auto-generate sticky profiles via the Run Settings UI. You can’t create them directly via API, but you can:
Create via UI (Run Settings).
Call
zw.browserContext.getDefaults()
to discover theuserDataDir
.Use that path programmatically.
Shared Browser Side Effects
Non-deterministic tab order across independent TaskBots. If you need to switch programmatically, match by URL and/or TaskBot ID. Example
// @zw-run-locally const TASKBOT_ID = 1243; const pagesForThisBot = zw.browserContext .listPages() .filter(page => page.usedBy === TASKBOT_ID);
Attach semantics — when a browser instance is already live for that
userDataDir
and a launch event attaches to it, browser-level arguments are ignored and others are applied. By default, applied items affect the whole context, so other TaskBots sharing the instance will see them (exceptkeepAlive
andbringToFront
, which apply to this run’s tabs only). Ignored on attach:launchOptions
contextOptions
contextControls.powerMode
,contextControls.maximize
contextProvider
Applied:
contextControls.keepAlive
,contextControls.bringToFront
— apply to this run's tabs only.scripts
— scripts that already ran are ignored; any new scripts are applied (context-wide).cookies
— duplicate cookies by the samename
+domain
+path
are ignored; any new cookies are applied (context-wide).⚠️ Be careful not to pass conflicting cookies or two distinct cookies with the same
name
/domain
/path
(duplicates will be ignored).
extraHeaders
— new headers are added (context-wide).onContextReady
— runs on every attach (gate it if you want it to run only on true (re)launches). Gate example:// @zw-run-locally await zw.browserContext.launch({ contextControls: { persistent: true, persistentProfile: { userDataDir: "/Users/me/profile/" }, }, onContextReady: async (context) => { // Runs on every attach; gate if you only want true (re)launches if (context._onContextReadyRan === true) { await zw.log("Attach detected — skipping onContextReady."); return; } await zw.log("Running onContextReady on a true (re)launch only."); context._onContextReadyRan = true; }, });
Anti-patterns
Manual and foreign instances block sharing. If you launch (manually or programmatically) your own browser with the same
userDataDir
outsidezw.browserContext.launch()
, ZeroWork can’t discover or attach to it, and later TaskBots that expect to share the session will fail (only one browser instance can lock a profile at a time).If you need a custom launch flow, use
zw.browserContext.launch()
withcontextProvider
.If you must use a profile manually, clone it first with
zw.browserContext.cloneProfile()
into an independent, non-conflicting profile.
keepAlive
caution. We don’t recommend settingkeepAlive
totrue
inzw.browserContext.launch()
orzw.browserContext.setDefaults()
. After a long device sleep, the connection can drop while the browser stays open, blocking the profile until you restart the Desktop Agent or quit that browser. Remember: only one browser instance can use a profile at a time. Prefer leavingkeepAlive: false
for sticky profiles.
6. Quit Browser — quit()
quit()
At a Glance
async
await zw.browserContext.quit(opts?: { forceQuit?: boolean })
→ closes the current context and the browser instance.
Behavior
Closes the current run’s pages and attempts to close the browser/context.
Since
zw.browserContext.quit()
is called explicitly, thekeepAlive
setting is ignored.In sticky profiles:
If other TaskBots are still using the same instance, pages from this run close, but the browser instance does not quit.
If no other TaskBots are using the instance, the browser quits fully.
forceQuit: true
forces termination even if shared. Use with care.
💡Contexts are automatically managed and closed when needed. See Core Concepts → Lifecycle. You only need to call
zw.browserContext.quit()
if you need it as part of your logic, as lifecycle management is already covered.
7. Active Page & Pages — setActivePage()
, getActivePage()
, isActivePage()
, createPage()
, listPages()
setActivePage()
, getActivePage()
, isActivePage()
, createPage()
, listPages()
At a Glance
async
await zw.browserContext.setActivePage(page: Page, options?: { forceContextMismatch?: boolean })
→ makes a Page the “active page” used by no-code building blocks that do web interactions.zw.browserContext.getActivePage()
→ returnsPage | null
, i.e. the page (if any) currently used by the TaskBot and its no-code building blocks.zw.browserContext.isActivePage(page: Page)
→ returnsboolean
async
zw.browserContext.createPage({ url?: string, context?: BrowserContext })
→ creates a new page and returnsPage
zw.browserContext.listPages()
→ returns:Array<{ page: Page, url: string, isActive: boolean, contextId: string, usedBy: number, // TaskBot ID }>
What’s an active page?
The active page is the page that no-code web-interaction blocks act on. There is one active page per run. You can open other pages in code, but unless you set one as active, no-code blocks won’t use it.
Example
// @zw-run-locally
const context = zw.browserContext.getContext();
const page = await zw.browserContext.createPage();
await zw.browserContext.setActivePage(page);
createPage
— context
By default, pages are created in the current main context. If you need to create a page in a self-managed, non-main context, you can pass it in context
, and the page will be created there instead. No-code blocks won’t act on it until you adopt and set it active:
await zw.browserContext.adoptContext(context);
await zw.browserContext.setActivePage(page);
Context mismatch & forceContextMismatch
If the page belongs to a different browser context than the current main context, an error is thrown similar to:
The page you provided belongs to a different browser context than the current main context. If you must use a custom launch flow, provide
contextProvider
inzw.browserContext.launch()
. If you must use a self-managed context, usezw.browserContext.adoptContext()
to adopt it before callingzw.browserContext.setActivePage()
. While not recommended, you can also setforceContextMismatch
totrue
.
forceContextMismatch
(default:false
) — advancedSetting to
true
is not recommended. Consider it an escape hatch.If set to
true
, the page is made active even if it belongs to a foreign context. No-code blocks will now operate on the active page, but inconsistencies can arise when Switch or Close Tabs is used, when a context relaunches, or when you usezw.browserContext.getContext()
.If you must launch a custom context, explore
contextProvider
inzw.browserContext.launch()
first. For more advanced use cases and self-managed contexts, explorezw.browserContext.adoptContext()
.
Two ways to hit a mismatch
Launching a second context with
makeMain: false
and creating a page there.Creating a context entirely outside
zw.browserContext.launch()
(e.g., using the PlaywrightBrowserType
API).
Bad (illustrative) pattern
// 🚫 Bad (illustrative) pattern
// Launch main context
const context1 = await zw.browserContext.launch();
// Launch a second independent context
const context2 = await zw.browserContext.launch({
// 🚫 Bad: makeMain is false → launched a loose, non-managed context
config: { makeMain: false }
});
const page = await context2.newPage();
// This will throw unless forceContextMismatch is set to true (not recommended)
await zw.browserContext.setActivePage(page);
8. Adopt Self-Managed Contexts (Advanced)
For advanced use cases, you can create additional (non-main), self-managed contexts via zw.browserContext.launch()
and then let ZeroWork adopt it as the active, managed context.
At a Glance
async
await zw.browserContext.adoptContext(context: BrowserContext)
→ adopts a non-main context launched byzw.browserContext.launch()
(withconfig.makeMain: false
).
⚠️ Contexts created directly via the Playwright
BrowserType
API are not accepted. If you need a custom launch flow, provide it viacontextProvider
inzw.browserContext.launch()
.
Example
Switching between two launched (non-main) contexts
// @zw-run-locally
// Launch two independent contexts (illustrative; only do this if you truly need it)
const LINKEDIN_ACCOUNT_A = [/* cookies */];
const PROXY_USA = { server: "socks5://..." };
const LINKEDIN_ACCOUNT_B = [/* cookies */];
const PROXY_ITALY = { server: "socks5://..." };
const launchArgsA = {
cookies: LINKEDIN_ACCOUNT_A,
launchOptions: { proxy: PROXY_USA },
contextControls: { keepAlive: true },
config: { makeMain: false }, // create as non-main
};
const contextA = await zw.browserContext.launch(launchArgsA);
const pageA = await zw.browserContext.createPage({ context: contextA });
const launchArgsB = {
cookies: LINKEDIN_ACCOUNT_B,
launchOptions: { proxy: PROXY_ITALY },
contextControls: { keepAlive: true },
config: { makeMain: false }, // create as non-main
};
const contextB = await zw.browserContext.launch(launchArgsB);
const pageB = await zw.browserContext.createPage({ context: contextB });
// Persist references across blocks and TaskBots (same Desktop Agent)
const globalState = zw.globalState.access();
globalState.contexts = {
A: { context: contextA, page: pageA, defaults: launchArgsA },
B: { context: contextB, page: pageB, defaults: launchArgsB },
};
const adoptSafely = async ({ context, page, defaults }) => {
// Adopt the context into the managed flow
await zw.browserContext.adoptContext(context);
// Ensure continuity on relaunches
await zw.browserContext.setDefaults(defaults);
// Point no-code blocks to the right page
await zw.browserContext.setActivePage(page);
};
// Persist helper so it can be called in other TaskBots/blocks
globalState.adoptSafely = adoptSafely;
// Later in another block or TaskBot: adopt and switch to A
await globalState.adoptSafely(globalState.contexts.A);
// Later in another block or TaskBot: adopt and switch to B
await globalState.adoptSafely(globalState.contexts.B);
Notes
Largely unavailable in browser execution. The
zw.browserContext.*
API is available mostly for local code execution. Only inspection methodszw.browserContext.getContextInfo()
andzw.browserContext.getDefaults()
are supported in the browser.
Last updated
Was this helpful?