- Remove native C++ addon dependency, no more pnpm approve-builds
- sql.js loads SQLite as WASM, zero compilation required
- WASM init is singleton (once per process)
- Add queryAsObjects() adapter for sql.js columnar → row format
- Tests migrated to sql.js (16 passing)
Implements RFC #63
Open each sense SQLite file read-only under data/senses. schema lists CREATE TABLE SQL from sqlite_master; query runs optional SQL or a default SELECT ordered by rowid. Human output uses aligned columns; --json for machine-readable output. Add better-sqlite3 to the CLI package and externalize it in tsup.
Tests cover sense-sqlite helpers and integration against a temp database.
Made-with: Cursor
- Add .cursor/rules/no-dynamic-import.mdc: ban dynamic import() in
production code with documented exceptions
- Add .cursor/rules/gitea-access.mdc: tea CLI usage guide
- Add explanatory comments on the 2 legitimate dynamic imports in
sense-runtime.ts and workflow-worker.ts
Convert 6 unnecessary `await import()` calls for Node built-in modules
(node:child_process, node:util) and project modules (../workspace.js)
to static top-level imports in init.ts and start.ts.
Closes#57
Ignore SIGINT/SIGTERM only when fork IPC is active (process.send) so terminal signals do not race the kernel shutdown in nerve dev, without breaking standalone worker CLIs (fixes#55).
Pipe worker stderr through the parent with a rolling capture buffer; log exit signal name and stderr tail on worker exit (fixes#56). Apply the same exit logging to workflow workers.
Made-with: Cursor
- createBlobStore(root) with write/read/exists API
- sha256 hex, first 2 chars as shard directory
- Atomic writes via temp file + rename
- CAS mismatch detection on read and write
- Inject blobStore into sense compute via options.blobs
- Export createBlobStore, normalizeBlobHash, BlobStore type
After pnpm install, verify better-sqlite3 actually loads by spawning
a test process. If it fails, rebuild up to 2 times. On final failure,
print actionable fix commands instead of a vague warning.
Closes#44
- Add meta table with archived_up_to watermark in logs.db
- Archive logs older than 30 days to data/archive/logs/YYYY-MM-DD.jsonl
- Idempotent: same-day re-export overwrites file
- Single transaction: DELETE + UPDATE meta
- Optional VACUUM after archive loop
- CLI: nerve store archive [--vacuum]
- 15+ new tests for archive logic
- daemon-ipc: add list-senses request type returning SenseInfo[]
- kernel: implement listSenses querying logStore for last signal time
- CLI: nerve sense list with table output, fallback to nerve.yaml when daemon is down
- 25 new tests across daemon-ipc and CLI
Implements nerve init sense <name> command that scaffolds a new sense directory under ~/.uncaged-nerve/senses/<name>/ with schema.ts, index.js, and migrations/0001_init.sql. Also auto-patches nerve.yaml to add the sense config and reflex entry. Includes full test coverage for all exported helpers.
Made-with: Cursor
- Move @uncaged/nerve-daemon from runtime to devDependencies
- Dynamic import daemon from workspace node_modules at runtime
- Add daemon-bootstrap.ts as separate entry for background daemon spawn
- Extract run-foreground-kernel.ts and workspace-daemon.ts modules
- Add daemon-types.ts for structural types (no runtime daemon import)
- Rebuild better-sqlite3 in workspace during nerve init
- Validate daemon process liveness after spawn in background mode
- Mark @uncaged/nerve-daemon as external in tsup config
Closes#41
- Remove private:true from cli and daemon package.json
- Add files and publishConfig fields
- Add shebang banner via tsup for CLI entry
- Add trigger-sense IPC support in daemon and client
Closes#40
小橘 <xiaoju@shazhou.work>
- SIGINT: use process.once instead of process.on
- Negative offset: validate and exit(1) with error to stderr
- Follow mode: sequential while loop replaces setInterval (no async race)
- Log rotation: reset size when newSize < size
- TODO: readAllLines large file optimization note
- 2 new tests for negative offset validation
小橘 <xiaoju@shazhou.work>
- nerve logs: tail last 50 lines by default
- -n <lines>: specify line count
- --offset <n>: pagination from line n (1-based)
- -f/--follow: real-time tail with 300ms polling
- Footer with stats + next-page command hint for AI agents
- No ANSI colors, emoji only, data→stdout, errors→stderr
- 19 new tests covering pagination, footer, edge cases
小橘 <xiaoju@shazhou.work>
The workspace package.json template listed @uncaged/nerve-core as a
dependency, but this package is not published to npm. Since the generated
workflow code only imports from @uncaged/nerve-daemon (which is also not
yet published but will be), remove the unnecessary dependency to unblock
`nerve init`.
小橘 <xiaoju@shazhou.work>
Per review: third candidate (here) is wrong — if bundled and source
candidates both miss, falling back to self reproduces the original bug.
Keep only the two valid candidates and throw on miss.
cliEntryScript() assumed source directory structure (src/commands/start.ts → ../cli.ts),
but after tsup bundles everything into dist/cli.js, import.meta.url points to dist/cli.js
and the '../cli.js' path resolves to a non-existent file.
Use candidate-based lookup: try same-dir, parent-dir, then self (bundled case).
Per review feedback from xiaoju: the three-level fallback was over-defensive.
Since start.ts and cli.ts have a fixed relative position (commands/start.ts → ../cli.ts),
we can derive the path directly from import.meta.url with an existsSync guard.
This makes path errors explicit (throw) instead of silently falling back to
a potentially wrong path.
The runDaemon function was using import.meta.url (pointing to start.js)
as the script for the spawned child process. This meant the child ran
`node start.js start` which has no CLI entry logic and exits immediately.
Added cliEntryScript() that resolves to the correct CLI entry (cli.js)
regardless of whether the code is bundled or split into separate files.
Closes#27
- daemon-ipc: wrap startWorkflow() in try/catch so errors are sent back
as {ok:false, error:msg} instead of silently dropping the socket
- init-workflow.test: import buildWorkflowTemplate from init.ts instead
of maintaining an inline copy
Addresses review follow-up suggestions from PR #31.
小橘 <xiaoju@shazhou.work>
Critical:
1. trigger: use Unix socket IPC to daemon instead of direct DB write
- new daemon-ipc.ts (server) + daemon-client.ts (client)
- kernel accepts ipcSocketPath, auto-starts IPC server
2. init workflow: validate name (lowercase alphanumeric + hyphens only)
Should fix:
3. getAllWorkflowRuns: SQL query on workflow_runs table instead of O(n) scan
4. limit/offset: robust parseIntArg() helper with NaN handling
5. statusIcon: exhaustive switch with never type check
6. trigger: end-to-end Unix socket tests added
12 new tests. All 224 tests pass.
小橘 🍊(NEKO Team)
Critical:
1. replayAndResume: remove double moderate() call, reuse loop result
2. drainAndRespawn: check workflow still in config before respawn
3. drain: mark in-flight runs as 'interrupted' in DB before clearing
Should fix:
4. crash recovery: dedup runId before re-queuing/re-activating
5. drain timeout: DEFAULT_DRAIN_TIMEOUT_MS > WORKER_SHUTDOWN_TIMEOUT_MS
6. crash-loop protection: max 5 crashes in 60s window, then stop respawn
5 new tests added. All 173 tests pass.
小橘 🍊(NEKO Team)
- Add mkdirSync for data/senses/ in init command (#23)
- Add defensive mkdirSync in sense-runtime before DB open (#23)
- Change init template output from index.ts to index.js (#24)
- Remove TypeScript type annotations from CPU usage template (#25)
Closes#23, closes#24, closes#25
- kernel.ts: create WorkflowManager, expose on Kernel, wire into
ReflexScheduler via workflowTriggerFn, add activeWorkflows to
KernelHealth, graceful shutdown ordering
- reflex-scheduler.ts: handle kind:'workflow' reflexes — subscribe to
bus signals and delegate to workflowTriggerFn
- workflow-manager.ts: add totalActiveCount(), updateConfig() for hot
reload support
All 140 tests pass.
小橘 🍊(NEKO Team)
Critical fixes:
1. triggerPayload spread → safe field assignment with validation
2. Graceful shutdown stopping flag → no false crash warnings
3. Shutdown awaits in-flight work (10s timeout) before exit
4. Thread loop safety valve (MAX_STEPS=1000)
5. active counter: bare number → Set<string> by runId
Should-fix:
6. ThreadEventMessage.eventType → union type with validation
7. SQLite status cast → runtime validateWorkflowRunStatus()
8. getActiveWorkflowRuns → pre-compiled prepared statements
小橘 <xiaoju@shazhou.work>