description: Default Tools - Utilities reference. 26 pure-compute tools - text, datetime, math, security, encoding, crypto, CSV. No network, no env vars, no filesystem.
The 26 tools split by concern.
timezoneConvert 🆓
:material-clock-alert-outline:
datetime · util L0
Converts a moment in time between IANA time zones. Returns the same instant rendered in the target zone (ISO with offset).
**Params** `text` · `toTimeZone`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | ISO date/time text |
| `toTimeZone` | `STRING` | ✓ | Target IANA time zone |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Renders the same instant in a different IANA time zone, with offset.
*
* Output is ISO-8601 with explicit ±HH:MM offset (NOT 'Z'), so the
* time zone is unambiguous in the rendered string.
*
* Pure ES2024 - Intl.DateTimeFormat for components + manual offset calc.
*/
if (text == null || text === '') throw new Error('text required');
if (toTimeZone == null || toTimeZone === '') throw new Error('toTimeZone required');
const d = new Date(String(text));
if (isNaN(d.getTime())) throw new Error('invalid date: ' + text);
const fmt = new Intl.DateTimeFormat('en-CA', {
timeZone: toTimeZone,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false,
fractionalSecondDigits: 3,
});
const parts = {};
for (const p of fmt.formatToParts(d)) parts[p.type] = p.value;
const hh = parts.hour === '24' ? '00' : parts.hour;
// Reconstruct the wall-clock instant in target zone, treat it as UTC,
// then derive the offset from the real UTC moment.
const wallUtc = Date.UTC(
Number(parts.year), Number(parts.month) - 1, Number(parts.day),
Number(hh), Number(parts.minute), Number(parts.second));
const offsetMin = Math.round((wallUtc - d.getTime()) / 60_000);
const sign = offsetMin >= 0 ? '+' : '-';
const aoh = Math.abs(offsetMin);
const oh = String(Math.floor(aoh / 60)).padStart(2, '0');
const om = String(aoh % 60).padStart(2, '0');
const frac = parts.fractionalSecond || '000';
return parts.year + '-' + parts.month + '-' + parts.day
+ 'T' + hh + ':' + parts.minute + ':' + parts.second + '.' + frac
+ sign + oh + ':' + om;
```
dateDiff 🆓
:material-calendar-arrow-right:
datetime · util L0
Computes b - a in the requested unit (days|hours|minutes|seconds|milliseconds). Returns a number which may be fractional.
**Params** `a` · `b` · `unit`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `a` | `STRING` | ✓ | Start ISO date/time |
| `b` | `STRING` | ✓ | End ISO date/time |
| `unit` | `STRING` | | Unit of the result (default 'milliseconds') |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Computes `b - a` in the requested unit. Result is a (possibly fractional)
* number - e.g. 5.5 days, 36.0 hours, 0.25 seconds.
*
* `unit` accepts: days / hours / minutes / seconds / milliseconds.
*
* Pure ES2024 - Date.getTime() based; not calendar-aware (use dateMath for
* month/year-level arithmetic).
*/
if (a == null || b == null) throw new Error('a and b required');
const ta = new Date(String(a)).getTime();
const tb = new Date(String(b)).getTime();
if (isNaN(ta) || isNaN(tb)) throw new Error('invalid date');
const diff = tb - ta;
switch (String(unit || 'milliseconds').toLowerCase()) {
case 'day': case 'days': return diff / 86_400_000;
case 'hour': case 'hours': return diff / 3_600_000;
case 'minute': case 'minutes': return diff / 60_000;
case 'second': case 'seconds': return diff / 1_000;
case 'millisecond': case 'milliseconds': case 'ms': return diff;
default: throw new Error('unsupported unit: ' + unit);
}
```
urlEncode 🆓
:material-link-variant:
text · util L0
Percent-encodes a string for use in a URL component. Equivalent to encodeURIComponent.
**Params** `text`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Text to URL-encode |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Percent-encodes a string for safe use inside a URL component.
*
* Encodes every character that is not in the RFC 3986 "unreserved" set
* (ALPHA / DIGIT / -._~). Spaces become %20 (NOT '+'); '&', '=', '?', '#'
* and other reserved characters are all encoded.
*
* Pure ES2024 - uses the standard encodeURIComponent only.
*/
if (text == null) throw new Error('text required');
return encodeURIComponent(String(text));
```
dateMath 🆓
:material-calendar-clock:
datetime · util L0
Adds (or subtracts) a duration to a date and returns the resulting ISO timestamp. Unit: years|months|weeks|days|hours|minutes|seconds|milliseconds.
**Params** `text` · `amount` · `unit`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | ISO date/time text |
| `amount` | `INTEGER` | ✓ | Amount to add (negative to subtract) |
| `unit` | `STRING` | ✓ | Duration unit |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Adds (or subtracts) a duration to an ISO date.
*
* `unit` accepts: years / months / weeks / days / hours / minutes /
* seconds / milliseconds (both singular and plural).
* Negative `amount` performs subtraction.
*
* Pure ES2024 - uses Date UTC setters so DST never bites.
*/
if (text == null || text === '') throw new Error('text required');
const d = new Date(String(text));
if (isNaN(d.getTime())) throw new Error('invalid date: ' + text);
const n = Number(amount);
if (!Number.isFinite(n)) throw new Error('amount must be a finite number');
switch (String(unit || '').toLowerCase()) {
case 'year': case 'years': d.setUTCFullYear(d.getUTCFullYear() + n); break;
case 'month': case 'months': d.setUTCMonth(d.getUTCMonth() + n); break;
case 'week': case 'weeks': d.setUTCDate(d.getUTCDate() + n * 7); break;
case 'day': case 'days': d.setUTCDate(d.getUTCDate() + n); break;
case 'hour': case 'hours': d.setUTCHours(d.getUTCHours() + n); break;
case 'minute': case 'minutes': d.setUTCMinutes(d.getUTCMinutes() + n); break;
case 'second': case 'seconds': d.setUTCSeconds(d.getUTCSeconds() + n); break;
case 'millisecond': case 'milliseconds': case 'ms':
d.setUTCMilliseconds(d.getUTCMilliseconds() + n); break;
default: throw new Error('unsupported unit: ' + unit);
}
return d.toISOString();
```
parseDate 🆓
:material-calendar-import:
datetime · util L0
Parses a date/time string (ISO 8601 or RFC 2822) and returns its components plus epochMillis.
**Params** `text` · `timeZone`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Date/time text |
| `timeZone` | `STRING` | | IANA time zone to interpret the components in (default UTC) |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Parses a date/time string and returns its individual components in the
* requested IANA time zone (default UTC), plus the canonical ISO timestamp
* and epoch millis.
*
* Return shape:
* { iso, epochMillis, year, month, day, hour, minute, second,
* weekday, timeZone }
*
* Pure ES2024 - uses Date + Intl.DateTimeFormat.formatToParts.
*/
if (text == null || text === '') throw new Error('text required');
const d = new Date(String(text));
if (isNaN(d.getTime())) throw new Error('invalid date: ' + text);
const tz = timeZone || 'UTC';
const fmt = new Intl.DateTimeFormat('en-CA', {
timeZone: tz,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false,
weekday: 'short',
});
const parts = {};
for (const part of fmt.formatToParts(d)) parts[part.type] = part.value;
return {
iso: d.toISOString(),
epochMillis: d.getTime(),
year: Number(parts.year),
month: Number(parts.month),
day: Number(parts.day),
// Some engines emit '24' for midnight; normalise to '0' for sanity.
hour: Number(parts.hour === '24' ? '0' : parts.hour),
minute: Number(parts.minute),
second: Number(parts.second),
weekday: parts.weekday,
timeZone: tz,
};
```
cronNext 🆓
:material-timer-cog-outline:
datetime · util L0
Computes the next datetime matching a standard 5-field cron expression (minute hour day month weekday). Supports * , - / and ? (treated as *). Returns ISO timestamp in UTC.
**Params** `expression` · `from` · `count`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `expression` | `STRING` | ✓ | Cron expression (5 fields) |
| `from` | `STRING` | | Starting ISO datetime (default: now) |
| `count` | `INTEGER` | | Number of next occurrences to return (default 1, max 100) |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Computes the next N occurrences matching a 5-field cron expression.
*
* Field layout: minute hour day-of-month month day-of-week
* Supports `*`, `?`, comma lists (`1,3,5`), ranges (`1-5`) and step
* suffixes (`* / 15`, `1-23/2`).
*
* All times are computed in UTC and returned as ISO timestamps.
*
* Pure ES2024 - Date + Set, no external scheduler library.
*/
if (expression == null || expression === '') throw new Error('expression required');
const fields = String(expression).trim().split(/\s+/);
if (fields.length !== 5) throw new Error('expected 5 cron fields, got ' + fields.length);
const ranges = [[0, 59], [0, 23], [1, 31], [1, 12], [0, 6]];
function parseField(spec, [min, max]) {
// '*' / '?' → full range
if (spec === '*' || spec === '?') {
const out = new Set();
for (let i = min; i <= max; i++) out.add(i);
return out;
}
const out = new Set();
for (const part of spec.split(',')) {
const stepIdx = part.indexOf('/');
const step = stepIdx >= 0 ? parseInt(part.slice(stepIdx + 1), 10) : 1;
const range = stepIdx >= 0 ? part.slice(0, stepIdx) : part;
let from, to;
if (range === '*') { from = min; to = max; }
else if (range.includes('-')) { const [aa, bb] = range.split('-').map(s => parseInt(s, 10)); from = aa; to = bb; }
else { const v = parseInt(range, 10); from = v; to = v; }
for (let i = from; i <= to; i += step) out.add(i);
}
return out;
}
const sets = fields.map((f, i) => parseField(f, ranges[i]));
const start = (from && from !== '') ? new Date(String(from)) : new Date();
if (isNaN(start.getTime())) throw new Error('invalid from: ' + from);
const want = count == null ? 1 : Number(count);
if (!Number.isInteger(want) || want < 1 || want > 100) throw new Error('count must be 1..100');
const results = [];
const d = new Date(start.getTime());
d.setUTCSeconds(0, 0);
d.setUTCMinutes(d.getUTCMinutes() + 1);
const limit = 366 * 24 * 60; // walk up to one year forward
let steps = 0;
while (results.length < want && steps < limit) {
steps++;
if (sets[0].has(d.getUTCMinutes())
&& sets[1].has(d.getUTCHours())
&& sets[2].has(d.getUTCDate())
&& sets[3].has(d.getUTCMonth() + 1)
&& sets[4].has(d.getUTCDay())) {
results.push(d.toISOString());
}
d.setUTCMinutes(d.getUTCMinutes() + 1);
}
if (results.length === 0) throw new Error('no occurrence found within one year');
return results;
```
diffText 🆓
:material-file-compare:
text · util L0
Returns a line-by-line diff between two texts. Each entry is {op, line} where op is one of '=' (unchanged), '-' (only in a), '+' (only in b).
**Params** `a` · `b`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `a` | `STRING` | ✓ | First text (the 'before' side) |
| `b` | `STRING` | ✓ | Second text (the 'after' side) |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Line-by-line diff between two texts via longest-common-subsequence.
*
* Returns an ordered list of { op, line } entries where `op` is:
* '=' unchanged line (present in both)
* '-' line only present in `a` (deletion)
* '+' line only present in `b` (insertion)
*
* Cost is O(n*m) which is fine for typical configs / smallish files.
* Pure ES2024.
*/
const A = (a == null ? '' : String(a)).split('\n');
const B = (b == null ? '' : String(b)).split('\n');
const n = A.length, m = B.length;
// 1) Build LCS length table.
const lcs = Array.from({length: n + 1}, () => new Int32Array(m + 1));
for (let i = 0; i < n; i++) {
for (let j = 0; j < m; j++) {
lcs[i+1][j+1] = A[i] === B[j]
? lcs[i][j] + 1
: Math.max(lcs[i+1][j], lcs[i][j+1]);
}
}
// 2) Walk the table backwards to recover the edit script.
const out = [];
let i = n, j = m;
while (i > 0 && j > 0) {
if (A[i-1] === B[j-1]) { out.push({ op: '=', line: A[i-1] }); i--; j--; }
else if (lcs[i-1][j] >= lcs[i][j-1]) { out.push({ op: '-', line: A[i-1] }); i--; }
else { out.push({ op: '+', line: B[j-1] }); j--; }
}
while (i > 0) { out.push({ op: '-', line: A[i-1] }); i--; }
while (j > 0) { out.push({ op: '+', line: B[j-1] }); j--; }
return out.reverse();
```
sortLines 🆓
:material-sort-alphabetical-ascending:
text · util L0
Sorts lines of text alphabetically. Supports reverse and case-insensitive options.
**Params** `text` · `reverse` · `caseInsensitive`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Multi-line text to sort |
| `reverse` | `BOOLEAN` | | Sort descending |
| `caseInsensitive` | `BOOLEAN` | | Compare case-insensitively |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Sorts text by line.
*
* - `reverse=true` - descending order.
* - `caseInsensitive=true` - compare with String.localeCompare ignoring case.
*
* Pure ES2024.
*/
if (text == null) return '';
const lines = String(text).split(/\r?\n/);
const cmp = caseInsensitive
? (x, y) => x.toLowerCase().localeCompare(y.toLowerCase())
: (x, y) => (x < y ? -1 : x > y ? 1 : 0);
lines.sort(cmp);
if (reverse) lines.reverse();
return lines.join('\n');
```
piiDetect 🆓
:material-shield-account-outline:
security · util L0
Scans text for personally identifiable information patterns (email, US SSN, US phone, credit card, IPv4). Returns an array of {type, masked, index}.
**Params** `text`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Text to scan |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Scans text for common PII patterns and returns each finding with its
* masked value + character offset.
*
* Detected:
* email (RFC-shaped, very permissive)
* us-phone ((+1)? AAA-PPP-NNNN with delimiters)
* us-ssn (AAA-GG-SSSS, valid area/group/serial blocks)
* ipv4 (0-255.0-255.0-255.0-255)
* credit-card (13-19 digits + Luhn checksum)
*
* Each finding's value is masked so the raw PII never leaves the tool.
* Pure ES2024.
*/
if (text == null || text === '') return [];
const s = String(text);
function luhn(num) {
// Standard mod-10 check used by all major card brands.
let sum = 0, even = false;
for (let i = num.length - 1; i >= 0; i--) {
let d = num.charCodeAt(i) - 48;
if (d < 0 || d > 9) return false;
if (even) { d *= 2; if (d > 9) d -= 9; }
sum += d; even = !even;
}
return sum > 0 && sum % 10 === 0;
}
const mask = v => v.length <= 6
? '*'.repeat(v.length)
: v.slice(0, 2) + '*'.repeat(v.length - 4) + v.slice(-2);
const patterns = [
{ type: 'email', re: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g },
{ type: 'us-phone', re: /\b(?:\+?1[-. ]?)?\(?[2-9]\d{2}\)?[-. ]?\d{3}[-. ]?\d{4}\b/g },
{ type: 'us-ssn', re: /\b(?!000|666|9\d{2})\d{3}[- ](?!00)\d{2}[- ](?!0000)\d{4}\b/g },
{ type: 'ipv4', re: /\b(?:25[0-5]|2[0-4]\d|1?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|1?\d{1,2})){3}\b/g },
];
const out = [];
for (const { type, re } of patterns) {
for (const m of s.matchAll(re)) {
out.push({ type, masked: mask(m[0]), index: m.index });
}
}
// Credit card detection - extract digit groups then validate Luhn.
const ccRe = /\b(?:\d[ -]?){13,19}\b/g;
for (const m of s.matchAll(ccRe)) {
const digits = m[0].replace(/[ -]/g, '');
if (digits.length >= 13 && digits.length <= 19 && luhn(digits)) {
out.push({ type: 'credit-card', masked: mask(digits), index: m.index });
}
}
out.sort((x, y) => x.index - y.index);
return out;
```
stats 🆓
:material-sigma:
math · util L0
Returns summary statistics (count, sum, min, max, mean, median, stddev) for an array of numbers.
**Params** `numbers`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `numbers` | `ARRAY` | ✓ | Array of numbers |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Summary statistics for a numeric array.
*
* Returns: { count, sum, min, max, mean, median, stddev }.
* Empty array → null for every aggregate except count/sum (both 0).
*
* Pure ES2024.
*/
if (numbers == null) throw new Error('numbers required');
// Coerce + validate each element.
const xs = [];
for (const v of numbers) {
const n = Number(v);
if (!Number.isFinite(n)) throw new Error('non-numeric value: ' + v);
xs.push(n);
}
if (xs.length === 0) {
return { count: 0, sum: 0, min: null, max: null, mean: null, median: null, stddev: null };
}
const sum = xs.reduce((a, b) => a + b, 0);
const mean = sum / xs.length;
const sorted = xs.slice().sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
const median = sorted.length % 2
? sorted[mid]
: (sorted[mid - 1] + sorted[mid]) / 2;
// Population variance (divide by N, not N-1).
const variance = xs.reduce((a, b) => a + (b - mean) ** 2, 0) / xs.length;
return {
count: xs.length,
sum,
min: sorted[0],
max: sorted[sorted.length - 1],
mean,
median,
stddev: Math.sqrt(variance),
};
```
secretPatternDetect 🆓
:material-key-alert-outline:
security · util L0
Scans text for well-known secret patterns (AWS keys, GitHub tokens, Slack tokens, OpenAI keys, Stripe keys, private keys). Returns an array of {type, masked, index}.
**Params** `text`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Text to scan |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Scans text for high-confidence secret patterns and returns each finding
* with its masked value + character offset.
*
* Detected formats:
* AWS access key id (AKIA...)
* GitHub PAT / OAuth / app (ghp_, gho_, ghu_, ghs_, ghr_)
* Slack token (xox[abprs]-...)
* OpenAI API key (sk-...)
* Stripe key (sk_/pk_/rk_test|live_...)
* PEM private key block (-----BEGIN ... PRIVATE KEY-----)
* JWT compact form (eyJ...eyJ...sig)
*
* Each finding is masked to `XXXX****XXXX` form so the secret never leaks
* into the result. Pure ES2024.
*/
if (text == null || text === '') return [];
const s = String(text);
const patterns = [
{ type: 'aws-access-key-id', re: /\bAKIA[0-9A-Z]{16}\b/g },
{ type: 'github-token', re: /\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b/g },
{ type: 'slack-token', re: /\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g },
{ type: 'openai-api-key', re: /\bsk-(proj-)?[A-Za-z0-9_-]{20,}\b/g },
{ type: 'stripe-key', re: /\b(sk|pk|rk)_(test|live)_[A-Za-z0-9]{16,}\b/g },
{ type: 'private-key-block', re: /-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g },
{ type: 'jwt', re: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g },
];
const mask = v => v.length <= 8
? '*'.repeat(v.length)
: v.slice(0, 4) + '*'.repeat(v.length - 8) + v.slice(-4);
const out = [];
for (const { type, re } of patterns) {
for (const m of s.matchAll(re)) {
out.push({ type, masked: mask(m[0]), index: m.index });
}
}
out.sort((x, y) => x.index - y.index);
return out;
```
regexReplace 🆓
:material-find-replace:
text · util L0
Replaces regex matches in the input with the given replacement string. Supports $1, $2 group back-references.
**Params** `text` · `pattern` · `replacement` · `flags`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Text to transform |
| `pattern` | `STRING` | ✓ | JavaScript-flavoured regex pattern |
| `replacement` | `STRING` | ✓ | Replacement string (supports $1, $2, ...) |
| `flags` | `STRING` | | Regex flags (e.g. 'g', 'i', 'gi') |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Regex-replaces text using a JavaScript-flavoured pattern.
*
* Replacement string supports the usual back-references: $1, $2, $&, $`, $'.
* Add the `g` flag to replace every match; otherwise only the first match
* is replaced.
*
* Pure ES2024 - uses RegExp + String.replace.
*/
if (text == null) return '';
if (pattern == null || pattern === '') throw new Error('pattern required');
const flagsStr = (typeof flags === 'string') ? flags : '';
const re = new RegExp(pattern, flagsStr);
return String(text).replace(re, replacement == null ? '' : String(replacement));
```
evalExpression 🆓
:material-calculator-variant-outline:
math · util L0
Evaluates a safe arithmetic/logical expression (no eval, no host access). Supports + - * / % ** parens, &&, ||, !, ==, !=, <, <=, >, >=, and numeric/string/boolean literals plus variables from the variables object.
**Params** `expression` · `variables`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `expression` | `STRING` | ✓ | Expression to evaluate |
| `variables` | `OBJECT` | | Map of variable bindings |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Safe arithmetic / logical expression evaluator.
*
* Supports: + - * / % ** ( ) !
* && || ! == != < <= > >=
* number / string literals, true / false / null
* variables resolved from the `variables` parameter.
*
* Does NOT call `eval`, `new Function`, or any host class - the parser is
* hand-written so untrusted expressions cannot break out of the sandbox.
*
* Pure ES2024.
*/
if (expression == null || expression === '') throw new Error('expression required');
// Copy polyglot foreign map into a plain JS object once.
const rawVars = variables || {};
const vars = {};
for (const k of Object.keys(rawVars)) vars[k] = rawVars[k];
const src = String(expression);
let i = 0;
function peek() { return src[i]; }
function next() { return src[i++]; }
function skipWs() { while (i < src.length && /\s/.test(src[i])) i++; }
function readNumber() {
let s = '';
while (i < src.length && /[0-9.]/.test(src[i])) s += next();
if (i < src.length && (src[i] === 'e' || src[i] === 'E')) {
s += next();
if (src[i] === '+' || src[i] === '-') s += next();
while (i < src.length && /[0-9]/.test(src[i])) s += next();
}
const n = parseFloat(s);
if (!Number.isFinite(n)) throw new Error('bad number: ' + s);
return n;
}
function readString(q) {
next(); let s = '';
while (i < src.length && src[i] !== q) {
if (src[i] === '\\' && i + 1 < src.length) { i++; s += src[i++]; continue; }
s += next();
}
if (src[i] !== q) throw new Error('unterminated string');
next();
return s;
}
function readIdent() {
let s = '';
while (i < src.length && /[A-Za-z0-9_$]/.test(src[i])) s += next();
return s;
}
function parseValue() {
skipWs();
const c = peek();
if (c === '(') { next(); const v = parseExpr(); skipWs(); if (next() !== ')') throw new Error('missing )'); return v; }
if (c === '!') { next(); return !parseValue(); }
if (c === '-') { next(); return -parseValue(); }
if (c === '+') { next(); return +parseValue(); }
if (c === '"' || c === "'") return readString(c);
if (/[0-9.]/.test(c)) return readNumber();
if (/[A-Za-z_$]/.test(c)) {
const id = readIdent();
if (id === 'true') return true;
if (id === 'false') return false;
if (id === 'null') return null;
if (!(id in vars)) throw new Error('undefined variable: ' + id);
return vars[id];
}
throw new Error('unexpected: ' + c);
}
// Operator precedence climbing - Pow > Mul/Div > Add/Sub > Cmp > Eq > And > Or.
function parsePow() { let a = parseValue(); skipWs();
if (src[i] === '*' && src[i+1] === '*') { i += 2; return a ** parsePow(); }
return a;
}
function parseMul() { let a = parsePow();
while (true) {
skipWs();
const c = peek();
if (c === '*' && src[i+1] !== '*') { next(); a = a * parsePow(); }
else if (c === '/') { next(); a = a / parsePow(); }
else if (c === '%') { next(); a = a % parsePow(); }
else break;
}
return a;
}
function parseAdd() { let a = parseMul();
while (true) {
skipWs();
const c = peek();
if (c === '+') { next(); a = a + parseMul(); }
else if (c === '-') { next(); a = a - parseMul(); }
else break;
}
return a;
}
function parseCmp() { let a = parseAdd(); skipWs();
if (src[i] === '<' && src[i+1] === '=') { i += 2; return a <= parseAdd(); }
if (src[i] === '>' && src[i+1] === '=') { i += 2; return a >= parseAdd(); }
if (src[i] === '<') { next(); return a < parseAdd(); }
if (src[i] === '>') { next(); return a > parseAdd(); }
return a;
}
function parseEq() { let a = parseCmp(); skipWs();
while (src[i] === '=' && src[i+1] === '=') { i += 2; if (src[i] === '=') i++; a = (a == parseCmp()); skipWs(); }
while (src[i] === '!' && src[i+1] === '=') { i += 2; if (src[i] === '=') i++; a = (a != parseCmp()); skipWs(); }
return a;
}
function parseAnd() { let a = parseEq(); skipWs(); while (src[i] === '&' && src[i+1] === '&') { i += 2; a = a && parseEq(); skipWs(); } return a; }
function parseOr () { let a = parseAnd(); skipWs(); while (src[i] === '|' && src[i+1] === '|') { i += 2; a = a || parseAnd(); skipWs(); } return a; }
function parseExpr() { return parseOr(); }
const result = parseExpr();
skipWs();
if (i < src.length) throw new Error('unexpected trailing input at ' + i);
return result;
```
base64 🆓
:material-code-tags:
encoding · util L0
Encodes UTF-8 text to base64, or decodes base64 back to UTF-8 text. Use mode='encode' (default) or 'decode'. URL-safe variant via urlSafe=true.
**Params** `text` · `mode` · `urlSafe`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Text to encode/decode |
| `mode` | `STRING` | | encode \| decode |
| `urlSafe` | `BOOLEAN` | | Use URL-safe alphabet (- _ instead of + /) |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Base64 encode / decode of UTF-8 text.
*
* - `mode='encode'` (default) - UTF-8 text → base64 string.
* - `mode='decode'` - base64 string → UTF-8 text.
* - `urlSafe=true` - emit/accept the URL-safe alphabet
* (`-` `_` instead of `+` `/`, padding stripped).
*
* Uses host-injected btoa / atob and the standard TextEncoder / TextDecoder.
*/
if (text == null) throw new Error('text required');
const m = (mode || 'encode').toLowerCase();
if (m === 'encode') {
const bytes = new TextEncoder().encode(String(text));
// btoa expects a Latin-1 binary string - assemble it byte-by-byte.
let bin = '';
for (const b of bytes) bin += String.fromCharCode(b);
let b64 = btoa(bin);
if (urlSafe) b64 = b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
return b64;
}
if (m === 'decode') {
let s = String(text);
if (urlSafe) {
s = s.replace(/-/g, '+').replace(/_/g, '/');
const pad = s.length % 4; if (pad) s += '='.repeat(4 - pad);
}
const bin = atob(s);
const bytes = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
return new TextDecoder('utf-8', { fatal: false }).decode(bytes);
}
throw new Error('mode must be encode or decode');
```
hex 🆓
:material-numeric:
encoding · util L0
Encodes UTF-8 text to hex string, or decodes hex back to UTF-8 text. Use mode='encode' (default) or 'decode'.
**Params** `text` · `mode` · `upperCase`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Text to encode/decode |
| `mode` | `STRING` | | encode \| decode |
| `upperCase` | `BOOLEAN` | | Use uppercase hex when encoding |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Hex encode / decode of UTF-8 text.
*
* - `mode='encode'` (default) - UTF-8 text → lowercase hex string.
* - `mode='decode'` - hex string → UTF-8 text.
* - `upperCase=true` - emit uppercase hex when encoding.
*
* Whitespace inside the hex input is ignored on decode.
* Uses host TextEncoder / TextDecoder.
*/
if (text == null) throw new Error('text required');
const m = (mode || 'encode').toLowerCase();
if (m === 'encode') {
const bytes = new TextEncoder().encode(String(text));
let out = '';
for (const b of bytes) out += b.toString(16).padStart(2, '0');
return upperCase ? out.toUpperCase() : out;
}
if (m === 'decode') {
const s = String(text).replace(/\s+/g, '');
if (s.length % 2 !== 0) throw new Error('hex length must be even');
if (!/^[0-9a-fA-F]*$/.test(s)) throw new Error('invalid hex character');
const bytes = new Uint8Array(s.length / 2);
for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(s.substr(i * 2, 2), 16);
return new TextDecoder('utf-8', { fatal: false }).decode(bytes);
}
throw new Error('mode must be encode or decode');
```
uuid 🆓
:material-identifier:
crypto · util L0
Generates a cryptographically random UUID v4 string.
Click for full reference · params · sandbox · JS source
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Generates a cryptographically random UUID v4.
*
* Uses the host-injected crypto.randomUUID() (no static seeding -
* each call is unpredictable).
*/
return crypto.randomUUID();
```
hash 🆓
:material-pound:
crypto · util L0
Computes the cryptographic hash of UTF-8 text. Algorithms: SHA-256 (default), SHA-384, SHA-512. Returns lowercase hex.
**Params** `text` · `algorithm`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | Text to hash |
| `algorithm` | `STRING` | | SHA-256 \| SHA-384 \| SHA-512 |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Computes a cryptographic hash of UTF-8 text and returns lowercase hex.
*
* Supported algorithms: SHA-256 (default), SHA-384, SHA-512.
*
* Uses the host-injected WebCrypto API (crypto.subtle.digest).
*/
if (text == null) throw new Error('text required');
const alg = algorithm || 'SHA-256';
const bytes = new TextEncoder().encode(String(text));
const digest = await crypto.subtle.digest(alg, bytes);
const arr = Array.isArray(digest) ? digest : Array.from(digest);
return arr.map(b => (b & 0xff).toString(16).padStart(2, '0')).join('');
```
hmac 🆓
:material-key-variant:
crypto · util L0
Computes an HMAC signature over UTF-8 text using a secret. Algorithms: SHA-256 (default), SHA-384, SHA-512. Returns lowercase hex.
**Params** `secret` · `text` · `algorithm`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `secret` | `STRING` | ✓ | Secret key (UTF-8 string) |
| `text` | `STRING` | ✓ | Text to sign |
| `algorithm` | `STRING` | | SHA-256 \| SHA-384 \| SHA-512 |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Computes an HMAC over UTF-8 text using a shared secret. Returns lowercase hex.
*
* Supported HMAC hashes: SHA-256 (default), SHA-384, SHA-512.
*
* Uses WebCrypto: importKey('raw', ..., HMAC) → sign('HMAC', key, data).
*/
if (secret == null || secret === '') throw new Error('secret required');
if (text == null) throw new Error('text required');
const alg = algorithm || 'SHA-256';
const keyBytes = new TextEncoder().encode(String(secret));
const key = await crypto.subtle.importKey(
'raw', keyBytes, { name: 'HMAC', hash: alg }, false, ['sign']);
const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(String(text)));
const arr = Array.isArray(sig) ? sig : Array.from(sig);
return arr.map(b => (b & 0xff).toString(16).padStart(2, '0')).join('');
```
secureRandom 🆓
:material-dice-6-outline:
crypto · util L0
Generates cryptographically secure random bytes. encoding: 'hex' (default), 'base64', or 'base64url'.
**Params** `bytes` · `encoding`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `bytes` | `INTEGER` | | Number of random bytes (default 16, max 4096) |
| `encoding` | `STRING` | | hex \| base64 \| base64url |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Generates cryptographically secure random bytes.
*
* - `bytes` - number of bytes (1..4096, default 16).
* - `encoding` - 'hex' (default), 'base64', or 'base64url'.
*
* Uses crypto.getRandomValues, which delegates to the OS CSPRNG.
*/
const n = bytes == null ? 16 : Number(bytes);
if (!Number.isInteger(n) || n < 1 || n > 4096) throw new Error('bytes must be 1..4096');
const buf = new Uint8Array(n);
crypto.getRandomValues(buf);
const enc = (encoding || 'hex').toLowerCase();
if (enc === 'hex') {
let out = '';
for (const b of buf) out += b.toString(16).padStart(2, '0');
return out;
}
let bin = '';
for (const b of buf) bin += String.fromCharCode(b);
let b64 = btoa(bin);
if (enc === 'base64url') b64 = b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
return b64;
```
passwordGenerate 🆓
:material-form-textbox-password:
crypto · util L0
Generates a strong random password from selected character classes. Uses crypto.getRandomValues for unbiased selection.
**Params** `length` · `includeLowercase` · `includeUppercase` · `includeDigits` · `includeSymbols`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `length` | `INTEGER` | | Password length (default 16, min 4, max 256) |
| `includeLowercase` | `BOOLEAN` | | Include a-z |
| `includeUppercase` | `BOOLEAN` | | Include A-Z |
| `includeDigits` | `BOOLEAN` | | Include 0-9 |
| `includeSymbols` | `BOOLEAN` | | Include ASCII punctuation |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Generates a strong random password from selected character classes.
*
* Guarantees one character from EACH enabled class, then fills the rest
* uniformly from the union and shuffles cryptographically. Uses unbiased
* rejection sampling - no modulo bias.
*
* Defaults: length 16, classes lower/upper/digits enabled, symbols off.
* Uses host crypto.getRandomValues.
*/
const len = length == null ? 16 : Number(length);
if (!Number.isInteger(len) || len < 4 || len > 256) throw new Error('length must be 4..256');
const lo = 'abcdefghijklmnopqrstuvwxyz';
const up = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const di = '0123456789';
const sy = '!@#$%^&*()-_=+[]{};:,.<>/?';
const useLo = includeLowercase !== false;
const useUp = includeUppercase !== false;
const useDi = includeDigits !== false;
const useSy = includeSymbols === true;
let pool = '';
if (useLo) pool += lo;
if (useUp) pool += up;
if (useDi) pool += di;
if (useSy) pool += sy;
if (pool.length === 0) throw new Error('at least one character class required');
const required = [];
if (useLo) required.push(lo);
if (useUp) required.push(up);
if (useDi) required.push(di);
if (useSy) required.push(sy);
// Unbiased pick via rejection sampling on the high-resolution random pool.
function pick(alphabet) {
const buf = new Uint32Array(1);
const max = Math.floor(0xFFFFFFFF / alphabet.length) * alphabet.length;
do { crypto.getRandomValues(buf); } while (buf[0] >= max);
return alphabet[buf[0] % alphabet.length];
}
const chars = required.map(pick);
while (chars.length < len) chars.push(pick(pool));
// Fisher-Yates shuffle, also with rejection-sampled randomness.
for (let i = chars.length - 1; i > 0; i--) {
const buf = new Uint32Array(1);
const max = Math.floor(0xFFFFFFFF / (i + 1)) * (i + 1);
do { crypto.getRandomValues(buf); } while (buf[0] >= max);
const j = buf[0] % (i + 1);
[chars[i], chars[j]] = [chars[j], chars[i]];
}
return chars.join('');
```
jwtDecode 🆓
:material-shield-key-outline:
crypto · util L0
Decodes a JWT without verifying its signature. Returns the header and payload as JSON, plus signaturePresent.
**Params** `token`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `token` | `STRING` | ✓ | JWT compact form (header.payload.signature) |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Decodes a JWT (header + payload) WITHOUT verifying the signature.
*
* Returns: { header, payload, signaturePresent }
*
* Use this for inspection only. To validate authenticity, pass the token
* through `jwtVerify` with the shared secret.
*
* Uses host-injected atob + standard TextDecoder.
*/
if (token == null || token === '') throw new Error('token required');
const parts = String(token).split('.');
if (parts.length < 2) throw new Error('not a JWT (expected at least 2 segments)');
function b64urlDecode(s) {
s = s.replace(/-/g, '+').replace(/_/g, '/');
const pad = s.length % 4; if (pad) s += '='.repeat(4 - pad);
return new TextDecoder('utf-8', { fatal: false })
.decode(Uint8Array.from(atob(s), c => c.charCodeAt(0)));
}
const header = JSON.parse(b64urlDecode(parts[0]));
const payload = JSON.parse(b64urlDecode(parts[1]));
return {
header,
payload,
signaturePresent: parts.length >= 3 && parts[2].length > 0,
};
```
jwtVerify 🆓
:material-shield-check-outline:
crypto · util L0
Verifies a HS256/HS384/HS512 JWT signature using a shared secret and returns the decoded payload on success. Also checks exp and nbf claims when present.
**Params** `token` · `secret`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `token` | `STRING` | ✓ | JWT compact form |
| `secret` | `STRING` | ✓ | Shared HMAC secret |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Verifies a HS256/HS384/HS512 JWT signature with a shared secret and
* returns the decoded payload, plus any structural validity issues.
*
* Return shape:
* { valid, algorithm, header, payload, reasons }
*
* `reasons` is empty on success; otherwise it lists every failed check
* (bad signature, token expired, token not yet valid).
*
* Uses host WebCrypto + atob. Constant-time-style equality (XOR-accumulate)
* so the verifier doesn't leak signature bytes via timing.
*/
if (token == null || token === '') throw new Error('token required');
if (secret == null || secret === '') throw new Error('secret required');
const parts = String(token).split('.');
if (parts.length !== 3) throw new Error('JWT must have 3 segments');
function b64urlDecode(s) {
s = s.replace(/-/g, '+').replace(/_/g, '/');
const pad = s.length % 4; if (pad) s += '='.repeat(4 - pad);
return Uint8Array.from(atob(s), c => c.charCodeAt(0));
}
function b64urlDecodeJson(s) {
return JSON.parse(new TextDecoder().decode(b64urlDecode(s)));
}
const header = b64urlDecodeJson(parts[0]);
const payload = b64urlDecodeJson(parts[1]);
const alg = String(header.alg || '').toUpperCase();
const hashAlg = ({ HS256: 'SHA-256', HS384: 'SHA-384', HS512: 'SHA-512' })[alg];
if (!hashAlg) throw new Error('unsupported alg: ' + alg);
const keyBytes = new TextEncoder().encode(String(secret));
const key = await crypto.subtle.importKey(
'raw', keyBytes, { name: 'HMAC', hash: hashAlg }, false, ['sign']);
const signingInput = new TextEncoder().encode(parts[0] + '.' + parts[1]);
const expected = await crypto.subtle.sign('HMAC', key, signingInput);
// Constant-time compare expected vs provided signature.
const expectedBytes = Array.isArray(expected) ? expected : Array.from(expected);
const actualBytes = Array.from(b64urlDecode(parts[2]));
let acc = 0;
const len = Math.max(expectedBytes.length, actualBytes.length);
for (let idx = 0; idx < len; idx++) {
acc |= (expectedBytes[idx] || 0) ^ (actualBytes[idx] || 0);
}
const sigOk = expectedBytes.length === actualBytes.length && acc === 0;
const now = Math.floor(Date.now() / 1000);
const reasons = [];
if (!sigOk) reasons.push('bad signature');
if (typeof payload.exp === 'number' && now >= payload.exp) reasons.push('token expired');
if (typeof payload.nbf === 'number' && now < payload.nbf) reasons.push('token not yet valid');
return {
valid: reasons.length === 0,
algorithm: alg,
header,
payload,
reasons,
};
```
parseCsv 🆓
:material-file-import-outline:
data · util L0
Parses CSV text into an array of rows. If header=true, each row is an object keyed by the first row.
**Params** `text` · `header` · `delimiter`
**Env** -
Click for full reference · params · sandbox · JS source
**Parameters**
| Param | Type | Req | Description |
|---|---|---|---|
| `text` | `STRING` | ✓ | CSV text |
| `header` | `BOOLEAN` | | Treat the first row as the header |
| `delimiter` | `STRING` | | Field delimiter (default ',') |
**Sandbox** - Runs at the sandbox **L0** baseline (Safest) - pure compute: no network, no filesystem.
JS source
```javascript
/**
* Parses RFC 4180 CSV via the host-bridged Apache Commons CSV parser
* (`safety.parser.csv`).
*
* - `header=true` - first row is the header; each subsequent row is
* returned as an object keyed by header.
* - `delimiter` - single-character field separator (default ',').
*
* Always available - no Java interop or network needed.
*/
if (text == null || text === '') return [];
const opts = { header: !!header };
if (delimiter) opts.delimiter = String(delimiter);
return safety.parser.csv(String(text), opts);
```
The 26 utilities are deliberately I/O-free, which makes them perfect for chains the agent runs entirely in-memory: