← Examples

Basic project environment

This small JavaScript package adds an “Example server” environment type. It uses a host-rendered create form and returns a ready environment rooted at the user’s selected directory.

Package shape

Shell
terminay-example-provider/
├── package.json
└── extension.js

This is intentionally minimal: the package manifest lives in package.json, and extension.js contains the provider definition and runtime.

Declare the contribution

JSON
{
  "name": "terminay-example-provider",
  "version": "1.0.0",
  "type": "module",
  "exports": { ".": "./extension.js" },
  "terminay": {
    "manifestVersion": 1,
    "id": "dev.example.basic",
    "displayName": "Example server",
    "description": "Minimal project-environment provider.",
    "api": "^1.1.0",
    "engines": { "terminay": ">=1.0.0", "node": ">=22" },
    "entrypoint": "extension.js",
    "permissions": [],
    "contributes": {
      "projectEnvironments": [{
        "id": "dev.example.basic/server",
        "displayName": "Example server",
        "capabilities": ["infrastructure"]
      }]
    }
  }
}

The project-environment contribution declares the provider ID and infrastructure capability. Its ID is repeated as providerId at registration time.

Register a definition and runtime

TypeScript
import { defineExtension } from "@terminay/extension-api";

const status = {
  state: "available",
  message: "Example is ready",
  defaultRoot: "/workspace",
  revision: 1,
};

export default defineExtension({
  activate(context) {
    context.registerProjectEnvironmentProvider({
      definition: {
        providerId: "dev.example.basic/server",
        displayName: "Example server",
        capabilities: ["infrastructure"],
        createForm: {
          id: "dev.example.basic/create",
          title: "Create example environment",
          sections: [{
            id: "connection",
            title: "Connection",
            fields: [{ id: "root", type: "text", label: "Default root", required: true }],
          }],
          submitLabel: "Create",
        },
      },
      runtime: {
        async testProfile() { return []; },
        async resolveOptions() { return { options: [] }; },
        async createEnvironment(request) {
          return {
            state: "ready",
            providerState: { root: request.values.root ?? "/workspace" },
            status,
          };
        },
        async resumeOperation(request) {
          return { state: "ready", providerState: request.providerState, status };
        },
        async getStatus() { return status; },
        async invokeAction(request) {
          return { state: "complete", providerState: request.providerState, status };
        },
      },
    });
  },
});

Terminay renders createForm itself. The runtime validates profiles, resolves options, creates or resumes the environment, reports status, and handles declared actions.

Package it

Shell
cd packages/extension-api/examples/basic-provider
npm pack
# Upload terminay-example-provider-1.0.0.tgz in Settings → Extensions

Give a copied provider a new extension ID and provider ID before publishing it. For optional operations such as updates, deletion, profiles, secrets, and dependencies, see the project-environment API.