Script injection and registry
The ScriptInjector
Section titled “The ScriptInjector”window.agentlet.utils.ScriptInjector provides a promise-based API for injecting code, functions, or files, with automatic detection of the current execution environment.
const injector = window.agentlet.utils.ScriptInjector;
// Inject codeawait injector.inject({ code: 'console.log("Hello from injected script!");' });
// Inject a function with argumentsconst testFunction = (name, count) => { for (let i = 0; i < count; i++) console.log(`Hello ${name}!`);};await injector.inject({ func: testFunction, args: ['World', 3] });
// Inject a moduleawait injector.injectModule({ moduleCode: moduleSource, moduleUrl: 'my-module://example' });inject(options) requires one of code, file, or func:
code(string): JavaScript code to injectfile(string): file path to inject, extension environmentfunc(function) andargs(array): a function and its argumentstabId(number): target tab ID, extension environmenttarget('main' | 'isolated'): execution worldallFrames(boolean): inject into all frames
Static helpers: ScriptInjector.isExtensionEnvironment(), ScriptInjector.isContentScriptEnvironment(), and ScriptInjector.createFunctionInjection(func, ...args).
Multi-environment support
Section titled “Multi-environment support”The injector supports three execution environments, falling back automatically:
- Extension background or popup: uses
chrome.scripting.executeScriptdirectly. - Content script: messages the background script for injection.
- Web page or bookmarklet: falls back to DOM
<script>tag manipulation.
Running chrome.scripting.executeScript where it is available (rather than always manipulating the DOM) gives more reliable execution timing, proper MAIN/ISOLATED world separation, and compatibility with a page’s Content Security Policy.
From a background script or popup
Section titled “From a background script or popup”const injector = new ScriptInjector();
await injector.inject({ code: 'alert("Injected from extension!");', tabId: currentTabId, target: 'main',});Registry loading via script injection
Section titled “Registry loading via script injection”Agentlet registries load through <script> tag injection instead of fetch(), to avoid CORS issues when loading registry configuration from a different domain, and to work reliably behind corporate firewalls and CSP.
Registry file format
Section titled “Registry file format”A registry file is a .js file that builds a registry object and dispatches it as a custom event, rather than a .json file requiring a CORS-compliant server:
(function () { 'use strict';
const registry = { agentlets: [{ name: 'hello-world', url: 'https://example.com/hello-world.js', module: 'HelloWorldModule' }], };
const event = new CustomEvent('agentletRegistryLoaded', { detail: registry }); setTimeout(() => window.dispatchEvent(event), 10);})();Loading proceeds in five steps: ModuleRegistry injects a <script> tag pointing at the registry URL, sets up a listener for the agentletRegistryLoaded event, applies a 10-second timeout to avoid hanging on a failed load, the registry script dispatches the event once loaded, and the event’s detail is processed the same way a fetched JSON payload would have been.
Configuration
Section titled “Configuration”const agentlet = new AgentletCore({ registryUrl: 'https://cdn.example.com/agentlets-registry.js',});Serve .js registry files with Content-Type: application/javascript. CORS headers are not required for script-tag loading, but may still be useful for other API calls made by the same backend.
Security considerations
Section titled “Security considerations”- Content integrity: consider
script.integrityandscript.crossOrigin = 'anonymous'for registry scripts served from a third-party CDN. - Trusted domains: validate
registryUrl’s hostname against an allowlist before injecting it. - CSP compatibility: make sure
script-srcin your Content Security Policy allows the registry’s domain.
Migrating from DOM injection
Section titled “Migrating from DOM injection”Earlier versions of agentlet-core injected code purely through DOM <script> tag manipulation. ScriptInjector replaces that with chrome.scripting.executeScript where available, keeping the DOM approach only as the web-page fallback described above.
function loadExternalScript(url) { const script = document.createElement('script'); script.src = url; script.onload = () => console.log('Loaded'); document.head.appendChild(script);}async function loadExternalScript(url) { const injector = window.agentlet.utils.ScriptInjector; await injector.inject({ file: url }); console.log('Loaded');}Existing modules that only used DOM injection in a web page environment keep working unchanged: the fallback chain (extension API, then background messaging, then DOM injection) means no breaking change to the public API.
Troubleshooting
Section titled “Troubleshooting”- “ScriptInjector not available”: ensure agentlet-core is loaded before using it.
- Content script injection timeout: verify the background script is responding and that extension permissions are granted.
- DOM injection setup failed: check for Content Security Policy restrictions and that
document.headis available.
Source: agentlet-core docs/registry-script-injection.md, docs/script-injection-migration.md, and src/types/public-api.d.ts at e3f78fa. injectModule()’s validateSecurity option, described in the migration source document, is not part of the current ScriptInjectorAPI type, so it was dropped from the examples here.