@nebularstreams/overlay-sdk 1.0.0 → 1.0.2

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.
Files changed (37) hide show
  1. package/package.json +3 -4
  2. package/preview/bundle.min.js +1 -1
  3. package/preview/index.css +2 -18
  4. package/skill/nebular-overlay/references/manifest.md +13 -14
  5. package/skill/nebular-overlay/references/runtime-and-design.md +12 -6
  6. package/templates/all/broadcast/newsticker/manifest.json +1 -1
  7. package/templates/all/broadcast/newsticker-frames/manifest.json +1 -1
  8. package/templates/all/broadcast/weather/manifest.json +0 -1
  9. package/templates/all/engagement/call-to-action/manifest.json +1 -1
  10. package/templates/all/gambling/bingo/manifest.json +0 -1
  11. package/templates/all/gambling/keno/manifest.json +0 -1
  12. package/templates/all/gambling/lottery/manifest.json +0 -1
  13. package/templates/all/gambling/prize-wall/manifest.json +0 -2
  14. package/templates/all/gambling/roulette/manifest.json +0 -1
  15. package/templates/all/gaming/battleship/manifest.json +0 -2
  16. package/templates/all/trivia/audience-poll/manifest.json +1 -2
  17. package/templates/all/trivia/classic-quiz/manifest.json +1 -1
  18. package/templates/all/trivia/lightning-round/manifest.json +0 -1
  19. package/templates/all/trivia/pasapalabra/manifest.json +0 -3
  20. package/templates/all/trivia/quiz-deck/manifest.json +0 -1
  21. package/templates/all/trivia/true-or-false/manifest.json +0 -1
  22. package/templates/basic/broadcast/newsticker/manifest.json +1 -1
  23. package/templates/basic/broadcast/weather/manifest.json +0 -1
  24. package/templates/basic/engagement/call-to-action/manifest.json +1 -1
  25. package/templates/games/gambling/bingo/manifest.json +0 -1
  26. package/templates/games/gambling/keno/manifest.json +0 -1
  27. package/templates/games/gambling/lottery/manifest.json +0 -1
  28. package/templates/games/gambling/prize-wall/manifest.json +0 -2
  29. package/templates/games/gambling/roulette/manifest.json +0 -1
  30. package/templates/games/gaming/battleship/manifest.json +0 -2
  31. package/templates/games/trivia/audience-poll/manifest.json +1 -1
  32. package/templates/games/trivia/classic-quiz/manifest.json +1 -1
  33. package/templates/games/trivia/lightning-round/manifest.json +0 -1
  34. package/templates/games/trivia/pasapalabra/manifest.json +0 -3
  35. package/templates/games/trivia/quiz-deck/manifest.json +0 -1
  36. package/templates/games/trivia/true-or-false/manifest.json +0 -1
  37. package/tool/cli.cjs +1 -1
@@ -7,7 +7,6 @@
7
7
  "phase": "edit",
8
8
  "label": "Questions", "hint": "Ordered question-and-answer cards used by the automatic loop",
9
9
  "max": 30,
