Interop Service
InteropService — Invoke commands in other extensions
Runs in: view only. Cross-extension invocation is currently view-bound — if the worker needs to invoke another extension's command, route the request through view code.
Permission required: extension:invoke.
Lets an extension trigger any command declared in another installed extension's manifest.json. This is the building block for extension composition: a workflow extension can chain steps across multiple tools, a shortcut extension can launch a specific view from another extension, and so on. Any command declared in a manifest is potentially invokable by other extensions that hold the required permission.
export interface IInteropService {
/**
* Invoke a command from another installed extension.
*
* @param extensionId - The target extension's manifest `id`
* (e.g. `'com.example.calc'`)
* @param commandId - The command's `id` as declared in the target manifest
* (e.g. `'run'`)
* @param args - Optional arguments forwarded to the command
*/
launchCommand(
extensionId: string,
commandId: string,
args?: Record<string, unknown>
): Promise<void>
}
Minimal usage:
import type { IInteropService } from 'asyar-sdk';
const interop = context.getService<IInteropService>('interop');
// Open the clipboard-history extension's default view
await interop.launchCommand('org.asyar.clipboard', 'show-clipboard');
// Pass arguments to the target command
await interop.launchCommand('com.example.calc', 'run', { query: '5+3' });
Finding the right extensionId and commandId:
Both values come from the target extension's manifest.json:
{
"id": "com.example.calc",
"commands": [
{ "id": "run", "name": "Calculate", "description": "…" }
]
}
The id at the top level is the extensionId. The id inside each commands entry is the commandId.
How it works under the hood
Calling extension (iframe) Host
────────────────────────────────────────────────────────
1. interop.launchCommand('com.example.calc', 'run', args)
2. InteropServiceProxy:
broker.invoke('interop:launchCommand',
{ extensionId, commandId, args: args ?? null })
─────────────────────────────────►
3. ExtensionIpcRouter:
permission check (extension:invoke)
inject callerExtensionId as first arg
4. InteropService.launchCommand():
objectId = 'cmd_com.example.calc_run'
command registered?
no → extension installed?
no → throw EXTENSION_NOT_FOUND
yes → throw COMMAND_NOT_FOUND
execute command via handleCommandAction
◄─────────────────────────────────
3. Promise resolves → void
The host validates that the target command is registered before executing it. If the target extension opens a view, the launcher's navigation stack updates exactly as if the user had selected that command from search.
Error handling
Errors cross the IPC boundary as plain Error objects with a message string. Match on the message content to distinguish error types:
try {
await interop.launchCommand('com.example.calc', 'run');
} catch (err) {
if (err.message.includes('is not installed')) {
// Target extension is not installed
} else if (err.message.includes('not found in extension')) {
// Extension is installed but that command ID doesn't exist
} else if (err.message.includes('Permission denied')) {
// extension:invoke not declared in caller's manifest
}
}
| Error message pattern | Cause |
|---|---|
Extension "..." is not installed |
No extension with that extensionId is installed |
Command "..." not found in extension "..." |
Extension is installed but has no command with that commandId |
Permission denied: "extension:invoke"... |
extension:invoke missing from caller's manifest |
Availability
The target command must be registered — i.e. the target extension must be loaded and active. Built-in features (org.asyar.*) are always available. Third-party extensions are available after first activation (which happens on first search or explicit launch).
Security
- Requires
extension:invokein the calling extension's manifest. - The host injects
callerExtensionIdinto the invocation context for audit logging — the platform always knows which extension initiated the call. - The target extension does not need to explicitly "export" commands; every command listed in its
manifest.jsonis invokable. - There is no mechanism for extensions to enumerate other extensions' commands at runtime. The caller must know the
extensionIdandcommandIdin advance (e.g., from the target extension's documentation).