npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@astrid-runtime/sdk

v0.2.0

Published

System SDK for building user-space capsules for Astrid OS in JavaScript and TypeScript.

Downloads

56

Readme

@astrid-runtime/sdk

License: MIT OR Apache-2.0 Node: >=20 TypeScript: 5.2+

The system library for Astrid OS user space, in JavaScript / TypeScript.

Sibling of astrid-sdk (Rust). Same host ABI, same WIT contract, same .capsule archive shape — the kernel can't tell which language built the binary. Where the Rust SDK mirrors std, this one mirrors Node + WHATWG. Same semantics, idiom translated.

Where it fits

Capsule code (TypeScript or JavaScript)
    |
  @astrid-runtime/sdk           typed modules: fs, net, ipc, kv, http, ...
    |
  WIT-imported bindings    versioned "astrid:<domain>/host" + "astrid:io/*@1.0.0" (ComponentizeJS-generated)
    |
  Kernel                   capability checks, VFS, IPC bus, audit

The SDK never reaches the network, filesystem, or any external service directly. Every operation is a WIT host call into the kernel. The kernel decides whether to allow it.

Module layout

| Module | Rust SDK | Node / WHATWG idiom | |---|---|---| | fs | astrid_sdk::fs (mirrors std::fs) | node:fs/promises shape: overload-aware readFile, recursive mkdir/rm, real Dirent predicates, copyFile/realpath/readlink/link, and disposable FileHandle objects | | net | astrid_sdk::net (mirrors std::sync::mpsc) | mpsc-shaped errors (RecvError / TryRecvError::{Empty,Closed} / SendError) + Symbol.asyncIterator streams | | process | astrid_sdk::process | node:child_process conventions: spawn() returns a disposable ChildProcess; spawnSync() captures output; injectedFiles is a discriminated object union | | env | astrid_sdk::env (mirrors std::env) | env.get(key) → string \| undefined, env.getOrThrow(key), CONFIG_SOCKET_PATH | | time | astrid_sdk::time | now() → Date, sleep(ms) → Promise<void>, plus precise bigint clocks | | log | astrid_sdk::log (mirrors log crate) | log.{trace,debug,info,warn,error}. No globalThis.console shadowing — the engine may already wire it. | | ipc | astrid_sdk::ipc | publish/publishJson; subscribe(topic) returns a Subscription with .poll()/.recv(timeoutMs) (full parity, surfaces lagged/dropped) AND Symbol.asyncIterator for convenience | | kv | astrid_sdk::kv | Map-shaped get<T>(key)/set<T>/has/delete; versioned reads narrow on kind; raw byte variants remain available | | http | astrid_sdk::http (mirrors reqwest) | http.fetch() returns a genuine WHATWG Response; the separate RequestBuilder/send surface provides fluent buffered calls without shadowing web-platform names | | runtime | astrid_sdk::runtime | signalReady(), caller() → CallerContext, socketPath() | | capabilities | astrid_sdk::capabilities | check(sourceUuid, capability) → boolean | | elicit | astrid_sdk::elicit | secret/hasSecret/text/textWithDefault/select/array (install/upgrade only) | | identity | astrid_sdk::identity | resolve/link/unlink/createUser/listLinks | | approval | astrid_sdk::approval | request(action, resource) → boolean | | uplink | astrid_sdk::uplink | register(name, platform, profile) → UplinkId, send(id, userId, content) → boolean | | interceptors | astrid_sdk::interceptors | bindings() / poll(bindings, handler) — usually unneeded; @interceptor decorator handles dispatch | | hooks | astrid_sdk::hook | HookEvent with .payload, .json(), .canReply, .reply(), .skip(), .respond(); @hook handles fail-open dispatch and scoped replies |

