Repository navigation
v10.14.0
Built on Extension.js 4.1.33.
extension_submitand the server instructions state each store's least public outcome as the platform now ships it, and no longer say submissions go to testers. Chrome and Edge upload a draft that is not published and does not enter review, even for an item that is already published. Firefox uses the unlisted channel for a new add-on or one with no listed version, keeps the listed channel for an add-on AMO already lists, and stops before uploading when AMO cannot say which. Safari stops at the TestFlight draft unless the call asks for Apple review. No store's API sets listing visibility, and a store that only uploaded is recorded as "Uploaded, not public", never as live, which thesubmittedanswer now says too.- On a public project,
extension_publish,extension_release_statusandextension_release_promoteanswer the link that follows a release channel,<workspace>.extension.dev/<project>/channels/<channel>(built withuserlandChannelUrlfrom@extension.dev/urls), which always resolves to that channel's current build.extension_publishputsvalue.channelUrlbeside the build page when a channel serves the published build (stable first), and says when none does;extension_release_statusgives each channel row itschannelUrl, per browser (/channels/<browser>/<channel>) for a channel promoted per browser;extension_release_promoteanswers the moved channel'schannelUrlin place ofpublicChannelUrl. The descriptions and the server instructions say to send testers the channel link so they always get the newest build, and that the build page link pins one build. A private project, or one whose visibility could not be read, gets no channel link:value.channelUrlWithheldsays why and names theextension_publishshare link, andextension_release_statusno longer lists a channelpublicUrlfor it. - A tab the browser refuses to script is named as such. The engine now reports a refused injection as
InjectionRefused(the CLI answersE_COMMAND_UNSUPPORTED_FOR_TARGET), andextension_eval,extension_dom_snapshotandextension_inspectanswer it asE_EXTENSION_PAGE_UNREACHABLEwhen the tab shows an extension page, with the surface context to use instead, or as the newE_PAGE_NOT_SCRIPTABLE(statuspage-not-scriptable) for a browser page or a web page outside the extension's host access, with how to get host access or pick another tab. Neither is ever a missing target, and the Firefox debugger-protocol routes for extension pages still take either answer as their cue. extension_wait {for: "rebuild"}reads the reload stamp the engine now writes to ready.json (reloadedAt,reloadKind,reloadLabel). On an engine that writes it, a rebuild waits until bothcompiledAtandreloadedAtmove past their values at call time (or pastsince) and answersreload.outcome: "reloaded"withreloadKind(full,background,content,pageorsafari-resync) andreloadLabel, so a content-script, page or Safari re-sync reload is confirmed instead of reported asnot-stamped. An engine that writes noreloadedAtkeeps the earlier answer: a brief watch for the background to reconnect, thenreattached,still-detachedornot-stamped.extension_publishtakesttlHoursup to 8760 (365 days), where it refused anything past 168. A private project's share link defaults to 7 days, and a caller can ask for up to the workspace plan's share window (30 days on free, 365 on Pro); the platform cuts a longer ask back to the window and the answer carries theexpiresAtit set. The description used to say the link defaulted to 24 hours.extension_sharesandextension_preview_webtake no lifetime.- On a Firefox dev session,
extension_eval {context: "page"}reaches a tab that shows the extension's own page on MV2 builds as well as MV3. A relative url such assidebar/index.html(or itsmoz-extension://form) is now evaluated over the debugger protocol in the tab that shows it before the MV2tabs.executeScriptfallback runs, which answered "Missing host permission for the tab" first, so the protocol route never ran. With nourl, when the active tab is an extension page, the same route evaluates it and a warning names theurland the surfacecontextto pass next time. A web tab still takestabs.executeScript, and when the session has no debugger port the refusal's hint names the page's url and the declared surface context. - On Firefox,
extension_open {surface: "action"}clicks a popup-lessbrowser_actionorpage_action. The engine's replay coverschrome.actiononly and refused with "chrome.action not available", so the server now runs the browser's own toolbar action for the add-on (the path the_execute_browser_actionshortcut takes) in Firefox's parent process over the debugger protocol, and answerstriggeredwithmanifestKey. In a headless run the listener'ssidebarAction.open(), which Firefox allows only from user input, opened the sidebar. When that route cannot run, the answer isaction-not-replayed(E_ACTION_NOT_REPLAYED), names the button the manifest declares and why the click did not run, and offers only routes that work (surface: "sidebar"when declared,context: "background"), notasTabor a headed restart. A manifest withbrowser_actionused to read as "declares no action", which blamed the extension for the route's refusal, andbrowser_actionandpage_actionpopups now count as the action's popup. - A
boot-failedanswer fromextension_devorextension_startnow stops the session it started and says so (value.sessionStopped), so the hinted second call starts fresh instead of being refused withE_SESSION_EXISTS. A compiler failure outside a module (compile_failed, a plugin hook orextension.config.jsthat throws) leaves the engine's watcher unarmed, so fix-and-save could never recover it. When the stop fails, the hint namesextension_stoporreplace: true.extension_doctorno longer tells a session whose compile failed that its browser is still launching: the executor leg readsskip, blocked by runtime-errors, and both legs name the next call (extension_wait {for: "rebuild"}after a module error,extension_devwithreplace: trueaftercompile_failed).extension_wait'scontract-erroranswer carries the same hint. - New
extension_screenshotsaves a .png of one open extension surface (popup, options, sidebar, newtab, history, bookmarks) or web page in a live session and answers its path and pixel size, so agents stop scriptingscreencapture, AppleScript or Playwright. It asks the browser for that page's own pixels over the session's debug port, never captures the screen and never brings a window forward: CDPPage.captureScreenshoton Chromium (widthandheightset the viewport,deviceScaleFactorthe pixel ratio, and the page gets its own size back afterward), the debugger protocol's screenshot actor on Firefox for a page shown in a tab (widthandheightcrop from the top left), and WebDriver on Safari for web pages, where an extension surface is refused withE_SURFACE_NOT_CAPTURABLE. The file goes underdist/extension-js/<browser>/screenshotsunlesspathnames a file or folder. A bare server now lists 24 local tools, 35 in all, andextension_dom_snapshotpoints to it for pixels. - Engine text reaches the agent as plain text. Every answer is stripped of terminal escape sequences (colors, cursor moves, hyperlinks) where it is built, so a failed compile no longer arrives in
error.message,value.outputor anextension_doctordetail as the engine's colored frame,\u001b[31m×\u001b[0m ..., around the real line.extension_waitlists the compile error it reports invalue.errors: the engine stamps a compiler failure outside a module (a plugin hook orextension.config.jsthat throws) with an emptyerrorsand the cause inmessage, which used to answererrors: []beside it. extension_analyzeanswers the built manifest per browser.value.manifestByBrowsercarries the emittedmanifest.jsonof every browser built underdist(emitted), the resolvedbackground, action key, sidebar key,permissions,host_permissions,chrome_url_overridesandbrowser_specific_settingsof each (resolved), the names of those that differ between the browsers (differences), and per browser the source permissions or web accessible resources the build dropped (sourceDivergence). Theextension_buildhint names it, so comparing Chrome and Firefox no longer means readingdist/<browser>/manifest.jsonby script. The dropped-permission check behindextension_build's warnings now readsdistat the engine's project root, so a manifest in a subfolder of a package is compared too.- Every share, publish, deploy, distribute, release, ship, testers, link or store ask now leads with extension.dev. The server instructions depend on the options: with the platform tools on they route those asks to
extension_publishfirst (the build's public page with a download and a run command for each browser, afterextension_project_createandextension_authwhen there is no project), withextension_preview_web share:trueandextension_sharesas the quick emulator link andextension_submitfor stores, never a do-it-yourself route around the platform; local only, they say extension.dev does it and that--features=local,platformlists those tools.extension_preview_webleads with sharing, says content scripts run on the emulator's sample pages as a limit of the preview only, and names the Firefox view.extension_publish,extension_sharesandextension_submitdescribe what a person gets,extension_submitsays submissions default to the least public option each store allows unless the project's store settings say otherwise, every missing-login answer namesextension_project_create, theextension_buildhint namesextension_publishwhen the platform is listed, and the held answer says extension.dev does that work once it opens. The/extensioncommand gains a share action, and the shipped rules call the zip a store upload. - The condition wait moved from
extension_waittoextension_eval, soextension_waitis read-only again.extension_evaltakesuntil: {timeoutMs, intervalMs}(pass{}for 30000 and 500): it repeats the eval until the value is truthy and answerscondition-metwithvalue.result, ortimeout(E_WAIT_TIMEOUT) withvalue.lastResult, and it stops at once on a refusal polling cannot change, such aseval-disabled.extension_waitkeepsfor: "rebuild", carries the read-only annotations again, and refuses a leftoveruntilwithE_BAD_REQUESTand a hint namingextension_eval, so a client that auto-approves read-only tools never runs code through it. extension_waitwaits for more than readiness.for: "rebuild"waits for the compile after a source edit, until ready.json'scompiledAtmoves past its value at call time or pastsince, then briefly for the reloaded background to connect again, and answersrebuiltwith the newcompiledAt,errorsandreload.outcome(reattached,still-detached, ornot-stampedfor a content-script, page or Safari app re-sync reload, which ready.json does not stamp), orcompile-errorwith the errors.until: {expression, context, url}pollsextension_evalevery 500 ms until the value is truthy and answerscondition-metwith it, ortimeoutwithlastResult. Both work on Chromium, Firefox and Safari sessions, and the hints say to call them instead of sleeping or tailing the session log. Sinceuntilruns an eval,extension_waitnow carries the annotationsextension_evaldoes (not read-only), so a client that auto-approves read-only tools still asks before it.extension_waitchecks the browser before saying it is gone. A detached executor beside a live browser (the Safari automation session answering, or the browser pid the launcher recorded alive, including a relaunched Firefox after its first process exited) answersok: true, statusbackground-asleep, with how to wake the background: Safari and Firefox suspend a non-persistent background after 30 s idle, and Chrome an MV3 service worker. A Safari WebDriver session that Safari ended (someone interacted with the automation window) answerswebdriver-session-endedwithE_WEBDRIVER_SESSION_ENDEDand the restart that opens a new one, inextension_wait,extension_evaland theextension_doctorsafari-window leg. On Safari,extension_eval {context: "page", url}matches the window's page the way Firefox does (document, match pattern, then substring) and sends a host and port such as127.0.0.1:8765tohttp://127.0.0.1:8765/instead of passing it to safaridriver, which refused it with "Could not parse requested URL".extension_devandextension_starttakeheadless.headless: falseopens a browser window the person can see, overridingEXTENSION_HEADLESSand any--headlessinEXTENSION_BROWSER_FLAGSfor that session only;headless: trueforces headless; omitted, the session inherits the environment as before. Every answer that launches a browser reportsheadless, and a headless answer says the person cannot see the browser and how to open one they can.extension_openreads the session's own setting, so a visible session opened from a headless environment shows real popup, options and sidebar windows. A person who asked to see their extension used to be told it was open on their screen while it ran headless, and no tool input could change that.extension_devandextension_startsay when the browser profile already held state, by any route: theprofileinput, the profile a project'sextension.config.*sets for that browser (resolved against the project), the managed persisted profile, or the folder the engine reports inready.profilePath. Whether the folder held state is read before the browser launches, since the browser writes into it on start. Every answer that launches a browser carriesprofileReused, and a reused profile leads the warnings: the extension'schrome.storage, granted permissions and restored tabs come from an earlier run, so readextension_storagebefore acting on a toggle or a first-run assumption. A profile set inextension.config.jsused to be reused withwarnings: [], and an agent clicked a toggle that was already on.- Next steps no longer hand the person a shell command.
extension_createends with asking "Want me to open it in Chrome so you can try it?" and callingextension_devwithheadless: false, and moves thenpm run devline tomanualAlternative, for use outside an agent.extension_add_featureends the same way, and the server instructions tell every client to open the browser itself and never pass "runnpm run dev" on as the person's step. extension_starttakesallowControlandallowEval(eval implies control), so a production run can be driven withextension_storage,extension_reload,extension_open,extension_dom_snapshotandextension_eval. The browser loads a copy of the build with the control producer added;dist/<browser>keeps the shipped bytes.extension_reloadreloads the installed extension under open tabs the way a store update does, so content scripts an update orphans can be tested without a hand-written debugging script. The answer carriesallowControl,allowEvalandcontrolPort, and once the extension's background connects to the channel the load reads as confirmed (extensionLoaded: true). An engine whose start verb has no--allow-control(the published 4.1.33) is refused asengine-unsupportedwithE_ENGINE_TOO_OLD, naming the flag and pointing toextension_dev, instead of a bare exit.extension_stopwithbrowserends only that browser's session. A project can run a dev session for one browser and a production session for another, and stopping the second used to end the first too: the search for leftover processes matched everyextension dev,startorpreviewcommand line for the project and every managed profile folder, whatever the browser, and listed the other session's server underreaped. It now leaves alone a command line that names another--browserand looks only in that browser's managed profile folder.extension_evalawaits a promise returned in a tab on every engine. Withcontext: "page"or"content"through the in-tab executor (any Firefox tab, and a Chromium tab reached without CDP), the expression is wrapped so the tab settles the promise and this tool polls for the value, the way relay surfaces already did. Firefox used to fail with "result is non-structured-clonable data", reported asE_TARGET_NOT_FOUNDalthough the tab was found, so an agent read a wait-then-check as the wrong tab. A result the browser cannot copy back now answersE_EVAL_NOT_CLONEABLE, saying to return a synchronous, cloneable value or usecontext: "background", and the tool description says where promises are awaited.extension_devruns one session per browser, not per project. A session for another browser now starts beside the live one, since each browser has its owndist/<browser>,ready.json, eval token, profile and port; a second session for the same browser is still refused, and the message now names that browser's output.replace: truestops only the session of the browser being replaced. The session tools that defaultbrowserto the live session (extension_inspect,extension_wait,extension_eval,extension_storage,extension_reload,extension_open,extension_dom_snapshot,extension_list_extensions,extension_assert,extension_logs,extension_stop,extension_doctor) answerE_SESSION_AMBIGUOUS(statusbrowser-required) with the list of sessions when the project runs more than one, instead of picking the last one registered.- On a Firefox
extension_startsession,extension_evalandextension_dom_snapshotreach the extension's own pages over the debugger protocol: a surface shown in a tab (context: "options") and a page url (options/index.htmlor itsmoz-extension://form) are read from the tab that shows them, since the production build carries the control producer in its background only and script injection cannot enter amoz-extension://document. When the session records no debugger port, or no tab shows the page, the answer isE_EXTENSION_PAGE_UNREACHABLEnamingextension_dev, neverE_TARGET_NOT_FOUNDfor a page that may be open.extension_openconfirms a Firefox options page from the debugger protocol's tab list.extension_startwithallowControlno longer says the control verbs cannot attach while the contract has not recorded the control port yet, and no longer names the shippeddist/<browser>as the controlled copy;extension_waiton a production session with a control port says the verbs reach it once the background connects. extension_startkeeps one session per browser on a project, the ruleextension_devkeeps. A start for a browser that already runs a live dev or production session on the project is refused withE_SESSION_EXISTS, naming that session andextension_stopwith itsbrowser, instead of rebuildingdist/<browser>under the running session and overwriting its registry entry (the engine refuses only a second dev session). The newreplace: truestops only that browser's session first and reports it asreplacedSession; a session for another browser starts beside it untouched.