@staal/node 0.0.0-stage → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +220 -0
- package/LICENSE-THIRD-PARTY.txt +17888 -0
- package/NOTICE +5 -0
- package/README.md +37 -2
- package/bin/linux-x64-gnu/staal.node +0 -0
- package/index.d.ts +77 -0
- package/index.js +80 -0
- package/package.json +45 -4
package/NOTICE
ADDED
package/README.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @staal/node: Staal for Node.js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run [Staal](https://github.com/laurells/staal) programs from Node.js and TypeScript.
|
|
4
|
+
Staal is a programming language for software where AI acts: its compiler refuses a
|
|
5
|
+
program that could act without permission, use a model's answer unchecked, or
|
|
6
|
+
overspend, and its runtime never repeats an action after a crash.
|
|
7
|
+
|
|
8
|
+
Install it with `npm install @staal/node`. This package holds no Staal logic of its own. It calls staalc's C interface
|
|
9
|
+
in-process, so a Node app gets exactly the results `staalc` and the Python package
|
|
10
|
+
give. Runs happen off the event loop and return Promises.
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
const staal = require('@staal/node');
|
|
14
|
+
|
|
15
|
+
// Your app decides the policy; Staal enforces it.
|
|
16
|
+
const limit = customer.tier === 'gold' ? 500 : 50;
|
|
17
|
+
const out = await staal.run('refund.staal', {
|
|
18
|
+
entry: 'main',
|
|
19
|
+
inputs: { complaint },
|
|
20
|
+
grants: { pay: { cap: 'payments.refund', each: { amount: String(limit), currency: 'USD' } } },
|
|
21
|
+
model: { provider: 'anthropic', model: 'claude-sonnet-5-5', api_key: { env: 'ANTHROPIC_API_KEY' } },
|
|
22
|
+
}, { journal: 'refund.journal' });
|
|
23
|
+
|
|
24
|
+
if (out.status === 'done') console.log(out.result);
|
|
25
|
+
else if (out.status === 'failed') console.log('refused:', out.failure.message);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`run`, `resume`, `resolve` and `check` resolve to the JSON objects described in
|
|
29
|
+
[include/staal.h](https://github.com/laurells/staal/blob/main/include/staal.h), typed
|
|
30
|
+
in `index.d.ts`: `done` with a `result`, `failed` with a `failure`, `stopped` (an
|
|
31
|
+
action in doubt: `resolve` it, then `resume`), `invalid` with located check errors,
|
|
32
|
+
or `error`. Pass `{ engine: 'native' }` to run pure code as machine code.
|
|
33
|
+
|
|
34
|
+
**Platforms.** Linux on x86_64 with glibc 2.28 or newer, Node 18 or newer. Other
|
|
35
|
+
platforms come later; until then, build `node/` from the repository.
|
|
36
|
+
|
|
37
|
+
**Licence.** Apache-2.0 WITH LLVM-exception. The addon contains staalc and its Rust
|
|
38
|
+
dependencies, whose licences are in `LICENSE-THIRD-PARTY.txt`.
|
|
Binary file
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Types for the staal package. Results are the JSON objects described in
|
|
2
|
+
// include/staal.h, as returned by staalc.
|
|
3
|
+
|
|
4
|
+
/** JSON as it crosses into and out of Staal. */
|
|
5
|
+
export type Json = null | boolean | number | string | Json[] | { [key: string]: Json };
|
|
6
|
+
|
|
7
|
+
/** One event of a run's log (RUNTIME.md section 10). */
|
|
8
|
+
export interface LogEvent {
|
|
9
|
+
event: string;
|
|
10
|
+
[field: string]: Json;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** A check error, with where it is and the text staalc check prints. */
|
|
14
|
+
export interface Diagnostic {
|
|
15
|
+
code: string;
|
|
16
|
+
file: string;
|
|
17
|
+
line: number;
|
|
18
|
+
col: number;
|
|
19
|
+
message: string;
|
|
20
|
+
text: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** What made a run fail (RUNTIME.md section 8). */
|
|
24
|
+
export type FailureKind =
|
|
25
|
+
| 'action'
|
|
26
|
+
| 'capability'
|
|
27
|
+
| 'validation'
|
|
28
|
+
| 'value'
|
|
29
|
+
| 'budget'
|
|
30
|
+
| 'provider'
|
|
31
|
+
| 'step_limit'
|
|
32
|
+
| 'depth_limit'
|
|
33
|
+
| 'internal'
|
|
34
|
+
| 'journal'
|
|
35
|
+
| 'in_doubt';
|
|
36
|
+
|
|
37
|
+
export interface Failure {
|
|
38
|
+
kind: FailureKind;
|
|
39
|
+
message: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface Ran {
|
|
43
|
+
log: LogEvent[];
|
|
44
|
+
/** Present when the run loaded C code, which Staal cannot check (RUNTIME.md section 19). */
|
|
45
|
+
unchecked?: Json[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type RunResult =
|
|
49
|
+
| (Ran & { status: 'done'; result: Json })
|
|
50
|
+
| (Ran & { status: 'failed'; failure: Failure })
|
|
51
|
+
/** Stopped and can continue: resolve the action in doubt, then resume. */
|
|
52
|
+
| (Ran & { status: 'stopped'; failure: Failure & { kind: 'in_doubt' | 'journal' } })
|
|
53
|
+
| { status: 'invalid'; errors: Diagnostic[] }
|
|
54
|
+
| { status: 'error'; error: string };
|
|
55
|
+
|
|
56
|
+
export type CheckResult = { status: 'ok' } | { status: 'invalid'; errors: Diagnostic[] } | { status: 'error'; error: string };
|
|
57
|
+
|
|
58
|
+
export type Resolution = { performed: string } | { status: number; body?: string } | { failed: string } | { retry: true };
|
|
59
|
+
|
|
60
|
+
export type ResolveResult = { status: 'resolved'; summary: Json } | { status: 'error'; error: string };
|
|
61
|
+
|
|
62
|
+
/** A run config (RUNTIME.md section 1): entry, inputs, grants, model and settings. */
|
|
63
|
+
export type RunConfig = { [key: string]: Json };
|
|
64
|
+
|
|
65
|
+
export interface RunOptions {
|
|
66
|
+
/** A journal file: the run survives a crash and can be resumed from it. */
|
|
67
|
+
journal?: string;
|
|
68
|
+
engine?: 'interpreter' | 'native';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** staalc's version, for example "0.23.0". */
|
|
72
|
+
export const version: string;
|
|
73
|
+
|
|
74
|
+
export function check(program: string): Promise<CheckResult>;
|
|
75
|
+
export function run(program: string, config: RunConfig, options?: RunOptions): Promise<RunResult>;
|
|
76
|
+
export function resume(program: string, config: RunConfig, journal: string, options?: Pick<RunOptions, 'engine'>): Promise<RunResult>;
|
|
77
|
+
export function resolve(journal: string, resolution: Resolution): Promise<ResolveResult>;
|
package/index.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Run Staal programs from Node.js, through Staal's C interface (libstaalc).
|
|
3
|
+
//
|
|
4
|
+
// This package holds no Staal logic: it passes strings to staalc and parses the JSON
|
|
5
|
+
// it returns, exactly as the C interface (include/staal.h) and the Python package do.
|
|
6
|
+
//
|
|
7
|
+
// const staal = require('@staal/node');
|
|
8
|
+
// const out = await staal.run('refund.staal', config, { journal: 'run.journal' });
|
|
9
|
+
// if (out.status === 'done') console.log(out.result);
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
function platformDir() {
|
|
15
|
+
if (process.platform === 'linux') {
|
|
16
|
+
// The build links against glibc; musl systems (Alpine) cannot load it.
|
|
17
|
+
const report = typeof process.report?.getReport === 'function' ? process.report.getReport() : null;
|
|
18
|
+
const glibc = report && report.header && report.header.glibcVersionRuntime;
|
|
19
|
+
return glibc ? `linux-${process.arch}-gnu` : `linux-${process.arch}-musl`;
|
|
20
|
+
}
|
|
21
|
+
return `${process.platform}-${process.arch}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function load() {
|
|
25
|
+
const override = process.env.STAAL_NODE_ADDON;
|
|
26
|
+
const file = override || path.join(__dirname, 'bin', platformDir(), 'staal.node');
|
|
27
|
+
if (!fs.existsSync(file)) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`staal: this package has no build for ${platformDir()} yet (looked for ${file}). ` +
|
|
30
|
+
'Builds exist for linux-x64-gnu; elsewhere, build node/ from https://github.com/laurells/staal',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return require(file);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const addon = load();
|
|
37
|
+
|
|
38
|
+
function parse(text) {
|
|
39
|
+
return JSON.parse(text);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function str(value, name) {
|
|
43
|
+
if (typeof value !== 'string') throw new TypeError(`staal: ${name} must be a string`);
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function engineOf(options) {
|
|
48
|
+
const engine = options && options.engine;
|
|
49
|
+
return engine === undefined || engine === null ? undefined : str(engine, 'engine');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** staalc's version, for example "0.23.0". */
|
|
53
|
+
const version = addon.version();
|
|
54
|
+
|
|
55
|
+
/** Checks a program without running it. */
|
|
56
|
+
async function check(program) {
|
|
57
|
+
return parse(await addon.check(str(program, 'program')));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Runs a program. `config` is the run config, as an object. */
|
|
61
|
+
async function run(program, config, options = {}) {
|
|
62
|
+
const journal = options.journal === undefined || options.journal === null ? undefined : str(options.journal, 'journal');
|
|
63
|
+
return parse(await addon.run(str(program, 'program'), JSON.stringify(config), journal, engineOf(options)));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Resumes a run from its journal: finished actions are replayed, not made again. */
|
|
67
|
+
async function resume(program, config, journal, options = {}) {
|
|
68
|
+
return parse(await addon.resume(str(program, 'program'), JSON.stringify(config), str(journal, 'journal'), engineOf(options)));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolves the action in doubt in a stopped run's journal, then call `resume`.
|
|
73
|
+
* `resolution` is one of { performed: "<receipt>" }, { status: 200, body: "..." },
|
|
74
|
+
* { failed: "<reason>" } or { retry: true }.
|
|
75
|
+
*/
|
|
76
|
+
async function resolve(journal, resolution) {
|
|
77
|
+
return parse(await addon.resolve(str(journal, 'journal'), JSON.stringify(resolution)));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { version, check, run, resume, resolve };
|
package/package.json
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@staal/node",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.23.0",
|
|
4
|
+
"description": "Run Staal programs from Node.js: the staalc compiler and runtime, through its C interface",
|
|
5
|
+
"license": "Apache-2.0 WITH LLVM-exception",
|
|
6
|
+
"homepage": "https://laurells.github.io/staal/",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/laurells/staal.git",
|
|
10
|
+
"directory": "node"
|
|
11
|
+
},
|
|
12
|
+
"bugs": "https://github.com/laurells/staal/issues",
|
|
13
|
+
"main": "index.js",
|
|
14
|
+
"types": "index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"index.d.ts",
|
|
18
|
+
"bin/",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"NOTICE",
|
|
22
|
+
"LICENSE-THIRD-PARTY.txt"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=18"
|
|
29
|
+
},
|
|
30
|
+
"os": [
|
|
31
|
+
"linux"
|
|
32
|
+
],
|
|
33
|
+
"cpu": [
|
|
34
|
+
"x64"
|
|
35
|
+
],
|
|
36
|
+
"libc": [
|
|
37
|
+
"glibc"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"test": "node --test test/staal.test.mjs",
|
|
41
|
+
"types": "tsc --noEmit -p test/tsconfig.json"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "22.10.2",
|
|
45
|
+
"typescript": "5.7.2"
|
|
46
|
+
}
|
|
47
|
+
}
|