diff --git a/.github/workflows/ai-maintenance-manual.yml b/.github/workflows/ai-maintenance-manual.yml index ba5f144..4d750db 100644 --- a/.github/workflows/ai-maintenance-manual.yml +++ b/.github/workflows/ai-maintenance-manual.yml @@ -1,4 +1,4 @@ -name: AI Site Maintenance (manual) +name: AI Site Maintenance (manual) on: workflow_dispatch: diff --git a/.github/workflows/ai-maintenance-run.yml b/.github/workflows/ai-maintenance-run.yml index ceeabd8..6b7d759 100644 --- a/.github/workflows/ai-maintenance-run.yml +++ b/.github/workflows/ai-maintenance-run.yml @@ -1,6 +1,4 @@ -on: - push: - branches: [ main ] +on: workflow_dispatch: permissions: diff --git a/.github/workflows/ai-maintenance-v3.yml b/.github/workflows/ai-maintenance-v3.yml index e31f2b2..ed7a92f 100644 --- a/.github/workflows/ai-maintenance-v3.yml +++ b/.github/workflows/ai-maintenance-v3.yml @@ -1,4 +1,4 @@ -name: AI Site Maintenance (v3, manual) +name: AI Site Maintenance (v3, manual) on: workflow_dispatch: diff --git a/.github/workflows/ai-maintenance.yml b/.github/workflows/ai-maintenance.yml index 8e73194..0f49989 100644 --- a/.github/workflows/ai-maintenance.yml +++ b/.github/workflows/ai-maintenance.yml @@ -1,8 +1,6 @@ -name: AI Site Maintenance +name: AI Site Maintenance on: - schedule: - - cron: "17 2 * * *" - workflow_dispatch: {} + workflow_dispatch: permissions: contents: write diff --git a/.github/workflows/ai-ops.yml b/.github/workflows/ai-ops.yml index e70a2a7..4c53bf2 100644 --- a/.github/workflows/ai-ops.yml +++ b/.github/workflows/ai-ops.yml @@ -1,7 +1,6 @@ -name: AI Ops Issue to PR +name: AI Ops Issue to PR on: - issues: - types: [opened, edited, labeled] + workflow_dispatch: permissions: contents: write diff --git a/_layouts/default.html b/_layouts/default.html index 1ba9e91..2e12ece 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -91,6 +91,9 @@ Last updated: {{ page.last_updated }}
{% endif %} + + + {% include footer.html %} diff --git a/assets/gpt5-actions.js b/assets/gpt5-actions.js new file mode 100644 index 0000000..1594fb0 --- /dev/null +++ b/assets/gpt5-actions.js @@ -0,0 +1,81 @@ +// ES5-safe helpers that use window.gpt5 (from /assets/gpt5.js) +(function () { + if (!window.gpt5) { + console.error("gpt5 helper not found. Include /assets/gpt5.js first."); + return; + } + + function $(id) { return document.getElementById(id); } + + function textFromPage() { + var el = + document.querySelector("main") || + document.querySelector("article") || + document.querySelector(".content") || + document.querySelector(".post-content") || + document.getElementById("content") || + document.body; + var t = el && (el.innerText || el.textContent) || ""; + t = (t || "").replace(/\s+/g, " ").trim(); + if (t.length > 6000) t = t.slice(0, 6000); // keep the demo snappy + return t; + } + + function setBtn(disabled, label) { + var b = $("ai-summary-btn"); + if (b) { + b.disabled = !!disabled; + if (label != null) b.textContent = label; + } + } + document.addEventListener("DOMContentLoaded", function () { + var btn = document.getElementById("ai-summary-btn"); + var stop = document.getElementById("ai-summary-stop"); + var out = document.getElementById("ai-summary"); + if (!btn || !out || !stop) return; + + function setBtn(disabled, label) { + btn.disabled = !!disabled; + if (label != null) btn.textContent = label; + } + + stop.addEventListener("click", function () { + try { gpt5.abort(); } catch (e) {} + setBtn(false, "AI Summary"); + }); + + btn.addEventListener("click", function () { + var el = + document.querySelector("main") || + document.querySelector("article") || + document.querySelector(".content") || + document.querySelector(".post-content") || + document.getElementById("content") || + document.body; + var content = el && (el.innerText || el.textContent) || ""; + content = (content || "").replace(/\s+/g, " ").trim(); + if (content.length > 6000) content = content.slice(0, 6000); + + if (!content) { + out.textContent = "Sorry—no readable content found on this page."; + return; + } + + out.textContent = "Thinking…"; + setBtn(true, "Summarizing…"); + var full = ""; + + gpt5.stream({ + system: "You are LLbot for OraDigit.com. Summarize for executives and prospects. 5–7 bullet points. Clear, concrete, jargon-light.", + messages: [{ role: "user", content: "Summarize this page for a prospective client:\n\n" + content }], + onToken: function (t) { full += t; out.textContent = full; } + }) + .then(function () { setBtn(false, "AI Summary"); }) + .catch(function (e) { + out.textContent = "Error: " + (e && e.message ? e.message : e); + setBtn(false, "AI Summary"); + }); + }); +}); + + diff --git a/assets/gpt5.js b/assets/gpt5.js new file mode 100644 index 0000000..57ab81a --- /dev/null +++ b/assets/gpt5.js @@ -0,0 +1,102 @@ +// Minimal client for OraDigit's GPT proxy (SSE). ES5-compatible. +// Adds smart token spacing and an abort() helper. +(function () { + var API = "https://auth.oradigit.com/api/chat"; // mapped to chat5 + var _controller = null; + + function needsSpace(prev, next) { + if (!prev || !next) return false; + var a = prev.slice(-1); + var b = next.charAt(0); + // insert a space when two alphanumerics touch (e.g., "Hello" + "How") + return /[A-Za-z0-9]/.test(a) && /[A-Za-z0-9]/.test(b); + } + + function stream(opts) { + if (!opts) opts = {}; + if (!Array.isArray(opts.messages)) throw new Error("messages[] required"); + + var payload = { messages: opts.messages }; + if (opts.system) payload.system = opts.system; + if (opts.model) payload.model = opts.model; + + _controller = new AbortController(); + var useSignal = opts.signal || _controller.signal; + + return fetch(API, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + signal: useSignal + }).then(function (resp) { + var ct = resp.headers.get("content-type"); + if (!resp.ok) { + return resp.text().then(function (txt) { + try { var j = JSON.parse(txt); throw new Error(j.error || j.message || ("HTTP " + resp.status)); } + catch (e) { throw new Error(txt || ("HTTP " + resp.status)); } + }); + } + + if (ct && ct.indexOf("text/event-stream") !== -1) { + var reader = resp.body.getReader(); + var decoder = new TextDecoder(); + var buffer = ""; + var full = ""; + + function pump() { + return reader.read().then(function (r) { + if (r.done) return full; + + buffer += decoder.decode(r.value, { stream: true }); + var parts = buffer.split("\n\n"); + buffer = parts.pop() || ""; + + for (var i = 0; i < parts.length; i++) { + var evt = parts[i]; + if (!evt.trim()) continue; + var lines = evt.split("\n"); + var type = (lines[0] || "").replace(/^event:\s*/, "").trim(); + var data = (lines.slice(1).join("\n") || "").replace(/^data:\s*/, ""); + + if (type === "message") { + var chunk = data; + if (needsSpace(full, chunk)) chunk = " " + chunk; + full += chunk; + if (typeof opts.onToken === "function") opts.onToken(chunk); + } else if (type === "error") { + try { var j = JSON.parse(data); throw new Error(j.message || "Server error"); } + catch (e) { throw new Error("Server error"); } + } + // ignore: open/info/done + } + return pump(); + }); + } + return pump(); + } + + // Non-SSE fallback + return resp.json().then(function (json) { + if (json && json.error) throw new Error(json.error); + return json; + })["catch"](function () { return {}; }); + }); + } + + function ask(prompt, opts) { + opts = opts || {}; + return stream({ + system: opts.system || "You are LLbot for OraDigit.com. Be concise.", + model: opts.model, + messages: [{ role: "user", content: prompt }], + onToken: opts.onToken, + signal: opts.signal + }); + } + + function abort() { + if (_controller) try { _controller.abort(); } catch (e) {} + } + + window.gpt5 = { stream: stream, ask: ask, abort: abort }; +})(); diff --git a/index.html b/index.html index 90276ad..b1a59d1 100644 --- a/index.html +++ b/index.html @@ -145,6 +145,13 @@