@termfleet/core
v0.2.1
Published
Termfleet core: contracts, the provider SDK, and the agent-transcript/session library (Claude Code / Codex / Gemini). The reusable layer, usable beyond the console.
Maintainers
Readme
@termfleet/core
The reusable core of termfleet — usable on its own, with no console and no UI. It is three things:
- An agent-transcript / session library — read and normalize Claude Code, Codex, and Gemini session transcripts straight off disk into one shape. No network, no provider, no daemon.
- A provider SDK —
ProviderClient, a typed HTTP + socket.io client for a running termfleet provider (health, snapshots, agent sessions, terminals, windows). - Isomorphic contracts — the types and grammar the whole system speaks
(provider/registry records, the
claude:/codex:/gemini:session-id grammar, canvas/layout contracts).
If all you want is to parse the transcript files your AI tools already write, this package does that standalone.
Install
npm install @termfleet/coreRequires Node 20+ and ESM ("type": "module", or import()).
Imports are explicit, per module
There is no barrel — you import the specific module you need, with a .js
extension:
import { readLocalAgentSession } from "@termfleet/core/agent-session.js";
import { parseAgentSessionId } from "@termfleet/core/agent-session-id.js";
import { ProviderClient, providerRefFromUrl } from "@termfleet/core/provider-client.js";This is deliberate: the transcript readers are Node-only (they touch the
filesystem), while the contracts are isomorphic. Importing by module means a
browser bundle that only pulls contracts/* never drags node:fs into the
build. import "@termfleet/core" (bare) does not resolve.
Read an agent transcript (standalone, no console)
readLocalAgentSession resolves the on-disk transcript under the agent home
(~/.claude/projects, ~/.codex/sessions, ~/.gemini/...) and normalizes it to
one AgentSessionDetails shape. The agent is chosen by the session-id prefix —
codex: / gemini:, defaulting to Claude:
import { readLocalAgentSession } from "@termfleet/core/agent-session.js";
const session = readLocalAgentSession({
cwd: "/path/to/the/project", // the project the session ran in
sessionId: "claude:0f9c…", // or "codex:…", "gemini:…", or a bare Claude uuid
});
console.log(session.sessionId); // "claude:0f9c…" (prefixed)
console.log(session.agentSessionId); // "0f9c…" (bare id, no prefix)
console.log(session.lastAssistantText);
console.log(session.endOfTurn); // is the turn finished?
for (const item of session.timeline) {
// normalized timeline items: user/assistant text, tool calls (with a
// category + isError), todos, subagent refs, …
}There's an async variant (readLocalAgentSessionAsync) and per-agent readers
(readLocalClaudeSession / readLocalCodexSession / readLocalGeminiSession).
For incremental reads, @termfleet/core/agent-session-tail.js exports
readLocalAgentSessionTailed, whose tailed read always deep-equals a fresh full
parse.
The session-id grammar is its own one-owner module:
import { parseAgentSessionId, formatAgentSessionId, agentProviders }
from "@termfleet/core/agent-session-id.js";
agentProviders; // ["claude", "codex", "gemini"]
parseAgentSessionId("gemini:abc"); // { agent: "gemini", bareId: "abc" }
formatAgentSessionId("claude", "uuid-1"); // "claude:uuid-1"Talk to a running provider (the SDK)
ProviderClient is a typed client for a termfleet provider's HTTP + socket.io
API — the same surface the console proxies:
import { ProviderClient, providerRefFromUrl } from "@termfleet/core/provider-client.js";
const client = new ProviderClient(providerRefFromUrl("http://127.0.0.1:7402"));
await client.health(); // ProviderHealth
const page = await client.listAgentSessions({ limit: 20 });
const details = await client.getAgentSession("claude", "0f9c…");
// live updates over socket.io
const socket = client.connect();
const off = client.onSnapshot((snapshot) => { /* … */ });
// create a fresh agent window (boot a claude/codex/gemini session)
await client.createAgentWindow({ /* AgentWindowCreateOptions */ });
// terminals + files: capture/send, and read/write through the provider filesystem
const { content } = await client.captureTerminal("term-1", 40);
await client.sendTerminalInput("term-1", "echo hi\n", { breakGlass: true });
const bytes = await client.readFile("term-1", "/path/file"); // Uint8Array (browser-safe)
const text = new TextDecoder().decode(bytes); // node: or Buffer.from(bytes)Pass { authToken } for an authenticated/shared provider, or a urlResolver
when proxying through a console; omit both for a direct connection. readFile
returns a Uint8Array (not a node Buffer, since this client also runs in the
browser) — decode with new TextDecoder().decode(bytes) or Buffer.from(bytes).
What's inside
| Module | What it gives you |
| --- | --- |
| agent-session.js | read/normalize Claude · Codex · Gemini transcripts |
| agent-session-id.js | the claude:/codex:/gemini: id grammar (parse/format) |
| agent-session-tail.js | incremental transcript tailing |
| provider-client.js | the provider HTTP + socket.io SDK |
| terminal-client.js | terminal stream client |
| registry-client.js | registry/auth client |
| contracts/registry.js | provider/registry record contracts + mergeRegistryProviders |
| contracts/console-layout.js | machine appearance / layout contract |
| contracts/canvas.js, collab/canvas-doc.js | Yjs collaborative canvas |
| session-lifecycle.js, session-attention.js | session analysis helpers |
| types.js | shared provider/terminal types |
Browser-safe modules (contracts/*, types.js, fetch-based clients) are
isomorphic; the transcript readers and lifecycle modules are Node-only.
License
Apache-2.0. Part of termfleet.
