lt4c-sdk
v1.0.2
Published
TypeScript client for the LT4C daemon API
Maintainers
Readme
lt4c-sdk
TypeScript/JavaScript SDK for the LT4C Daemon API. The client works in Node.js services, browser dashboards (with a compatible fetch), and lightweight edge workers. It ships as a dual ESM + CommonJS package so it can be consumed from modern bundlers as well as legacy require-based code.
Features
- Lt4cClient wrapper for every documented daemon endpoint
- Built-in authentication via
X-DAEMON-TOKEN - Strict URL handling so
baseUrl+ relative paths never double up - File/folder helpers with
~/~/pathshortcuts, UTF-8/Base64 conversion, and binary reads pollStatusandtailLogsutilities for higher-level workflows- Fully typed responses + error model (
Lt4cApiError) - Tested with Vitest + MSW, bundled by tsup, linted via ESLint
Installation
npm install lt4c-sdkToken management & security
The daemon exposes a single bearer-style token that must be attached to every request. The SDK reads this from your code; the daemon does not provide a login exchange.
- The token is stored at
LT4C_TOKEN_PATH(file path). Your service is responsible for reading it (e.g., usingfs.readFileSync). - Never ship daemon tokens to untrusted browser clients. For dashboards, proxy requests through a backend that injects the token.
Example helper:
import fs from "node:fs";
function readDaemonToken(): string {
const tokenPath = process.env.LT4C_TOKEN_PATH ?? "/var/run/lt4c/token";
return fs.readFileSync(tokenPath, "utf8").trim();
}Usage
Node.js (ESM)
import { Lt4cClient } from "lt4c-sdk";
const client = new Lt4cClient("http://localhost:8080", readDaemonToken(), {
timeout: 30_000,
userAgent: "my-service/1.2.3",
});Node.js (CommonJS)
const { Lt4cClient } = require("lt4c-sdk");
const client = new Lt4cClient(process.env.LT4C_BASE_URL, readDaemonToken());Browser / Edge runtimes
import { Lt4cClient } from "lt4c-sdk";
import fetch from "cross-fetch";
const client = new Lt4cClient(baseUrl, token, { fetchImpl: fetch });Creating a box
const box = await client.createBox({
userId: "demo-user",
image: "node:18",
internalPort: 3000,
command: "npm start",
resourceLimits: { cpu: 0.5, memoryMb: 512 },
});The client maps these camelCase fields to the daemon's expected payloads (docker_image, resource_limits, string command, etc.), matching the LT4C Daemon API Lab HTML tool.
Fetching status
const status = await client.getStatus(box.id);
if (status.status === "RUNNING") {
console.log("ready");
}Reading / writing files
await client.writeFile(box.id, "~/app/index.js", "console.log('hi');", "utf8");
const contents = await client.readTextFile(box.id, "~/app/index.js");writeFile first attempts PATCH and, if the daemon returns 404, falls back to POST. Binary payloads should pass Uint8Array and specify encoding: "base64"; the helper automatically base64-encodes the bytes.
Polling for RUNNING
await client.pollStatus(box.id, {
intervalMs: 1000,
timeoutMs: 120_000,
until: (payload) => payload.status === "RUNNING" || payload.status === "ERROR",
});Tailing logs
const abort = new AbortController();
for await (const batch of client.tailLogs(box.id, { signal: abort.signal, limit: 100 })) {
for (const line of batch) {
console.log(`[${line.timestamp}] ${line.message}`);
}
}Health checks
const health = await client.health();
if (health === true || health.status === "ok") {
console.log("daemon reachable");
}Examples
examples/create-box.ts: minimal CLI that creates a box and waits for RUNNING.examples/tail-logs.ts: CLI log tailer (Ctrl+C to stop).examples/sdk-lab.html: browser playground for all Lt4cClient helpers. Runnpm run build, then serveexamples/(e.g.,npx serve examples) so the page can import../dist/index.js.
Run with ts-node or compile first:
npx ts-node examples/create-box.tsFile/folder API semantics
- Paths accept
~,~/sub/path,., and/prefixes. They normalize to root-relative segments so requests hit/boxes/{id}/files/...exactly like the HTML lab helper. - Folder rename payloads follow
{ new_path: "<relative-path>" }, so the SDK simply sanitizes segments before sending the value. - File GET responses are expected to look like
{ path, type, encoding, contents }; directories can return{ children: [...] }. Whenencoding === "base64", the SDK decodes to UTF-8 (forreadTextFile) or bytes (forreadBinaryFile).
API coverage
| Endpoint | Helper |
| --- | --- |
| POST /boxes | createBox |
| GET /boxes | listBoxes |
| GET /boxes/:id | getBox |
| PATCH /boxes/:id | updateBox |
| DELETE /boxes/:id | deleteBox |
| GET /boxes/:id/logs | getLogs, tailLogs |
| PATCH /boxes/:id/command | updateCommand |
| PATCH /boxes/:id/domain | updateDomain |
| GET /boxes/:id/ports | getPorts |
| GET /boxes/:id/status | getStatus, pollStatus |
| GET /boxes/:id/files/* | browsePath, readTextFile, readBinaryFile |
| POST/PATCH/DELETE /boxes/:id/files/* | createFile, updateFile, writeFile, deleteFile |
| POST/PATCH/DELETE /boxes/:id/folders/* | createFolder, renameFolder, deleteFolder |
| GET /healthz | health |
Building & testing
npm install
npm run lint
npm test
npm run buildtsupbuilds both ESM (dist/index.js) and CommonJS (dist/index.cjs) bundles plus declarations.vitest+mswcover client configuration, error handling, boxes/files flows, utilities, and log polling.eslint(with@typescript-eslint+importrules) keeps the codebase consistent.
CI/CD
Two GitHub Actions workflows are included:
.github/workflows/ci.yml– runs on pushes / PRs targetingmainwith a Node 18/20 matrix. Steps: checkout ? setup-node (with caching) ?npm ci?npm run lint?npm test?npm run build..github/workflows/release.yml– runs on tags that matchv*.*.*. Installs deps, runs tests + build, then executesnpm publish --access public(requiresNPM_TOKENsecret).
Publishing & versioning
- Update
CHANGELOG.md(recommended sections: Added, Changed, Fixed, Breaking). - Bump the version in
package.jsonusing SEMVER:MAJOR– backward-incompatible changes.MINOR– new backwards-compatible features.PATCH– bug fixes.
- Commit, tag
vX.Y.Z, push. The release workflow builds/tests and publishes whenNPM_TOKENis configured. - Manual publish alternative:
npm login
npm publish --access publicAssumptions
- Box schema includes
id,docker_image,user_id,status, timestamps, optional stringcommand,domain,resource_limits,ports, andinternal_portmetadata. - File browsing returns
FileNodeResponseobjects withtype: "file" | "directory", optionalencoding,contents, and nestedchildrenfor directories. - Log endpoints support
limitandsinceIdquery params. - Folder rename payloads use
{ new_path: "src/ui" }(no implicit/homeprefix). - Response bodies can include
requestId,responseId, oridto identify the request; all are captured onLt4cApiError.
If the live daemon deviates from these assumptions, adjust the corresponding types and helper payloads.
Example CLI scripts
node examples/create-box.js– create + wait for a box (after compiling or using ts-node).node examples/tail-logs.js <boxId>– stream logs until Ctrl+C.
OpenAPI
An approximate openapi.json can be produced later if LT4C publishes a formal spec. Until then, the TypeScript types (src/types.ts) document the schema that informed the SDK.
