API 1.7.0
Manifest and SDK reference
This section is generated at website build time from the actual contract and dependency-free SDK declaration. API version: 1.7.0. Import the interfaces as TypeScript types; window.tabsExtension exists only in an active desktop extension main frame.
Choosing an API
Use the injected bridge, not Electron imports or direct fetch. The host binds calls to the current installed package, project, and assigned profile; callers never supply those identities. A capability in the manifest requests access but does not grant it. Ask users to grant it in Tabs, and make denial an ordinary inline UI state.
storage.get, storage.set, and storage.delete
Use these for non-secret account/profile state such as drafts or preferences. get takes a key and resolves to JSON or null when absent. set takes a key and JSON value and resolves without a value; delete takes a key and also resolves without a value. JSON excludes functions, undefined, binary objects, and cycles. The host chooses the assigned profile, so changing Work/Personal assignments does not merge documents. Use the credentials settings for tokens instead of storing them here.
workspace.readText
Read a UTF-8 text file from the active project using a relative path, for example README.md. The promise resolves to its text. Do not pass an absolute path, traversal path, or a path from another project. There is no file-write, directory-listing, shell, or arbitrary filesystem API in this bridge. An unsupported project or denied grant must be shown as unavailable, not silently replaced with another project's data.
git.status
Read the active project's branch and dirty flag. The method takes no arguments and resolves to an object containing branch and dirty; it does not return changed filenames or run caller-supplied commands. It is appropriate for a status indicator, not a general Git client.
network.getText
Send an HTTPS GET to an exact declared network host and resolve to text. The only option is useProfileCredential; it asks the trusted host to attach the active profile's saved Bearer credential. There is no arbitrary method, header, redirect, or credential-read API. Treat service failures as retryable UI states without logging credentials or response bodies containing account secrets.
logic.invoke
Pass a declared command ID and JSON input to packaged pure logic; the promise resolves to JSON output. This is not a privileged background extension host. The logic runtime cannot call storage, workspace, Git, network, or credentials. AI exposure additionally depends on the manifest contribution and project grant; invoking a command from the UI does not imply it is available to an agent.
Lifecycle and failure handling
Read data again after a host-owned reload or project/profile change. Do not assume that a previously granted call remains authorized: uninstall, disable, revocation, and assignment changes can invalidate it. Catch rejected promises, retain unsaved UI input where possible, and explain which host setting the user should check. Raw exception messages are not stable API error codes. Do not repeatedly retry a permission denial or treat authentication expiry as package revocation.
Manifest contract
export const TabsExtensionManifest = Schema.Struct({
manifestVersion: Schema.Literal(1),
publisher: Schema.String,
name: Schema.String,
version: Schema.String,
displayName: Schema.String,
description: Schema.String,
releaseNotes: Schema.optionalKey(Schema.String),
sourceUrl: Schema.optionalKey(Schema.String),
supportUrl: Schema.optionalKey(Schema.String),
privacyUrl: Schema.optionalKey(Schema.String),
listing: Schema.optionalKey(
Schema.Struct({
readme: Schema.optionalKey(Schema.String),
icon: Schema.optionalKey(Schema.String),
screenshots: Schema.optionalKey(
Schema.Array(Schema.Struct({ path: Schema.String, alt: Schema.String })),
),
license: Schema.optionalKey(Schema.String),
categories: Schema.optionalKey(Schema.Array(Schema.String)),
keywords: Schema.optionalKey(Schema.Array(Schema.String)),
externalServices: Schema.optionalKey(Schema.String),
}),
),
engines: Schema.Struct({
tabs: Schema.String,
api: Schema.optionalKey(Schema.String),
}),
networkHosts: Schema.optionalKey(Schema.Array(Schema.String)),
storage: Schema.optionalKey(
Schema.Struct({
version: Schema.Number,
migrations: Schema.optionalKey(Schema.Array(TabsExtensionStorageMigration)),
}),
),
logic: Schema.optionalKey(Schema.Struct({ entry: Schema.String })),
capabilities: Schema.optionalKey(
Schema.Array(
Schema.Literals([
"profile-storage",
"workspace-read",
"git-status",
"network",
"credentials",
"ai-tools",
]),
),
),
contributes: Schema.Struct({
tools: Schema.Array(TabsExtensionTool),
commands: Schema.optionalKey(Schema.Array(TabsExtensionCommand)),
}),
});
Public API declarations
export declare const TABS_EXTENSION_API_VERSION: "1.7.0";
export interface TabsExtensionStorageMigration {
readonly from: number;
readonly to: number;
/** Rename top-level keys in each shared or project-isolated profile document. */
readonly renames: ReadonlyArray<{ readonly from: string; readonly to: string }>;
}
export type TabsExtensionCapability =
| "profile-storage"
| "workspace-read"
| "git-status"
| "network"
| "credentials"
| "ai-tools";
export interface TabsExtensionTool {
readonly id: string;
readonly label: string;
readonly entry: string;
readonly icon?: string;
}
export interface TabsExtensionCommand {
readonly id: string;
readonly label: string;
readonly description: string;
/** Pure JSON command exposed to agents only after a per-project grant. */
readonly aiCallable?: boolean;
}
/** Logic commands run in a disposable, JSON-only runtime with no host capabilities. */
export interface TabsExtensionManifest {
readonly manifestVersion: 1;
readonly publisher: string;
readonly name: string;
readonly version: string;
readonly displayName: string;
readonly description: string;
readonly releaseNotes?: string;
readonly sourceUrl?: string;
readonly supportUrl?: string;
readonly privacyUrl?: string;
/** Packaged presentation metadata; requires engines.api >=1.6.0. */
readonly listing?: {
readonly readme?: string;
readonly icon?: string;
/** Up to six packaged raster previews with accessible descriptions; requires engines.api >=1.7.0. */
readonly screenshots?: ReadonlyArray<{ readonly path: string; readonly alt: string }>;
readonly license?: string;
readonly categories?: ReadonlyArray<string>;
readonly keywords?: ReadonlyArray<string>;
readonly externalServices?: string;
};
readonly engines: {
readonly tabs: string;
/** Omission is treated as API v1 for older packages. New packages should declare it. */
readonly api?: string;
};
readonly capabilities?: ReadonlyArray<TabsExtensionCapability>;
readonly networkHosts?: ReadonlyArray<string>;
/** Omission means schema version 1; migrations are host-run before first activation. */
readonly storage?: {
readonly version: number;
readonly migrations?: ReadonlyArray<TabsExtensionStorageMigration>;
};
readonly logic?: { readonly entry: string };
readonly contributes: {
readonly tools: ReadonlyArray<TabsExtensionTool>;
readonly commands?: ReadonlyArray<TabsExtensionCommand>;
};
}
export type TabsExtensionJsonValue =
| null
| boolean
| number
| string
| ReadonlyArray<TabsExtensionJsonValue>
| { readonly [key: string]: TabsExtensionJsonValue };
/** Calls are authorized against the active extension, project, and assigned profile. */
export interface TabsExtensionHostBridge {
readonly storage: {
get(key: string): Promise<TabsExtensionJsonValue | null>;
set(key: string, value: TabsExtensionJsonValue): Promise<void>;
delete(key: string): Promise<void>;
};
readonly workspace: {
readText(relativePath: string): Promise<string>;
};
readonly git: {
status(): Promise<{ readonly branch: string; readonly dirty: boolean }>;
};
readonly network: {
getText(url: string, options?: { readonly useProfileCredential?: boolean }): Promise<string>;
};
readonly logic: {
invoke(commandId: string, input: TabsExtensionJsonValue): Promise<TabsExtensionJsonValue>;
};
}
declare global {
interface Window {
readonly tabsExtension: TabsExtensionHostBridge;
}
}
Method limits and failure behavior
| Method | Required grant | Result and limits |
|---|---|---|
| storage.get/set/delete | profile-storage | Non-secret JSON; each value at most 64 KiB, each profile document at most 1 MiB. Keys match [a-zA-Z][a-zA-Z0-9._-]{0,127}. Missing keys return null. |
| workspace.readText | workspace-read | Active project relative UTF-8 path; at most 1 MiB. Traversal and escaping links rejected. |
| git.status | git-status | Only branch and dirty state; no caller paths or Git commands. |
| network.getText | network | HTTPS GET to an exact declared host; no redirects, caller headers, or private-address access; 1 MiB text maximum, 15-second request deadline. |
| network.getText with useProfileCredential | network and credentials | Host attaches active profile Bearer token; bridge never returns the saved token directly. |
| logic.invoke | declared logic command | JSON input/output at most 64 KiB each; source at most 256 KiB; default 3-second outer watchdog; no privileged brokers. |
All privileged calls reject when the active identity, project, profile, or grant becomes invalid. Handle errors with readable inline status and retry controls; do not depend on raw host error strings as stable API codes.
Storage examples
These examples require the listed manifest capabilities and current project grants; the zero-permission starter deliberately cannot call privileged methods yet. Keep account tokens out of this non-secret storage.
await window.tabsExtension.storage.set("draft", { text: "A local note" });
const draft = await window.tabsExtension.storage.get("draft");
await window.tabsExtension.storage.delete("draft");
Read-only Git
Request git-status and API >=1.5.0. The host chooses the active project; no command or repository path is accepted from the extension.
const { branch, dirty } = await window.tabsExtension.git.status();
console.log(branch, dirty);
Network and credentials
Declare network and the exact networkHosts entry, then request a project grant. The URL below is an illustrative service, not a Tabs endpoint. Credential use additionally needs credentials and a token set through the active host profile's credential settings; the token is never returned to extension JavaScript.
const publicText = await window.tabsExtension.network.getText("https://api.example.com/status");
const accountText = await window.tabsExtension.network.getText("https://api.example.com/me", {
useProfileCredential: true,
});
Pure logic
Declare a packaged logic entry and a command named sum. Its synchronous run implementation receives JSON; it cannot call privileged SDK methods. This example assumes your command implements that sum operation.
const result = await window.tabsExtension.logic.invoke("sum", { values: [1, 2, 3] });
Workspace errors
try {
const text = await window.tabsExtension.workspace.readText("README.md");
// Render as text, never unsanitized HTML.
console.log(text.length);
} catch {
// Show a visible error and explain the required project permission.
}
Publisher/name must match ^[a-z][a-z0-9-]{1,62}$. Listing metadata requires engines.api starting at 1.6.0. README is a packaged .md path; listing icons are PNG/JPEG/WebP. Categories and keywords have at most 12 unique lowercase tags. A license identifier declares a license; it does not replace the license text or third-party notices.
listing.screenshots requires an API minimum of 1.7.0. Supply at most six unique packaged raster paths with nonempty accessible descriptions: [{ "path": "preview.png", "alt": "Project board showing pending tasks" }]. Stage those PNG/JPEG/WebP files with the package; each is limited to 1 MiB. Remote paths, SVG, traversal, duplicate paths, and missing descriptions are rejected. Scanning validates advertised image bytes before manual review. Only signed, approved releases expose preview assets by numeric index.