SDK reference
The VibeOS SDK
Sandboxed apps reach the operating system through one global object: window.vos. It gives you a permission-scoped filesystem, hosted AI, a proxied network client and app metadata — no keys, no imports, no build step.
What the SDK is
When VibeOS opens a .vibe app, your HTML document runs inside a sandboxed iframe with no direct access to the desktop, other apps, or the network. Before your scripts run, the host injects a single global object — window.vos — which acts as the only door out of the sandbox.
- Nothing to install. No npm package, no
<script src>. The object simply exists at runtime. - Everything is a Promise. Each call is a message to the host and back, so results are always awaited.
- Everything is permission-scoped. What your app may touch is decided by the permissions in your manifest and approved by the user at install time.
Booting your app
The SDK is injected asynchronously, and VibeOS fires a one-shot vos-ready event on window once window.vos is available. Because your script may run before or after that moment, always use the defensive pattern below rather than a bare event listener.
<script>
// window.vos is injected asynchronously. Two things can be true when your
// script runs: the SDK is already there, or it isn't yet.
// This pattern handles both — always use it.
function start() {
// Safe to call window.vos.* from here on.
window.vos.app.getInfo().then(function (info) {
document.title = info.name;
});
}
if (window.vos) start();
else window.addEventListener("vos-ready", start, { once: true });
</script>A bare vos-ready listener can miss the event
vos-ready fires once and does not replay. If your listener is attached after it fired — which happens routinely — your app never starts. Check window.vos first, then fall back to the event.
// Every method returns a Promise, so async/await works normally.
async function start() {
const info = await window.vos.app.getInfo();
const files = await window.vos.fs.readDir(info.sandboxPath);
console.log(info.name, "has", files.length, "files");
}// Calls reject with a normal Error. Always handle rejection —
// a missing file, a denied path and an offline API all arrive this way.
try {
const text = await window.vos.fs.readFile("home/Documents", "note.md");
} catch (err) {
console.warn("read failed:", err.message);
}API surface
Four namespaces, eleven methods. Each namespace has its own reference page with full signatures, return shapes, recipes and failure modes.
| Namespace | What it does | Requires |
|---|---|---|
| vos.fs | Read, write, delete, rename and watch files in the user's Home directory or your app's private sandbox. | read |
| vos.ai | Hosted chat completions through the VibeOS AI gateway, plus an entitlement pre-check. | ai: true |
| vos.net | Outbound HTTPS requests proxied by the host, CORS-free and restricted to declared origins. | network |
| vos.app | Metadata about the running instance: name, id, sandbox path, granted permissions. | Nothing |
How calls work
Every method is a remote procedure call. The SDK posts a message to the host window, the host validates and performs the operation, then posts the result back and your Promise settles. Three consequences matter when you build:
- Calls have a 30-second timeout. If the host does not answer within 30s the Promise rejects. Keep operations small and batch thoughtfully.
- Permissions are re-checked every single call. The host reads the grant the user approved at install time from its own storage — never from the manifest inside your HTML. An app can never widen its own scope at runtime.
- Errors arrive as plain messages. Rejections are a normal
Errorwhosemessagedescribes the failure. See errors & limits for the full catalogue and the caveat about error codes.
Test everything in the browser IDE
The VibeOS IDE runs your app against a live SDK implementation with a JavaScript console and a file explorer, so you can call window.vos.* by hand and watch the filesystem change before you ever submit.
What's new
The SDK is versioned with the VibeOS desktop, and the host is backwards compatible — nothing has been renamed or removed. The most recent additions are all in the filesystem namespace:
| Addition | Why it matters |
|---|---|
fs.deleteDir(path) | Recursively delete a folder in one call instead of walking and deleting each file. |
fs.rename(from, to) / fs.move(from, to) | Native rename and move. Same-volume moves are metadata-only, so large files move instantly. |
fs.onChange(cb) | Subscribe to every filesystem change so your UI stays in sync when the user edits files outside your app. |
fs.watch(path, cb, opts) | Debounced, path-filtered watching — the recommended way to keep a document view live. |
Migrating older apps
If your app implemented rename by read-write-delete, or polled for external edits, replace those with fs.rename and fs.watch. The old patterns still work, so migration is optional and can be done incrementally.
TypeScript types
Apps ship as a single inline HTML document, so most are plain JavaScript. If you author in TypeScript and compile to one file, these declarations describe the whole runtime surface.
// Optional: drop this into your app for editor autocomplete.
// The runtime object is injected by VibeOS — you never import anything.
type VosEntry = { name: string; type: string };
interface Vos {
fs: {
readDir(path: string): Promise<VosEntry[]>;
readFile(path: string, name: string): Promise<string | null>;
writeFile(path: string, name: string, content: string): Promise<boolean>;
deleteFile(path: string, name: string): Promise<boolean>;
deleteDir(path: string): Promise<boolean>;
rename(fromPath: string, toPath: string): Promise<boolean>;
move(fromPath: string, toPath: string): Promise<boolean>;
onChange(cb: (d: { affectedPath?: string }) => void): () => void;
watch(
path: string,
cb: (d: { affectedPath?: string }) => void,
opts?: { debounceMs?: number },
): () => void;
};
ai: {
chat(messages: { role: "system" | "user" | "assistant"; content: string }[]): Promise<string>;
canUse(category?: string): Promise<{ ok: boolean; code?: string; message?: string }>;
};
net: {
fetch(url: string, init?: { method?: string; headers?: Record<string, string>; body?: string }):
Promise<{ status: number; statusText: string; headers: Record<string, string>; body?: string; bodyBase64?: string }>;
};
app: {
getInfo(): Promise<{
name: string;
id: string;
closeBehavior: "close" | "minimize";
sandboxPath: string;
permissions: { hasRead: boolean; hasWrite: boolean; hasAi: boolean; hasNet: boolean; network: string[] };
}>;
};
}
declare global {
interface Window { vos: Vos }
}Next steps
vos.fs
Paths, the private sandbox, every method, and recipes for folders, moves and live watching.
vos.ai
Hosted chat completions, entitlement checks and the patterns that pass review.
vos.net
Proxied HTTPS requests, allow-list matching, binary responses and hard limits.
vos.app & events
Instance metadata, permission introspection, and the events VibeOS sends your window.