clawpowers 1.1.4 → 2.2.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/CHANGELOG.md +126 -0
- package/COMPATIBILITY.md +13 -0
- package/KNOWN_LIMITATIONS.md +19 -0
- package/LICENSE +44 -0
- package/LICENSING.md +10 -0
- package/README.md +378 -210
- package/SECURITY.md +52 -0
- package/dist/index.d.ts +1477 -0
- package/dist/index.js +3464 -0
- package/dist/index.js.map +1 -0
- package/native/Cargo.lock +4863 -0
- package/native/Cargo.toml +73 -0
- package/native/crates/canonical/Cargo.toml +24 -0
- package/native/crates/canonical/src/lib.rs +673 -0
- package/native/crates/compression/Cargo.toml +20 -0
- package/native/crates/compression/benches/compression_bench.rs +42 -0
- package/native/crates/compression/src/lib.rs +393 -0
- package/native/crates/evm-eth/Cargo.toml +13 -0
- package/native/crates/evm-eth/src/lib.rs +105 -0
- package/native/crates/fee/Cargo.toml +15 -0
- package/native/crates/fee/src/lib.rs +281 -0
- package/native/crates/index/Cargo.toml +16 -0
- package/native/crates/index/src/lib.rs +277 -0
- package/native/crates/policy/Cargo.toml +17 -0
- package/native/crates/policy/src/lib.rs +614 -0
- package/native/crates/security/Cargo.toml +22 -0
- package/native/crates/security/src/lib.rs +478 -0
- package/native/crates/tokens/Cargo.toml +13 -0
- package/native/crates/tokens/src/lib.rs +534 -0
- package/native/crates/verification/Cargo.toml +23 -0
- package/native/crates/verification/src/lib.rs +333 -0
- package/native/crates/wallet/Cargo.toml +20 -0
- package/native/crates/wallet/src/lib.rs +261 -0
- package/native/crates/x402/Cargo.toml +30 -0
- package/native/crates/x402/src/lib.rs +423 -0
- package/native/ffi/Cargo.toml +34 -0
- package/native/ffi/build.rs +4 -0
- package/native/ffi/index.node +0 -0
- package/native/ffi/src/lib.rs +352 -0
- package/native/ffi/tests/integration.rs +354 -0
- package/native/pyo3/Cargo.toml +26 -0
- package/native/pyo3/pyproject.toml +16 -0
- package/native/pyo3/src/lib.rs +407 -0
- package/native/pyo3/tests/test_smoke.py +180 -0
- package/native/wasm/Cargo.toml +44 -0
- package/native/wasm/pkg/.gitignore +6 -0
- package/native/wasm/pkg/clawpowers_wasm.d.ts +208 -0
- package/native/wasm/pkg/clawpowers_wasm.js +872 -0
- package/native/wasm/pkg/clawpowers_wasm_bg.wasm +0 -0
- package/native/wasm/pkg/clawpowers_wasm_bg.wasm.d.ts +40 -0
- package/native/wasm/pkg/package.json +17 -0
- package/native/wasm/pkg-node/.gitignore +6 -0
- package/native/wasm/pkg-node/clawpowers_wasm.d.ts +143 -0
- package/native/wasm/pkg-node/clawpowers_wasm.js +798 -0
- package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm +0 -0
- package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm.d.ts +40 -0
- package/native/wasm/pkg-node/package.json +13 -0
- package/native/wasm/src/lib.rs +433 -0
- package/package.json +71 -44
- package/src/skills/catalog.ts +435 -0
- package/src/skills/executor.ts +56 -0
- package/src/skills/index.ts +3 -0
- package/src/skills/itp/SKILL.md +112 -0
- package/src/skills/loader.ts +193 -0
- package/.claude-plugin/manifest.json +0 -19
- package/.codex/INSTALL.md +0 -36
- package/.cursor-plugin/manifest.json +0 -21
- package/.opencode/INSTALL.md +0 -52
- package/ARCHITECTURE.md +0 -69
- package/bin/clawpowers.js +0 -625
- package/bin/clawpowers.sh +0 -91
- package/docs/demo/clawpowers-demo.cast +0 -197
- package/docs/demo/clawpowers-demo.gif +0 -0
- package/docs/launch-images/25-skills-breakdown.jpg +0 -0
- package/docs/launch-images/clawpowers-vs-superpowers.jpg +0 -0
- package/docs/launch-images/economic-code-optimization.jpg +0 -0
- package/docs/launch-images/native-vs-bridge-2.jpg +0 -0
- package/docs/launch-images/native-vs-bridge.jpg +0 -0
- package/docs/launch-images/post1-hero-lobster.jpg +0 -0
- package/docs/launch-images/post2-dashboard.jpg +0 -0
- package/docs/launch-images/post3-superpowers.jpg +0 -0
- package/docs/launch-images/post4-before-after.jpg +0 -0
- package/docs/launch-images/post5-install-now.jpg +0 -0
- package/docs/launch-images/ultimate-stack.jpg +0 -0
- package/docs/launch-posts.md +0 -76
- package/docs/quickstart-first-transaction.md +0 -204
- package/gemini-extension.json +0 -32
- package/hooks/session-start +0 -205
- package/hooks/session-start.cmd +0 -43
- package/hooks/session-start.js +0 -163
- package/runtime/demo/README.md +0 -78
- package/runtime/demo/x402-mock-server.js +0 -230
- package/runtime/feedback/analyze.js +0 -621
- package/runtime/feedback/analyze.sh +0 -546
- package/runtime/init.js +0 -210
- package/runtime/init.sh +0 -178
- package/runtime/metrics/collector.js +0 -361
- package/runtime/metrics/collector.sh +0 -308
- package/runtime/payments/ledger.js +0 -305
- package/runtime/payments/ledger.sh +0 -262
- package/runtime/payments/pipeline.js +0 -455
- package/runtime/persistence/store.js +0 -433
- package/runtime/persistence/store.sh +0 -303
- package/skill.json +0 -106
- package/skills/agent-bounties/SKILL.md +0 -553
- package/skills/agent-payments/SKILL.md +0 -479
- package/skills/brainstorming/SKILL.md +0 -233
- package/skills/content-pipeline/SKILL.md +0 -282
- package/skills/cross-project-knowledge/SKILL.md +0 -345
- package/skills/dispatching-parallel-agents/SKILL.md +0 -305
- package/skills/economic-code-optimization/SKILL.md +0 -265
- package/skills/executing-plans/SKILL.md +0 -255
- package/skills/finishing-a-development-branch/SKILL.md +0 -260
- package/skills/formal-verification-lite/SKILL.md +0 -441
- package/skills/learn-how-to-learn/SKILL.md +0 -235
- package/skills/market-intelligence/SKILL.md +0 -323
- package/skills/meta-skill-evolution/SKILL.md +0 -325
- package/skills/prospecting/SKILL.md +0 -454
- package/skills/receiving-code-review/SKILL.md +0 -225
- package/skills/requesting-code-review/SKILL.md +0 -206
- package/skills/security-audit/SKILL.md +0 -353
- package/skills/self-healing-code/SKILL.md +0 -369
- package/skills/subagent-driven-development/SKILL.md +0 -244
- package/skills/systematic-debugging/SKILL.md +0 -355
- package/skills/test-driven-development/SKILL.md +0 -416
- package/skills/using-clawpowers/SKILL.md +0 -160
- package/skills/using-git-worktrees/SKILL.md +0 -261
- package/skills/validator/SKILL.md +0 -281
- package/skills/verification-before-completion/SKILL.md +0 -254
- package/skills/writing-plans/SKILL.md +0 -276
- package/skills/writing-skills/SKILL.md +0 -260
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
//! clawpowers-ffi — napi-rs Node.js bindings for clawpowers-core.
|
|
2
|
+
|
|
3
|
+
#![allow(clippy::new_without_default)]
|
|
4
|
+
|
|
5
|
+
use napi::bindgen_prelude::*;
|
|
6
|
+
use napi_derive::napi;
|
|
7
|
+
|
|
8
|
+
fn to_napi_err<E: std::fmt::Display>(e: E) -> napi::Error {
|
|
9
|
+
napi::Error::from_reason(e.to_string())
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/// Keccak-256 digest of raw bytes. Returns `0x` + 64 hex chars (for wallet / EVM helpers).
|
|
13
|
+
#[napi(js_name = "keccak256Bytes")]
|
|
14
|
+
pub fn keccak256_bytes(data: Buffer) -> String {
|
|
15
|
+
let h = alloy_primitives::keccak256(data.as_ref());
|
|
16
|
+
format!("0x{:x}", h)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// Ethereum address from 32-byte secp256k1 private key (`0x` + 20 bytes, EIP-55 checksum).
|
|
20
|
+
#[napi(js_name = "deriveEthereumAddress")]
|
|
21
|
+
pub fn derive_ethereum_address_ffi(private_key: Buffer) -> Result<String> {
|
|
22
|
+
clawpowers_evm_eth::derive_ethereum_address(private_key.as_ref()).map_err(to_napi_err)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Uncompressed public key: 64 bytes (x || y), no `0x04` prefix.
|
|
26
|
+
#[napi(js_name = "derivePublicKey")]
|
|
27
|
+
pub fn derive_public_key_ffi(private_key: Buffer) -> Result<Buffer> {
|
|
28
|
+
let v = clawpowers_evm_eth::derive_public_key(private_key.as_ref()).map_err(to_napi_err)?;
|
|
29
|
+
Ok(Buffer::from(v))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// ECDSA sign 32-byte message hash → 65 bytes (r || s || recovery_id).
|
|
33
|
+
#[napi(js_name = "signEcdsa")]
|
|
34
|
+
pub fn sign_ecdsa_ffi(private_key: Buffer, message_hash: Buffer) -> Result<Buffer> {
|
|
35
|
+
let v = clawpowers_evm_eth::sign_ecdsa(private_key.as_ref(), message_hash.as_ref())
|
|
36
|
+
.map_err(to_napi_err)?;
|
|
37
|
+
Ok(Buffer::from(v))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[napi(js_name = "verifyEcdsa")]
|
|
41
|
+
pub fn verify_ecdsa_ffi(
|
|
42
|
+
public_key: Buffer,
|
|
43
|
+
message_hash: Buffer,
|
|
44
|
+
signature: Buffer,
|
|
45
|
+
) -> Result<bool> {
|
|
46
|
+
clawpowers_evm_eth::verify_ecdsa(
|
|
47
|
+
public_key.as_ref(),
|
|
48
|
+
message_hash.as_ref(),
|
|
49
|
+
signature.as_ref(),
|
|
50
|
+
)
|
|
51
|
+
.map_err(to_napi_err)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
55
|
+
// WALLET
|
|
56
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
57
|
+
|
|
58
|
+
/// EVM agent wallet — key management and message signing.
|
|
59
|
+
#[napi]
|
|
60
|
+
pub struct JsAgentWallet {
|
|
61
|
+
inner: clawpowers_wallet::AgentWallet,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[napi]
|
|
65
|
+
impl JsAgentWallet {
|
|
66
|
+
/// Generate a fresh random wallet.
|
|
67
|
+
#[napi(factory)]
|
|
68
|
+
pub fn generate() -> Self {
|
|
69
|
+
Self {
|
|
70
|
+
inner: clawpowers_wallet::AgentWallet::generate(),
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// Import a wallet from a hex private key string.
|
|
75
|
+
#[napi(factory)]
|
|
76
|
+
pub fn from_private_key(hex: String) -> napi::Result<Self> {
|
|
77
|
+
let inner = clawpowers_wallet::AgentWallet::from_private_key(&hex).map_err(to_napi_err)?;
|
|
78
|
+
Ok(Self { inner })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/// Return the checksummed EVM address.
|
|
82
|
+
#[napi]
|
|
83
|
+
pub fn address(&self) -> String {
|
|
84
|
+
format!("{:#x}", self.inner.address())
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// Sign a raw byte buffer. Returns the signature as debug string.
|
|
88
|
+
#[napi]
|
|
89
|
+
pub fn sign_message(&self, msg: Buffer) -> napi::Result<String> {
|
|
90
|
+
let sig = self.inner.sign_message(msg.as_ref()).map_err(to_napi_err)?;
|
|
91
|
+
Ok(format!("{sig:?}"))
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
96
|
+
// TOKENS
|
|
97
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
98
|
+
|
|
99
|
+
/// Fixed-point token amounts with decimal precision.
|
|
100
|
+
#[napi]
|
|
101
|
+
pub struct JsTokenAmount {
|
|
102
|
+
inner: clawpowers_tokens::TokenAmount,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#[napi]
|
|
106
|
+
impl JsTokenAmount {
|
|
107
|
+
/// Create a token amount from a human-readable f64 value.
|
|
108
|
+
#[napi(factory)]
|
|
109
|
+
pub fn from_human(amount: f64, decimals: u32) -> Self {
|
|
110
|
+
Self {
|
|
111
|
+
inner: clawpowers_tokens::TokenAmount::from_human(amount, decimals as u8),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/// Convert to a human-readable f64.
|
|
116
|
+
#[napi]
|
|
117
|
+
pub fn to_human(&self) -> f64 {
|
|
118
|
+
self.inner.to_human()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Serialize to JSON.
|
|
122
|
+
#[napi]
|
|
123
|
+
pub fn to_json(&self) -> napi::Result<String> {
|
|
124
|
+
serde_json::to_string(&self.inner).map_err(to_napi_err)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
129
|
+
// FEE
|
|
130
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
131
|
+
|
|
132
|
+
/// Fee schedule calculation.
|
|
133
|
+
#[napi]
|
|
134
|
+
pub struct JsFeeSchedule {
|
|
135
|
+
inner: clawpowers_fee::FeeSchedule,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#[napi]
|
|
139
|
+
impl JsFeeSchedule {
|
|
140
|
+
/// Create a fee schedule with default rates (77 bps tx, 30 bps swap).
|
|
141
|
+
#[napi(factory)]
|
|
142
|
+
pub fn with_defaults() -> Self {
|
|
143
|
+
Self {
|
|
144
|
+
inner: clawpowers_fee::FeeSchedule::default(),
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// Create a fee schedule with custom rates and recipient address.
|
|
149
|
+
#[napi(constructor)]
|
|
150
|
+
pub fn new(tx_bps: i64, swap_bps: i64, recipient_hex: String) -> napi::Result<Self> {
|
|
151
|
+
let recipient: alloy_primitives::Address = recipient_hex.parse().map_err(to_napi_err)?;
|
|
152
|
+
Ok(Self {
|
|
153
|
+
inner: clawpowers_fee::FeeSchedule::new(tx_bps as u64, swap_bps as u64, recipient),
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// Calculate fee. fee_type: "transaction", "swap", or "custom:<bps>".
|
|
158
|
+
#[napi]
|
|
159
|
+
pub fn calculate(&self, amount: f64, decimals: u32, fee_type: String) -> napi::Result<String> {
|
|
160
|
+
let amt = clawpowers_tokens::TokenAmount::from_human(amount, decimals as u8);
|
|
161
|
+
let ft = match fee_type.as_str() {
|
|
162
|
+
"transaction" => clawpowers_fee::FeeType::Transaction,
|
|
163
|
+
"swap" => clawpowers_fee::FeeType::Swap,
|
|
164
|
+
s if s.starts_with("custom:") => {
|
|
165
|
+
let bps: u64 = s[7..].parse().map_err(to_napi_err)?;
|
|
166
|
+
clawpowers_fee::FeeType::Custom(bps)
|
|
167
|
+
}
|
|
168
|
+
_ => {
|
|
169
|
+
return Err(napi::Error::from_reason(format!(
|
|
170
|
+
"unknown fee type: {fee_type}"
|
|
171
|
+
)));
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
let calc = self.inner.calculate(amt, ft).map_err(to_napi_err)?;
|
|
175
|
+
let result = serde_json::json!({
|
|
176
|
+
"gross": calc.gross_amount.to_human(),
|
|
177
|
+
"fee": calc.fee_amount.to_human(),
|
|
178
|
+
"net": calc.net_amount.to_human(),
|
|
179
|
+
"fee_recipient": format!("{:#x}", calc.fee_recipient),
|
|
180
|
+
});
|
|
181
|
+
Ok(result.to_string())
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
186
|
+
// X402
|
|
187
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
188
|
+
|
|
189
|
+
/// HTTP 402 Payment Required protocol client.
|
|
190
|
+
#[napi]
|
|
191
|
+
pub struct JsX402Client {
|
|
192
|
+
#[allow(dead_code)]
|
|
193
|
+
inner: clawpowers_x402::X402Client,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
#[napi]
|
|
197
|
+
impl JsX402Client {
|
|
198
|
+
/// Create a new x402 client.
|
|
199
|
+
#[napi(constructor)]
|
|
200
|
+
pub fn new() -> Self {
|
|
201
|
+
Self {
|
|
202
|
+
inner: clawpowers_x402::X402Client::new(),
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/// Build an X-Payment header value from payment JSON and signature.
|
|
207
|
+
#[napi]
|
|
208
|
+
pub fn create_payment_header(
|
|
209
|
+
&self,
|
|
210
|
+
payment_json: String,
|
|
211
|
+
signature: String,
|
|
212
|
+
) -> napi::Result<String> {
|
|
213
|
+
let payment: clawpowers_x402::X402PaymentRequired =
|
|
214
|
+
serde_json::from_str(&payment_json).map_err(to_napi_err)?;
|
|
215
|
+
Ok(clawpowers_x402::X402Client::create_payment_header(
|
|
216
|
+
&payment, &signature,
|
|
217
|
+
))
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
222
|
+
// CANONICAL STORE
|
|
223
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
224
|
+
|
|
225
|
+
/// Append-only canonical record store backed by SQLite.
|
|
226
|
+
#[napi]
|
|
227
|
+
pub struct JsCanonicalStore {
|
|
228
|
+
inner: clawpowers_canonical::CanonicalStore,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
#[napi]
|
|
232
|
+
impl JsCanonicalStore {
|
|
233
|
+
/// Open or create a persistent store at path.
|
|
234
|
+
#[napi(factory)]
|
|
235
|
+
pub fn open(path: String) -> napi::Result<Self> {
|
|
236
|
+
let inner = clawpowers_canonical::CanonicalStore::new(&path).map_err(to_napi_err)?;
|
|
237
|
+
Ok(Self { inner })
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/// Create an in-memory store (non-persistent).
|
|
241
|
+
#[napi(factory)]
|
|
242
|
+
pub fn in_memory() -> napi::Result<Self> {
|
|
243
|
+
let inner = clawpowers_canonical::CanonicalStore::in_memory().map_err(to_napi_err)?;
|
|
244
|
+
Ok(Self { inner })
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/// Insert a record (JSON). Returns assigned UUID.
|
|
248
|
+
#[napi]
|
|
249
|
+
pub fn insert(&self, record_json: String) -> napi::Result<String> {
|
|
250
|
+
let record: clawpowers_canonical::CanonicalRecord =
|
|
251
|
+
serde_json::from_str(&record_json).map_err(to_napi_err)?;
|
|
252
|
+
let id = self.inner.insert(&record).map_err(to_napi_err)?;
|
|
253
|
+
Ok(id.to_string())
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/// Get a record by UUID. Returns JSON or null.
|
|
257
|
+
#[napi]
|
|
258
|
+
pub fn get(&self, id: String) -> napi::Result<Option<String>> {
|
|
259
|
+
let uuid: uuid::Uuid = id.parse().map_err(to_napi_err)?;
|
|
260
|
+
let record = self.inner.get(&uuid).map_err(to_napi_err)?;
|
|
261
|
+
match record {
|
|
262
|
+
Some(r) => Ok(Some(serde_json::to_string(&r).map_err(to_napi_err)?)),
|
|
263
|
+
None => Ok(None),
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/// Verify record integrity by re-hashing.
|
|
268
|
+
#[napi]
|
|
269
|
+
pub fn verify_integrity(&self, id: String) -> napi::Result<bool> {
|
|
270
|
+
let uuid: uuid::Uuid = id.parse().map_err(to_napi_err)?;
|
|
271
|
+
self.inner.verify_integrity(&uuid).map_err(to_napi_err)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
276
|
+
// TURBO COMPRESSOR
|
|
277
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
278
|
+
|
|
279
|
+
/// TurboQuant vector compressor for embeddings.
|
|
280
|
+
#[napi]
|
|
281
|
+
pub struct JsTurboCompressor {
|
|
282
|
+
inner: clawpowers_compression::TurboCompressor,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#[napi]
|
|
286
|
+
impl JsTurboCompressor {
|
|
287
|
+
/// Create a new compressor for the given dimensions and quantization bits.
|
|
288
|
+
#[napi(constructor)]
|
|
289
|
+
pub fn new(dimensions: u32, bits: u32) -> Self {
|
|
290
|
+
Self {
|
|
291
|
+
inner: clawpowers_compression::TurboCompressor::new(
|
|
292
|
+
clawpowers_compression::CompressionConfig {
|
|
293
|
+
dimensions: dimensions as usize,
|
|
294
|
+
quantization_bits: bits as u8,
|
|
295
|
+
rotation_seed: 0xDEAD_BEEF_CAFE_1234,
|
|
296
|
+
},
|
|
297
|
+
),
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/// Compress a Float32Array. Returns JSON.
|
|
302
|
+
#[napi]
|
|
303
|
+
pub fn compress(&self, vector: Float32Array) -> napi::Result<String> {
|
|
304
|
+
let compressed = self.inner.compress(vector.as_ref()).map_err(to_napi_err)?;
|
|
305
|
+
serde_json::to_string(&compressed).map_err(to_napi_err)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/// Decompress a JSON compressed vector back to Float32Array.
|
|
309
|
+
#[napi]
|
|
310
|
+
pub fn decompress(&self, compressed_json: String) -> napi::Result<Float32Array> {
|
|
311
|
+
let compressed: clawpowers_compression::CompressedVector =
|
|
312
|
+
serde_json::from_str(&compressed_json).map_err(to_napi_err)?;
|
|
313
|
+
let values = self.inner.decompress(&compressed).map_err(to_napi_err)?;
|
|
314
|
+
Ok(Float32Array::new(values))
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
319
|
+
// WRITE FIREWALL
|
|
320
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
321
|
+
|
|
322
|
+
/// Write access control firewall.
|
|
323
|
+
#[napi]
|
|
324
|
+
pub struct JsWriteFirewall {
|
|
325
|
+
inner: clawpowers_security::WriteFirewall,
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
#[napi]
|
|
329
|
+
impl JsWriteFirewall {
|
|
330
|
+
/// Create a firewall from a JSON config.
|
|
331
|
+
#[napi(constructor)]
|
|
332
|
+
pub fn new(config_json: String) -> napi::Result<Self> {
|
|
333
|
+
let config: FirewallConfig = serde_json::from_str(&config_json).map_err(to_napi_err)?;
|
|
334
|
+
Ok(Self {
|
|
335
|
+
inner: clawpowers_security::WriteFirewall::new(config.allowed_namespaces),
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/// Evaluate a write request (JSON). Returns JSON decision.
|
|
340
|
+
#[napi]
|
|
341
|
+
pub fn evaluate(&self, request_json: String) -> napi::Result<String> {
|
|
342
|
+
let req: clawpowers_security::WriteRequest =
|
|
343
|
+
serde_json::from_str(&request_json).map_err(to_napi_err)?;
|
|
344
|
+
let decision = self.inner.evaluate(&req);
|
|
345
|
+
serde_json::to_string(&decision).map_err(to_napi_err)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
#[derive(serde::Deserialize)]
|
|
350
|
+
struct FirewallConfig {
|
|
351
|
+
allowed_namespaces: Vec<String>,
|
|
352
|
+
}
|