@yysng/ai-edit-engine 2.2.2 → 2.3.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.
@@ -0,0 +1 @@
1
+ export declare function mountAiEditor(root: HTMLElement): void;
@@ -0,0 +1,63 @@
1
+ export function mountAiEditor(root) {
2
+ root.innerHTML = `
3
+ <h1>AI Editor</h1>
4
+
5
+ <select id="ai-section">
6
+ <option value="hero">Hero</option>
7
+ <option value="cta">CTA</option>
8
+ </select>
9
+
10
+ <br/><br/>
11
+
12
+ <textarea id="ai-input" placeholder="Describe your change..."></textarea>
13
+
14
+ <br/><br/>
15
+
16
+ <button id="ai-apply">Apply Change</button>
17
+ <span id="ai-status"></span>
18
+
19
+ <pre id="ai-output"></pre>
20
+ `;
21
+ const textarea = root.querySelector("#ai-input");
22
+ const button = root.querySelector("#ai-apply");
23
+ const output = root.querySelector("#ai-output");
24
+ const select = root.querySelector("#ai-section");
25
+ const status = root.querySelector("#ai-status");
26
+ if (!textarea || !button || !output || !select || !status) {
27
+ throw new Error("AI Editor UI failed to mount");
28
+ }
29
+ button.addEventListener("click", async () => {
30
+ const instruction = textarea.value;
31
+ const section = select.value;
32
+ button.disabled = true;
33
+ status.textContent = `Editing ${section}...`;
34
+ try {
35
+ // 1️⃣ Generate
36
+ const genRes = await fetch("/api/ai-generate", {
37
+ method: "POST",
38
+ headers: { "Content-Type": "application/json" },
39
+ body: JSON.stringify({ instruction, section })
40
+ });
41
+ const data = await genRes.json();
42
+ output.textContent = JSON.stringify(data, null, 2);
43
+ // 2️⃣ Live preview
44
+ window.dispatchEvent(new CustomEvent("ai-preview", {
45
+ detail: { section, data }
46
+ }));
47
+ // 3️⃣ Persist
48
+ await fetch("/api/ai-edit", {
49
+ method: "POST",
50
+ headers: { "Content-Type": "application/json" },
51
+ body: JSON.stringify({ section, content: data })
52
+ });
53
+ status.textContent = `Saved to ${section}`;
54
+ }
55
+ catch (err) {
56
+ console.error(err);
57
+ status.textContent = "Error applying change";
58
+ }
59
+ finally {
60
+ button.disabled = false;
61
+ }
62
+ });
63
+ }
@@ -0,0 +1,2 @@
1
+ export type UpdateContentFn = (section: string, content: any, runtimeEnv: any) => Promise<any>;
2
+ export declare function editContent(section: string, content: any, runtimeEnv: any, updateContent: UpdateContentFn): Promise<any>;
@@ -0,0 +1,3 @@
1
+ export async function editContent(section, content, runtimeEnv, updateContent) {
2
+ return await updateContent(section, content, runtimeEnv);
3
+ }
@@ -0,0 +1 @@
1
+ export declare function generateContent(section: "hero" | "cta", instruction: string, runtimeEnv: any): Promise<any>;
@@ -0,0 +1,51 @@
1
+ import OpenAI from "openai";
2
+ export async function generateContent(section, instruction, runtimeEnv) {
3
+ const client = new OpenAI({
4
+ apiKey: runtimeEnv.OPENAI_API_KEY
5
+ });
6
+ if (!client.apiKey) {
7
+ throw new Error("Missing OPENAI_API_KEY");
8
+ }
9
+ if (section === "cta") {
10
+ const prompt = `
11
+ Return JSON ONLY:
12
+ {
13
+ "heading": string,
14
+ "description": string,
15
+ "button": { "label": string, "href": string }
16
+ }
17
+
18
+ User instruction:
19
+ ${instruction}
20
+ `;
21
+ const completion = await client.chat.completions.create({
22
+ model: "gpt-4o-mini",
23
+ messages: [{ role: "user", content: prompt }],
24
+ temperature: 0.7
25
+ });
26
+ const raw = completion.choices[0].message.content;
27
+ if (!raw)
28
+ throw new Error("OpenAI returned empty content");
29
+ return JSON.parse(raw);
30
+ }
31
+ const heroPrompt = `
32
+ Return JSON ONLY:
33
+ {
34
+ "title": string,
35
+ "subtitle": string,
36
+ "cta": { "label": string, "href": string }
37
+ }
38
+
39
+ User instruction:
40
+ ${instruction}
41
+ `;
42
+ const completion = await client.chat.completions.create({
43
+ model: "gpt-4o-mini",
44
+ messages: [{ role: "user", content: heroPrompt }],
45
+ temperature: 0.7
46
+ });
47
+ const raw = completion.choices[0].message.content;
48
+ if (!raw)
49
+ throw new Error("OpenAI returned empty content");
50
+ return JSON.parse(raw);
51
+ }
@@ -0,0 +1 @@
1
+ export * from "./server/index.js";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from "./server/index.js";
@@ -0,0 +1,3 @@
1
+ export { handleEdit } from "./routes/edit.js";
2
+ export { handleGenerate } from "./routes/generate.js";
3
+ export { handleUI } from "./routes/ui.js";
@@ -0,0 +1,3 @@
1
+ export { handleEdit } from "./routes/edit.js";
2
+ export { handleGenerate } from "./routes/generate.js";
3
+ export { handleUI } from "./routes/ui.js";
@@ -0,0 +1 @@
1
+ export declare function handleEdit(request: Request, env: any): Promise<Response>;
@@ -0,0 +1,11 @@
1
+ import { editContent } from "../../core/edit.js";
2
+ export async function handleEdit(request, env) {
3
+ const { section, content } = await request.json();
4
+ // The client will inject this at runtime
5
+ const updateContent = env.UPDATE_CONTENT;
6
+ if (!updateContent) {
7
+ throw new Error("UPDATE_CONTENT not provided to engine");
8
+ }
9
+ const result = await editContent(section, content, env, updateContent);
10
+ return Response.json({ result });
11
+ }
@@ -0,0 +1 @@
1
+ export declare function handleGenerate(request: Request, env: any): Promise<Response>;
@@ -0,0 +1,6 @@
1
+ import { generateContent } from "../../core/generate.js";
2
+ export async function handleGenerate(request, env) {
3
+ const { section, instruction } = await request.json();
4
+ const result = await generateContent(section, instruction, env);
5
+ return Response.json(result);
6
+ }
@@ -0,0 +1 @@
1
+ export declare function handleUI(): Response;
@@ -0,0 +1,15 @@
1
+ export function handleUI() {
2
+ return new Response(`
3
+ <!DOCTYPE html>
4
+ <html>
5
+ <head>
6
+ <meta charset="utf-8" />
7
+ <title>AI Editor</title>
8
+ </head>
9
+ <body>
10
+ <div id="ai-editor-root"></div>
11
+ <script type="module" src="/__ai_edit_ui_app.js"></script>
12
+ </body>
13
+ </html>
14
+ `, { headers: { "Content-Type": "text/html" } });
15
+ }
@@ -0,0 +1 @@
1
+ export declare function renderEditor(root: HTMLElement): void;
@@ -0,0 +1,19 @@
1
+ export function renderEditor(root) {
2
+ root.innerHTML = `
3
+ <div style="font-family: system-ui; max-width: 900px; margin: 2rem auto">
4
+ <h1>AI Editor</h1>
5
+
6
+ <textarea id="instruction" rows="4" style="width:100%" placeholder="Describe your change..."></textarea>
7
+
8
+ <div style="margin-top:1rem">
9
+ <button id="ping">Ping Engine</button>
10
+ <span id="status" style="margin-left:1rem;color:#555"></span>
11
+ </div>
12
+ </div>
13
+ `;
14
+ const status = root.querySelector("#status");
15
+ const btn = root.querySelector("#ping");
16
+ btn.addEventListener("click", () => {
17
+ status.textContent = "Engine connected successfully.";
18
+ });
19
+ }
package/package.json CHANGED
@@ -1,46 +1,36 @@
1
1
  {
2
2
  "name": "@yysng/ai-edit-engine",
3
- "version": "2.2.2",
3
+ "version": "2.3.0",
4
4
  "type": "module",
5
- "description": "AI Editing Engine for Astro sites (generation, validation, content mutation, Cloudflare-safe)",
5
+ "sideEffects": false,
6
+
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+
6
10
  "exports": {
7
11
  ".": {
8
- "edge": "./runtime/edge/index.js",
9
- "import": "./runtime/edge/index.js",
10
- "node": "./runtime/node/index.js",
11
- "default": "./runtime/edge/index.js"
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
12
14
  },
13
- "./api": {
14
- "edge": "./runtime/edge/engine/api/index.js",
15
- "import": "./runtime/edge/engine/api/index.js",
16
- "node": "./runtime/node/index.js",
17
- "default": "./runtime/edge/engine/api/index.js"
15
+ "./client": {
16
+ "import": "./dist/client/index.js",
17
+ "types": "./dist/client/index.d.ts"
18
18
  },
19
- "./schemas": {
20
- "edge": "./runtime/edge/engine/schemas/index.js",
21
- "import": "./runtime/edge/engine/schemas/index.js",
22
- "node": "./runtime/edge/engine/schemas/index.js",
23
- "default": "./runtime/edge/engine/schemas/index.js"
24
- }
19
+ "./server": {
20
+ "import": "./dist/server/index.js",
21
+ "types": "./dist/server/index.d.ts"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
25
+
26
+ "files": ["dist"],
27
+
28
+ "scripts": {
29
+ "build": "tsc"
25
30
  },
26
- "files": [
27
- "runtime"
28
- ],
31
+
29
32
  "dependencies": {
30
33
  "openai": "^6.13.0",
31
34
  "zod": "^3.23.8"
32
- },
33
- "peerDependencies": {
34
- "astro": "^5.0.0",
35
- "@yysng/astro-boilerplate": "^1.1.36"
36
- },
37
- "keywords": [
38
- "astro",
39
- "ai",
40
- "cms",
41
- "content-engine",
42
- "cloudflare",
43
- "ai-editor"
44
- ],
45
- "license": "MIT"
46
- }
35
+ }
36
+ }
package/README.md DELETED
@@ -1,9 +0,0 @@
1
- ## 🔐 Production Stability Lock
2
-
3
- This site is locked to:
4
-
5
- - Boilerplate: `@yysng/astro-boilerplate@1.1.36`
6
- - Boilerplate Tag: `stable-edge-v1`
7
- - Client Tag: `stable-edge-v1`
8
-
9
- Validated on 2025-12-30.
@@ -1,45 +0,0 @@
1
- export const prerender = false;
2
-
3
- import * as Engine from "@yysng/astro-boilerplate";
4
-
5
- const { updateContent } = Engine;
6
-
7
- // --------------------------------------------------
8
- // 🧠 ENGINE API (used by npm consumers)
9
- // --------------------------------------------------
10
- export async function editContent({ section, content, env = {} }) {
11
- if (!section || !content) {
12
- throw new Error("Missing section or content");
13
- }
14
-
15
- return updateContent(section, content, env);
16
- }
17
-
18
- // --------------------------------------------------
19
- // 🌐 ASTRO ENDPOINT (used by playground / client site)
20
- // --------------------------------------------------
21
- export async function POST({ request, locals }) {
22
- try {
23
- const body = await request.json();
24
- const { section, content } = body;
25
-
26
- const result = await editContent({
27
- section,
28
- content,
29
- env: locals?.runtime?.env
30
- });
31
-
32
- return new Response(
33
- JSON.stringify({ success: true, updated: section, result }),
34
- { status: 200 }
35
- );
36
-
37
- } catch (error) {
38
- console.error("AI Edit Error:", error);
39
-
40
- return new Response(
41
- JSON.stringify({ error: "Internal error", message: error.message }),
42
- { status: 500 }
43
- );
44
- }
45
- }
@@ -1,112 +0,0 @@
1
- export const prerender = false;
2
-
3
- import OpenAI from "openai";
4
-
5
- // --------------------------------------------------
6
- // 🧠 ENGINE API (used by npm consumers)
7
- // --------------------------------------------------
8
- export async function generateContent({ instruction, section, env = {} }) {
9
- if (!instruction || typeof instruction !== "string") {
10
- throw new Error("Invalid instruction");
11
- }
12
-
13
- const client = new OpenAI({
14
- apiKey: env.OPENAI_API_KEY
15
- });
16
-
17
- // ------------------------------
18
- // CTA GENERATION
19
- // ------------------------------
20
- if (section === "cta") {
21
- const prompt = `
22
- You are an AI editor responsible for updating the CTA section of a website.
23
-
24
- Rules:
25
- - Output JSON only
26
- - Follow the schema exactly
27
- - Prefer partial updates
28
- - Do not invent fields
29
-
30
- Return JSON ONLY in this exact format:
31
-
32
- {
33
- "heading": string,
34
- "description": string,
35
- "button": {
36
- "label": string,
37
- "href": string
38
- }
39
- }
40
-
41
- User instruction:
42
- ${instruction}
43
- `;
44
-
45
- const completion = await client.chat.completions.create({
46
- model: "gpt-4o-mini",
47
- messages: [{ role: "user", content: prompt }],
48
- temperature: 0.7
49
- });
50
-
51
- return JSON.parse(completion.choices[0].message.content);
52
- }
53
-
54
- // ------------------------------
55
- // HERO GENERATION
56
- // ------------------------------
57
- const heroPrompt = `
58
- You are an AI editor responsible for updating the hero section of a website.
59
-
60
- Rules:
61
- - Output JSON only
62
- - Follow the schema exactly
63
- - Prefer partial updates
64
- - Do not invent fields
65
-
66
- Return JSON ONLY in this exact format:
67
-
68
- {
69
- "title": string,
70
- "subtitle": string,
71
- "cta": {
72
- "label": string,
73
- "href": string
74
- }
75
- }
76
-
77
- User instruction:
78
- ${instruction}
79
- `;
80
-
81
- const completion = await client.chat.completions.create({
82
- model: "gpt-4o-mini",
83
- messages: [{ role: "user", content: heroPrompt }],
84
- temperature: 0.7
85
- });
86
-
87
- return JSON.parse(completion.choices[0].message.content);
88
- }
89
-
90
- // --------------------------------------------------
91
- // 🌐 ASTRO ENDPOINT (used by playground / client site)
92
- // --------------------------------------------------
93
- export async function POST({ request, locals }) {
94
- try {
95
- const { instruction, section } = await request.json();
96
-
97
- const result = await generateContent({
98
- instruction,
99
- section,
100
- env: locals?.runtime?.env
101
- });
102
-
103
- return new Response(JSON.stringify(result), { status: 200 });
104
-
105
- } catch (err) {
106
- console.error("AI generate error:", err);
107
- return new Response(
108
- JSON.stringify({ error: "AI generation failed", message: err.message }),
109
- { status: 500 }
110
- );
111
- }
112
- }
@@ -1 +0,0 @@
1
- export * from "./engine/index.js";
@@ -1,11 +0,0 @@
1
- {
2
- "allowedEdits": [
3
- "content.hero",
4
- "content.design.hero"
5
- ],
6
- "forbidden": [
7
- "src/components/**",
8
- "src/layouts/**",
9
- "astro.config.*"
10
- ]
11
- }
@@ -1 +0,0 @@
1
- export * from "./storage/index.js";
@@ -1 +0,0 @@
1
- export { createStorage } from "./storage.node.js";