
freelensapp/freelens-example-extension
Developer toolsExample of a Freelens extension
Overview
This repository is the reference template for building, type-checking, testing and releasing an extension for the Freelens application. The Freelens extension documentation describes the extension API and points here for everything around it. Start a new extension from a copy of this repository. The extension adds a cluster page, a details panel and a context menu item for a custom resource, Example, and a setting on the preferences page: - Example is implemented for v1alpha1 and v1alpha2, each with its own model, list page, details and menu item. v1alpha2 renames spec.active to spec.suspended, with the meaning inverted. - The "Examples" page shows the newest version the cluster serves, or a message when the CRDs are not installed. - While the extension is enabled, Example is left out of the Custom Resources tree of the cluster sidebar, so it is listed once, under "Examples". - Per-object logic is a static method that takes the object, such as Example.getSuspended(object).
README
@freelensapp/example-extension
Overview
This repository is the reference template for building, type-checking, testing and releasing an extension for the Freelens application. The Freelens extension documentation describes the extension API and points here for everything around it. Start a new extension from a copy of this repository.
The extension adds a cluster page, a details panel and a context menu item
for a custom resource, Example, and a setting on the preferences page:
- Two API versions of one CRD.
Exampleis implemented forv1alpha1andv1alpha2, each with its own model, list page, details and menu item.v1alpha2renamesspec.activetospec.suspended, with the meaning inverted. - The served API version picked at runtime. The “Examples” page shows the newest version the cluster serves, or a message when the CRDs are not installed.
- One sidebar entry for the resource. While the extension is enabled,
Exampleis left out of the Custom Resources tree of the cluster sidebar, so it is listed once, under “Examples”. - Static helpers on the model. Per-object logic is a
staticmethod that takes the object, such asExample.getSuspended(object). - An error boundary. Each page, details panel and menu item renders an error message instead of breaking the view when it throws.
- A persisted preference. The “Example checkbox” setting is kept across restarts and shown in the details panel.
Agent skills
Two skills in the Agent Skills format guide a coding agent, such as Claude Code, Codex, GitHub Copilot, Cursor or Gemini CLI, through an extension in its own repository:
create-freelens-extensionstarts a new Freelens extension from a copy of this repository.port-freelens-extension-to-v2ports an extension written for the v1 extension API to v2.
Install both into the extension’s repository, or one of them with --skill:
npx skills add freelensapp/freelens-example-extension
npx skills add freelensapp/freelens-example-extension --skill port-freelens-extension-to-v2
or copy its directory from skills/ to where the agent reads skills
from.
Requirements
- Freelens >= 2.0.0
- Kubernetes >= 1.24
Supported APIs
example.freelens.app
| API Version | Kind | Scope | Description |
|---|---|---|---|
| v1alpha1 | Example |
Namespaced | Example custom resource (v1alpha1) |
| v1alpha2 | Example |
Namespaced | Example custom resource (v1alpha2) |
To install the Custom Resource Definitions for this example, run:
kubectl apply -k examples/v1alpha1/crds
kubectl apply -k examples/v1alpha2/crds
Example resources for testing:
kubectl apply -k examples/v1alpha2/test
# or
kubectl apply -k examples/v1alpha1/test
Install
Open Freelens and go to Extensions (ctrl+shift+E or
cmd+shift+E). The field at the top takes a package name, the URL of a
tarball, or the path to a tarball or a directory.
From the registry
Enter @freelensapp/example-extension and press Install.
Alternatively, open the following URL in the browser to install directly:
freelens://app/extensions/install/%40freelensapp%2Fexample-extension
From a release tarball
Each release
has the extension as freelensapp-example-extension-.tgz, with its
checksum in freelensapp-example-extension-.tgz.sha256. Use a
release whose major version matches your Freelens version.
- Enter the URL of the
.tgzasset and press Install. Freelens downloads the.tgz.sha256next to it and checks the tarball against it. - Or download both files into one directory and enter the path to the
.tgz, or drop the.tgzon the Freelens window. Freelens checks it against the.tgz.sha256next to it.
From a directory
Build the extension (see below), then enter the path to your checkout, the
directory with package.json, and press Install. Freelens runs the extension
from that directory, from the files in dist/, and lists it as unverified.
This is how you work on the extension: see
Development loop.
Build from the source
Prerequisites
Use NVM,
mise-en-place, or
windows-nvm to install the
Node.js version in .nvmrc.
From the root of this repository:
nvm install
# or
mise install
# or
winget install CoreyButler.NVMforWindows
nvm install "$(cat .nvmrc)"
nvm use "$(cat .nvmrc)"
Install pnpm:
corepack install
# or
curl -fsSL https://get.pnpm.io/install.sh | sh -
# or
winget install pnpm.pnpm
Build extension
pnpm install
pnpm build
The extension is built into dist/. To pack it into a tarball:
pnpm pack
One script to bump the prerelease version, build and pack the extension for testing:
pnpm pack:dev
The tarball is placed in the current directory. Install it as described in From a release tarball.
Development loop
Install the extension from your checkout once, as described in From a directory, then run:
pnpm dev
It rebuilds the extension whenever a source file changes, and Freelens
reloads the extension after each rebuild, without a restart and without
packing. Stop it with ctrl+C.
Neither pnpm build nor pnpm dev type-checks; run pnpm type:check for
that. A change to main or renderer in package.json needs Freelens
restarted once.
To check the extension in Freelens with an AI agent, see “Checking the Extension in Freelens Dev” in AGENTS.md.
Check the code
pnpm type:check
pnpm test:unit
pnpm lint:check
pnpm knip:check
and, for the formats that Biome does not cover:
pnpm trunk:check
Integration tests
The integration tests drive Freelens dev, started from a freelensapp/freelens checkout, against a kind cluster. In the Freelens checkout:
pnpm dev
Freelens dev lists no cluster until a kubeconfig is added in Preferences > Kubernetes > Kubeconfig Syncs. Add one with the kind cluster, for example:
kubectl config view --minify --flatten --context kind-kind > ~/kind-kind.kubeconfig
Then, with the kind cluster running, in the extension repository:
pnpm build
pnpm test:integration
The tests install the checkout in place, replacing any other installation of
the extension in Freelens dev. They delete the examples.example.freelens.app
CRD, and with it every Example, so use a kind cluster with nothing worth
keeping. Without Freelens dev or the cluster the tests are skipped.
Optional variables:
TEST_KIND_CLUSTER_NAME: the kind cluster,kindby default, used through the contextkind-.FREELENS_CDP_URL: the CDP endpoint of Freelens dev,http://127.0.0.1:9223by default.FREELENS_MAIN_LOG: a log of the main process to check for errors, such as itslens.log. Without it only the renderer’s errors are checked.
Testing the extension with unpublished Freelens
In the Freelens working repository:
rm -f *.tgz
pnpm i
pnpm build
pnpm pack -r
Then in the extension repository:
echo "overrides:" >> pnpm-workspace.yaml
for i in ../freelens/*.tgz; do
name=$(tar zxOf $i package/package.json | yq -r .name)
echo " \"$name\": $i" >> pnpm-workspace.yaml
done
pnpm clean:node_modules
pnpm build
License
Copyright © 2025-2026 Freelens Authors.
Recommended Tools
Try a different keyword or remove a filter.
Install
npx skillfish add freelensapp/freelens-example-extension