The fs-safe repo lives at github.com/openclaw/fs-safe. Contributions welcome — issues, bug reports, focused PRs.
git clone https://github.com/openclaw/fs-safe.git
cd fs-safe
pnpm installNode 22 or newer. The dev toolchain and lockfile use pnpm; use the package
manager version declared in package.json.
pnpm buildRuns tsc -p tsconfig.json. Output lands in dist/. The package's prepack hook re-runs the build before publishing — manual pnpm build is only required when you want to inspect the output or run a freshly-built copy locally.
pnpm testVitest. Tests live in test/ and follow *.test.ts. Run a single file with:
pnpm test test/archive.test.tsUse vi.mock sparingly. Most tests should drive real disk operations in a mkdtemp-created scratch directory, asserting on observable behavior. The library has test hooks for the rare cases where you need to inject a TOCTOU race deterministically.
Vitest timeouts do not cancel filesystem promises. Shared fixtures with expensive
setup use useSuiteFixture from test/helpers/suite-fixture.ts: setup has a separate
30-second hook budget, and teardown waits for tracked setup and test work before
removing directories. Run shared-state corpora sequentially with a deadline per
payload. Keep child-process liveness limits separate from fixture preparation.
The Windows CI slow-copy proof runs the real package-copy process-exit test with a
six-second copy delay, retaining its four-second child deadline:
pnpm build
pnpm test --config scripts/slow-package-copy.config.tsRun the complete repository gate before handoff:
pnpm checkThis runs the filesystem boundary checks, build, tests, and package tarball/import validation.
After pnpm build and a fresh pnpm native:build, run pnpm package:smoke.
It packs the real root and host binding, then runs root-only npm and the
declared pnpm version against a disposable loopback registry. The root's exact
optional dependencies stay unchanged. Each consumer lives outside the workspace
with isolated configuration, caches, and stores; the registry never proxies to
the Internet. The smoke verifies root integrity, consumer-local resolution,
OS/CPU/libc selection, a native-required SHA-256 operation, and fresh-process
auto/off fallbacks and require failures for missing bindings and omitted
optionals. Omitted-optionals installs also verify that all public subpaths can
be imported, without implying every operation remains available.
Host-only smoke supplies the six foreign packages using their unchanged real
manifests and clearly marked synthetic, non-executable payloads. Every foreign
metadata/tarball endpoint is checked before installation, so a missing fixture
cannot masquerade as successful platform filtering. These temporary fixtures
never enter packages/, release artifacts, or the publish manifest. They prove
installer filtering, not foreign native compilation or execution. Full release
collection uses the actual seven collected native tarballs instead. Run it with
pnpm package:collect after assembling all seven real bindings; missing targets
fail collection. pnpm package:collect --allow-host-only exercises the same
lifecycle boundary locally but proves only the host. Both collection commands
require the pnpm lifecycle CLI path; direct node invocation is unsupported. Archive
codecs and their dependencies are packed from the installed dependency graph.
PR CI builds and executes four host targets: Linux x64 glibc, Linux x64 musl
(Alpine), macOS arm64, and Windows x64. The root-only smoke runs on each. The
seven-target source build matrix runs on release tags; packaging all seven is
not execution proof for every architecture. The smoke writes manager versions,
cases, and synthetic-fixture scope to release-artifacts/consumer-proof.json.
The docs site is rendered recursively from Markdown files under docs/ by scripts/build-docs-site.mjs. Build locally to preview:
pnpm docs:site
open dist/docs-site/index.htmlThe build validates internal links and embedded anchors. Broken links fail the build — fix them before pushing. Navigation must list every non-excluded Markdown page exactly once: missing, nonexistent, and duplicate entries fail before the build replaces existing site output. The builder and navigation tests share discovery and validation in scripts/docs-site-navigation.mjs.
Adding a new doc page:
- Create
docs/<page>.md. Use a leading# Titleheading. - Add the page to the appropriate section in
scripts/docs-site-navigation.mjs(sectionsarray near the top). Nested pages use slash-separated paths relative todocs/, such asguides/example.md. - Cross-link from
docs/index.mdif it's a major surface. - Run
pnpm test test/docs-site-navigation.test.tsand re-run the local build.
Internal links use relative *.md paths — the builder rewrites them to the rendered HTML. Code fences support GitHub-flavored markdown.
Small, focused PRs land faster. The general shape:
- One concern per PR. Bug fixes separate from new APIs.
- A regression test for every bug fix where the test framework can express it.
- A changelog entry under
## Unreleasedwhen behavior visibly changes. - For new public APIs: a docs page in
docs/plus a sidebar entry.
Maintainers publish from a protected vX.Y.Z tag on main through
.github/workflows/release.yml. The workflow requires the package version and a
dated CHANGELOG.md section to match the tag. It builds and publishes all seven
platform packages before publishing @openclaw/fs-safe, verifies every registry
artifact and provenance statement, and then creates the GitHub release.
Each package needs its own npm trusted-publisher configuration for
openclaw/fs-safe and release.yml. A new platform package must be created and
configured on npm before the first tag that references it; npm trust is
package-specific and cannot be bootstrapped by the tag workflow itself.
External contributors do not need to do anything beyond getting the pull request merged. Maintainers must not publish locally or add npm automation tokens.
Suspected security issues belong in private disclosure first. See SECURITY.md in the repo for the current contact path. Don't open a public issue for a credential-stealing or sandbox-escape bug — coordinate the disclosure first.
By contributing you agree that your contributions are licensed under the project's MIT license.