bilig gives TypeScript code a workbook it can edit, calculate, and save.
Use it when the clearest model is still a spreadsheet, but the runtime is a
Node service, queue worker, serverless route, test, or coding-agent tool. The
main package is @bilig/headless:
it gives you a WorkPaper object, not a browser grid.
The loop is small: create sheets, write cells, recalculate formulas, read the value that came out, and persist the workbook as JSON. That is useful for pricing rules, budget checks, payout models, import validation, and agent tools that need real readback after an edit.
It is not trying to replace Excel's UI. It is the headless runtime you put behind product code when formulas are the clearest way to explain the logic.
Project site: https://proompteng.github.io/bilig/
This uses the published npm package. It builds a workbook, changes one input, reads the calculated value, saves JSON, restores the workbook, and prints the same value again.
mkdir bilig-headless-eval
cd bilig-headless-eval
npm init -y
npm pkg set type=module
npm install @bilig/headless
npm install -D tsx typescript @types/node
curl -fsSLo quickstart.ts https://proompteng.github.io/bilig/npm-eval.ts
npx tsx quickstart.tsExpected output:
{
"before": 24000,
"after": 38400,
"afterRestore": 38400,
"sheets": ["Inputs", "Summary"],
"bytes": 1000,
"verified": true
}The TypeScript file is maintained in
examples/headless-workpaper/npm-eval.ts.
The exact byte count can change between package versions; verified: true and
matching after/afterRestore values are the check.
Most integrations are just this: build a workbook, write an input, read the calculated value, and save the workbook state.
import { WorkPaper, exportWorkPaperDocument, serializeWorkPaperDocument } from '@bilig/headless'
const workbook = WorkPaper.buildFromSheets({
Inputs: [
['Metric', 'Value'],
['Customers', 20],
['Average revenue', 1200],
],
Summary: [
['Metric', 'Value'],
['Revenue', '=Inputs!B2*Inputs!B3'],
],
})
const inputs = workbook.getSheetId('Inputs')
const summary = workbook.getSheetId('Summary')
if (inputs === undefined || summary === undefined) {
throw new Error('Workbook is missing required sheets')
}
workbook.setCellContents({ sheet: inputs, row: 1, col: 1 }, 32)
const revenue = workbook.getCellDisplayValue({ sheet: summary, row: 1, col: 1 })
const saved = serializeWorkPaperDocument(exportWorkPaperDocument(workbook, { includeConfig: true }))
console.log({ revenue, savedBytes: saved.length })Use @bilig/headless when:
- a Node service owns a workbook-shaped calculation;
- an agent needs tools such as
readRangeandsetInputCell, with computed before/after values instead of screenshots; - tests need deterministic spreadsheet state and formula readback;
- a workflow needs to save the edited workbook as JSON and restore it later.
Use something else when you need a visual spreadsheet grid, Office macros, desktop Excel automation, or a one-off arithmetic helper. Do not treat embedded XLSX cached formula values as truth; use the Excel oracle workflow when accuracy matters.
Pick the path closest to what you are building.
- If you are evaluating the npm package: 90-second npm eval and Node quickstart.
- If you are writing code against the API: packages/headless/README.md and the WorkPaper read/write cheat sheet.
- If you are putting workbook logic in a service:
Node service recipe,
server-side spreadsheet automation,
Node spreadsheet formula engine,
framework adapters, and
workbook automation examples.
The runnable serverless boundary lives in
examples/serverless-workpaper-api;
run
npm run next-route-handler,npm run next-server-action,npm run next-server-action-formdata,npm run framework-adapters, andnpm run persistence-adaptersfrom that example. - If an agent needs workbook tools: agent tool-calling recipe, OpenAI Responses tools, AI SDK and LangChain adapters, agent framework adapters, MCP server guide, MCP tool server shape, MCP directory status, MCP client setup, and Claude Desktop MCPB bundle.
- If you are comparing libraries: evaluate Excel formulas in Node.js, Google Sheets API boundary, docs/javascript-spreadsheet-library-headless-node.md, docs/sheetjs-exceljs-alternative-formula-workbook-api.md, headless engine comparison, and HyperFormula notes.
- If you want a first contribution: starter issues, first-timers-only issues, GitHub Discussions, and CONTRIBUTING.md.
The runnable examples are TypeScript files. Some source imports end in .js
because Node ESM resolves compiled package output that way; the files you edit
and run are still .ts.
From examples/headless-workpaper:
npm install
npm start
npm run json-records
npm run csv-shaped
npm run invoice-totals
npm run budget-variance
npm run fulfillment-capacity
npm run quote-approval
npm run subscription-mrr
npm run persistenceThe most useful entry points:
- JSON records input
- CSV shaped input
- invoice totals
- budget variance alerts
- fulfillment capacity plan
- quote approval threshold
- subscription MRR forecast
For agent tools:
npm run agent:verify
npm run agent:tool-call
npm run agent:openai-responses
npm run agent:ai-sdk-generate-text
npm run agent:ai-sdk-stream-text
npm run agent:framework-adapters
npm run agent:mcp-tools
npm run agent:mcp-stdioThe AI SDK example uses
ai-sdk-generate-text-tool-smoke.ts.
The OpenAI Responses guide is
docs/openai-responses-workpaper-tool-call.md.
The agent framework guide is
docs/vercel-ai-sdk-langchain-spreadsheet-tool.md.
The package also ships the MCP stdio binary:
npm exec --package @bilig/headless -- bilig-workpaper-mcpIt is published in the official MCP Registry as
io.github.proompteng/bilig-workpaper:
https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.proompteng%2Fbilig-workpaper.
- The 90-second TypeScript check above edits one input, restores the saved JSON document, and verifies the dependent formula result.
- Run
pnpm workpaper:bench:competitive:check. The checked-in artifact shows46/46comparable WorkPaper mean wins and names the slower p95 row:lookup-approximate-duplicatesat1.043x. - The benchmark card is generated from that artifact:
docs/assets/workpaper-benchmark-card.png. - Read the compatibility limits before importing real Excel workbooks.
- For XLSX accuracy audits, use the Excel oracle harness. It separates import success, timeouts, stale cached formula values, and fresh Microsoft Excel recalculation results.
- Track public signals in the growth snapshot: stars, npm downloads, starter issues, Discussions, traffic, and clones.
- The WorkPaper MCP server is listed in the official MCP Registry and on Glama. The directory status page keeps the npm command and directory evidence in one place.
- Public feedback threads: workflow questions, service examples, persistence adapters, JavaScript spreadsheet library guide, OpenAI Responses tool calls, and benchmark critique.
If the 90-second check matches a problem you have, star or bookmark the repo: https://github.com/proompteng/bilig/stargazers.
Cached formula values embedded in .xlsx files are cache diagnostics, not an
accuracy verdict. A Bilig correctness bug should only be claimed when the
expected value came from a fresh Excel recalculation oracle.
OUT=.cache/excel-oracle-evaluation
pnpm workpaper:xlsx-oracle -- prepare-oracle /path/to/xlsx-corpus "$OUT"
pnpm workpaper:xlsx-oracle -- evaluate-cache /path/to/xlsx-corpus "$OUT"
pnpm workpaper:xlsx-oracle -- evaluate-oracle /path/to/xlsx-corpus "$OUT/recalculated" "$OUT"
pnpm workpaper:xlsx-oracle -- summarize "$OUT"evaluate-cache writes cache-diagnostic.json and stays non-authoritative.
evaluate-oracle writes excel-oracle-report.json, and summarize writes
summary.md. If Excel automation is unavailable, cells are classified as
missing_excel_oracle instead of being promoted to bugs.
packages/headless: WorkPaper runtime and npm package.packages/excel-import: XLSX import/export boundary. Install both packages withpnpm add @bilig/headless @bilig/excel-importwhen you need file import and export.packages/formula: formula parser, binder, compiler, and evaluator.packages/core: workbook engine, snapshots, mutation flow, and scheduler.packages/gridandapps/web: browser spreadsheet shell.apps/bilig: fullstack monolith runtime, API surface, and static asset server.packages/renderer: React workbook renderer.packages/protocol,packages/binary-protocol,packages/agent-api, andpackages/worker-transport: protocol and integration boundaries.packages/wasm-kernel: AssemblyScript/WASM numeric fast path.packages/benchmarks: benchmark harness and performance contracts.
For XLSX import/export from TypeScript:
import { WorkPaper } from '@bilig/headless'
import { exportXlsx, importXlsx } from '@bilig/excel-import'Use WorkPaper.buildFromSnapshot(imported.snapshot) after import and
workbook.exportSnapshot() before exportXlsx().
Use Node 24+, Bun, and pnpm@10.32.1.
pnpm install
pnpm dev:web
pnpm dev:web-local
pnpm dev:syncFor a full local preflight:
pnpm lint
pnpm typecheck
pnpm test
pnpm test:browser
pnpm run ciGenerated sources and public evidence are checked:
pnpm protocol:check
pnpm formula-inventory:check
pnpm workspace-resolution:check
pnpm workpaper:bench:competitive:check
pnpm docs:discovery:checkStart with the public package boundary unless the task is explicitly engine work.
- Read
packages/headless/README.mdbefore touching WorkPaper behavior. - Use public exports from
@bilig/headless; do not reach intosrc/ordist/when writing consumer examples. - Keep examples TypeScript-first.
- Do not call stale XLSX cached formula values an accuracy oracle.
- Add focused tests before changing formulas, persistence, range bounds, config rebuilds, events, row/column moves, or sheet lifecycle.
- Run the focused package tests first, then broaden to
pnpm run ci.
Read CONTRIBUTING.md before opening a PR. If this is your first patch, start with the new contributor guide and then claim a scoped starter issue.
Good first patches usually fit one of these shapes:
- formula fixtures with clear expected behavior;
- small WorkPaper examples that prove a real service or agent workflow;
- focused correctness fixes with regression tests;
- grid accessibility and keyboard-behavior improvements;
- docs that turn an existing architecture note into a runnable command.
The shortest public on-ramp is the
starter issues queue. It keeps code/test picks,
example tasks, adapters, and focused docs work in one current list, with small
acceptance commands for first patches.
If this is your first contribution to bilig, use the
first-timers-only
filter.
Forgejo Actions is the primary CI surface via
.forgejo/workflows/forgejo-ci.yml. GitHub Actions mirrors the verification
contract in .github/workflows/ci.yml.
The strict gate includes frozen lockfile install, full pnpm run ci, artifact
budget checks, browser smoke, and tracked-file cleanliness checks.
MIT.