This package (asyar-sdk) provides the Software Development Kit (SDK) for building extensions for the Asyar Launcher. It defines the core interfaces, types, and services that extensions interact with.
The Asyar SDK enables developers to create extensions that integrate seamlessly with the Asyar core application. It provides access to essential services like logging, extension management, action handling, clipboard history, and notifications.
Install the SDK as a dependency in your extension project:
npm install -g asyar-sdk # installs the CLI globallyOr add it to your project:
pnpm add asyar-sdkThe asyar CLI drives the full extension development workflow:
| Command | Description |
|---|---|
asyar dev |
Start development mode with hot reload |
asyar build |
Production build of your extension |
asyar validate |
Check manifest and project structure |
asyar link |
Symlink your extension into the app's extensions directory |
asyar attach |
Register an extension directory for dev loading in the launcher |
asyar detach |
Unregister a dev extension from the launcher |
asyar publish |
Build, package, and publish to the Asyar Store |
asyar doctor |
Diagnose environment issues |
asyar --version |
Show CLI version |
The publish command includes automatic guards:
- Stale build detection — blocks publishing if source files are newer than the build output
- Duplicate version check — blocks publishing if the version is already live in the store
The attach and detach commands enable a fast developer loop:
- Attach:
asyar attach .— Builds the extension and registers its current directory with the Asyar App. The launcher will now load this extension directly from your project folder during development. - Bulk Attach:
asyar attach --all /path/to/extensions— Scans for and attaches all extensions in a category/folder. - Detach:
asyar detach— Removes the dev registration.
Unlike asyar link, which uses symlinks/copying into a specialized system directory, attach allows the launcher to read your project in-place (respecting your development build outputs).
If you are contributing to the SDK itself, use the workspace setup described in the Asyar README.
The SDK has two build targets — the library (types/interfaces for extensions) and the CLI (developer tools):
pnpm run build # library only (tsconfig.json → dist/)
pnpm run build:cli # CLI only (tsconfig.cli.json → dist/cli/)
pnpm run build:all # both (recommended)The prepare script runs build:all automatically on pnpm install, so the CLI is always compiled when dependencies are installed.
When used inside the recommended workspace layout, the SDK is symlinked into sibling packages:
Asyar-Project/
├── asyar/ → asyar/node_modules/asyar-sdk symlinks here
├── asyar-sdk/ → you are here
└── extensions/ → extensions/*/node_modules/asyar-sdk symlinks here
After editing SDK source, run pnpm run build:all — changes are instantly available to all linked packages. No manual copying needed.
See docs/RELEASING.md for instructions on how to version and publish new SDK releases.
This SDK is the bridge allowing Asyar extensions to interact with the Host Application. It dynamically adapts its behavior based on the execution context of the extension utilizing it.
Refer to the Extension Development Guide for detailed instructions on building extensions.
The SDK supports two distinct environments seamlessly:
- Tier 1 (Built-in Extensions):
- These extensions run directly inside the main Asyar Window context.
- The SDK bypasses strict
<iframe>security verifications. MessageBrokerrequests automatically resolve against Host services synchronously.
- Tier 2 (Installed Extensions):
- These extensions are executed within strictly isolated, secure
<iframe>sandboxes. - The SDK transparently serializes all Native SDK queries (such as navigating to a view, throwing a notification, checking the clipboard) into remote
postMessageIPC payloads. - The Host Application intercepts these simulated payloads, validates the iframe's
extensionId, unpacks the variables via positional mapping, and returns a Promise.
- These extensions are executed within strictly isolated, secure
Warning
IPC Payload Requirements for SDK Contributors:
When adding new proxy boundaries to ExtensionManagerProxy, you MUST send payloads as named-key property objects where keys correspond to the Host's parameter names in order (e.g., broker.invoke('method', { query, limit })).
Sending raw primitives will cause the generic deserializer inside the Asyar Host to convert the argument into "[object Object]", silently breaking the pipeline.
Key exports include:
- Interfaces:
Extension,ExtensionContext,ILogService,IExtensionManager,IActionService,IClipboardHistoryService,INotificationService,IStatusBarService, etc. - Types:
ExtensionResult,ExtensionAction,ClipboardItem,IStatusBarItem, etc. - Proxies:
StatusBarServiceProxyand others providing safe cross-context RPC.
Example import in an extension's index.ts:
import type {
Extension,
ExtensionContext,
ExtensionResult,
ILogService,
IExtensionManager,
} from "asyar-sdk";
import type { ExtensionAction, IActionService } from "asyar-sdk";
class MyExtension implements Extension {
private logService?: ILogService;
async initialize(context: ExtensionContext): Promise<void> {
this.logService = context.getService<ILogService>("LogService");
this.logService?.info("Extension initialized using asyar-sdk");
}
// ... other methods
}
export default new MyExtension();Add an icon field to your manifest to show a branded icon next to your commands in the launcher search results.
| Format | Example | Where rendered |
|---|---|---|
| Built-in icon | "icon:calculator" |
Manifests, commands, search results, actions — rendered by the host |
| Emoji | "👋" |
Manifests, commands, search results, actions — rendered by the host |
| Web URL | "https://example.com/icon.png" |
Commands and search results — rendered by the host |
| Local path | "assets/icon.png" |
Commands and search results — rendered by the host |
Use the <asyar-icon> custom element to render built-in icons inside your extension views. Icons are rendered as SVGs with viewBox="0 0 24 24", fill="none", and stroke="currentColor".
<!-- Register the element (usually in your main.ts) -->
<!-- import { registerIconElement } from 'asyar-sdk'; -->
<!-- registerIconElement(); -->
<!-- Use in your HTML/Svelte/React templates -->
<asyar-icon name="calculator" size="20" stroke="2"></asyar-icon>| Attribute | Default | Description |
|---|---|---|
name |
(required) | The name of the built-in icon (e.g., calculator, settings) |
size |
24 |
The width and height of the SVG in pixels |
stroke |
1.5 |
The stroke-width of the icon paths |
Extension-level icon (applies to all commands as default):
{
"id": "com.example.my-extension",
"icon": "🚀",
"commands": [...]
}Command-level icon (overrides the extension icon for a specific command):
{
"commands": [
{ "id": "open", "name": "Open My Extension", "icon": "🚀" },
{ "id": "quick-run", "name": "Quick Run", "icon": "⚡" }
]
}The Asyar host automatically injects two things into every extension iframe:
- Design tokens — the full set of CSS custom properties (
var(--token-name)) - Fonts — Satoshi and JetBrains Mono are delivered as base64 data URIs so
var(--font-ui)andvar(--font-mono)render the real typefaces, not system fallbacks
No setup needed — just use the variables in your CSS.
Theme changes are live. When the user switches between light and dark mode, the host re-injects updated token values. Your extension's UI updates without a reload. Fonts are sent once on load and cached.
.card {
background: var(--bg-secondary);
color: var(--text-primary);
border: 1px solid var(--separator);
border-radius: var(--radius-md);
padding: var(--space-6);
}During development, import the static fallback file for IDE autocomplete and neutral defaults:
// vite.config or main.ts
import 'asyar-sdk/tokens.css';Available token categories
| Category | Variables |
|---|---|
| Backgrounds | --bg-primary, --bg-secondary, --bg-tertiary, --bg-hover, --bg-selected, --bg-popup |
| Text | --text-primary, --text-secondary, --text-tertiary |
| Borders | --border-color, --separator |
| Accent | --accent-primary, --accent-success, --accent-warning, --accent-danger |
| Brand | --asyar-brand, --asyar-brand-hover, --asyar-brand-muted, --asyar-brand-subtle |
| Shadows | --shadow-xs → --shadow-xl, --shadow-popup, --shadow-focus |
| Radius | --radius-xs → --radius-full |
| Spacing | --space-1 (4px) → --space-11 (48px) |
| Font sizes | --font-size-2xs (10px) → --font-size-display (2.25rem) |
| Font families | --font-ui (Satoshi), --font-mono (JetBrains Mono) |
| Transitions | --transition-fast, --transition-normal, --transition-smooth, --transition-slow |
See tokens.css for the full list with fallback values.
Add a platforms field to your manifest to restrict your extension to specific operating systems. Extensions that don't support the current OS are hidden in the store and blocked from loading by the host.
{
"id": "com.example.my-extension",
"platforms": ["macos", "linux"],
"commands": [...]
}Valid values: "macos", "windows", "linux". You can list any combination.
Omit the field entirely for a universal extension — that is the default. The asyar validate command enforces the allowed values and rejects anything outside the list.
| Manifest value | Behaviour |
|---|---|
| Field absent | Works on all platforms (universal) |
["macos"] |
macOS only |
["macos", "linux"] |
macOS and Linux, not Windows |
["windows", "linux"] |
Windows and Linux, not macOS |
| Script | Description |
|---|---|
build |
Compiles the SDK library (types, interfaces, proxies) |
build:cli |
Compiles the CLI tool |
build:all |
Compiles both SDK library and CLI |
prepare |
Runs build:all automatically on install |
watch |
Compiles the SDK library in watch mode |
Distributed under the AGPLv3 License. See LICENSE.md for more information.
Extensions can register actions that appear in the ⌘K panel. Actions support optional grouping via the category field and icons via icon.
import { ActionContext, ActionCategory } from 'asyar-sdk';
actionService.registerAction({
id: 'my-extension:do-thing',
title: 'Do Something',
description: 'A helpful description shown in the panel',
icon: '✨',
category: ActionCategory.PRIMARY, // Optional — groups related actions
extensionId: context.extensionId,
context: ActionContext.GLOBAL,
execute: async () => {
// your action logic
}
})| Constant | Display name | Use for |
|---|---|---|
ActionCategory.PRIMARY |
Primary | Main actions for the extension |
ActionCategory.NAVIGATION |
Navigation | Opening views, going back |
ActionCategory.EDIT |
Edit | Create, update, delete operations |
ActionCategory.SHARE |
Share | Export, copy, send |
ActionCategory.DESTRUCTIVE |
Destructive | Irreversible actions (delete, reset) |
ActionCategory.SYSTEM |
System | Reserved for built-in host actions |
Custom strings are always allowed. ActionCategory provides recommended names for consistency across extensions. If no category is set, the ⌘K panel automatically groups the action under the extension's display name.