initial: greenshades native backend v0.1.0 (x86_64-linux prebuild)
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
- run: node --check index.js
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ["v*"]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
- name: Auth for Gitea npm registry
|
||||||
|
run: |
|
||||||
|
echo "@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/" > .npmrc
|
||||||
|
echo "//hub.kl1.tenere.ai/api/packages/Polyweave/npm/:_authToken=${{ secrets.PACKAGE_TOKEN }}" >> .npmrc
|
||||||
|
- run: npm publish
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
Tenere Labs Inc. — All Rights Reserved.
|
||||||
|
|
||||||
|
PROPRIETARY AND CONFIDENTIAL.
|
||||||
|
Source-available for auditing and inspection only. No use, reproduction,
|
||||||
|
modification, or distribution is permitted without a paid written license.
|
||||||
|
|
||||||
|
Unauthorized use will be prosecuted to the fullest extent of the law.
|
||||||
|
|
||||||
|
For commercial licensing: licensing@tenere.ai
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
// index.js — Greenshades native N-API loader (lazy).
|
||||||
|
//
|
||||||
|
// The Zig backend's napi_register_module_v1 entry point performs a
|
||||||
|
// ServiceCellPool.init that forks 4 child processes. On non-Linux, in
|
||||||
|
// containers without seccomp, or in test runners that don't intend to
|
||||||
|
// exercise the native code, this fork sequence can corrupt glibc's heap
|
||||||
|
// or simply block for ~4 seconds (one blocked read per cell).
|
||||||
|
//
|
||||||
|
// To avoid loading the binary on import (which would run that init at
|
||||||
|
// unpredictable times), the binary is loaded LAZILY — only when a
|
||||||
|
// caller actually accesses an export. Detection at module-load time
|
||||||
|
// uses a synchronous fs stat call to confirm the prebuild exists.
|
||||||
|
//
|
||||||
|
// Env vars:
|
||||||
|
// GREENSHADES_ZIG_NATIVE=1 — prefer a freshly-built zig-out/ binary
|
||||||
|
// over the bundled prebuild
|
||||||
|
// GREENSHADES_ZIG_SKIP=1 — disable native loading entirely (use
|
||||||
|
// synthetic JS-only stubs for tests)
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const os = require('os');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
function platformArch() {
|
||||||
|
const p = os.platform();
|
||||||
|
const a = os.arch();
|
||||||
|
if (p === 'linux' && a === 'x64') return 'x86_64-linux';
|
||||||
|
if (p === 'linux' && a === 'arm64') return 'aarch64-linux';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformKey = platformArch();
|
||||||
|
const useBuilt = process.env.GREENSHADES_ZIG_NATIVE === '1';
|
||||||
|
const skipNative = process.env.GREENSHADES_ZIG_SKIP === '1';
|
||||||
|
|
||||||
|
function candidatePath() {
|
||||||
|
if (useBuilt) {
|
||||||
|
const built = path.join(__dirname, '..', 'zig-out', 'greenshades_backend.node');
|
||||||
|
if (fs.existsSync(built)) return built;
|
||||||
|
const projRoot = path.join(__dirname, '..', 'greenshades_backend.node');
|
||||||
|
if (fs.existsSync(projRoot)) return projRoot;
|
||||||
|
}
|
||||||
|
if (!platformKey) return null;
|
||||||
|
const prebuild = path.join(__dirname, 'prebuilds', platformKey, 'greenshades_backend.node');
|
||||||
|
return fs.existsSync(prebuild) ? prebuild : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureLoaded() {
|
||||||
|
if (module.exports.__loaded) return true;
|
||||||
|
const candidate = candidatePath();
|
||||||
|
if (!candidate) return false;
|
||||||
|
const { dlopen } = require('process');
|
||||||
|
const constants = process.binding('constants');
|
||||||
|
const RTLD_LAZY = constants.os.dlopen.RTLD_LAZY;
|
||||||
|
const RTLD_LOCAL = constants.os.dlopen.RTLD_LOCAL;
|
||||||
|
try {
|
||||||
|
const mod = { exports: {} };
|
||||||
|
dlopen(mod, candidate, RTLD_LAZY | RTLD_LOCAL);
|
||||||
|
if (typeof mod.exports.createBackend === 'function') {
|
||||||
|
for (const k of Object.keys(mod.exports)) {
|
||||||
|
module.exports[k] = mod.exports[k];
|
||||||
|
}
|
||||||
|
module.exports.__loaded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skipNative) {
|
||||||
|
const candidate = candidatePath();
|
||||||
|
module.exports.__nativeCandidatePath = candidate;
|
||||||
|
module.exports.__nativeCandidatePresent = !!candidate;
|
||||||
|
module.exports.__nativeAvailable = false;
|
||||||
|
module.exports.__loaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.__platformKey = platformKey;
|
||||||
|
module.exports.__ensureLoaded = ensureLoaded;
|
||||||
|
|
||||||
|
function notLoadedError() {
|
||||||
|
return new Error([
|
||||||
|
'Greenshades native addon not available for ' + (platformKey || os.platform() + '-' + os.arch()) + '.',
|
||||||
|
'',
|
||||||
|
'To build from source:',
|
||||||
|
' 1. Install Zig 0.16+',
|
||||||
|
' 2. Run: cd zig-native && zig build napi',
|
||||||
|
' 3. Copy zig-out/greenshades_backend.node to:',
|
||||||
|
' zig-native/node/prebuilds/' + (platformKey || 'YOUR_PLATFORM') + '/greenshades_backend.node',
|
||||||
|
].join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = {
|
||||||
|
get(target, prop) {
|
||||||
|
if (typeof prop === 'string' && prop.startsWith('__')) {
|
||||||
|
return target[prop];
|
||||||
|
}
|
||||||
|
if (typeof prop === 'string' && target[prop] === undefined) {
|
||||||
|
if (!ensureLoaded()) throw notLoadedError();
|
||||||
|
return target[prop];
|
||||||
|
}
|
||||||
|
return target[prop];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = new Proxy(module.exports, handler);
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "@polyweave/greenshades-native",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Native N-API bindings for the Greenshades execution backend",
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "commonjs",
|
||||||
|
"os": ["linux"],
|
||||||
|
"cpu": ["x64", "arm64"],
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"prebuilds/"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"greenshades",
|
||||||
|
"sandbox",
|
||||||
|
"seccomp",
|
||||||
|
"native",
|
||||||
|
"napi",
|
||||||
|
"zig"
|
||||||
|
],
|
||||||
|
"author": "John Dvorak <john@johnsdvorak.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user