Decorators (replaces #[capsule] macro)

| Rust attribute | TypeScript decorator | |---|---| | #[capsule] on impl block | @capsule on class | | #[astrid::tool("name")] | @tool("name", { mutable?, description?, inputSchema? }) | | #[astrid::interceptor("topic")] | @interceptor("topic", { mutable? }) | | #[astrid::hook("name")] | @hook("name", { mutable? }) | | #[astrid::command("name")] | @command("name", { mutable? }) | | #[astrid::install] | @install | | #[astrid::upgrade] | @upgrade | | #[astrid::run] | @run |

TypeScript 5.2+ standard decorators (TC39 Stage 3). No experimentalDecorators flag — default behaviour.

Quick start

npm install @astrid-runtime/sdk
npm install --save-dev @astrid-runtime/build typescript
import { capsule, tool, install, log, kv } from "@astrid-runtime/sdk";

@capsule
export class MyCapsule {
  greetings = 0;

  @tool("greet", { mutable: true })
  greet({ name }: { name: string }): { message: string; count: number } {
    this.greetings++;
    log.info(`greeting ${name} (#${this.greetings})`);
    return { message: `Hello, ${name}!`, count: this.greetings };
  }

  @install
  onInstall(): void {
    log.info("my-capsule installed");
  }
}

astrid build (from the Rust kernel CLI) detects package.json + Capsule.toml, shells out to the @astrid-runtime/build Node orchestrator, and emits a .capsule archive packaged identically to a Rust capsule's.

Error model: throw, don't Result

The Rust SDK returns Result<T, SysError>. This SDK throws a SysError extends Error. Its kind records the origin (HostError, JsonError, or ApiError) and code preserves a typed host error such as quota or capability-denied. Fighting JS to add Rust-shaped result values would be the opposite of "feels native".

Versioned KV storage

kv.setVersioned writes the same {"__sv": version, "data": value} envelope as Rust. kv.getVersioned returns a kind-discriminated union (current, needsMigration, unversioned, or notFound) for exhaustive narrowing. kv.getVersionedOrMigrate writes a successful migration back at the current version and rejects forward-version data.

const settings = kv.getVersioned<Settings>("settings", 3);
switch (settings.kind) {
  case "current":
    return settings.value;
  case "needsMigration":
    return migrate(settings.value, settings.storedVersion);
  case "unversioned":
  case "notFound":
    return defaults;
}

HTTP and child processes

The main HTTP path uses web-platform objects. Astrid controls are additive and the returned value remains an ordinary Response, so bodyUsed, clone(), text(), json(), blob(), and stream consumption behave normally.

const response = await http.fetch("https://api.example/v1/items", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ name: "capsule" }),
  timeoutMs: 5_000,
  maxResponseBytes: 1_000_000,
  httpsOnly: true,
});

if (!response.ok) throw new Error(`HTTP ${response.status}`);
const item = await response.json();

using child = process.spawn("worker", ["--serve"]);
child.writeStdin(new TextEncoder().encode("ready\n"));
const exit = child.wait(10_000);

const captured = process.spawnSync("worker", ["--version"]);

Lifecycle hooks

@hook("before_tool_call") receives a HookEvent. Return { skip: true } or { data: "..." } to publish on the correlation-scoped response topic, or return undefined to observe without replying. event.canReply identifies fire-and-forget events; reply, skip, and respond return whether they published. Malformed payloads and handler/reply failures are logged and return continue, preserving the hook bridge's fail-open contract.

Async model

The Rust SDK is synchronous because WASM exports are synchronous. APIs that are conventionally promise-based in JavaScript, such as fs.readFile, http.fetch, and time.sleep, retain that shape. Explicitly synchronous APIs say so (process.spawnSync); resource methods expose the underlying bounded host operation directly. ComponentizeJS's syncify settles promises at the WASM boundary, so await works normally inside @tool, @interceptor, and @run handlers.

@run handlers that loop forever (while (true) { await ipc.recv(...) }) block the WIT run export until the loop exits, matching the Rust SDK's daemon-style capsules exactly.

tool_describe and tool_execute_<name>

The bridge auto-generates the tool_describe aggregated schema payload (lazy, cached) on first invocation, matching what schemars::schema_for! produces from Rust capsules. Tool input schemas come from the decorator's inputSchema option or, at build time, from ts-json-schema-generator walking the parameter types. Schemas may differ in subtle ways from Rust's schemars output (e.g. Option<T> vs T | undefined representation) — acceptable for LLM tool-calling, monitored via a conformance test.

tool_execute_<name> dispatch handles mutable: true tools by loading __state from KV before the call and persisting after on success, matching the Rust macro's behaviour exactly. Result is published to tool.v1.execute.<name>.result via IPC.

Subpath exports

| Import | Contents | |---|---| | @astrid-runtime/sdk | Public API barrel | | @astrid-runtime/sdk/runtime | Internal registry + bridge — exposed for advanced authors only | | @astrid-runtime/sdk/contracts | Auto-generated TS types from astrid-contracts.wit (Message, ToolCall, GenerateRequest, StreamEvent, etc.) |

Status

Alpha. End-to-end build + install + kernel-lifecycle execution proven. See ../../notes/phase-{0,1,2,3-install}.md.

License

Dual MIT/Apache-2.0. See LICENSE-MIT and LICENSE-APACHE.