Give your App a Console
Start an App-owned Console and contribute pages and authorized services without an Agent.
This Console belongs to the application you build. It can run with no Agent.
The Agent-oriented Console workspace is a separate launch
path; npx @lenso/agent web does not add a Console to your application.
Start from the precompiled development kit
Use the Console development package matching your platform and add its bin directory
to PATH. It includes Engine CLI, Bun, the admitted native Host, embedded Shell,
page compiler and SDK. App authors need no Cargo, rustc, Node, pnpm or Console
source checkout. Initial dependency installation still needs registry access.
Download the matching archive and checksum from the
current development-kit prerelease.
Choose aarch64-apple-darwin for Apple silicon or x86_64-unknown-linux-gnu
for Linux x64, then verify and extract the archive before adding its tools to
your shell:
kit_tag=console-dev-b50d10087226-35466769287
target=aarch64-apple-darwin # or x86_64-unknown-linux-gnu
archive="console-development-host-$target.tar.gz"
base="https://github.com/LioRael/lenso-console/releases/download/$kit_tag"
curl -fLO "$base/$archive"
curl -fLO "$base/$archive.sha256"
shasum -a 256 -c "$archive.sha256"
tar -xzf "$archive"
export PATH="$PWD/development-host/bin:$PATH"
lenso app create my-app --console
lenso app dev --root my-app
# After stopping development:
lenso app build --root my-app
lenso app start --from my-app/dist
Open the HTTP address printed by the running Host. Development performs full
rebuild/restart, not React Fast Refresh; a successful restart may print a new
port. The scaffold records local kit paths in development_host and
plugin_sources in lenso.toml. Update both if moving the kit.
As of 2026-09-20, macOS ARM64 and Linux x64 have passed native CI acceptance, including creation, build and startup from an extracted archive using only kit tools, plus real HTTP service allow/deny checks. Windows is outside this POSIX package. The Console SDK is included in the kit; this does not imply a separate npm SDK release. Native Rust additions need a compatible precompiled Host or a source build with Cargo; source identity, target and integrity mismatches fail without a silent Cargo fallback.
Contribute a page
The generated app/orders/console/ directory is a page contribution:
console/
├── page.tsx
├── layout.tsx # optional
├── loading.tsx # optional
├── error.tsx # optional
├── not-found.tsx # optional, root only
├── services.ts # optional, server only
└── orders/[id]/page.tsx
import type { PageProps } from '@lenso/console-sdk';
export default function Order({ params, navigation }: PageProps) {
return <section>
<h1>Order {String(params.id)}</h1>
<a href={navigation.href([])}>Back to workspace</a>
</section>;
}
Pages use the Shell’s React singleton and ordinary React hooks. Do not create a
second React root. Navigation is scoped to the contribution’s mount. The SDK
also exports definePage, LayoutProps, ErrorProps, Contribution projections
and Workspace Service projections.
| Convention | Behavior |
|---|---|
[id] |
String parameter |
[...path] |
Nonempty string-array parameter |
[[...path]] |
String array, possibly empty |
layout.tsx |
Wraps descendants through children |
loading.tsx |
Suspense fallback |
error.tsx |
Descendant render boundary with error and reset |
root not-found.tsx |
Unmatched route fallback |
Static routes precede dynamic routes. Catch-all segments must be terminal and
ambiguous patterns fail compilation. Layout failures bubble to the parent
boundary; navigation resets error state. All authored TS/TSX and component
signatures are checked before bundling using an isolated strict configuration.
A private console/package.json may declare frontend dependencies. These are
specific supported semantics, not a promise of all Next.js features or SSR.
Register owner services
Optional console/services.ts exports defineServices(...) using
@lenso/console-sdk/server. Each operation supplies parse, authorize and
handle; authorization must succeed before the handler runs. The kit’s Orders
example permits order 42 and rejects other IDs as an illustrative business rule,
not production identity authentication.
import { defineServices, operation } from "@lenso/console-sdk/server";
export default defineServices({
orders: {
capabilityId: "example.orders.query@1",
version: "1.0.0",
operations: {
read: operation({
parse(value: unknown) {
if (
typeof value !== "object" ||
value === null ||
!("id" in value) ||
typeof value.id !== "string"
) {
throw new Error("An order ID is required");
}
return { id: value.id };
},
// Example business rule. Production authorization belongs here or in
// the domain provider this adapter invokes, never in page navigation.
authorize(_context, input) {
return input.id === "42";
},
handle(input) {
return { id: input.id, title: "Example order" };
},
}),
},
},
});
A page invokes an admitted alias through its supplied SDK:
const order = await props.services.invoke<
{ id: string }, { id: string; title: string }
>('orders', 'read', { id: '42' }, { signal: props.signal });
The generated service adapter and contribution share one Plugin Instance. Aliases use the existing owner-scoped Plan contract; arbitrary upstream URLs are not service aliases. The compiler rejects server SDK and service-source imports in browser bundles. This helper currently exposes request operations; handwritten Workspace Service providers retain stream support.
Domain Plugins retain their private state and final authorization. Access to another Plugin still requires explicit generated Capability dependencies. Adding a page, route or service alias grants no business permission or Agent Tool authority.
Remove the optional experience
The scaffold selects Console with
plugins/lenso.console.web/default.toml. Add an empty default.disabled
beside it and rebuild into a fresh output directory to disable Console and page
compilation. Verify both the selected and disabled App. The Shell can contain
factories that are available but not activated by the App.