@tryvitris/website-harness 0.1.0-beta.8 → 1.0.1
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/README.md +5 -1
- package/agent/refs/admin.md +11 -0
- package/agent/refs/launch.md +13 -11
- package/bin/api/agent/evaluate.js +1 -1
- package/bin/api/agent/grade.js +1 -1
- package/bin/api/cli/api.js +9 -9
- package/bin/api/cli.js +11 -11
- package/bin/api/engine.js +2 -2
- package/bin/api/hosting.js +3 -3
- package/bin/chunks/{chunk-QQOMS2DL.js → chunk-445FZZEC.js} +125 -19
- package/bin/chunks/{chunk-PYCRUMCT.js → chunk-54QPYUM2.js} +1 -1
- package/bin/chunks/{chunk-R2WRQDVE.js → chunk-65LRZIBP.js} +1 -1
- package/bin/chunks/{chunk-FGSQE3DU.js → chunk-72Y4YMTV.js} +1 -1
- package/bin/chunks/{chunk-XY73ZLHB.js → chunk-GQPUILFR.js} +33 -5
- package/bin/chunks/{chunk-A2XMAPVW.js → chunk-JBSGJR7X.js} +1 -1
- package/bin/chunks/{chunk-WNE543KF.js → chunk-KIWEFRNG.js} +5 -5
- package/bin/chunks/{chunk-DYKLM4I3.js → chunk-M72KKZIB.js} +24 -6
- package/bin/chunks/{chunk-FEFLSJM5.js → chunk-RE4GOHSU.js} +3 -3
- package/bin/chunks/{chunk-WMJVLMU2.js → chunk-UR4NWYVW.js} +2 -2
- package/bin/chunks/{chunk-3OKR5AZJ.js → chunk-VKU5TY5C.js} +1 -1
- package/bin/chunks/{design-context-store-W3VTZ4HA.js → design-context-store-ETVS4N6W.js} +1 -1
- package/bin/chunks/{packets-LBMDIFPO.js → packets-XFXKHSEY.js} +1 -1
- package/bin/chunks/{preparation-AV4PDQY6.js → preparation-3OR7LVPQ.js} +1 -1
- package/bin/cloudflare/backend.js +143 -61
- package/bin/types/src/admin/connection.d.ts +3 -0
- package/bin/types/src/agent/refine-document-checks.d.ts +5 -0
- package/bin/types/src/cli/admin.d.ts +21 -0
- package/bin/types/src/cli/site-create.d.ts +2 -0
- package/bin/vitris-art.js +1 -1
- package/bin/vitris-create-site.js +5 -3
- package/bin/vitris-cursor-hook.js +1 -1
- package/bin/vitris-eval.js +3 -3
- package/bin/vitris-go.js +3 -3
- package/bin/vitris-harvest.js +2 -2
- package/bin/vitris-knowledge.js +2 -2
- package/bin/vitris-launch-preflight.js +1 -1
- package/bin/vitris-motion-view.js +1 -1
- package/bin/vitris-refresh.js +2 -2
- package/bin/vitris-stock.js +1 -1
- package/bin/vitris-themes.js +1 -1
- package/bin/vitris-upgrade.js +2 -2
- package/bin/vitris-verify-browser.js +1 -1
- package/bin/vitris.js +11 -11
- package/docs/ADMIN.md +85 -0
- package/docs/CLI.md +11 -5
- package/docs/CLOUDFLARE.md +24 -2
- package/docs/DISTRIBUTION.md +5 -3
- package/docs/PRESERVE.md +1 -1
- package/docs/SECURITY.md +2 -2
- package/package.json +28 -23
- package/templates/site/AGENTS.md +3 -1
- package/templates/site/README.md +5 -3
- package/templates/site/deployment/README.md +24 -0
- package/templates/site/{.github/workflows/deploy.yml → deployment/github-actions.yml.example} +5 -6
- package/templates/site/gitignore +3 -0
- package/templates/site/package.json +2 -1
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
gatherBlankOnboard,
|
|
9
9
|
persistOnboard
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-54QPYUM2.js";
|
|
11
11
|
import {
|
|
12
12
|
persistSiteInputs,
|
|
13
13
|
recordManagedKit,
|
|
14
14
|
resolveSiteInputsLive
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-GQPUILFR.js";
|
|
16
16
|
import {
|
|
17
17
|
initialState,
|
|
18
18
|
parseGraphYaml
|
|
@@ -151,7 +151,9 @@ Preserved custom website from ${fromArg}.
|
|
|
151
151
|
2. npx playwright install chromium
|
|
152
152
|
3. npm run go
|
|
153
153
|
4. npm run preview
|
|
154
|
-
5. npm run deploy (temporary Cloudflare preview)
|
|
154
|
+
5. npm run deploy (build and verify locally, then upload a temporary Cloudflare preview)
|
|
155
|
+
|
|
156
|
+
Local builds and direct uploads are the default. GitHub stores source; no deployment workflow runs automatically. Keep dist/ and release bundles out of Git.
|
|
155
157
|
|
|
156
158
|
Custom source: preserved/. Verification: agent/reviews/preserve.json. See AGENTS.md and the installed harness docs/PRESERVE.md. Run vitris forms inspect and vitris forms setup to register and connect contact forms; backendOrigin alone does not enable submission or notifications. See docs/CONTACT-FORMS.md.
|
|
157
159
|
`);
|
|
@@ -161,10 +163,14 @@ Run npm run go to capture and verify the existing client-facing site. Keep its a
|
|
|
161
163
|
|
|
162
164
|
Repair source in preserved/ when agent/reviews/preserve.json reports a failure; run npm run go again. Never change reference screenshots or hashes to make a check pass. Do not overwrite source by recapturing. onboarding/preserve.json records the source and captured routes. Contact forms require explicit registration and public launch.json forms bindings. backendOrigin alone does not enable delivery. Default previews show a notice without submitting. See docs/CONTACT-FORMS.md.
|
|
163
165
|
|
|
164
|
-
Use npm run deploy
|
|
166
|
+
Use npm run deploy to build and verify locally and directly upload a temporary Cloudflare preview. Keep dist/ and release bundles out of Git. Hosted automation is opt-in; deployment/github-actions.yml.example is inactive by default. Production and customer domains are outside this mode. Scaffold a later redesign separately from the same source URL.
|
|
165
167
|
`);
|
|
166
168
|
}
|
|
167
|
-
|
|
169
|
+
if (mode === "preserve") {
|
|
170
|
+
const instructions = join(dest, "AGENTS.md");
|
|
171
|
+
writeFileSync(instructions, readFileSync(instructions, "utf8") + "\nFor a requested sibling business admin, read agent/refs/admin.md and the installed harness docs/ADMIN.md. Use vitris admin setup --plan then reviewed --apply. Preserve current forms and backend identity; cloud provisioning, authentication and deployment remain separate.\n");
|
|
172
|
+
}
|
|
173
|
+
const workflow = join(dest, "deployment", "github-actions.yml.example");
|
|
168
174
|
if (existsSync(workflow)) {
|
|
169
175
|
if (mode === "preserve") {
|
|
170
176
|
writeFileSync(workflow, `name: Preserve preview
|
|
@@ -198,6 +204,18 @@ jobs:
|
|
|
198
204
|
copyMarkdown: gathered.copyMarkdown
|
|
199
205
|
});
|
|
200
206
|
persistSiteInputs(dest, inputs);
|
|
207
|
+
if (opts.admin) {
|
|
208
|
+
writeFileSync(join(dest, "ADMIN-SETUP.md"), `# Optional business admin
|
|
209
|
+
|
|
210
|
+
Read node_modules/@tryvitris/website-harness/docs/ADMIN.md and agent/refs/admin.md.
|
|
211
|
+
|
|
212
|
+
Prepare a separate sibling installation after confirming the exact website origin, existing tenant ID and backend origin:
|
|
213
|
+
|
|
214
|
+
vitris admin setup --site . --dir ../${slug}-admin --domain admin.CUSTOMER_DOMAIN --website https://www.CUSTOMER_DOMAIN --tenant EXISTING_TENANT_ID --backend-origin https://BACKEND --plan
|
|
215
|
+
|
|
216
|
+
Review artifacts/admin/setup-plan.json, then repeat with --apply and a dedicated VITRIS_ADMIN_API_TOKEN in the operator environment. This opt-in has created instructions only. No backend, grant, account, collection, authentication, DNS or cloud resources have been changed.
|
|
217
|
+
`);
|
|
218
|
+
}
|
|
201
219
|
const graphPath = join(dest, "agent", "graph.yaml");
|
|
202
220
|
if (existsSync(graphPath)) {
|
|
203
221
|
const graph = parseGraphYaml(readFileSync(graphPath, "utf8"));
|
|
@@ -210,7 +228,7 @@ jobs:
|
|
|
210
228
|
return {
|
|
211
229
|
ok: true,
|
|
212
230
|
exitCode: EXIT_CODES.SUCCESS,
|
|
213
|
-
summary: `Created site ${slug} at ${dest}`,
|
|
231
|
+
summary: `Created site ${slug} at ${dest}${opts.admin ? "; optional admin next steps: ADMIN-SETUP.md" : ""}`,
|
|
214
232
|
data: { dest, slug, name }
|
|
215
233
|
};
|
|
216
234
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
capturePreservedSite,
|
|
3
3
|
verifyPreservedSite
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-65LRZIBP.js";
|
|
5
5
|
import {
|
|
6
6
|
AGENT_RUNTIME_HISTORY_ENTRIES
|
|
7
7
|
} from "./chunk-34H54KEY.js";
|
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
verifyAllowedWrites,
|
|
69
69
|
withMutationLock,
|
|
70
70
|
writeVisualProbe
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-GQPUILFR.js";
|
|
72
72
|
import {
|
|
73
73
|
QUESTION_VERSION,
|
|
74
74
|
questionSemanticsHash
|
|
@@ -253,7 +253,7 @@ function scaffoldNestedSite(opts) {
|
|
|
253
253
|
for (const [key, value] of Object.entries(pkg.scripts)) pkg.scripts[key] = value.replaceAll("__SLUG__", opts.slug);
|
|
254
254
|
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
255
255
|
`);
|
|
256
|
-
for (const file2 of ["site.json", "README.md", "AGENTS.md"]) {
|
|
256
|
+
for (const file2 of ["site.json", "README.md", "AGENTS.md", "deployment/github-actions.yml.example"]) {
|
|
257
257
|
const p = join2(dest, file2);
|
|
258
258
|
if (!existsSync2(p)) continue;
|
|
259
259
|
if (file2 === "site.json") {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-2NW7KSPA.js";
|
|
4
4
|
import {
|
|
5
5
|
verifyPreservedSite
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-65LRZIBP.js";
|
|
7
7
|
import {
|
|
8
8
|
EXIT_CODES
|
|
9
9
|
} from "./chunk-JCRGE6AL.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
gradeAll,
|
|
15
15
|
sourceRevision
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-GQPUILFR.js";
|
|
17
17
|
import {
|
|
18
18
|
parseReleaseRecord
|
|
19
19
|
} from "./chunk-M4LNHYYR.js";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
loadPersistedDesignContextPacket,
|
|
4
4
|
persistDesignContextArtifacts,
|
|
5
5
|
resolveDesignContextExpansion
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GQPUILFR.js";
|
|
7
7
|
import "./chunk-KVMZSQ3D.js";
|
|
8
8
|
import "./chunk-WOORR67N.js";
|
|
9
9
|
import "./chunk-PRLL7B63.js";
|
|
@@ -881,67 +881,6 @@ async function handleInboundEmail(message, env) {
|
|
|
881
881
|
});
|
|
882
882
|
}
|
|
883
883
|
|
|
884
|
-
// src/html.ts
|
|
885
|
-
function escapeHtml(value) {
|
|
886
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
887
|
-
}
|
|
888
|
-
function escapeAttr(value) {
|
|
889
|
-
return escapeHtml(value);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// src/types.ts
|
|
893
|
-
var defaultTheme = {
|
|
894
|
-
colorBg: "#F7F4EF",
|
|
895
|
-
colorText: "#1A1714",
|
|
896
|
-
colorMuted: "#5C574F",
|
|
897
|
-
colorAccent: "#B44A2C",
|
|
898
|
-
colorAccentText: "#FFFFFF",
|
|
899
|
-
colorSurface: "#FFFFFF",
|
|
900
|
-
colorSurface2: "#EFEAE3",
|
|
901
|
-
colorBorder: "#E4DDD2",
|
|
902
|
-
colorFocus: "#B44A2C",
|
|
903
|
-
fontBody: 'ui-sans-serif, system-ui, "Segoe UI", sans-serif',
|
|
904
|
-
fontDisplay: "ui-serif, Georgia, 'Times New Roman', serif",
|
|
905
|
-
radius: "8px",
|
|
906
|
-
radiusLg: "16px",
|
|
907
|
-
radiusPill: "999px",
|
|
908
|
-
maxWidth: "1120px"
|
|
909
|
-
};
|
|
910
|
-
|
|
911
|
-
// src/worker/tenant.ts
|
|
912
|
-
async function tenantBySlug(env, slug) {
|
|
913
|
-
return env.DB.prepare("SELECT * FROM tenants WHERE slug = ?").bind(slug).first();
|
|
914
|
-
}
|
|
915
|
-
async function tenantById(env, id) {
|
|
916
|
-
return env.DB.prepare("SELECT * FROM tenants WHERE id = ?").bind(id).first();
|
|
917
|
-
}
|
|
918
|
-
async function tenantByHostname(env, hostname) {
|
|
919
|
-
const cached = await env.KV.get(`host:${hostname}`);
|
|
920
|
-
if (cached) return tenantById(env, cached);
|
|
921
|
-
const row = await env.DB.prepare(
|
|
922
|
-
`SELECT tenants.* FROM tenants JOIN hostnames ON hostnames.tenant_id = tenants.id WHERE hostnames.hostname = ?`
|
|
923
|
-
).bind(hostname.toLowerCase()).first();
|
|
924
|
-
if (row) await env.KV.put(`host:${hostname}`, row.id, { expirationTtl: 3600 });
|
|
925
|
-
return row;
|
|
926
|
-
}
|
|
927
|
-
async function loadSiteSnapshot(env, tenantId) {
|
|
928
|
-
const siteRow = await env.DB.prepare("SELECT json FROM site_config WHERE tenant_id = ?").bind(tenantId).first();
|
|
929
|
-
const contentRow = await env.DB.prepare("SELECT json FROM content WHERE tenant_id = ?").bind(tenantId).first();
|
|
930
|
-
const themeRow = await env.DB.prepare("SELECT json FROM theme WHERE tenant_id = ?").bind(tenantId).first();
|
|
931
|
-
if (!siteRow || !contentRow || !themeRow) return null;
|
|
932
|
-
return {
|
|
933
|
-
site: JSON.parse(siteRow.json),
|
|
934
|
-
content: JSON.parse(contentRow.json),
|
|
935
|
-
theme: { ...defaultTheme, ...JSON.parse(themeRow.json) }
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
async function bumpVersion(env, tenantId) {
|
|
939
|
-
const row = await env.DB.prepare(
|
|
940
|
-
"UPDATE tenants SET content_version = content_version + 1 WHERE id = ? RETURNING content_version"
|
|
941
|
-
).bind(tenantId).first();
|
|
942
|
-
return row?.content_version ?? 1;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
884
|
// src/forms/config.ts
|
|
946
885
|
var safeId = (s) => typeof s === "string" && /^[a-zA-Z0-9_-]{1,80}$/.test(s);
|
|
947
886
|
var safeEmail = (s) => {
|
|
@@ -977,6 +916,14 @@ function validateFormConfig(value) {
|
|
|
977
916
|
return c2;
|
|
978
917
|
}
|
|
979
918
|
|
|
919
|
+
// src/html.ts
|
|
920
|
+
function escapeHtml(value) {
|
|
921
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
922
|
+
}
|
|
923
|
+
function escapeAttr(value) {
|
|
924
|
+
return escapeHtml(value);
|
|
925
|
+
}
|
|
926
|
+
|
|
980
927
|
// src/worker/forms.ts
|
|
981
928
|
var MAX_BODY = 32768;
|
|
982
929
|
function sandboxMailReadiness(env) {
|
|
@@ -1220,6 +1167,140 @@ async function handleFormsAdmin(env, request) {
|
|
|
1220
1167
|
}
|
|
1221
1168
|
}
|
|
1222
1169
|
|
|
1170
|
+
// src/worker/admin.ts
|
|
1171
|
+
var scopes = ["leads:read", "analytics:read", "leads:write"];
|
|
1172
|
+
var json2 = (data, status = 200) => Response.json(data, { status, headers: { "cache-control": "no-store", "x-content-type-options": "nosniff" } });
|
|
1173
|
+
async function handleAdmin(env, request) {
|
|
1174
|
+
const url = new URL(request.url);
|
|
1175
|
+
const match = url.pathname.match(/^\/api\/admin\/v1\/([a-zA-Z0-9_-]{1,100})\/(status|leads|analytics|leads\/status)$/);
|
|
1176
|
+
if (!match) return json2({ error: "Not found" }, 404);
|
|
1177
|
+
const [, tenant, resource] = match;
|
|
1178
|
+
if (request.headers.has("origin")) return json2({ error: "Server credential required" }, 403);
|
|
1179
|
+
const installationId = request.headers.get("x-vitris-installation") || "";
|
|
1180
|
+
const token = request.headers.get("authorization")?.match(/^Bearer ([a-zA-Z0-9_-]{32,200})$/)?.[1];
|
|
1181
|
+
let grant;
|
|
1182
|
+
try {
|
|
1183
|
+
const map = JSON.parse(env.ADMIN_CONNECTIONS || "{}");
|
|
1184
|
+
if (map && typeof map === "object" && !Array.isArray(map) && Object.hasOwn(map, installationId)) grant = map[installationId];
|
|
1185
|
+
} catch {
|
|
1186
|
+
}
|
|
1187
|
+
if (!grant || !token || !/^[a-f0-9]{64}$/.test(grant.tokenHash) || await digest(token) !== grant.tokenHash) return json2({ error: "Unauthorized" }, 401);
|
|
1188
|
+
if (grant.tenantId !== tenant || grant.siteOrigin !== request.headers.get("x-vitris-site-origin") || !Array.isArray(grant.scopes) || grant.scopes.some((s) => !scopes.includes(s))) return json2({ error: "Forbidden" }, 403);
|
|
1189
|
+
if (!await env.DB.prepare("SELECT id FROM tenants WHERE id=?").bind(tenant).first()) return json2({ error: "Forbidden" }, 403);
|
|
1190
|
+
const identity = { schemaVersion: 1, installationId, tenantId: tenant, siteOrigin: grant.siteOrigin };
|
|
1191
|
+
const required = resource === "analytics" ? "analytics:read" : resource === "leads/status" ? "leads:write" : resource === "leads" ? "leads:read" : void 0;
|
|
1192
|
+
if (required && !grant.scopes.includes(required)) return json2({ error: "Forbidden" }, 403);
|
|
1193
|
+
if (request.method !== (resource === "leads/status" ? "POST" : "GET")) return json2({ error: "Method not allowed" }, 405);
|
|
1194
|
+
if (resource === "status") return json2({ ...identity, scopes: grant.scopes, capabilities: { leads: grant.scopes.includes("leads:read"), testSubmissions: grant.scopes.includes("leads:read"), analytics: grant.scopes.includes("analytics:read"), leadStatus: grant.scopes.includes("leads:write") } });
|
|
1195
|
+
if (resource === "leads/status") {
|
|
1196
|
+
if (url.searchParams.has("mode")) return json2({ error: "Test submissions are read-only" }, 400);
|
|
1197
|
+
if (!request.headers.get("content-type")?.startsWith("application/json")) return json2({ error: "JSON required" }, 400);
|
|
1198
|
+
const reader = request.body?.getReader();
|
|
1199
|
+
let body = "";
|
|
1200
|
+
let size = 0;
|
|
1201
|
+
if (reader) for (; ; ) {
|
|
1202
|
+
const { done, value } = await reader.read();
|
|
1203
|
+
if (done) break;
|
|
1204
|
+
size += value.length;
|
|
1205
|
+
if (size > 2048) {
|
|
1206
|
+
await reader.cancel();
|
|
1207
|
+
return json2({ error: "Body too large" }, 413);
|
|
1208
|
+
}
|
|
1209
|
+
body += new TextDecoder().decode(value);
|
|
1210
|
+
}
|
|
1211
|
+
let data;
|
|
1212
|
+
try {
|
|
1213
|
+
data = JSON.parse(body);
|
|
1214
|
+
} catch {
|
|
1215
|
+
return json2({ error: "Invalid JSON" }, 400);
|
|
1216
|
+
}
|
|
1217
|
+
if (!data || typeof data.id !== "string" || data.id.length > 200 || !["new", "contacted", "closed"].includes(data.status)) return json2({ error: "Invalid lead status" }, 400);
|
|
1218
|
+
const result = await env.DB.prepare("UPDATE leads SET status=? WHERE id=? AND tenant_id=? RETURNING id").bind(data.status, data.id, tenant).first();
|
|
1219
|
+
return result ? json2({ ok: true }) : json2({ error: "Lead not found" }, 404);
|
|
1220
|
+
}
|
|
1221
|
+
if (resource === "leads") {
|
|
1222
|
+
const mode = url.searchParams.get("mode") || "live";
|
|
1223
|
+
if (!["live", "test"].includes(mode)) return json2({ error: "Invalid mode" }, 400);
|
|
1224
|
+
const sql = mode === "test" ? `SELECT s.id,s.name,s.email,s.phone,s.message,s.page_path,s.created_at,s.fields_json,'test' AS status,s.form_id,'test' AS mode,o.status AS notification_status,o.last_error AS notification_error,o.test_output
|
|
1225
|
+
FROM form_submissions s LEFT JOIN form_outbox o ON o.id=s.id AND o.tenant_id=s.tenant_id WHERE s.tenant_id=? AND s.mode='test' ORDER BY s.created_at DESC,s.id DESC LIMIT 200` : `SELECT l.id,l.name,l.email,l.phone,l.message,l.page_path,l.status,l.created_at,s.fields_json,o.status AS notification_status,o.last_error AS notification_error
|
|
1226
|
+
FROM leads l LEFT JOIN form_submissions s ON s.id=l.id AND s.tenant_id=l.tenant_id AND s.mode='live'
|
|
1227
|
+
LEFT JOIN form_outbox o ON o.id=s.id AND o.tenant_id=l.tenant_id WHERE l.tenant_id=? ORDER BY l.created_at DESC,l.id DESC LIMIT 200`;
|
|
1228
|
+
const stored = (await env.DB.prepare(sql).bind(tenant).all()).results;
|
|
1229
|
+
const rows2 = [];
|
|
1230
|
+
let size = 0;
|
|
1231
|
+
for (const row of stored) {
|
|
1232
|
+
size += new TextEncoder().encode(JSON.stringify(row)).length;
|
|
1233
|
+
if (size > 35e5) break;
|
|
1234
|
+
rows2.push(row);
|
|
1235
|
+
}
|
|
1236
|
+
return json2({ ...identity, source: "connected", mode: mode === "live" ? "production" : "test", writable: mode !== "test" && grant.scopes.includes("leads:write"), truncated: rows2.length < stored.length || stored.length === 200, rows: rows2 });
|
|
1237
|
+
}
|
|
1238
|
+
const days = Number(url.searchParams.get("days") || "30");
|
|
1239
|
+
if (!Number.isInteger(days) || days < 1 || days > 90) return json2({ error: "days must be 1\u201390" }, 400);
|
|
1240
|
+
const rows = (await env.DB.prepare(`WITH days AS (
|
|
1241
|
+
SELECT day FROM analytics_events WHERE tenant_id=? AND (src IS NULL OR src='' OR src='live')
|
|
1242
|
+
UNION SELECT substr(created_at,1,10) FROM leads WHERE tenant_id=?
|
|
1243
|
+
) SELECT day,
|
|
1244
|
+
(SELECT COUNT(*) FROM analytics_events e WHERE e.tenant_id=? AND e.day=days.day AND e.kind='pageview' AND (e.src IS NULL OR e.src='' OR e.src='live')) AS views,
|
|
1245
|
+
(SELECT COUNT(DISTINCT vid) FROM analytics_events e WHERE e.tenant_id=? AND e.day=days.day AND e.kind='pageview' AND (e.src IS NULL OR e.src='' OR e.src='live')) AS visitors,
|
|
1246
|
+
(SELECT COUNT(*) FROM leads l WHERE l.tenant_id=? AND substr(l.created_at,1,10)=days.day) AS enquiries
|
|
1247
|
+
FROM days WHERE day>=date('now',?) AND day<=date('now') ORDER BY day`).bind(tenant, tenant, tenant, tenant, tenant, `-${days - 1} days`).all()).results;
|
|
1248
|
+
return json2({ ...identity, source: "connected", rows });
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// src/types.ts
|
|
1252
|
+
var defaultTheme = {
|
|
1253
|
+
colorBg: "#F7F4EF",
|
|
1254
|
+
colorText: "#1A1714",
|
|
1255
|
+
colorMuted: "#5C574F",
|
|
1256
|
+
colorAccent: "#B44A2C",
|
|
1257
|
+
colorAccentText: "#FFFFFF",
|
|
1258
|
+
colorSurface: "#FFFFFF",
|
|
1259
|
+
colorSurface2: "#EFEAE3",
|
|
1260
|
+
colorBorder: "#E4DDD2",
|
|
1261
|
+
colorFocus: "#B44A2C",
|
|
1262
|
+
fontBody: 'ui-sans-serif, system-ui, "Segoe UI", sans-serif',
|
|
1263
|
+
fontDisplay: "ui-serif, Georgia, 'Times New Roman', serif",
|
|
1264
|
+
radius: "8px",
|
|
1265
|
+
radiusLg: "16px",
|
|
1266
|
+
radiusPill: "999px",
|
|
1267
|
+
maxWidth: "1120px"
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
// src/worker/tenant.ts
|
|
1271
|
+
async function tenantBySlug(env, slug) {
|
|
1272
|
+
return env.DB.prepare("SELECT * FROM tenants WHERE slug = ?").bind(slug).first();
|
|
1273
|
+
}
|
|
1274
|
+
async function tenantById(env, id) {
|
|
1275
|
+
return env.DB.prepare("SELECT * FROM tenants WHERE id = ?").bind(id).first();
|
|
1276
|
+
}
|
|
1277
|
+
async function tenantByHostname(env, hostname) {
|
|
1278
|
+
const cached = await env.KV.get(`host:${hostname}`);
|
|
1279
|
+
if (cached) return tenantById(env, cached);
|
|
1280
|
+
const row = await env.DB.prepare(
|
|
1281
|
+
`SELECT tenants.* FROM tenants JOIN hostnames ON hostnames.tenant_id = tenants.id WHERE hostnames.hostname = ?`
|
|
1282
|
+
).bind(hostname.toLowerCase()).first();
|
|
1283
|
+
if (row) await env.KV.put(`host:${hostname}`, row.id, { expirationTtl: 3600 });
|
|
1284
|
+
return row;
|
|
1285
|
+
}
|
|
1286
|
+
async function loadSiteSnapshot(env, tenantId) {
|
|
1287
|
+
const siteRow = await env.DB.prepare("SELECT json FROM site_config WHERE tenant_id = ?").bind(tenantId).first();
|
|
1288
|
+
const contentRow = await env.DB.prepare("SELECT json FROM content WHERE tenant_id = ?").bind(tenantId).first();
|
|
1289
|
+
const themeRow = await env.DB.prepare("SELECT json FROM theme WHERE tenant_id = ?").bind(tenantId).first();
|
|
1290
|
+
if (!siteRow || !contentRow || !themeRow) return null;
|
|
1291
|
+
return {
|
|
1292
|
+
site: JSON.parse(siteRow.json),
|
|
1293
|
+
content: JSON.parse(contentRow.json),
|
|
1294
|
+
theme: { ...defaultTheme, ...JSON.parse(themeRow.json) }
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
async function bumpVersion(env, tenantId) {
|
|
1298
|
+
const row = await env.DB.prepare(
|
|
1299
|
+
"UPDATE tenants SET content_version = content_version + 1 WHERE id = ? RETURNING content_version"
|
|
1300
|
+
).bind(tenantId).first();
|
|
1301
|
+
return row?.content_version ?? 1;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1223
1304
|
// src/worker/surfaces/api.ts
|
|
1224
1305
|
var KINDS = /* @__PURE__ */ new Set(["pageview", "leave", "convert", "engage", "ping"]);
|
|
1225
1306
|
var GOALS = /* @__PURE__ */ new Set(["form", "tel", "mailto", "maps"]);
|
|
@@ -23687,6 +23768,7 @@ async function route(env, request) {
|
|
|
23687
23768
|
const path = url.pathname;
|
|
23688
23769
|
if (isPreviewHost(env, request)) return handlePreview(env, request);
|
|
23689
23770
|
if (path.startsWith("/preview/")) return previewHandoff(env, request);
|
|
23771
|
+
if (path.startsWith("/api/admin/")) return handleAdmin(env, request);
|
|
23690
23772
|
const approve = await handleApproveReject(env, request);
|
|
23691
23773
|
if (approve) return approve;
|
|
23692
23774
|
const updatesApi = await handleUpdatesApi(env, request);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { AdminManifest } from '@tryvitris/website-admin-contracts';
|
|
2
|
+
/** Identity and response requirements of the published alpha adapter. */
|
|
3
|
+
export declare function readConnection(manifest: AdminManifest, token: string, resource: 'status' | 'leads' | 'analytics', test?: boolean, transport?: typeof fetch): Promise<any>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function hasRefinementPlaceholder(html: string): boolean;
|
|
2
|
+
/** Load laid-out lazy images in the isolated verification page before judging them. */
|
|
3
|
+
export declare function settleRefinementImages(page: {
|
|
4
|
+
evaluate: (expression: string) => Promise<unknown>;
|
|
5
|
+
}, timeoutMs?: number): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CommandResult } from './types.ts';
|
|
2
|
+
export declare const ADMIN_VERSION = "0.1.0-alpha.1";
|
|
3
|
+
export type AdminOptions = {
|
|
4
|
+
site?: string;
|
|
5
|
+
dir?: string;
|
|
6
|
+
domain?: string;
|
|
7
|
+
website?: string;
|
|
8
|
+
tenant?: string;
|
|
9
|
+
backendOrigin?: string;
|
|
10
|
+
plan?: boolean;
|
|
11
|
+
apply?: boolean;
|
|
12
|
+
leadWrite?: boolean;
|
|
13
|
+
dryRun?: boolean;
|
|
14
|
+
grants?: string;
|
|
15
|
+
out?: string;
|
|
16
|
+
};
|
|
17
|
+
/** Merge the operator's COMPLETE map, never replace unrelated installations. */
|
|
18
|
+
export declare function mergeAdminGrant(existing: unknown, addition: unknown): {
|
|
19
|
+
[x: string]: unknown;
|
|
20
|
+
};
|
|
21
|
+
export declare function adminCommand(command: string, options: AdminOptions): Promise<CommandResult>;
|
package/bin/vitris-art.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
retireProducedAsset,
|
|
13
13
|
validateAssetProduction,
|
|
14
14
|
withMutationLock
|
|
15
|
-
} from "./chunks/chunk-
|
|
15
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
16
16
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
17
17
|
import "./chunks/chunk-WOORR67N.js";
|
|
18
18
|
import "./chunks/chunk-PRLL7B63.js";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createSite
|
|
4
|
-
} from "./chunks/chunk-
|
|
4
|
+
} from "./chunks/chunk-M72KKZIB.js";
|
|
5
5
|
import "./chunks/chunk-34H54KEY.js";
|
|
6
6
|
import "./chunks/chunk-JCRGE6AL.js";
|
|
7
7
|
import {
|
|
8
8
|
promptSourceUrl
|
|
9
|
-
} from "./chunks/chunk-
|
|
9
|
+
} from "./chunks/chunk-54QPYUM2.js";
|
|
10
10
|
import "./chunks/chunk-XGTHGWPW.js";
|
|
11
11
|
import {
|
|
12
12
|
siteInputsFromArgv
|
|
13
|
-
} from "./chunks/chunk-
|
|
13
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
14
14
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
15
15
|
import "./chunks/chunk-WOORR67N.js";
|
|
16
16
|
import "./chunks/chunk-PRLL7B63.js";
|
|
@@ -41,6 +41,7 @@ var arg = (flag) => {
|
|
|
41
41
|
};
|
|
42
42
|
var usage = `Usage: vitris-create-site --dir ../acme --slug acme [--name "Acme Shop"] [--from URL | --blank]
|
|
43
43
|
--owner EMAIL Owner login email
|
|
44
|
+
--admin Prepare optional sibling admin instructions (no cloud writes)
|
|
44
45
|
--template ID Catalog ID, name, or hosted template URL; continue automatically
|
|
45
46
|
--template-release R Optional catalog revision
|
|
46
47
|
--content TEXT Additional business content or instructions
|
|
@@ -67,6 +68,7 @@ async function main() {
|
|
|
67
68
|
blank,
|
|
68
69
|
...siteInputsFromArgv(process.argv),
|
|
69
70
|
mode: arg("--mode"),
|
|
71
|
+
admin: process.argv.includes("--admin"),
|
|
70
72
|
owner: arg("--owner"),
|
|
71
73
|
backendOrigin: arg("--backend-origin") || arg("--kernel-origin"),
|
|
72
74
|
harnessSpec: arg("--harness") || arg("--kernel")
|
package/bin/vitris-eval.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
gradeAll,
|
|
4
4
|
gradeNode,
|
|
5
5
|
reviewSubject
|
|
6
|
-
} from "./chunks/chunk-
|
|
6
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
7
7
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
8
8
|
import "./chunks/chunk-WOORR67N.js";
|
|
9
9
|
import "./chunks/chunk-PRLL7B63.js";
|
|
@@ -44,7 +44,7 @@ if (paths.length) {
|
|
|
44
44
|
} else if (process.argv.includes("--expand-packet")) {
|
|
45
45
|
const expId = arg("--expand-packet");
|
|
46
46
|
const manifestHash = arg("--manifest-hash");
|
|
47
|
-
const { expandPacketItem } = await import("./chunks/packets-
|
|
47
|
+
const { expandPacketItem } = await import("./chunks/packets-XFXKHSEY.js");
|
|
48
48
|
console.log(expandPacketItem(root, node || "business-review", expId, manifestHash));
|
|
49
49
|
} else if (process.argv.includes("--expand-design-context")) {
|
|
50
50
|
const preparationId = arg("--preparation");
|
|
@@ -52,7 +52,7 @@ if (paths.length) {
|
|
|
52
52
|
if (!preparationId || !expansionId || expansionId.startsWith("--")) {
|
|
53
53
|
throw new Error("Use --expand-design-context <ID> --preparation <PREP_ID> [--site PATH]");
|
|
54
54
|
}
|
|
55
|
-
const { expandDesignContextForPreparation } = await import("./chunks/design-context-store-
|
|
55
|
+
const { expandDesignContextForPreparation } = await import("./chunks/design-context-store-ETVS4N6W.js");
|
|
56
56
|
const result = expandDesignContextForPreparation(root, preparationId, expansionId);
|
|
57
57
|
if (!result.ok) {
|
|
58
58
|
console.error(result.reason);
|
package/bin/vitris-go.js
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
runCreativeProfile,
|
|
8
8
|
runSite,
|
|
9
9
|
statusSite
|
|
10
|
-
} from "./chunks/chunk-
|
|
11
|
-
import "./chunks/chunk-
|
|
10
|
+
} from "./chunks/chunk-RE4GOHSU.js";
|
|
11
|
+
import "./chunks/chunk-65LRZIBP.js";
|
|
12
12
|
import "./chunks/chunk-34H54KEY.js";
|
|
13
13
|
import "./chunks/chunk-JCRGE6AL.js";
|
|
14
14
|
import "./chunks/chunk-XL2DRAYI.js";
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
resolveSiteInputsLive,
|
|
20
20
|
runWorkflow,
|
|
21
21
|
siteInputsFromArgv
|
|
22
|
-
} from "./chunks/chunk-
|
|
22
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
23
23
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
24
24
|
import "./chunks/chunk-WOORR67N.js";
|
|
25
25
|
import "./chunks/chunk-PRLL7B63.js";
|
package/bin/vitris-harvest.js
CHANGED
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
gatherOnboard,
|
|
4
4
|
normalizeSourceUrl,
|
|
5
5
|
persistOnboard
|
|
6
|
-
} from "./chunks/chunk-
|
|
6
|
+
} from "./chunks/chunk-54QPYUM2.js";
|
|
7
7
|
import {
|
|
8
8
|
loadDevVars
|
|
9
9
|
} from "./chunks/chunk-XGTHGWPW.js";
|
|
10
10
|
import {
|
|
11
11
|
gradeHarvest
|
|
12
|
-
} from "./chunks/chunk-
|
|
12
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
13
13
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
14
14
|
import "./chunks/chunk-WOORR67N.js";
|
|
15
15
|
import "./chunks/chunk-PRLL7B63.js";
|
package/bin/vitris-knowledge.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
explainStage
|
|
4
|
-
} from "./chunks/chunk-
|
|
5
|
-
import "./chunks/chunk-
|
|
4
|
+
} from "./chunks/chunk-VKU5TY5C.js";
|
|
5
|
+
import "./chunks/chunk-GQPUILFR.js";
|
|
6
6
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
7
7
|
import "./chunks/chunk-WOORR67N.js";
|
|
8
8
|
import "./chunks/chunk-PRLL7B63.js";
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunks/chunk-XL2DRAYI.js";
|
|
11
11
|
import {
|
|
12
12
|
templateAdaptationFailures
|
|
13
|
-
} from "./chunks/chunk-
|
|
13
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
14
14
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
15
15
|
import "./chunks/chunk-WOORR67N.js";
|
|
16
16
|
import "./chunks/chunk-PRLL7B63.js";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
TEMPLATE_MOTION_PATH,
|
|
4
4
|
currentSelectionHash,
|
|
5
5
|
templateMotionHash
|
|
6
|
-
} from "./chunks/chunk-
|
|
6
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
7
7
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
8
8
|
import "./chunks/chunk-WOORR67N.js";
|
|
9
9
|
import "./chunks/chunk-PRLL7B63.js";
|
package/bin/vitris-refresh.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
upgradeSite
|
|
4
|
-
} from "./chunks/chunk-
|
|
4
|
+
} from "./chunks/chunk-JBSGJR7X.js";
|
|
5
5
|
import "./chunks/chunk-JCRGE6AL.js";
|
|
6
|
-
import "./chunks/chunk-
|
|
6
|
+
import "./chunks/chunk-GQPUILFR.js";
|
|
7
7
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
8
8
|
import "./chunks/chunk-WOORR67N.js";
|
|
9
9
|
import "./chunks/chunk-PRLL7B63.js";
|
package/bin/vitris-stock.js
CHANGED
|
@@ -33,7 +33,7 @@ if (!command || command === "--help") {
|
|
|
33
33
|
if (!site) throw new Error("--site required");
|
|
34
34
|
const item = readStockCatalog(catalogPath).items.find((i) => i.id === id);
|
|
35
35
|
if (!item) throw new Error("Unknown stock asset");
|
|
36
|
-
const { findActivePreparation } = await import("./chunks/preparation-
|
|
36
|
+
const { findActivePreparation } = await import("./chunks/preparation-3OR7LVPQ.js");
|
|
37
37
|
const current = existsSync(join(site, "agent/PREPARED.json"));
|
|
38
38
|
if (current && !findActivePreparation(site, "media") && !findActivePreparation(site, "asset-production")) throw new Error("Import stock during media or asset-production; route later additions through the declared owner");
|
|
39
39
|
const launch = existsSync(join(site, "launch.json")) ? json(join(site, "launch.json")) : {};
|
package/bin/vitris-themes.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
thumbnailService,
|
|
20
20
|
validateAdaptationPreferences,
|
|
21
21
|
writeThemePicker
|
|
22
|
-
} from "./chunks/chunk-
|
|
22
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
23
23
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
24
24
|
import "./chunks/chunk-WOORR67N.js";
|
|
25
25
|
import "./chunks/chunk-PRLL7B63.js";
|
package/bin/vitris-upgrade.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
upgradeSite
|
|
4
|
-
} from "./chunks/chunk-
|
|
4
|
+
} from "./chunks/chunk-JBSGJR7X.js";
|
|
5
5
|
import "./chunks/chunk-JCRGE6AL.js";
|
|
6
|
-
import "./chunks/chunk-
|
|
6
|
+
import "./chunks/chunk-GQPUILFR.js";
|
|
7
7
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
8
8
|
import "./chunks/chunk-WOORR67N.js";
|
|
9
9
|
import "./chunks/chunk-PRLL7B63.js";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
observeMotion,
|
|
12
12
|
templateMotionHash,
|
|
13
13
|
validateTemplateMotionPlan
|
|
14
|
-
} from "./chunks/chunk-
|
|
14
|
+
} from "./chunks/chunk-GQPUILFR.js";
|
|
15
15
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
16
16
|
import "./chunks/chunk-WOORR67N.js";
|
|
17
17
|
import "./chunks/chunk-PRLL7B63.js";
|