feat(scripts): add mustache render-template via Responses pages

Add HTML template kind to Responses with a seeded system email-default
template, render-template.js using Mustache, and html body support in
send-email. Templates are editable but system pages cannot be deleted.

Co-authored-by: Nasyarobby Putra <nasyarobby@gmail.com>
This commit is contained in:
Cursor Agent
2026-08-15 01:44:51 +00:00
co-authored by nsrb
parent 23ce72fec2
commit 46e2cbb83b
12 changed files with 426 additions and 50 deletions
+31
View File
@@ -8,6 +8,7 @@ import { createKvApi } from "./kv-store.js";
import { createFingerprintApi } from "./script-fingerprint.js";
import { SCRIPTS_DIR } from "./paths.js";
import { isSecret, Secret, unwrapSecretsDeep } from "./secret-value.js";
import { getHttpPageByName, getHttpTemplateByName } from "./http-pages-store.js";
import { getSecretPlaintext } from "./secrets-store.js";
const hostRequire = createRequire(import.meta.url);
@@ -15,6 +16,7 @@ const hostRequire = createRequire(import.meta.url);
const ALLOWED_MODULES = new Set([
"axios",
"jsonata",
"mustache",
"node-html-parser",
"nodemailer",
"rss-parser",
@@ -324,6 +326,33 @@ function createSecretsApi(owner) {
};
}
/**
* Load HTML template pages from the Responses store.
*/
function createResponsesApi() {
return {
/**
* @param {string} name
*/
async getTemplate(name) {
if (typeof name !== "string" || name.length === 0) {
throw new Error("template name is required");
}
const page = await getHttpTemplateByName(name);
if (!page) {
const any = await getHttpPageByName(name);
if (any && any.kind !== "template") {
throw new Error(
`"${name}" is an HTTP response page, not an HTML template`,
);
}
throw new Error(`html template "${name}" not found`);
}
return page.content;
},
};
}
const $workflowsStub = {
async trigger() {
throw new Error("workflow runner is not available");
@@ -351,6 +380,7 @@ function createScriptSandbox({
const $kv = createKvApi(workflowName);
const $fingerprint = createFingerprintApi($kv);
const $secrets = createSecretsApi(owner);
const $responses = createResponsesApi();
const sandbox = {
...pickBuiltins(),
log: scriptLog,
@@ -359,6 +389,7 @@ function createScriptSandbox({
$kv,
$fingerprint,
$secrets,
$responses,
$workflows,
require: createRestrictedRequire($axios),
};