10
- "container": "big",
11
10
  "default": [
12
11
  {"question": "Capital of Japan?", "answer": "Tokyo"},
13
12
  {"question": "Fastest land animal?", "answer": "Cheetah"},
@@ -5,7 +5,6 @@
5
5
  "type": "number",
6
6
  "phase": "edit",
7
7
  "label": "Time", "hint": "Enter seconds",
8
- "container": "small",
9
8
  "min": 0,
10
9
  "max": 600,
11
10
  "default": 60
@@ -23,7 +22,6 @@
23
22
  "game/correct": {
24
23
  "type": "multi",
25
24
  "phase": "play",
26
- "container": "big",
27
25
  "label": "Correct answers", "hint": "Letters answered correctly",
28
26
  "choose": [
29
27
  "A", "B", "C", "D", "E", "F", "G", "H", "I",
@@ -39,7 +37,6 @@
39
37
  "game/failed": {
40
38
  "type": "multi",
41
39
  "phase": "play",
42
- "container": "big",
43
40
  "label": "Failed answers", "hint": "Letters answered incorrectly",
44
41
  "choose": [
45
42
  "A", "B", "C", "D", "E", "F", "G", "H", "I",
@@ -7,7 +7,6 @@
7
7
  "phase": "edit",
8
8
  "label": "Question deck", "hint": "Ordered collection of questions and possible answers",
9
9
  "max": 20,
10
- "container": "big",
11
10
  "default": [
12
11
  {"question": "What is the largest ocean on Earth?", "answer": "The Pacific Ocean"},
13
12
  {"question": "How many sides does a dodecagon have?", "answer": "12"},
@@ -6,7 +6,6 @@
6
6
  "phase": "edit",
7
7
  "label": "Questions", "hint": "Statements and their true-or-false answers",
8
8
  "max": 30,
9
- "container": "big",
10
9
  "default": [
11
10
  {"category": "SCIENCE", "statement": "Lightning never strikes the same place twice.", "correct": "false", "explanation": "Lightning can strike the same place repeatedly."},
12
11
  {"category": "GEOGRAPHY", "statement": "The Great Wall of China is visible from the Moon with the naked eye.", "correct": "false", "explanation": "It is far too narrow to be distinguished from the Moon."},
@@ -3,7 +3,7 @@
3
3
  "meta": {
4
4
  "content/brand": {"type": "string", "phase": "edit", "label": "Brand text", "hint": "US OPEN", "default": "US OPEN"},
5
5
  "news": {
6
- "type": "set", "phase": "edit", "label": "News items", "hint": "Headlines shown in their playback order", "container": "big", "max": 30,
6
+ "type": "set", "phase": "edit", "label": "News items", "hint": "Headlines shown in their playback order", "max": 30,
7
7
  "default": [
8
8
  {"topic": "MEN'S DOUBLES", "tagline": "Carlos Alcaraz defeats Tommy Paul in memorable performance, 6-4 6–4 6–2"},
9
9
  {"topic": "MEN'S DOUBLES", "tagline": "The quarter-final line-up is now complete in New York"},
@@ -68,7 +68,6 @@
68
68
  "forecast": {
69
69
  "type": "set",
70
70
  "phase": "play",
71
- "container": "big",
72
71
  "label": "Forecast", "hint": "Forecast periods displayed alongside current conditions",
73
72
  "max": 3,
74
73
  "default": [
@@ -3,7 +3,7 @@
3
3
  "meta": {
4
4
  "content/eyebrow": {"type": "string", "phase": "edit", "label": "Eyebrow", "hint": "Enter short label", "default": "VIEWER GOAL"},
5
5
  "content/title": {"type": "string", "phase": "edit", "label": "Title", "hint": "Enter title", "default": "Help us reach 10,000 followers"},
6
- "content/subtitle": {"type": "string", "phase": "edit", "label": "Description", "hint": "Enter description", "default": "Follow the channel and join the Nebular community."},
6
+ "content/subtitle": {"type": "string", "phase": "edit", "label": "Description", "hint": "Enter description", "default": "Follow the channel and join the Nebular community.", "size": 12},
7
7
  "content/action": {"type": "string", "phase": "edit", "label": "Action", "hint": "e.g. FOLLOW NOW", "default": "FOLLOW NOW"},
8
8
  "goal/playTarget": {"type": "number", "phase": "play", "label": "Target", "hint": "Goal currently used by the live progress display", "source": "goal/target", "readonly": true},
9
9
  "goal/current": {"type": "number", "phase": "play", "label": "Current", "hint": "Enter current value", "min": 0, "default": 7240},
@@ -23,7 +23,6 @@
23
23
  "type": "multi",
24
24
  "phase": "play",
25
25
  "label": "Called numbers", "hint": "Numbers called so far; the latest selection is highlighted",
26
- "container": "big",
27
26
  "choose": [
28
27
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
29
28
  11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
@@ -16,7 +16,6 @@
16
16
  "draw/numbers": {
17
17
  "type": "multi",
18
18
  "phase": "play",
19
- "container": "big",
20
19
  "label": "Drawn numbers", "hint": "Numbers selected in the current draw",
21
20
  "choose": [
22
21
  1,
@@ -16,7 +16,6 @@
16
16
  "draw/numbers": {
17
17
  "type": "multi",
18
18
  "phase": "play",
19
- "container": "big",
20
19
  "label": "Drawn numbers", "hint": "Numbers selected in the current draw",
21
20
  "choose": [
22
21
  1,
@@ -11,7 +11,6 @@
11
11
  "type": "set",
12
12
  "phase": "edit",
13
13
  "label": "Prizes", "hint": "Assign prizes to positions on the wall",
14
- "container": "big",
15
14
  "max": 30,
16
15
  "default": [
17
16
  {"position": 2, "prize": "Buick Limousine", "icon": "car"},
@@ -48,7 +47,6 @@
48
47
  "type": "multi",
49
48
  "phase": "play",
50
49
  "label": "Opened doors", "hint": "Doors that have already been opened",
51
- "container": "big",
52
50
  "choose": [
53
51
  1,
54
52
  2,
@@ -10,7 +10,6 @@
10
10
  "game/history": {
11
11
  "type": "multi",
12
12
  "phase": "play",
13
- "container": "big",
14
13
  "label": "Numbers hit", "hint": "Winning numbers in the order they were called",
15
14
  "choose": [
16
15
  0,
@@ -22,7 +22,6 @@
22
22
  "board/fleet": {
23
23
  "type": "multi",
24
24
  "phase": "edit",
25
- "container": "big",
26
25
  "label": "Fleet positions", "hint": "Every coordinate occupied by the player's ships",
27
26
  "choose": [
28
27
  "A1",
@@ -155,7 +154,6 @@
155
154
  "board/shots": {
156
155
  "type": "multi",
157
156
  "phase": "play",
158
- "container": "big",
159
157
  "label": "Player shots", "hint": "Coordinates fired upon by the player",
160
158
  "choose": [
161
159
  "A1",
@@ -2,7 +2,7 @@
2
2
  "instructions": "Build up to four poll options and enter their raw vote counts; percentages are calculated automatically. Keep Display on question while voting, then select results and press Update to reveal the animated outcome.",
3
3
  "meta": {
4
4
  "content/eyebrow": {"type": "string", "phase": "edit", "label": "Label", "hint": "Enter short label", "default": "AUDIENCE POLL"},
5
- "content/question": {"type": "string", "phase": "edit", "label": "Question", "hint": "Enter question", "container": "big", "default": "Which destination should we visit next?"},
5
+ "content/question": {"type": "string", "phase": "edit", "label": "Question", "hint": "Enter question", "default": "Which destination should we visit next?"},
6
6
  "options": {
7
7
  "type": "set",
8
8
  "label": "Poll options", "hint": "Answer choices and their current raw vote counts",
@@ -2,7 +2,7 @@
2
2
  "instructions": "Enter the category, question and four possible answers, then select the correct answer. Publish in question mode first; switch Display to answer and press Update when it is time to reveal the result.",
3
3
  "meta": {
4
4
  "content/category": {"type": "string", "phase": "edit", "label": "Category", "hint": "Enter category", "default": "GENERAL KNOWLEDGE"},
5
- "content/question": {"type": "string", "phase": "edit", "label": "Question", "hint": "Enter question", "container": "big", "default": "Which planet is known as the Red Planet?"},
5
+ "content/question": {"type": "string", "phase": "edit", "label": "Question", "hint": "Enter question", "default": "Which planet is known as the Red Planet?"},
6
6
  "answers/a": {"type": "string", "phase": "edit", "label": "Answer A", "hint": "Enter answer A", "default": "Venus"},
7
7
  "answers/b": {"type": "string", "phase": "edit", "label": "Answer B", "hint": "Enter answer B", "default": "Mars"},
8
8
  "answers/c": {"type": "string", "phase": "edit", "label": "Answer C", "hint": "Enter answer C", "default": "Jupiter"},
@@ -7,7 +7,6 @@
7
7
  "phase": "edit",
8
8
  "label": "Questions", "hint": "Ordered question-and-answer cards used by the automatic loop",
9
9
  "max": 30,
10
- "container": "big",
11
10
  "default": [
12
11
  {"question": "Capital of Japan?", "answer": "Tokyo"},
13
12
  {"question": "Fastest land animal?", "answer": "Cheetah"},
@@ -5,7 +5,6 @@
5
5
  "type": "number",
6
6
  "phase": "edit",
7
7
  "label": "Time", "hint": "Enter seconds",
8
- "container": "small",
9
8
  "min": 0,
10
9
  "max": 600,
11
10
  "default": 60
@@ -23,7 +22,6 @@
23
22
  "game/correct": {
24
23
  "type": "multi",
25
24
  "phase": "play",
26
- "container": "big",
27
25
  "label": "Correct answers", "hint": "Letters answered correctly",
28
26
  "choose": [
29
27
  "A", "B", "C", "D", "E", "F", "G", "H", "I",
@@ -39,7 +37,6 @@
39
37
  "game/failed": {
40
38
  "type": "multi",
41
39
  "phase": "play",
42
- "container": "big",
43
40
  "label": "Failed answers", "hint": "Letters answered incorrectly",
44
41
  "choose": [
45
42
  "A", "B", "C", "D", "E", "F", "G", "H", "I",
@@ -7,7 +7,6 @@
7
7
  "phase": "edit",
8
8
  "label": "Question deck", "hint": "Ordered collection of questions and possible answers",
9
9
  "max": 20,
10
- "container": "big",
11
10
  "default": [
12
11
  {"question": "What is the largest ocean on Earth?", "answer": "The Pacific Ocean"},
13
12
  {"question": "How many sides does a dodecagon have?", "answer": "12"},
@@ -6,7 +6,6 @@
6
6
  "phase": "edit",
7
7
  "label": "Questions", "hint": "Statements and their true-or-false answers",
8
8
  "max": 30,
9
- "container": "big",
10
9
  "default": [
11
10
  {"category": "SCIENCE", "statement": "Lightning never strikes the same place twice.", "correct": "false", "explanation": "Lightning can strike the same place repeatedly."},
12
11
  {"category": "GEOGRAPHY", "statement": "The Great Wall of China is visible from the Moon with the naked eye.", "correct": "false", "explanation": "It is far too narrow to be distinguished from the Moon."},
package/tool/cli.cjs CHANGED
@@ -4,4 +4,4 @@
4
4
  * Copyright (c) 2026 Nebular Streams
5
5
  * All rights reserved.
6
6
  */
7
- "use strict";var e=require("node:fs"),t=require("node:os"),n=require("node:path"),o=require("node:child_process"),r=require("node:http");const i=o,s=e,a=r,c=n,l="127.0.0.1",p=/^[A-Za-z0-9_-]+$/,f=/^[A-Za-z0-9_.-]+$/,u=new Set([".css",".jpg",".jpeg",".png",".webp",".otf",".ttf",".woff",".woff2"]),d={".css":"text/css; charset=utf-8",".html":"text/html; charset=utf-8",".jpg":"image/jpeg",".jpeg":"image/jpeg",".json":"application/json; charset=utf-8",".png":"image/png",".webp":"image/webp",".otf":"font/otf",".ttf":"font/ttf",".woff":"font/woff",".woff2":"font/woff2",".js":"text/javascript; charset=utf-8"};function h(e,t){const n=new Error(t);return n.status=e,n}function m(e,t){const n=c.relative(e,t);return""===n||!n.startsWith("..")&&!c.isAbsolute(n)}function w(e,t=""){const n=c.join(e,t);return s.existsSync(c.join(n,"index.html"))?t?[t.split(c.sep).join("/")]:[]:s.readdirSync(n,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.name.startsWith(".")&&"node_modules"!==e.name).flatMap(n=>w(e,c.join(t,n.name)))}function y(e,t,n){const o=JSON.stringify(n);e.writeHead(t,{"Content-Type":d[".json"],"Content-Length":Buffer.byteLength(o),"Cache-Control":"no-store","X-Content-Type-Options":"nosniff"}),e.end(o)}function v(e,t,n){e.writeHead(t,{"Content-Type":"text/plain; charset=utf-8","Content-Length":Buffer.byteLength(n),"Cache-Control":"no-store","X-Content-Type-Options":"nosniff"}),e.end(n)}function g(e,t,n,o,r={}){t.writeHead(200,{"Content-Type":d[o]||"application/octet-stream","Content-Length":n.length,"Cache-Control":"no-store","X-Content-Type-Options":"nosniff",...r}),"HEAD"===e.method?t.end():t.end(n)}function $(e){let t;try{t=s.statSync(e)}catch(e){if("ENOENT"===e.code)throw h(404,"File not found");throw e}if(!t.isFile())throw h(404,"File not found");if(t.size>5242880)throw h(413,"File exceeds 5 MiB");return s.readFileSync(e)}function S(e){try{return e.split("/").filter(Boolean).map(decodeURIComponent)}catch{throw h(400,"Invalid URL path")}}function x(e){return e.length>0&&e.every(e=>f.test(e)&&!e.startsWith("."))}function j(e,t){const n=e.toLowerCase().lastIndexOf("</body>");return n>=0?`${e.slice(0,n)}${t}${e.slice(n)}`:`${e}${t}`}function C(e,t){return new Promise((n,o)=>{e.once("error",o),e.listen(t,l,()=>{e.removeListener("error",o),n()})})}const b=e,E=t,k=n,O=async function(e=".",t=[]){const n=c.resolve(e);if(!s.existsSync(n)||!s.statSync(n).isDirectory())throw new Error(`Template directory does not exist: ${e}`);const{port:o,shouldOpen:r}=function(e){let t=4173,n=!0;for(let o=0;o<e.length;o++){const r=e[o];if("--no-open"===r)n=!1;else if("--port"===r)t=Number(e[++o]);else{if(!r.startsWith("--port="))throw new Error(`Unknown preview option: ${r}`);t=Number(r.slice(7))}}if(!Number.isInteger(t)||t<1||t>65534)throw new Error("Preview port must be an integer between 1 and 65534");return{port:t,shouldOpen:n}}(t),f=o+1,d=`http://${l}:${o}`,b=`http://${l}:${f}`,E=c.resolve(__dirname,"..","preview"),k=function(e,t){return a.createServer((n,o)=>{try{if(!["GET","HEAD"].includes(n.method))throw h(405,"Method not allowed");const r=new URL(n.url,"http://preview.invalid");if(!r.pathname.startsWith("/templates/"))throw h(404,"Not found");const i=S(r.pathname.slice(10)),s=w(e).sort((e,t)=>t.length-e.length).find(e=>e.split("/").every((e,t)=>i[t]===e));if(!s)throw h(404,"Template not found");const a=s.split("/");let l=i.slice(a.length);if(0===l.length&&(l=["index.html"]),!x(l))throw h(400,"Invalid template resource path");const p=c.extname(l.at(-1)).toLowerCase(),f=1===l.length&&"index.html"===l[0];if(!f&&!u.has(p))throw h(404,"Unsupported template resource");const d=c.join(e,...a),y=c.resolve(d,...l);if(!m(d,y))throw h(400,"Invalid template resource path");let v=$(y);f&&(v=Buffer.from(j(v.toString("utf8"),function(e){return`<script>\n\t\t(() => {\n\t\t const previewOrigin = ${JSON.stringify(e)};\n\t\t window.addEventListener("message", (event) => {\n\t\t\tif (event.source !== window.parent || event.origin !== previewOrigin) return;\n\t\t\tif (event.data?.type !== "nebular-overlay:update") return;\n\t\t\twindow.dispatchEvent(new CustomEvent("nebular-overlay:update", {detail: event.data.values || {}}));\n\t\t });\n\t\t})();\n\t<\/script>`}(t)))),g(n,o,v,p,{"Content-Security-Policy":["default-src 'none'","script-src 'unsafe-inline'","style-src 'self' 'unsafe-inline'","img-src 'self' data:","font-src 'self'","connect-src 'none'","frame-src 'none'","media-src 'none'","object-src 'none'","worker-src 'none'","base-uri 'none'","form-action 'none'"].join("; "),"Permissions-Policy":"camera=(), microphone=(), geolocation=(), display-capture=(), usb=(), serial=(), hid=(), clipboard-read=()","Referrer-Policy":"no-referrer"})}catch(e){v(o,e.status||500,e.status?e.message:"Preview server error")}})}(n,d),O=function(e,t,n){return a.createServer((o,r)=>{try{if(!["GET","HEAD"].includes(o.method))throw h(405,"Method not allowed");const i=new URL(o.url,"http://preview.invalid");if("/api/templates"===i.pathname)return y(r,200,{templates:w(e).sort()});if("/api/manifest"===i.pathname){const t=i.searchParams.get("id");if(!function(e){return"string"==typeof e&&e.length>0&&e.split("/").every(e=>p.test(e))}(t))throw h(400,"Invalid template id");const n=c.resolve(e,...t.split("/"));if(!m(e,n)||!s.existsSync(c.join(n,"index.html")))throw h(404,"Template not found");return y(r,200,JSON.parse($(c.join(n,"manifest.json")).toString("utf8")))}const a="/"===i.pathname?"index.html":i.pathname.slice(1),l=S(a);if(!x(l))throw h(400,"Invalid application resource path");const f=c.resolve(t,...l);if(!m(t,f))throw h(400,"Invalid application resource path");const u=c.extname(f).toLowerCase();let d=$(f);if("index.html"===a){const e=`<script>window.__NEBULAR_OVERLAY_PREVIEW__=${JSON.stringify({templateOrigin:n})};<\/script>`;d=Buffer.from(j(d.toString("utf8"),e))}g(o,r,d,u)}catch(e){if(e instanceof SyntaxError)return v(r,422,`Invalid template manifest: ${e.message}`);v(r,e.status||500,e.status?e.message:"Preview server error")}})}(n,E,b);try{await C(k,f),await C(O,o)}catch(e){throw k.close(),O.close(),e}console.log(`Previewing ${n}`),console.log(`Application: ${d}`),console.log(`Templates: ${b}`),console.log("Press Ctrl-C to stop."),r&&function(e){let t,n;"darwin"===process.platform?[t,n]=["open",[e]]:"win32"===process.platform?[t,n]=["cmd",["/c","start","",e]]:[t,n]=["xdg-open",[e]];const o=i.spawn(t,n,{detached:!0,stdio:"ignore"});o.on("error",()=>{}),o.unref()}(d),await new Promise(e=>{let t=!1;const n=()=>{if(t)return;t=!0;let n=2;const o=()=>{n-=1,0===n&&e()};O.close(o),k.close(o)};process.once("SIGINT",n),process.once("SIGTERM",n)})},I="nebular-overlay-sdk",L="nebular-overlay",T="studio.nebular.tv",_=k.resolve(__dirname,".."),D=new Set(["init","preview","skill"]);function N(){return`Usage:\n ${I} init <destination> <template>\n ${I} preview [directory] [--port <port>] [--no-open]\n ${I} skill install [--force]\n\t\nInstructions: \n - Using the SDK: https://${T}/doc/sdk/\n - Creating templates: https://${T}/doc/sdk/templates\n`}function P(e,t=1){const n=new Error(e);throw n.exitCode=t,n}function U(){return b.readdirSync(k.join(_,"templates"),{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).sort()}function R(e,t){e&&t||P(`Usage: ${I} init <destination> <template>\nAvailable templates: ${U().join(", ")}`);const n=k.resolve(e),o=k.join(_,"templates",t);b.existsSync(n)&&P(`Destination already exists: ${e}`),b.existsSync(o)&&b.statSync(o).isDirectory()||P(`Unknown template: ${t}\nAvailable templates: ${U().join(", ")}`),b.cpSync(o,n,{recursive:!0,errorOnExist:!0}),console.log(`Initialized ${t} template in ${n}`)}async function A(e=".",...t){e.startsWith("--")&&(t.unshift(e),e="."),console.log("-----------------------------------\nNebular Overlay SDK v1.0.0\n(c) 2026 Nebular Streams <software@nebular.tv>\nAll Rights Reserved\n-----------------------------------"),await O(e,t)}function H(e,...t){"install"!==e&&P(`Usage: ${I} skill install [--force]`),function(e){const t=e.filter(e=>"--force"!==e);t.length&&P(`Unknown skill install option: ${t[0]}`);const n=e.includes("--force"),o=k.join(_,"skill",L),r=process.env.CODEX_HOME?k.resolve(process.env.CODEX_HOME):k.join(E.homedir(),".codex"),i=k.join(r,"skills"),s=k.join(i,L);b.existsSync(k.join(o,"SKILL.md"))||P(`Packaged skill is missing: ${o}`),b.existsSync(s)&&!n&&P(`Skill is already installed at ${s}\nRun again with --force to replace it.`),b.mkdirSync(i,{recursive:!0});const a=k.join(i,`.${L}-install-${process.pid}-${Date.now()}`);if(b.cpSync(o,a,{recursive:!0,errorOnExist:!0}),b.existsSync(s)){const e=k.join(i,`.${L}-backup-${process.pid}-${Date.now()}`);b.renameSync(s,e);try{b.renameSync(a,s),b.rmSync(e,{recursive:!0,force:!0})}catch(t){throw b.existsSync(a)&&b.rmSync(a,{recursive:!0,force:!0}),b.existsSync(s)||b.renameSync(e,s),t}}else b.renameSync(a,s);console.log(`Installed ${L} skill in ${s}`),console.log("Restart or refresh Codex, then invoke it with $nebular-overlay.")}(t)}(async function(e){if(e.includes("--help")||e.includes("-h")||0===e.length)return void console.log(N());const[t,n]=function(e){if(D.has(e[0]))return[e[0],e.slice(1)];P(`Unknown command: ${e[0]}\n\n${N()}`)}(e);await{init:R,preview:A,skill:H}[t](...n)})(process.argv.slice(2)).catch(e=>{console.error(`nebular-overlay: ${e.message}`),process.exitCode=e.exitCode||1}),module.exports={};
7
+ "use strict";var e=require("node:fs"),t=require("node:os"),n=require("node:path"),o=require("node:child_process"),r=require("node:http");const i=o,s=e,a=r,c=n,l="127.0.0.1",p=/^[A-Za-z0-9_-]+$/,f=/^[A-Za-z0-9_.-]+$/,u=new Set([".css",".jpg",".jpeg",".png",".webp",".otf",".ttf",".woff",".woff2"]),d={".css":"text/css; charset=utf-8",".html":"text/html; charset=utf-8",".jpg":"image/jpeg",".jpeg":"image/jpeg",".json":"application/json; charset=utf-8",".png":"image/png",".webp":"image/webp",".otf":"font/otf",".ttf":"font/ttf",".woff":"font/woff",".woff2":"font/woff2",".js":"text/javascript; charset=utf-8"};function h(e,t){const n=new Error(t);return n.status=e,n}function m(e,t){const n=c.relative(e,t);return""===n||!n.startsWith("..")&&!c.isAbsolute(n)}function w(e,t=""){const n=c.join(e,t);return s.existsSync(c.join(n,"index.html"))?t?[t.split(c.sep).join("/")]:[]:s.readdirSync(n,{withFileTypes:!0}).filter(e=>e.isDirectory()&&!e.name.startsWith(".")&&"node_modules"!==e.name).flatMap(n=>w(e,c.join(t,n.name)))}function y(e,t,n){const o=JSON.stringify(n);e.writeHead(t,{"Content-Type":d[".json"],"Content-Length":Buffer.byteLength(o),"Cache-Control":"no-store","X-Content-Type-Options":"nosniff"}),e.end(o)}function v(e,t,n){e.writeHead(t,{"Content-Type":"text/plain; charset=utf-8","Content-Length":Buffer.byteLength(n),"Cache-Control":"no-store","X-Content-Type-Options":"nosniff"}),e.end(n)}function g(e,t,n,o,r={}){t.writeHead(200,{"Content-Type":d[o]||"application/octet-stream","Content-Length":n.length,"Cache-Control":"no-store","X-Content-Type-Options":"nosniff",...r}),"HEAD"===e.method?t.end():t.end(n)}function $(e){let t;try{t=s.statSync(e)}catch(e){if("ENOENT"===e.code)throw h(404,"File not found");throw e}if(!t.isFile())throw h(404,"File not found");if(t.size>5242880)throw h(413,"File exceeds 5 MiB");return s.readFileSync(e)}function S(e){try{return e.split("/").filter(Boolean).map(decodeURIComponent)}catch{throw h(400,"Invalid URL path")}}function x(e){return e.length>0&&e.every(e=>f.test(e)&&!e.startsWith("."))}function j(e,t){const n=e.toLowerCase().lastIndexOf("</body>");return n>=0?`${e.slice(0,n)}${t}${e.slice(n)}`:`${e}${t}`}function C(e,t){return new Promise((n,o)=>{e.once("error",o),e.listen(t,l,()=>{e.removeListener("error",o),n()})})}const b=e,E=t,k=n,O=async function(e=".",t=[]){const n=c.resolve(e);if(!s.existsSync(n)||!s.statSync(n).isDirectory())throw new Error(`Template directory does not exist: ${e}`);const{port:o,shouldOpen:r}=function(e){let t=4173,n=!0;for(let o=0;o<e.length;o++){const r=e[o];if("--no-open"===r)n=!1;else if("--port"===r)t=Number(e[++o]);else{if(!r.startsWith("--port="))throw new Error(`Unknown preview option: ${r}`);t=Number(r.slice(7))}}if(!Number.isInteger(t)||t<1||t>65534)throw new Error("Preview port must be an integer between 1 and 65534");return{port:t,shouldOpen:n}}(t),f=o+1,d=`http://${l}:${o}`,b=`http://${l}:${f}`,E=c.resolve(__dirname,"..","preview"),k=function(e,t){return a.createServer((n,o)=>{try{if(!["GET","HEAD"].includes(n.method))throw h(405,"Method not allowed");const r=new URL(n.url,"http://preview.invalid");if(!r.pathname.startsWith("/templates/"))throw h(404,"Not found");const i=S(r.pathname.slice(10)),s=w(e).sort((e,t)=>t.length-e.length).find(e=>e.split("/").every((e,t)=>i[t]===e));if(!s)throw h(404,"Template not found");const a=s.split("/");let l=i.slice(a.length);if(0===l.length&&(l=["index.html"]),!x(l))throw h(400,"Invalid template resource path");const p=c.extname(l.at(-1)).toLowerCase(),f=1===l.length&&"index.html"===l[0];if(!f&&!u.has(p))throw h(404,"Unsupported template resource");const d=c.join(e,...a),y=c.resolve(d,...l);if(!m(d,y))throw h(400,"Invalid template resource path");let v=$(y);f&&(v=Buffer.from(j(v.toString("utf8"),function(e){return`<script>\n\t\t(() => {\n\t\t const previewOrigin = ${JSON.stringify(e)};\n\t\t window.addEventListener("message", (event) => {\n\t\t\tif (event.source !== window.parent || event.origin !== previewOrigin) return;\n\t\t\tif (event.data?.type !== "nebular-overlay:update") return;\n\t\t\twindow.dispatchEvent(new CustomEvent("nebular-overlay:update", {detail: event.data.values || {}}));\n\t\t });\n\t\t})();\n\t<\/script>`}(t)))),g(n,o,v,p,{"Content-Security-Policy":["default-src 'none'","script-src 'unsafe-inline'","style-src 'self' 'unsafe-inline'","img-src 'self' data:","font-src 'self'","connect-src 'none'","frame-src 'none'","media-src 'none'","object-src 'none'","worker-src 'none'","base-uri 'none'","form-action 'none'"].join("; "),"Permissions-Policy":"camera=(), microphone=(), geolocation=(), display-capture=(), usb=(), serial=(), hid=(), clipboard-read=()","Referrer-Policy":"no-referrer"})}catch(e){v(o,e.status||500,e.status?e.message:"Preview server error")}})}(n,d),O=function(e,t,n){return a.createServer((o,r)=>{try{if(!["GET","HEAD"].includes(o.method))throw h(405,"Method not allowed");const i=new URL(o.url,"http://preview.invalid");if("/api/templates"===i.pathname)return y(r,200,{templates:w(e).sort()});if("/api/manifest"===i.pathname){const t=i.searchParams.get("id");if(!function(e){return"string"==typeof e&&e.length>0&&e.split("/").every(e=>p.test(e))}(t))throw h(400,"Invalid template id");const n=c.resolve(e,...t.split("/"));if(!m(e,n)||!s.existsSync(c.join(n,"index.html")))throw h(404,"Template not found");return y(r,200,JSON.parse($(c.join(n,"manifest.json")).toString("utf8")))}const a="/"===i.pathname?"index.html":i.pathname.slice(1),l=S(a);if(!x(l))throw h(400,"Invalid application resource path");const f=c.resolve(t,...l);if(!m(t,f))throw h(400,"Invalid application resource path");const u=c.extname(f).toLowerCase();let d=$(f);if("index.html"===a){const e=`<script>window.__NEBULAR_OVERLAY_PREVIEW__=${JSON.stringify({templateOrigin:n})};<\/script>`;d=Buffer.from(j(d.toString("utf8"),e))}g(o,r,d,u)}catch(e){if(e instanceof SyntaxError)return v(r,422,`Invalid template manifest: ${e.message}`);v(r,e.status||500,e.status?e.message:"Preview server error")}})}(n,E,b);try{await C(k,f),await C(O,o)}catch(e){throw k.close(),O.close(),e}console.log(`Previewing ${n}`),console.log(`Application: ${d}`),console.log("Press Ctrl-C to stop."),r&&function(e){let t,n;"darwin"===process.platform?[t,n]=["open",[e]]:"win32"===process.platform?[t,n]=["cmd",["/c","start","",e]]:[t,n]=["xdg-open",[e]];const o=i.spawn(t,n,{detached:!0,stdio:"ignore"});o.on("error",()=>{}),o.unref()}(d),await new Promise(e=>{let t=!1;const n=()=>{if(t)return;t=!0;let n=2;const o=()=>{n-=1,0===n&&e()};O.close(o),k.close(o)};process.once("SIGINT",n),process.once("SIGTERM",n)})},I="nebular-overlay-sdk",L="nebular-overlay",T="studio.nebular.tv",_=k.resolve(__dirname,".."),D=new Set(["init","preview","skill"]);function N(){return`Usage:\n ${I} init <destination> <template>\n ${I} preview [directory] [--port <port>] [--no-open]\n ${I} skill install [--force]\n\t\nInstructions: \n - Using the SDK: https://${T}/doc/sdk/\n - Creating templates: https://${T}/doc/sdk/templates\n`}function P(e,t=1){const n=new Error(e);throw n.exitCode=t,n}function U(){return b.readdirSync(k.join(_,"templates"),{withFileTypes:!0}).filter(e=>e.isDirectory()).map(e=>e.name).sort()}function R(e,t){e&&t||P(`Usage: ${I} init <destination> <template>\nAvailable templates: ${U().join(", ")}`);const n=k.resolve(e),o=k.join(_,"templates",t);b.existsSync(n)&&P(`Destination already exists: ${e}`),b.existsSync(o)&&b.statSync(o).isDirectory()||P(`Unknown template: ${t}\nAvailable templates: ${U().join(", ")}`),b.cpSync(o,n,{recursive:!0,errorOnExist:!0}),console.log(`Initialized ${t} template in ${n}`)}async function A(e=".",...t){e.startsWith("--")&&(t.unshift(e),e="."),console.log("-----------------------------------\nNebular Overlay SDK v1.0.2\n(c) 2026 Nebular Streams <software@nebular.tv>\nAll Rights Reserved\n-----------------------------------"),await O(e,t)}function H(e,...t){"install"!==e&&P(`Usage: ${I} skill install [--force]`),function(e){const t=e.filter(e=>"--force"!==e);t.length&&P(`Unknown skill install option: ${t[0]}`);const n=e.includes("--force"),o=k.join(_,"skill",L),r=process.env.CODEX_HOME?k.resolve(process.env.CODEX_HOME):k.join(E.homedir(),".codex"),i=k.join(r,"skills"),s=k.join(i,L);b.existsSync(k.join(o,"SKILL.md"))||P(`Packaged skill is missing: ${o}`),b.existsSync(s)&&!n&&P(`Skill is already installed at ${s}\nRun again with --force to replace it.`),b.mkdirSync(i,{recursive:!0});const a=k.join(i,`.${L}-install-${process.pid}-${Date.now()}`);if(b.cpSync(o,a,{recursive:!0,errorOnExist:!0}),b.existsSync(s)){const e=k.join(i,`.${L}-backup-${process.pid}-${Date.now()}`);b.renameSync(s,e);try{b.renameSync(a,s),b.rmSync(e,{recursive:!0,force:!0})}catch(t){throw b.existsSync(a)&&b.rmSync(a,{recursive:!0,force:!0}),b.existsSync(s)||b.renameSync(e,s),t}}else b.renameSync(a,s);console.log(`Installed ${L} skill in ${s}`),console.log("Restart or refresh Codex, then invoke it with $nebular-overlay.")}(t)}(async function(e){if(e.includes("--help")||e.includes("-h")||0===e.length)return void console.log(N());const[t,n]=function(e){if(D.has(e[0]))return[e[0],e.slice(1)];P(`Unknown command: ${e[0]}\n\n${N()}`)}(e);await{init:R,preview:A,skill:H}[t](...n)})(process.argv.slice(2)).catch(e=>{console.error(`nebular-overlay: ${e.message}`),process.exitCode=e.exitCode||1}),module.exports={};