@netnodeag/kraftwerk 0.38.1 → 0.39.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/dist/cli/init.js +1 -0
- package/dist/cli/kraftwerk.js +2 -0
- package/dist/cli/vibeables.d.ts +2 -0
- package/dist/cli/vibeables.js +89 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +29 -10
- package/dist/inspector/chat/acp.js +6 -0
- package/dist/inspector/chat/sessions.d.ts +25 -0
- package/dist/inspector/chat/sessions.js +121 -11
- package/dist/inspector/chat/types.d.ts +2 -0
- package/dist/inspector/git.js +5 -3
- package/dist/inspector/mtime.d.ts +3 -0
- package/dist/inspector/mtime.js +31 -0
- package/dist/inspector/repos.d.ts +2 -0
- package/dist/inspector/repos.js +5 -1
- package/dist/inspector/server.js +96 -1
- package/dist/inspector/settings.d.ts +3 -1
- package/dist/inspector/settings.js +26 -8
- package/dist/inspector/vibeables.d.ts +153 -0
- package/dist/inspector/vibeables.js +628 -0
- package/inspector/dist/assets/apl-gfMMiTNf.js +1 -0
- package/inspector/dist/assets/asciiarmor-C_eisQCp.js +1 -0
- package/inspector/dist/assets/asn1-DCl_8MCW.js +1 -0
- package/inspector/dist/assets/asterisk-BEn0Cwpx.js +1 -0
- package/inspector/dist/assets/brainfuck-D5EjA2JK.js +1 -0
- package/inspector/dist/assets/clike-D_6L3tnO.js +1 -0
- package/inspector/dist/assets/clojure-BflJGmDX.js +1 -0
- package/inspector/dist/assets/cmake-84nbDH0I.js +1 -0
- package/inspector/dist/assets/cobol-DHLQUbN7.js +1 -0
- package/inspector/dist/assets/coffeescript-De_zI4J0.js +1 -0
- package/inspector/dist/assets/commonlisp-DgAKBe0r.js +1 -0
- package/inspector/dist/assets/crystal-Bsdphhtm.js +1 -0
- package/inspector/dist/assets/css-CnaYoQib.js +1 -0
- package/inspector/dist/assets/cypher-p9eYesGn.js +1 -0
- package/inspector/dist/assets/d-DWkQyNnU.js +1 -0
- package/inspector/dist/assets/diff-ChtP43wD.js +1 -0
- package/inspector/dist/assets/dist-5wk4daHl.js +1 -0
- package/inspector/dist/assets/dist-BAHymHVh.js +1 -0
- package/inspector/dist/assets/dist-BUzvZfC2.js +23 -0
- package/inspector/dist/assets/dist-CF7An11X.js +1 -0
- package/inspector/dist/assets/dist-CMK0Dv1Q.js +1 -0
- package/inspector/dist/assets/dist-CPlSMtdY.js +1 -0
- package/inspector/dist/assets/dist-CWxCXHGC.js +1 -0
- package/inspector/dist/assets/dist-CXS6W3P9.js +9 -0
- package/inspector/dist/assets/dist-Ceimhh7q.js +1 -0
- package/inspector/dist/assets/dist-Cn3OeZ9x.js +1 -0
- package/inspector/dist/assets/dist-D01HLRsD.js +1 -0
- package/inspector/dist/assets/dist-D0WPeXAf.js +1 -0
- package/inspector/dist/assets/dist-D8nmL5S9.js +1 -0
- package/inspector/dist/assets/dist-DNBDudvB.js +1 -0
- package/inspector/dist/assets/dist-DQujkoko.js +1 -0
- package/inspector/dist/assets/dist-DYqdkWRb.js +1 -0
- package/inspector/dist/assets/dist-DaG9QYNU.js +1 -0
- package/inspector/dist/assets/dist-DfmPNdMI.js +1 -0
- package/inspector/dist/assets/dist-FnKl1oDp.js +1 -0
- package/inspector/dist/assets/dist-H3rerbMg.js +13 -0
- package/inspector/dist/assets/dist-ZMc9InB9.js +1 -0
- package/inspector/dist/assets/dist-rE2oRtJp.js +11 -0
- package/inspector/dist/assets/dist-yFKhRrHN.js +2 -0
- package/inspector/dist/assets/dockerfile-DJkHQgkl.js +1 -0
- package/inspector/dist/assets/dtd-DTF72YNO.js +1 -0
- package/inspector/dist/assets/dylan-BCDoL_-p.js +1 -0
- package/inspector/dist/assets/ebnf-D4c0_ac3.js +1 -0
- package/inspector/dist/assets/ecl-BVIeWmwY.js +1 -0
- package/inspector/dist/assets/editor-B1Jq9YIq.css +1 -0
- package/inspector/dist/assets/editor-CVAfyi-9.js +120 -0
- package/inspector/dist/assets/eiffel-BEjRio4Q.js +1 -0
- package/inspector/dist/assets/elm-Cshzl8qu.js +1 -0
- package/inspector/dist/assets/erlang-BV0c4Hdn.js +1 -0
- package/inspector/dist/assets/factor-LKH5gN8L.js +1 -0
- package/inspector/dist/assets/fcl-ClOhbFR7.js +1 -0
- package/inspector/dist/assets/forth-D6RzAl64.js +1 -0
- package/inspector/dist/assets/fortran-Bt6PBEDR.js +1 -0
- package/inspector/dist/assets/gas-BHEdbvp9.js +1 -0
- package/inspector/dist/assets/gherkin-oBAE_ms0.js +1 -0
- package/inspector/dist/assets/groovy-CUpZ7dEl.js +1 -0
- package/inspector/dist/assets/haskell-BRsoo5mP.js +1 -0
- package/inspector/dist/assets/haxe-DjVOVvNP.js +1 -0
- package/inspector/dist/assets/http-CLVgA2GD.js +1 -0
- package/inspector/dist/assets/idl-BUZw3wgd.js +1 -0
- package/inspector/dist/assets/index-Ba-uGp4N.js +218 -0
- package/inspector/dist/assets/index-CL7d8WY1.css +1 -0
- package/inspector/dist/assets/javascript-Bu8mWwtz.js +1 -0
- package/inspector/dist/assets/julia-CQfgDRfP.js +1 -0
- package/inspector/dist/assets/livescript-BXxG0Yva.js +1 -0
- package/inspector/dist/assets/lua-DPCEkfD_.js +1 -0
- package/inspector/dist/assets/material-symbols-rounded-DzDTtRam.woff2 +0 -0
- package/inspector/dist/assets/mathematica-u8YMmbU0.js +1 -0
- package/inspector/dist/assets/mbox-BEMVcqjs.js +1 -0
- package/inspector/dist/assets/{mirc-CjQqDB4T.js → mirc-C9zpzAZU.js} +1 -1
- package/inspector/dist/assets/mllike-B45xgp2S.js +1 -0
- package/inspector/dist/assets/modelica-CqHI_q-D.js +1 -0
- package/inspector/dist/assets/mscgen-qgSLujhx.js +1 -0
- package/inspector/dist/assets/mumps-BSzj6Xyz.js +1 -0
- package/inspector/dist/assets/{nginx-DdIZxoE0.js → nginx-8H7HVH4w.js} +1 -1
- package/inspector/dist/assets/nsis-viPvXFiq.js +1 -0
- package/inspector/dist/assets/ntriples-B9h1VB_g.js +1 -0
- package/inspector/dist/assets/octave-D2Q8cUp1.js +1 -0
- package/inspector/dist/assets/oz-BwTct_5T.js +1 -0
- package/inspector/dist/assets/pascal-Dp_KdFgX.js +1 -0
- package/inspector/dist/assets/perl-BkSkiI9m.js +1 -0
- package/inspector/dist/assets/pig-AF4Hwhju.js +1 -0
- package/inspector/dist/assets/powershell-BcIto6Sf.js +1 -0
- package/inspector/dist/assets/properties-C357ku5U.js +1 -0
- package/inspector/dist/assets/protobuf-D1ij_kNL.js +1 -0
- package/inspector/dist/assets/pug-3K-JMW4g.js +1 -0
- package/inspector/dist/assets/puppet-BZyAS7I4.js +1 -0
- package/inspector/dist/assets/python-B_1Jd8HM.js +1 -0
- package/inspector/dist/assets/q-B9NhS7L_.js +1 -0
- package/inspector/dist/assets/r-CxDmU1I4.js +1 -0
- package/inspector/dist/assets/rpm-gpDK5sbt.js +1 -0
- package/inspector/dist/assets/ruby-ChfNwlCY.js +1 -0
- package/inspector/dist/assets/{sas-B4kiWyti.js → sas-C2OyD2Y6.js} +1 -1
- package/inspector/dist/assets/scheme-D9qYN0V5.js +1 -0
- package/inspector/dist/assets/shell-DwuoZtxw.js +1 -0
- package/inspector/dist/assets/sieve-tLWDfEkZ.js +1 -0
- package/inspector/dist/assets/simple-mode-DXiLM1-G.js +1 -0
- package/inspector/dist/assets/smalltalk-BLp5-jwC.js +1 -0
- package/inspector/dist/assets/solr-B78wSQJQ.js +1 -0
- package/inspector/dist/assets/sparql-Dz-mlCAC.js +1 -0
- package/inspector/dist/assets/spreadsheet-CTIncYxj.js +1 -0
- package/inspector/dist/assets/sql-DuQMFDvy.js +1 -0
- package/inspector/dist/assets/stex-DNzd62Q_.js +1 -0
- package/inspector/dist/assets/stylus-BOVgKnng.js +1 -0
- package/inspector/dist/assets/swift-DKB6_1j6.js +1 -0
- package/inspector/dist/assets/tcl-BGfruO1u.js +1 -0
- package/inspector/dist/assets/textile-C4jVoHG1.js +1 -0
- package/inspector/dist/assets/tiddlywiki-VlUiK86J.js +1 -0
- package/inspector/dist/assets/tiki-AK6FwT8q.js +1 -0
- package/inspector/dist/assets/toml-BPTmHmyx.js +1 -0
- package/inspector/dist/assets/troff-C2dAgh7P.js +1 -0
- package/inspector/dist/assets/ttcn-PPSTk1ow.js +1 -0
- package/inspector/dist/assets/ttcn-cfg-2b4Gb5sE.js +1 -0
- package/inspector/dist/assets/turtle-Cx3rYX2g.js +1 -0
- package/inspector/dist/assets/vb-BthdM_4N.js +1 -0
- package/inspector/dist/assets/vbscript-D9f6rSsU.js +1 -0
- package/inspector/dist/assets/velocity-CqftRsjg.js +1 -0
- package/inspector/dist/assets/verilog-CjI9Y6RI.js +1 -0
- package/inspector/dist/assets/vhdl-kS471soi.js +1 -0
- package/inspector/dist/assets/webidl-D5jiJvOU.js +1 -0
- package/inspector/dist/assets/xquery-BKJx4mf9.js +1 -0
- package/inspector/dist/assets/yacas-CXXgtw0p.js +1 -0
- package/inspector/dist/assets/z80-Co3PbHdE.js +1 -0
- package/inspector/dist/index.html +2 -2
- package/package.json +6 -6
- package/inspector/dist/assets/apl-B4CMkyY2.js +0 -1
- package/inspector/dist/assets/asciiarmor-Df11BRmG.js +0 -1
- package/inspector/dist/assets/asn1-EdZsLKOL.js +0 -1
- package/inspector/dist/assets/asterisk-B-8jnY81.js +0 -1
- package/inspector/dist/assets/brainfuck-C4LP7Hcl.js +0 -1
- package/inspector/dist/assets/clike-B9uivgTg.js +0 -1
- package/inspector/dist/assets/clojure-BMjYHr_A.js +0 -1
- package/inspector/dist/assets/cmake-BQqOBYOt.js +0 -1
- package/inspector/dist/assets/cobol-CWcv1MsR.js +0 -1
- package/inspector/dist/assets/coffeescript-S37ZYGWr.js +0 -1
- package/inspector/dist/assets/commonlisp-DBKNyK5s.js +0 -1
- package/inspector/dist/assets/crystal-SjHAIU92.js +0 -1
- package/inspector/dist/assets/css-BnMrqG3P.js +0 -1
- package/inspector/dist/assets/cypher-C_CwsFkJ.js +0 -1
- package/inspector/dist/assets/d-pRatUO7H.js +0 -1
- package/inspector/dist/assets/diff-DbItnlRl.js +0 -1
- package/inspector/dist/assets/dockerfile-BKs6k2Af.js +0 -1
- package/inspector/dist/assets/dtd-DF_7sFjM.js +0 -1
- package/inspector/dist/assets/dylan-DwRh75JA.js +0 -1
- package/inspector/dist/assets/ebnf-CDyGwa7X.js +0 -1
- package/inspector/dist/assets/ecl-Cabwm37j.js +0 -1
- package/inspector/dist/assets/editor-9rr2cQaW.css +0 -1
- package/inspector/dist/assets/editor-CONb__vl.js +0 -144
- package/inspector/dist/assets/eiffel-CnydiIhH.js +0 -1
- package/inspector/dist/assets/elm-vLlmbW-K.js +0 -1
- package/inspector/dist/assets/erlang-BNw1qcRV.js +0 -1
- package/inspector/dist/assets/factor-kuTfRLto.js +0 -1
- package/inspector/dist/assets/fcl-Kvtd6kyn.js +0 -1
- package/inspector/dist/assets/forth-Ffai-XNe.js +0 -1
- package/inspector/dist/assets/fortran-DYz_wnZ1.js +0 -1
- package/inspector/dist/assets/gas-Bneqetm1.js +0 -1
- package/inspector/dist/assets/gherkin-heZmZLOM.js +0 -1
- package/inspector/dist/assets/groovy-D9Dt4D0W.js +0 -1
- package/inspector/dist/assets/haskell-Cw1EW3IL.js +0 -1
- package/inspector/dist/assets/haxe-H-WmDvRZ.js +0 -1
- package/inspector/dist/assets/http-DBlCnlav.js +0 -1
- package/inspector/dist/assets/idl-BEugSyMb.js +0 -1
- package/inspector/dist/assets/index-6VtYKf_K.js +0 -1
- package/inspector/dist/assets/index-B_BJEGG5.js +0 -1
- package/inspector/dist/assets/index-BkznuDSS.js +0 -1
- package/inspector/dist/assets/index-BrGHSw5I.js +0 -1
- package/inspector/dist/assets/index-BuiRW-03.js +0 -1
- package/inspector/dist/assets/index-C1tY1uK0.js +0 -1
- package/inspector/dist/assets/index-CDY-uFno.js +0 -7
- package/inspector/dist/assets/index-CcGsEtey.js +0 -1
- package/inspector/dist/assets/index-CntcQxRr.js +0 -1
- package/inspector/dist/assets/index-CpDzWqX0.js +0 -3
- package/inspector/dist/assets/index-Cw6eNCH7.js +0 -1
- package/inspector/dist/assets/index-D1_Glu0y.css +0 -1
- package/inspector/dist/assets/index-DMWUqEQi.js +0 -217
- package/inspector/dist/assets/index-DfK1f9gz.js +0 -1
- package/inspector/dist/assets/index-IosqBxL0.js +0 -1
- package/inspector/dist/assets/index-iRFYcgNN.js +0 -1
- package/inspector/dist/assets/index-p5w5E19N.js +0 -1
- package/inspector/dist/assets/index-rAZqRLhj.js +0 -2
- package/inspector/dist/assets/index-yGDq5Egm.js +0 -1
- package/inspector/dist/assets/javascript-iXu5QeM3.js +0 -1
- package/inspector/dist/assets/julia-DuME0IfC.js +0 -1
- package/inspector/dist/assets/livescript-BwQOo05w.js +0 -1
- package/inspector/dist/assets/lua-VAEuO923.js +0 -1
- package/inspector/dist/assets/material-symbols-rounded-DxXFL64Y.woff2 +0 -0
- package/inspector/dist/assets/mathematica-DTrFuWx2.js +0 -1
- package/inspector/dist/assets/mbox-CNhZ1qSd.js +0 -1
- package/inspector/dist/assets/mllike-CXdrOF99.js +0 -1
- package/inspector/dist/assets/modelica-Dc1JOy9r.js +0 -1
- package/inspector/dist/assets/mscgen-BA5vi2Kp.js +0 -1
- package/inspector/dist/assets/mumps-BT43cFF4.js +0 -1
- package/inspector/dist/assets/nsis-LY0VT_u5.js +0 -1
- package/inspector/dist/assets/ntriples-BfvgReVJ.js +0 -1
- package/inspector/dist/assets/octave-Ck1zUtKM.js +0 -1
- package/inspector/dist/assets/oz-BzwKVEFT.js +0 -1
- package/inspector/dist/assets/pascal--L3eBynH.js +0 -1
- package/inspector/dist/assets/perl-CdXCOZ3F.js +0 -1
- package/inspector/dist/assets/pig-CevX1Tat.js +0 -1
- package/inspector/dist/assets/powershell-CFHJl5sT.js +0 -1
- package/inspector/dist/assets/properties-C78fOPTZ.js +0 -1
- package/inspector/dist/assets/protobuf-ChK-085T.js +0 -1
- package/inspector/dist/assets/pug-DeIclll2.js +0 -1
- package/inspector/dist/assets/puppet-DMA9R1ak.js +0 -1
- package/inspector/dist/assets/python-BuPzkPfP.js +0 -1
- package/inspector/dist/assets/q-pXgVlZs6.js +0 -1
- package/inspector/dist/assets/r-B6wPVr8A.js +0 -1
- package/inspector/dist/assets/rpm-CTu-6PCP.js +0 -1
- package/inspector/dist/assets/ruby-BK5aLJev.js +0 -1
- package/inspector/dist/assets/scheme-C41bIUwD.js +0 -1
- package/inspector/dist/assets/shell-CjFT_Tl9.js +0 -1
- package/inspector/dist/assets/sieve-C3Gn_uJK.js +0 -1
- package/inspector/dist/assets/simple-mode-GW_nhZxv.js +0 -1
- package/inspector/dist/assets/smalltalk-CnHTOXQT.js +0 -1
- package/inspector/dist/assets/solr-DehyRSwq.js +0 -1
- package/inspector/dist/assets/sparql-DkYu6x3z.js +0 -1
- package/inspector/dist/assets/spreadsheet-BCZA_wO0.js +0 -1
- package/inspector/dist/assets/sql-D0XecflT.js +0 -1
- package/inspector/dist/assets/stex-C3f8Ysf7.js +0 -1
- package/inspector/dist/assets/stylus-B533Al4x.js +0 -1
- package/inspector/dist/assets/swift-BzpIVaGY.js +0 -1
- package/inspector/dist/assets/tcl-DVfN8rqt.js +0 -1
- package/inspector/dist/assets/textile-CnDTJFAw.js +0 -1
- package/inspector/dist/assets/tiddlywiki-DO-Gjzrf.js +0 -1
- package/inspector/dist/assets/tiki-DGYXhP31.js +0 -1
- package/inspector/dist/assets/toml-Bm5Em-hy.js +0 -1
- package/inspector/dist/assets/troff-wAsdV37c.js +0 -1
- package/inspector/dist/assets/ttcn-CfJYG6tj.js +0 -1
- package/inspector/dist/assets/ttcn-cfg-B9xdYoR4.js +0 -1
- package/inspector/dist/assets/turtle-B1tBg_DP.js +0 -1
- package/inspector/dist/assets/vb-CmGdzxic.js +0 -1
- package/inspector/dist/assets/vbscript-BuJXcnF6.js +0 -1
- package/inspector/dist/assets/velocity-D8B20fx6.js +0 -1
- package/inspector/dist/assets/verilog-C6RDOZhf.js +0 -1
- package/inspector/dist/assets/vhdl-lSbBsy5d.js +0 -1
- package/inspector/dist/assets/webidl-ZXfAyPTL.js +0 -1
- package/inspector/dist/assets/xquery-DzFWVndE.js +0 -1
- package/inspector/dist/assets/yacas-BJ4BC0dw.js +0 -1
- package/inspector/dist/assets/z80-Hz9HOZM7.js +0 -1
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createReadStream, promises as fs, watch } from "node:fs";
|
|
3
|
+
import http from "node:http";
|
|
4
|
+
import net from "node:net";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { parse } from "yaml";
|
|
7
|
+
import { resolveProject, vibeablesRootFor } from "../config.js";
|
|
8
|
+
import { getProjectRoot } from "./context.js";
|
|
9
|
+
import { newestMtime } from "./mtime.js";
|
|
10
|
+
export const VIBEABLE_CONFIG_FILE = "vibeable.yml";
|
|
11
|
+
const SLUG_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
12
|
+
export function safeVibeableSlug(slug) {
|
|
13
|
+
if (!SLUG_RE.test(slug) || slug === "." || slug === "..")
|
|
14
|
+
throw new Error(`invalid vibeable name "${slug}"`);
|
|
15
|
+
return slug;
|
|
16
|
+
}
|
|
17
|
+
/* ---------- starter ---------- */
|
|
18
|
+
/** The files a fresh vibeable starts with: a page that renders, and the config template. */
|
|
19
|
+
export function vibeableStarter(name) {
|
|
20
|
+
const title = name.trim() || "vibeable";
|
|
21
|
+
return {
|
|
22
|
+
"index.html": `<!doctype html>\n<html lang="en">\n<head>\n <meta charset="utf-8">\n <meta name="viewport" content="width=device-width, initial-scale=1">\n` +
|
|
23
|
+
` <title>${escapeHtml(title)}</title>\n <link rel="stylesheet" href="./style.css">\n</head>\n<body>\n <main>\n <h1>${escapeHtml(title)}</h1>\n` +
|
|
24
|
+
` <p>This vibeable is live. Tell the agent what to build and watch it change here.</p>\n </main>\n <script type="module" src="./app.js"></script>\n</body>\n</html>\n`,
|
|
25
|
+
"style.css":
|
|
26
|
+
// Explicit colours: the pane is white, and a `light dark` scheme would paint light text on it in dark mode.
|
|
27
|
+
`:root { color-scheme: light; font-family: system-ui, sans-serif; background: #fff; color: #1b1b1f; }\n` +
|
|
28
|
+
`body { margin: 0; min-height: 100vh; display: grid; place-items: center; }\n` +
|
|
29
|
+
`main { padding: 32px; max-width: 60ch; }\nh1 { font-weight: 500; }\np { color: #46464f; }\n`,
|
|
30
|
+
"app.js": `// Plain ES module; relative imports and CDN imports (https://esm.sh/...) work.\nconsole.log("${title.replace(/["\\]/g, "")} ready");\n`,
|
|
31
|
+
[VIBEABLE_CONFIG_FILE]: `# vibeable.yml — how kraftwerk previews this app. Everything is optional.\n` +
|
|
32
|
+
`# Without a dev command the folder is served as-is (index.html, no build step).\n#\n` +
|
|
33
|
+
`# dev: npm run dev # command that starts a dev server; it gets PORT in its environment\n` +
|
|
34
|
+
`# port: 5173 # the port that command listens on, when it ignores PORT\n` +
|
|
35
|
+
`# dir: dist # folder to serve in static mode (default: this folder)\n`,
|
|
36
|
+
// Nested .gitignore: an npm install inside the app must not flood the workspace git.
|
|
37
|
+
".gitignore": "node_modules/\n.env\n",
|
|
38
|
+
"README.md": `# ${title}\n\nBuilt live as a kraftwerk vibeable. Open it from a chat in the inspector; \`index.html\` is the entry.\n`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const escapeHtml = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
42
|
+
/* ---------- root + listing ---------- */
|
|
43
|
+
/** Resolve the project and its vibeables block. `off` when the feature is not on. */
|
|
44
|
+
export async function openVibeables() {
|
|
45
|
+
let project;
|
|
46
|
+
try {
|
|
47
|
+
project = await resolveProject(getProjectRoot());
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return { error: err.message };
|
|
51
|
+
}
|
|
52
|
+
const root = vibeablesRootFor(project);
|
|
53
|
+
if (!root)
|
|
54
|
+
return { off: true, error: "vibeables are off (enable them in settings or add `vibeables:` to kraftwerk.yml)" };
|
|
55
|
+
return { project, root };
|
|
56
|
+
}
|
|
57
|
+
/** Make sure the root exists; runs when the feature is switched on. */
|
|
58
|
+
export async function ensureVibeablesRoot() {
|
|
59
|
+
const opened = await openVibeables();
|
|
60
|
+
if (opened.root)
|
|
61
|
+
await fs.mkdir(opened.root, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
/** Read vibeable.yml; a missing file is an empty config, a broken one is reported and ignored. */
|
|
64
|
+
async function readConfig(dir) {
|
|
65
|
+
let raw;
|
|
66
|
+
try {
|
|
67
|
+
raw = await fs.readFile(path.join(dir, VIBEABLE_CONFIG_FILE), "utf8");
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { config: {} };
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const data = (parse(raw) ?? {});
|
|
74
|
+
if (typeof data !== "object" || Array.isArray(data))
|
|
75
|
+
throw new Error("must be a mapping");
|
|
76
|
+
const config = {};
|
|
77
|
+
if (data.dev !== undefined) {
|
|
78
|
+
if (typeof data.dev !== "string" || !data.dev.trim())
|
|
79
|
+
throw new Error("dev must be a command string");
|
|
80
|
+
config.dev = data.dev.trim();
|
|
81
|
+
}
|
|
82
|
+
if (data.port !== undefined) {
|
|
83
|
+
if (!Number.isInteger(data.port) || data.port < 1 || data.port > 65535)
|
|
84
|
+
throw new Error("port must be 1–65535");
|
|
85
|
+
config.port = data.port;
|
|
86
|
+
}
|
|
87
|
+
if (data.dir !== undefined) {
|
|
88
|
+
if (typeof data.dir !== "string" || !data.dir.trim())
|
|
89
|
+
throw new Error("dir must be a folder path");
|
|
90
|
+
const abs = path.resolve(dir, data.dir);
|
|
91
|
+
if (abs !== dir && !abs.startsWith(dir + path.sep))
|
|
92
|
+
throw new Error("dir must be inside the vibeable");
|
|
93
|
+
config.dir = data.dir.trim();
|
|
94
|
+
}
|
|
95
|
+
return { config };
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
return { config: {}, error: `${VIBEABLE_CONFIG_FILE}: ${err.message}` };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** The folder behind a slug, or throw `no vibeable "<slug>"`. */
|
|
102
|
+
export async function resolveVibeable(slug) {
|
|
103
|
+
safeVibeableSlug(slug);
|
|
104
|
+
const opened = await openVibeables();
|
|
105
|
+
if (!opened.root)
|
|
106
|
+
throw new Error(opened.error ?? "vibeables are off");
|
|
107
|
+
const dir = path.join(opened.root, slug);
|
|
108
|
+
const st = await fs.stat(dir).catch(() => null);
|
|
109
|
+
if (!st?.isDirectory())
|
|
110
|
+
throw new Error(`no vibeable "${slug}"`);
|
|
111
|
+
const { config, error } = await readConfig(dir);
|
|
112
|
+
return { slug, dir, servedDir: path.resolve(dir, config.dir ?? "."), config, configError: error };
|
|
113
|
+
}
|
|
114
|
+
async function describe(root, slug) {
|
|
115
|
+
const dir = path.join(root, slug);
|
|
116
|
+
const { config, error } = await readConfig(dir);
|
|
117
|
+
const served = path.resolve(dir, config.dir ?? ".");
|
|
118
|
+
const [index, st, newest] = await Promise.all([
|
|
119
|
+
fs.stat(path.join(served, "index.html")).catch(() => null),
|
|
120
|
+
fs.stat(dir).catch(() => null),
|
|
121
|
+
newestMtime(dir),
|
|
122
|
+
]);
|
|
123
|
+
const updated = Math.max(newest, st?.mtimeMs ?? 0);
|
|
124
|
+
return {
|
|
125
|
+
slug,
|
|
126
|
+
path: dir,
|
|
127
|
+
hasIndex: !!index?.isFile(),
|
|
128
|
+
...(config.dev ? { dev: config.dev } : {}),
|
|
129
|
+
...(error ? { configError: error } : {}),
|
|
130
|
+
...(updated ? { updatedAt: new Date(updated).toISOString() } : {}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Every folder under the root, alphabetically. Dot folders and odd names are skipped. */
|
|
134
|
+
export async function listVibeables() {
|
|
135
|
+
const opened = await openVibeables();
|
|
136
|
+
if (!opened.root)
|
|
137
|
+
return { enabled: false, vibeables: [], error: opened.error };
|
|
138
|
+
const root = opened.root;
|
|
139
|
+
const entries = await fs.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
140
|
+
const slugs = entries
|
|
141
|
+
.filter((e) => e.isDirectory() && SLUG_RE.test(e.name))
|
|
142
|
+
.map((e) => e.name)
|
|
143
|
+
.sort((a, b) => a.localeCompare(b));
|
|
144
|
+
return { enabled: true, root, vibeables: await Promise.all(slugs.map((s) => describe(root, s))) };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Create <root>/<name> with the starter files. The folder is claimed with a
|
|
148
|
+
* plain mkdir first: it fails when anything is there already, so two
|
|
149
|
+
* creates of the same name cannot both win.
|
|
150
|
+
*/
|
|
151
|
+
export async function createVibeable(name) {
|
|
152
|
+
const opened = await openVibeables();
|
|
153
|
+
if (!opened.root)
|
|
154
|
+
throw new Error(opened.error ?? "vibeables are off");
|
|
155
|
+
const slug = safeVibeableSlug(name.trim());
|
|
156
|
+
const dir = path.join(opened.root, slug);
|
|
157
|
+
await fs.mkdir(opened.root, { recursive: true });
|
|
158
|
+
try {
|
|
159
|
+
await fs.mkdir(dir);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
if (err.code === "EEXIST")
|
|
163
|
+
throw new Error(`"${slug}" already exists under ${opened.root}`);
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
166
|
+
for (const [rel, content] of Object.entries(vibeableStarter(slug)))
|
|
167
|
+
await fs.writeFile(path.join(dir, rel), content);
|
|
168
|
+
return describe(opened.root, slug);
|
|
169
|
+
}
|
|
170
|
+
/** Delete a vibeable folder. Its history stays in the workspace git. */
|
|
171
|
+
export async function deleteVibeable(slug) {
|
|
172
|
+
const r = await resolveVibeable(slug);
|
|
173
|
+
await stopDev(slug).catch(() => { });
|
|
174
|
+
dropChannel(slug);
|
|
175
|
+
await fs.rm(r.dir, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
export async function vibeableStatus(slug) {
|
|
178
|
+
const r = await resolveVibeable(slug);
|
|
179
|
+
const dev = devView(slug);
|
|
180
|
+
return {
|
|
181
|
+
slug,
|
|
182
|
+
path: r.dir,
|
|
183
|
+
dir: r.servedDir,
|
|
184
|
+
mode: dev?.running ? "dev" : "static",
|
|
185
|
+
url: `/vibeables/${encodeURIComponent(slug)}/`,
|
|
186
|
+
config: r.config,
|
|
187
|
+
...(r.configError ? { configError: r.configError } : {}),
|
|
188
|
+
...(dev ? { dev } : {}),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/* ---------- static serving ---------- */
|
|
192
|
+
const MIME = {
|
|
193
|
+
".html": "text/html; charset=utf-8",
|
|
194
|
+
".htm": "text/html; charset=utf-8",
|
|
195
|
+
".js": "text/javascript; charset=utf-8",
|
|
196
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
197
|
+
".css": "text/css; charset=utf-8",
|
|
198
|
+
".json": "application/json; charset=utf-8",
|
|
199
|
+
".map": "application/json; charset=utf-8",
|
|
200
|
+
".svg": "image/svg+xml",
|
|
201
|
+
".png": "image/png",
|
|
202
|
+
".jpg": "image/jpeg",
|
|
203
|
+
".jpeg": "image/jpeg",
|
|
204
|
+
".gif": "image/gif",
|
|
205
|
+
".webp": "image/webp",
|
|
206
|
+
".avif": "image/avif",
|
|
207
|
+
".ico": "image/x-icon",
|
|
208
|
+
".pdf": "application/pdf",
|
|
209
|
+
".txt": "text/plain; charset=utf-8",
|
|
210
|
+
".md": "text/markdown; charset=utf-8",
|
|
211
|
+
".csv": "text/csv; charset=utf-8",
|
|
212
|
+
".xml": "application/xml; charset=utf-8",
|
|
213
|
+
".wasm": "application/wasm",
|
|
214
|
+
".woff": "font/woff",
|
|
215
|
+
".woff2": "font/woff2",
|
|
216
|
+
".ttf": "font/ttf",
|
|
217
|
+
".otf": "font/otf",
|
|
218
|
+
".mp3": "audio/mpeg",
|
|
219
|
+
".wav": "audio/wav",
|
|
220
|
+
".mp4": "video/mp4",
|
|
221
|
+
".webm": "video/webm",
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* The document is sandboxed by header, so it also holds when opened in its
|
|
225
|
+
* own tab: scripts and forms work, but the origin is opaque — no cookies,
|
|
226
|
+
* no storage, and no same-origin calls into the inspector API.
|
|
227
|
+
*/
|
|
228
|
+
const VIBEABLE_CSP = "sandbox allow-scripts allow-forms allow-popups allow-modals allow-downloads";
|
|
229
|
+
/**
|
|
230
|
+
* Headers every preview response carries. The sandbox makes the document's
|
|
231
|
+
* origin opaque, and an opaque origin fetches module scripts, imports and
|
|
232
|
+
* fetch() in CORS mode — without this allow header the app's own app.js
|
|
233
|
+
* never runs. Allowing any origin is safe: the files are the app's public
|
|
234
|
+
* sources, and the sandbox (not the origin) is what keeps them away from
|
|
235
|
+
* the inspector API.
|
|
236
|
+
*/
|
|
237
|
+
const PREVIEW_HEADERS = {
|
|
238
|
+
"cache-control": "no-store",
|
|
239
|
+
"content-security-policy": VIBEABLE_CSP,
|
|
240
|
+
"access-control-allow-origin": "*",
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* GET /vibeables/<slug>/<file>. Dot segments (.env, .git) are never served;
|
|
244
|
+
* a directory answers with its index.html, and a directory url without the
|
|
245
|
+
* trailing slash redirects so the page's relative links resolve.
|
|
246
|
+
*/
|
|
247
|
+
export async function serveVibeable(req, res, url) {
|
|
248
|
+
const fail = (status, error) => {
|
|
249
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
|
|
250
|
+
res.end(JSON.stringify({ error }));
|
|
251
|
+
};
|
|
252
|
+
if (req.method !== "GET" && req.method !== "HEAD")
|
|
253
|
+
return fail(405, "method not allowed");
|
|
254
|
+
const seg = url.pathname.split("/").slice(2); // ["", "vibeables", slug, ...rest] → [slug, ...rest]
|
|
255
|
+
let slug;
|
|
256
|
+
try {
|
|
257
|
+
slug = safeVibeableSlug(decodeURIComponent(seg[0] ?? ""));
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
return fail(404, "not found");
|
|
261
|
+
}
|
|
262
|
+
let r;
|
|
263
|
+
try {
|
|
264
|
+
r = await resolveVibeable(slug);
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
return fail(404, err.message);
|
|
268
|
+
}
|
|
269
|
+
// A running dev server owns the whole prefix: the pane and a new tab reach
|
|
270
|
+
// it through the inspector's origin, which is what a container or reverse
|
|
271
|
+
// proxy exposes — a random port on the host is not.
|
|
272
|
+
const dev = devs.get(slug);
|
|
273
|
+
if (dev && dev.exitCode === undefined && dev.child)
|
|
274
|
+
return proxyToDev(req, res, url, slug, dev.port);
|
|
275
|
+
const rest = seg.slice(1).map((s) => {
|
|
276
|
+
try {
|
|
277
|
+
return decodeURIComponent(s);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return "\0";
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
if (rest.some((s) => s.startsWith(".") || s.includes("\0") || s.includes("/") || s.includes("\\")))
|
|
284
|
+
return fail(404, "not found");
|
|
285
|
+
let abs = path.resolve(r.servedDir, ...rest);
|
|
286
|
+
if (abs !== r.servedDir && !abs.startsWith(r.servedDir + path.sep))
|
|
287
|
+
return fail(404, "not found");
|
|
288
|
+
let st = await fs.stat(abs).catch(() => null);
|
|
289
|
+
if (st?.isDirectory()) {
|
|
290
|
+
if (!url.pathname.endsWith("/")) {
|
|
291
|
+
res.writeHead(302, { location: `${url.pathname}/${url.search}`, "cache-control": "no-store" });
|
|
292
|
+
return void res.end();
|
|
293
|
+
}
|
|
294
|
+
abs = path.join(abs, "index.html");
|
|
295
|
+
st = await fs.stat(abs).catch(() => null);
|
|
296
|
+
}
|
|
297
|
+
if (!st?.isFile()) {
|
|
298
|
+
// A missing index is the normal state while the agent rewrites the app:
|
|
299
|
+
// the pane gets a quiet page, not a JSON error, and reloads on the next change.
|
|
300
|
+
if (rest.length === 0 || rest[rest.length - 1] === "")
|
|
301
|
+
return placeholder(res, slug);
|
|
302
|
+
return fail(404, "not found");
|
|
303
|
+
}
|
|
304
|
+
const ext = path.extname(abs).toLowerCase();
|
|
305
|
+
res.writeHead(200, {
|
|
306
|
+
"content-type": MIME[ext] ?? "application/octet-stream",
|
|
307
|
+
"content-length": st.size,
|
|
308
|
+
...PREVIEW_HEADERS,
|
|
309
|
+
});
|
|
310
|
+
if (req.method === "HEAD")
|
|
311
|
+
return void res.end();
|
|
312
|
+
const stream = createReadStream(abs);
|
|
313
|
+
stream.on("error", () => res.destroy());
|
|
314
|
+
stream.pipe(res);
|
|
315
|
+
}
|
|
316
|
+
function placeholder(res, slug) {
|
|
317
|
+
const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${escapeHtml(slug)}</title>` +
|
|
318
|
+
`<style>:root{color-scheme:light;background:#fff;color:#46464f;font:14px/1.5 system-ui,sans-serif}` +
|
|
319
|
+
`body{margin:0;min-height:100vh;display:grid;place-items:center}main{text-align:center;padding:32px}` +
|
|
320
|
+
`b{display:block;color:#1b1b1f;font-size:16px;font-weight:500;margin-bottom:4px}</style></head>` +
|
|
321
|
+
`<body><main><b>${escapeHtml(slug)}</b>no index.html yet — the preview appears as soon as the agent writes one.</main></body></html>`;
|
|
322
|
+
res.writeHead(404, { "content-type": "text/html; charset=utf-8", ...PREVIEW_HEADERS });
|
|
323
|
+
res.end(html);
|
|
324
|
+
}
|
|
325
|
+
/* ---------- dev proxy ---------- */
|
|
326
|
+
/** "/vibeables/<slug>/x/y?q" → "/x/y?q" for the dev server. */
|
|
327
|
+
function devPath(url, slug) {
|
|
328
|
+
const prefix = `/vibeables/${encodeURIComponent(slug)}`;
|
|
329
|
+
const rest = url.pathname.startsWith(prefix) ? url.pathname.slice(prefix.length) : url.pathname;
|
|
330
|
+
return `${rest || "/"}${url.search}`;
|
|
331
|
+
}
|
|
332
|
+
/** Hop-by-hop headers must not be forwarded either way. */
|
|
333
|
+
const HOP = new Set(["connection", "keep-alive", "transfer-encoding", "te", "trailer", "upgrade", "proxy-connection"]);
|
|
334
|
+
function proxyToDev(req, res, url, slug, port) {
|
|
335
|
+
const headers = {};
|
|
336
|
+
for (const [k, v] of Object.entries(req.headers))
|
|
337
|
+
if (!HOP.has(k) && v !== undefined)
|
|
338
|
+
headers[k] = v;
|
|
339
|
+
headers.host = `127.0.0.1:${port}`;
|
|
340
|
+
const up = http.request({ host: "127.0.0.1", port, method: req.method, path: devPath(url, slug), headers }, (r) => {
|
|
341
|
+
const out = {};
|
|
342
|
+
for (const [k, v] of Object.entries(r.headers))
|
|
343
|
+
if (!HOP.has(k) && v !== undefined)
|
|
344
|
+
out[k] = v;
|
|
345
|
+
// The sandbox applies to the dev server's pages like to static files,
|
|
346
|
+
// and the inspector's rule wins over whatever the dev server sent.
|
|
347
|
+
Object.assign(out, PREVIEW_HEADERS);
|
|
348
|
+
if (r.statusCode && r.statusCode >= 300 && r.statusCode < 400 && typeof out.location === "string" && out.location.startsWith("/")) {
|
|
349
|
+
out.location = `/vibeables/${encodeURIComponent(slug)}${out.location}`;
|
|
350
|
+
}
|
|
351
|
+
res.writeHead(r.statusCode ?? 502, out);
|
|
352
|
+
r.pipe(res);
|
|
353
|
+
});
|
|
354
|
+
up.on("error", (err) => {
|
|
355
|
+
if (!res.headersSent) {
|
|
356
|
+
res.writeHead(502, { "content-type": "text/html; charset=utf-8", ...PREVIEW_HEADERS });
|
|
357
|
+
}
|
|
358
|
+
res.end(`<!doctype html><meta charset="utf-8"><p style="font:14px system-ui;color:#46464f;padding:24px">dev server not answering: ${escapeHtml(err.message)}</p>`);
|
|
359
|
+
});
|
|
360
|
+
req.pipe(up);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* WebSocket upgrades under /vibeables/<slug>/ go to the dev server too — that
|
|
364
|
+
* is how Vite-style HMR reaches the pane. Anything else is closed: the
|
|
365
|
+
* inspector has no sockets of its own.
|
|
366
|
+
*/
|
|
367
|
+
export function proxyUpgrade(req, socket, head) {
|
|
368
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
369
|
+
const seg = url.pathname.split("/");
|
|
370
|
+
const slug = seg[1] === "vibeables" ? decodeURIComponent(seg[2] ?? "") : "";
|
|
371
|
+
const dev = slug ? devs.get(slug) : undefined;
|
|
372
|
+
if (!dev || dev.exitCode !== undefined || !dev.child)
|
|
373
|
+
return void socket.destroy();
|
|
374
|
+
const upstream = net.connect(dev.port, "127.0.0.1", () => {
|
|
375
|
+
const lines = [`${req.method} ${devPath(url, slug)} HTTP/1.1`];
|
|
376
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
377
|
+
if (v === undefined)
|
|
378
|
+
continue;
|
|
379
|
+
lines.push(`${k}: ${k === "host" ? `127.0.0.1:${dev.port}` : Array.isArray(v) ? v.join(", ") : v}`);
|
|
380
|
+
}
|
|
381
|
+
upstream.write(lines.join("\r\n") + "\r\n\r\n");
|
|
382
|
+
if (head.length)
|
|
383
|
+
upstream.write(head);
|
|
384
|
+
socket.pipe(upstream).pipe(socket);
|
|
385
|
+
});
|
|
386
|
+
const drop = () => {
|
|
387
|
+
socket.destroy();
|
|
388
|
+
upstream.destroy();
|
|
389
|
+
};
|
|
390
|
+
upstream.on("error", drop);
|
|
391
|
+
socket.on("error", drop);
|
|
392
|
+
}
|
|
393
|
+
const channels = new Map();
|
|
394
|
+
const IGNORED = /(^|[\\/])(\.git|node_modules|\.cache|\.vite|\.next)([\\/]|$)/;
|
|
395
|
+
/**
|
|
396
|
+
* Stream a vibeable's events: file changes (debounced, node_modules and
|
|
397
|
+
* the like excluded) and dev-server state. The watcher exists while
|
|
398
|
+
* someone listens. `fs.watch` recursive covers macOS and Linux on Node 20+;
|
|
399
|
+
* where it cannot watch, the pane still has its manual reload.
|
|
400
|
+
*/
|
|
401
|
+
export function subscribeVibeable(slug, dir, fn) {
|
|
402
|
+
let ch = channels.get(slug);
|
|
403
|
+
if (!ch) {
|
|
404
|
+
ch = { watcher: null, subs: new Set(), pending: new Set(), timer: null };
|
|
405
|
+
channels.set(slug, ch);
|
|
406
|
+
const c = ch;
|
|
407
|
+
try {
|
|
408
|
+
c.watcher = watch(dir, { recursive: true, persistent: false }, (_event, filename) => {
|
|
409
|
+
const rel = filename == null ? "" : String(filename);
|
|
410
|
+
if (IGNORED.test(rel))
|
|
411
|
+
return;
|
|
412
|
+
c.pending.add(rel);
|
|
413
|
+
if (c.timer)
|
|
414
|
+
clearTimeout(c.timer);
|
|
415
|
+
c.timer = setTimeout(() => {
|
|
416
|
+
c.timer = null;
|
|
417
|
+
const files = [...c.pending].filter(Boolean).sort();
|
|
418
|
+
c.pending.clear();
|
|
419
|
+
for (const s of c.subs)
|
|
420
|
+
s({ type: "change", files });
|
|
421
|
+
}, 200);
|
|
422
|
+
});
|
|
423
|
+
c.watcher.on("error", (err) => {
|
|
424
|
+
for (const s of c.subs)
|
|
425
|
+
s({ type: "error", message: `file watcher stopped: ${err.message}` });
|
|
426
|
+
// Drop the channel: the next subscriber gets a fresh watch() attempt
|
|
427
|
+
// instead of joining a dead one that never reports anything.
|
|
428
|
+
dropChannel(slug);
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
dropChannel(slug);
|
|
433
|
+
queueMicrotask(() => fn({ type: "error", message: `cannot watch ${dir}: ${err.message}` }));
|
|
434
|
+
return () => { };
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
ch.subs.add(fn);
|
|
438
|
+
return () => {
|
|
439
|
+
const c = channels.get(slug);
|
|
440
|
+
if (!c)
|
|
441
|
+
return;
|
|
442
|
+
c.subs.delete(fn);
|
|
443
|
+
if (c.subs.size === 0) {
|
|
444
|
+
if (c.timer)
|
|
445
|
+
clearTimeout(c.timer);
|
|
446
|
+
c.watcher?.close();
|
|
447
|
+
channels.delete(slug);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/** Close a channel's watcher and forget it; subscribers are left to reconnect. */
|
|
452
|
+
function dropChannel(slug) {
|
|
453
|
+
const c = channels.get(slug);
|
|
454
|
+
if (!c)
|
|
455
|
+
return;
|
|
456
|
+
if (c.timer)
|
|
457
|
+
clearTimeout(c.timer);
|
|
458
|
+
c.watcher?.close();
|
|
459
|
+
channels.delete(slug);
|
|
460
|
+
}
|
|
461
|
+
function broadcast(slug, ev) {
|
|
462
|
+
const c = channels.get(slug);
|
|
463
|
+
if (!c)
|
|
464
|
+
return;
|
|
465
|
+
for (const s of c.subs)
|
|
466
|
+
s(ev);
|
|
467
|
+
}
|
|
468
|
+
const devs = new Map();
|
|
469
|
+
const LOG_LINES = 200;
|
|
470
|
+
const READY_TIMEOUT = 90_000;
|
|
471
|
+
function devView(slug) {
|
|
472
|
+
const d = devs.get(slug);
|
|
473
|
+
if (!d)
|
|
474
|
+
return undefined;
|
|
475
|
+
return {
|
|
476
|
+
command: d.command,
|
|
477
|
+
port: d.port,
|
|
478
|
+
running: d.exitCode === undefined && d.child !== null,
|
|
479
|
+
ready: d.ready && d.exitCode === undefined,
|
|
480
|
+
...(d.child?.pid ? { pid: d.child.pid } : {}),
|
|
481
|
+
startedAt: d.startedAt,
|
|
482
|
+
...(d.exitCode !== undefined ? { exitCode: d.exitCode } : {}),
|
|
483
|
+
log: d.log,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
function freePort() {
|
|
487
|
+
return new Promise((resolve, reject) => {
|
|
488
|
+
const srv = net.createServer();
|
|
489
|
+
srv.once("error", reject);
|
|
490
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
491
|
+
const port = srv.address().port;
|
|
492
|
+
srv.close(() => resolve(port));
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
/** Any HTTP answer on the port counts as ready — a 404 from a dev server is still a server. */
|
|
497
|
+
function probe(port) {
|
|
498
|
+
return new Promise((resolve) => {
|
|
499
|
+
const req = http.get({ host: "127.0.0.1", port, path: "/", timeout: 1_000 }, (res) => {
|
|
500
|
+
res.resume();
|
|
501
|
+
resolve(true);
|
|
502
|
+
});
|
|
503
|
+
req.on("timeout", () => req.destroy());
|
|
504
|
+
req.on("error", () => resolve(false));
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
function pushLog(d, chunk) {
|
|
508
|
+
for (const line of chunk.toString("utf8").split(/\r?\n/)) {
|
|
509
|
+
if (!line)
|
|
510
|
+
continue;
|
|
511
|
+
// Strip ANSI colour codes; the pane is not a terminal.
|
|
512
|
+
d.log.push(line.replace(/\x1b\[[0-9;]*[A-Za-z]/g, ""));
|
|
513
|
+
}
|
|
514
|
+
if (d.log.length > LOG_LINES)
|
|
515
|
+
d.log.splice(0, d.log.length - LOG_LINES);
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Start the vibeable's dev command. Idempotent while it runs. The child
|
|
519
|
+
* leads its own process group so stopping it also stops what it spawned
|
|
520
|
+
* (npm → vite → esbuild).
|
|
521
|
+
*/
|
|
522
|
+
/** Starts in flight per slug: two clicks (or two tabs) share one spawn instead of leaking a process. */
|
|
523
|
+
const starting = new Map();
|
|
524
|
+
export function startDev(slug) {
|
|
525
|
+
const inFlight = starting.get(slug);
|
|
526
|
+
if (inFlight)
|
|
527
|
+
return inFlight;
|
|
528
|
+
const p = startDevNow(slug).finally(() => starting.delete(slug));
|
|
529
|
+
starting.set(slug, p);
|
|
530
|
+
return p;
|
|
531
|
+
}
|
|
532
|
+
async function startDevNow(slug) {
|
|
533
|
+
const r = await resolveVibeable(slug);
|
|
534
|
+
if (r.configError)
|
|
535
|
+
throw new Error(r.configError);
|
|
536
|
+
if (!r.config.dev)
|
|
537
|
+
throw new Error(`no dev command — add \`dev: <command>\` to ${VIBEABLE_CONFIG_FILE}`);
|
|
538
|
+
const existing = devs.get(slug);
|
|
539
|
+
if (existing && existing.exitCode === undefined && existing.child)
|
|
540
|
+
return vibeableStatus(slug);
|
|
541
|
+
const port = r.config.port ?? (await freePort());
|
|
542
|
+
// The pane reaches the server through /vibeables/<slug>/ on the inspector,
|
|
543
|
+
// so a dev server that emits absolute URLs needs that as its base.
|
|
544
|
+
const basePath = `/vibeables/${encodeURIComponent(slug)}/`;
|
|
545
|
+
const child = spawn(r.config.dev, [], {
|
|
546
|
+
cwd: r.dir,
|
|
547
|
+
shell: true,
|
|
548
|
+
detached: process.platform !== "win32",
|
|
549
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
550
|
+
env: {
|
|
551
|
+
...process.env,
|
|
552
|
+
PORT: String(port),
|
|
553
|
+
BASE_PATH: basePath,
|
|
554
|
+
PUBLIC_URL: basePath,
|
|
555
|
+
BROWSER: "none",
|
|
556
|
+
FORCE_COLOR: "0",
|
|
557
|
+
NO_COLOR: "1",
|
|
558
|
+
CI: process.env.CI ?? "1",
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
const d = { child, command: r.config.dev, port, ready: false, startedAt: new Date().toISOString(), log: [] };
|
|
562
|
+
devs.set(slug, d);
|
|
563
|
+
child.stdout?.on("data", (c) => pushLog(d, c));
|
|
564
|
+
child.stderr?.on("data", (c) => pushLog(d, c));
|
|
565
|
+
child.on("error", (err) => {
|
|
566
|
+
pushLog(d, Buffer.from(`spawn failed: ${err.message}\n`));
|
|
567
|
+
d.exitCode = null;
|
|
568
|
+
d.ready = false;
|
|
569
|
+
broadcast(slug, { type: "dev", dev: devView(slug) });
|
|
570
|
+
});
|
|
571
|
+
child.on("exit", (code) => {
|
|
572
|
+
d.exitCode = code;
|
|
573
|
+
d.ready = false;
|
|
574
|
+
pushLog(d, Buffer.from(`[exited with ${code ?? "signal"}]\n`));
|
|
575
|
+
broadcast(slug, { type: "dev", dev: devView(slug) });
|
|
576
|
+
});
|
|
577
|
+
broadcast(slug, { type: "dev", dev: devView(slug) });
|
|
578
|
+
// Readiness runs in the background; the pane follows the dev events.
|
|
579
|
+
void (async () => {
|
|
580
|
+
const until = Date.now() + READY_TIMEOUT;
|
|
581
|
+
while (Date.now() < until && d.exitCode === undefined && devs.get(slug) === d) {
|
|
582
|
+
if (await probe(port)) {
|
|
583
|
+
d.ready = true;
|
|
584
|
+
broadcast(slug, { type: "dev", dev: devView(slug) });
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
await new Promise((res) => setTimeout(res, 500));
|
|
588
|
+
}
|
|
589
|
+
})();
|
|
590
|
+
return vibeableStatus(slug);
|
|
591
|
+
}
|
|
592
|
+
function killGroup(d, signal) {
|
|
593
|
+
const pid = d.child?.pid;
|
|
594
|
+
if (!pid)
|
|
595
|
+
return;
|
|
596
|
+
try {
|
|
597
|
+
if (process.platform !== "win32")
|
|
598
|
+
process.kill(-pid, signal);
|
|
599
|
+
else
|
|
600
|
+
d.child?.kill(signal);
|
|
601
|
+
}
|
|
602
|
+
catch {
|
|
603
|
+
try {
|
|
604
|
+
d.child?.kill(signal);
|
|
605
|
+
}
|
|
606
|
+
catch { }
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
/** SIGTERM the dev process group, SIGKILL what is still there after 3 s. */
|
|
610
|
+
export async function stopDev(slug) {
|
|
611
|
+
// Kill before resolving: a folder that vanished or a feature switched off
|
|
612
|
+
// must not leave the process running.
|
|
613
|
+
const d = devs.get(slug);
|
|
614
|
+
if (d && d.exitCode === undefined && d.child) {
|
|
615
|
+
const exited = new Promise((resolve) => d.child.once("exit", () => resolve()));
|
|
616
|
+
killGroup(d, "SIGTERM");
|
|
617
|
+
const timer = setTimeout(() => killGroup(d, "SIGKILL"), 3_000);
|
|
618
|
+
await exited;
|
|
619
|
+
clearTimeout(timer);
|
|
620
|
+
}
|
|
621
|
+
return vibeableStatus(slug);
|
|
622
|
+
}
|
|
623
|
+
/** Server shutdown: no dev server may outlive the inspector. */
|
|
624
|
+
export function disposeAllDevs() {
|
|
625
|
+
for (const d of devs.values())
|
|
626
|
+
if (d.exitCode === undefined)
|
|
627
|
+
killGroup(d, "SIGTERM");
|
|
628
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={"+":[`conjugate`,`add`],"−":[`negate`,`subtract`],"×":[`signOf`,`multiply`],"÷":[`reciprocal`,`divide`],"⌈":[`ceiling`,`greaterOf`],"⌊":[`floor`,`lesserOf`],"∣":[`absolute`,`residue`],"⍳":[`indexGenerate`,`indexOf`],"?":[`roll`,`deal`],"⋆":[`exponentiate`,`toThePowerOf`],"⍟":[`naturalLog`,`logToTheBase`],"○":[`piTimes`,`circularFuncs`],"!":[`factorial`,`binomial`],"⌹":[`matrixInverse`,`matrixDivide`],"<":[null,`lessThan`],"≤":[null,`lessThanOrEqual`],"=":[null,`equals`],">":[null,`greaterThan`],"≥":[null,`greaterThanOrEqual`],"≠":[null,`notEqual`],"≡":[`depth`,`match`],"≢":[null,`notMatch`],"∈":[`enlist`,`membership`],"⍷":[null,`find`],"∪":[`unique`,`union`],"∩":[null,`intersection`],"∼":[`not`,`without`],"∨":[null,`or`],"∧":[null,`and`],"⍱":[null,`nor`],"⍲":[null,`nand`],"⍴":[`shapeOf`,`reshape`],",":[`ravel`,`catenate`],"⍪":[null,`firstAxisCatenate`],"⌽":[`reverse`,`rotate`],"⊖":[`axis1Reverse`,`axis1Rotate`],"⍉":[`transpose`,null],"↑":[`first`,`take`],"↓":[null,`drop`],"⊂":[`enclose`,`partitionWithAxis`],"⊃":[`diclose`,`pick`],"⌷":[null,`index`],"⍋":[`gradeUp`,null],"⍒":[`gradeDown`,null],"⊤":[`encode`,null],"⊥":[`decode`,null],"⍕":[`format`,`formatByExample`],"⍎":[`execute`,null],"⊣":[`stop`,`left`],"⊢":[`pass`,`right`]},t=/[\.\/⌿⍀¨⍣]/,n=/⍬/,r=/[\+−×÷⌈⌊∣⍳\?⋆⍟○!⌹<≤=>≥≠≡≢∈⍷∪∩∼∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⌷⍋⍒⊤⊥⍕⍎⊣⊢]/,i=/←/,a=/[⍝#].*$/,o=function(e){var t=!1;return function(n){return t=n,n!==e||t===`\\`}},s={name:`apl`,startState:function(){return{prev:!1,func:!1,op:!1,string:!1,escape:!1}},token:function(s,c){var l;return s.eatSpace()?null:(l=s.next(),l===`"`||l===`'`?(s.eatWhile(o(l)),s.next(),c.prev=!0,`string`):/[\[{\(]/.test(l)?(c.prev=!1,null):/[\]}\)]/.test(l)?(c.prev=!0,null):n.test(l)?(c.prev=!1,`atom`):/[¯\d]/.test(l)?(c.func?(c.func=!1,c.prev=!1):c.prev=!0,s.eatWhile(/[\w\.]/),`number`):t.test(l)||i.test(l)?`operator`:r.test(l)?(c.func=!0,c.prev=!1,e[l]?`variableName.function.standard`:`variableName.function`):a.test(l)?(s.skipToEnd(),`comment`):l===`∘`&&s.peek()===`.`?(s.next(),`variableName.function`):(s.eatWhile(/[\w\$_]/),c.prev=!0,`keyword`))}};export{s as apl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){var t=e.match(/^\s*\S/);return e.skipToEnd(),t?`error`:null}var t={name:`asciiarmor`,token:function(t,n){var r;if(n.state==`top`)return t.sol()&&(r=t.match(/^-----BEGIN (.*)?-----\s*$/))?(n.state=`headers`,n.type=r[1],`tag`):e(t);if(n.state==`headers`){if(t.sol()&&t.match(/^\w+:/))return n.state=`header`,`atom`;var i=e(t);return i&&(n.state=`body`),i}if(n.state==`header`)return t.skipToEnd(),n.state=`headers`,`string`;if(n.state==`body`)return t.sol()&&(r=t.match(/^-----END (.*)?-----\s*$/))?r[1]==n.type?(n.state=`end`,`tag`):`error`:t.eatWhile(/[A-Za-z0-9+\/=]/)?null:(t.next(),`error`);if(n.state==`end`)return e(t)},blankLine:function(e){e.state==`headers`&&(e.state=`body`)},startState:function(){return{state:`top`,type:null}}};export{t as asciiArmor};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){for(var t={},n=e.split(` `),r=0;r<n.length;++r)t[n[r]]=!0;return t}var t={keywords:e(`DEFINITIONS OBJECTS IF DERIVED INFORMATION ACTION REPLY ANY NAMED CHARACTERIZED BEHAVIOUR REGISTERED WITH AS IDENTIFIED CONSTRAINED BY PRESENT BEGIN IMPORTS FROM UNITS SYNTAX MIN-ACCESS MAX-ACCESS MINACCESS MAXACCESS REVISION STATUS DESCRIPTION SEQUENCE SET COMPONENTS OF CHOICE DistinguishedName ENUMERATED SIZE MODULE END INDEX AUGMENTS EXTENSIBILITY IMPLIED EXPORTS`),cmipVerbs:e(`ACTIONS ADD GET NOTIFICATIONS REPLACE REMOVE`),compareTypes:e(`OPTIONAL DEFAULT MANAGED MODULE-TYPE MODULE_IDENTITY MODULE-COMPLIANCE OBJECT-TYPE OBJECT-IDENTITY OBJECT-COMPLIANCE MODE CONFIRMED CONDITIONAL SUBORDINATE SUPERIOR CLASS TRUE FALSE NULL TEXTUAL-CONVENTION`),status:e(`current deprecated mandatory obsolete`),tags:e(`APPLICATION AUTOMATIC EXPLICIT IMPLICIT PRIVATE TAGS UNIVERSAL`),storage:e(`BOOLEAN INTEGER OBJECT IDENTIFIER BIT OCTET STRING UTCTime InterfaceIndex IANAifType CMIP-Attribute REAL PACKAGE PACKAGES IpAddress PhysAddress NetworkAddress BITS BMPString TimeStamp TimeTicks TruthValue RowStatus DisplayString GeneralString GraphicString IA5String NumericString PrintableString SnmpAdminString TeletexString UTF8String VideotexString VisibleString StringStore ISO646String T61String UniversalString Unsigned32 Integer32 Gauge Gauge32 Counter Counter32 Counter64`),modifier:e(`ATTRIBUTE ATTRIBUTES MANDATORY-GROUP MANDATORY-GROUPS GROUP GROUPS ELEMENTS EQUALITY ORDERING SUBSTRINGS DEFINED`),accessTypes:e(`not-accessible accessible-for-notify read-only read-create read-write`),multiLineStrings:!0};function n(e){var n=e.keywords||t.keywords,r=e.cmipVerbs||t.cmipVerbs,i=e.compareTypes||t.compareTypes,a=e.status||t.status,o=e.tags||t.tags,s=e.storage||t.storage,c=e.modifier||t.modifier,l=e.accessTypes||t.accessTypes,u=e.multiLineStrings||t.multiLineStrings,d=e.indentStatements!==!1,f=/[\|\^]/,p;function m(e,t){var u=e.next();if(u==`"`||u==`'`)return t.tokenize=h(u),t.tokenize(e,t);if(/[\[\]\(\){}:=,;]/.test(u))return p=u,`punctuation`;if(u==`-`&&e.eat(`-`))return e.skipToEnd(),`comment`;if(/\d/.test(u))return e.eatWhile(/[\w\.]/),`number`;if(f.test(u))return e.eatWhile(f),`operator`;e.eatWhile(/[\w\-]/);var d=e.current();return n.propertyIsEnumerable(d)?`keyword`:r.propertyIsEnumerable(d)?`variableName`:i.propertyIsEnumerable(d)?`atom`:a.propertyIsEnumerable(d)?`comment`:o.propertyIsEnumerable(d)?`typeName`:s.propertyIsEnumerable(d)||c.propertyIsEnumerable(d)||l.propertyIsEnumerable(d)?`modifier`:`variableName`}function h(e){return function(t,n){for(var r=!1,i,a=!1;(i=t.next())!=null;){if(i==e&&!r){var o=t.peek();o&&(o=o.toLowerCase(),(o==`b`||o==`h`||o==`o`)&&t.next()),a=!0;break}r=!r&&i==`\\`}return(a||!(r||u))&&(n.tokenize=null),`string`}}function g(e,t,n,r,i){this.indented=e,this.column=t,this.type=n,this.align=r,this.prev=i}function _(e,t,n){var r=e.indented;return e.context&&e.context.type==`statement`&&(r=e.context.indented),e.context=new g(r,t,n,null,e.context)}function v(e){var t=e.context.type;return(t==`)`||t==`]`||t==`}`)&&(e.indented=e.context.indented),e.context=e.context.prev}return{name:`asn1`,startState:function(){return{tokenize:null,context:new g(-2,0,`top`,!1),indented:0,startOfLine:!0}},token:function(e,t){var n=t.context;if(e.sol()&&(n.align??(n.align=!1),t.indented=e.indentation(),t.startOfLine=!0),e.eatSpace())return null;p=null;var r=(t.tokenize||m)(e,t);if(r==`comment`)return r;if(n.align??(n.align=!0),(p==`;`||p==`:`||p==`,`)&&n.type==`statement`)v(t);else if(p==`{`)_(t,e.column(),`}`);else if(p==`[`)_(t,e.column(),`]`);else if(p==`(`)_(t,e.column(),`)`);else if(p==`}`){for(;n.type==`statement`;)n=v(t);for(n.type==`}`&&(n=v(t));n.type==`statement`;)n=v(t)}else p==n.type?v(t):d&&((n.type==`}`||n.type==`top`)&&p!=`;`||n.type==`statement`&&p==`newstatement`)&&_(t,e.column(),`statement`);return t.startOfLine=!1,r},languageData:{indentOnInput:/^\s*[{}]$/,commentTokens:{line:`--`}}}}export{n as asn1};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=[`exten`,`same`,`include`,`ignorepat`,`switch`],t=[`#include`,`#exec`],n=`addqueuemember.adsiprog.aelsub.agentlogin.agentmonitoroutgoing.agi.alarmreceiver.amd.answer.authenticate.background.backgrounddetect.bridge.busy.callcompletioncancel.callcompletionrequest.celgenuserevent.changemonitor.chanisavail.channelredirect.chanspy.clearhash.confbridge.congestion.continuewhile.controlplayback.dahdiacceptr2call.dahdibarge.dahdiras.dahdiscan.dahdisendcallreroutingfacility.dahdisendkeypadfacility.datetime.dbdel.dbdeltree.deadagi.dial.dictate.directory.disa.dumpchan.eagi.echo.endwhile.exec.execif.execiftime.exitwhile.extenspy.externalivr.festival.flash.followme.forkcdr.getcpeid.gosub.gosubif.goto.gotoif.gotoiftime.hangup.iax2provision.ices.importvar.incomplete.ivrdemo.jabberjoin.jabberleave.jabbersend.jabbersendgroup.jabberstatus.jack.log.macro.macroexclusive.macroexit.macroif.mailboxexists.meetme.meetmeadmin.meetmechanneladmin.meetmecount.milliwatt.minivmaccmess.minivmdelete.minivmgreet.minivmmwi.minivmnotify.minivmrecord.mixmonitor.monitor.morsecode.mp3player.mset.musiconhold.nbscat.nocdr.noop.odbc.odbc.odbcfinish.originate.ospauth.ospfinish.osplookup.ospnext.page.park.parkandannounce.parkedcall.pausemonitor.pausequeuemember.pickup.pickupchan.playback.playtones.privacymanager.proceeding.progress.queue.queuelog.raiseexception.read.readexten.readfile.receivefax.receivefax.receivefax.record.removequeuemember.resetcdr.retrydial.return.ringing.sayalpha.saycountedadj.saycountednoun.saycountpl.saydigits.saynumber.sayphonetic.sayunixtime.senddtmf.sendfax.sendfax.sendfax.sendimage.sendtext.sendurl.set.setamaflags.setcallerpres.setmusiconhold.sipaddheader.sipdtmfmode.sipremoveheader.skel.slastation.slatrunk.sms.softhangup.speechactivategrammar.speechbackground.speechcreate.speechdeactivategrammar.speechdestroy.speechloadgrammar.speechprocessingsound.speechstart.speechunloadgrammar.stackpop.startmusiconhold.stopmixmonitor.stopmonitor.stopmusiconhold.stopplaytones.system.testclient.testserver.transfer.tryexec.trysystem.unpausemonitor.unpausequeuemember.userevent.verbose.vmauthenticate.vmsayname.voicemail.voicemailmain.wait.waitexten.waitfornoise.waitforring.waitforsilence.waitmusiconhold.waituntil.while.zapateller`.split(`.`);function r(n,r){var i=``,a=n.next();if(r.blockComment)return a==`-`&&n.match(`-;`,!0)?r.blockComment=!1:n.skipTo(`--;`)?(n.next(),n.next(),n.next(),r.blockComment=!1):n.skipToEnd(),`comment`;if(a==`;`)return n.match(`--`,!0)&&!n.match(`-`,!1)?(r.blockComment=!0,`comment`):(n.skipToEnd(),`comment`);if(a==`[`)return n.skipTo(`]`),n.eat(`]`),`header`;if(a==`"`)return n.skipTo(`"`),`string`;if(a==`'`)return n.skipTo(`'`),`string.special`;if(a==`#`&&(n.eatWhile(/\w/),i=n.current(),t.indexOf(i)!==-1))return n.skipToEnd(),`strong`;if(a==`$`&&n.peek()==`{`)return n.skipTo(`}`),n.eat(`}`),`variableName.special`;if(n.eatWhile(/\w/),i=n.current(),e.indexOf(i)!==-1){switch(r.extenStart=!0,i){case`same`:r.extenSame=!0;break;case`include`:case`switch`:case`ignorepat`:r.extenInclude=!0}return`atom`}}var i={name:`asterisk`,startState:function(){return{blockComment:!1,extenStart:!1,extenSame:!1,extenInclude:!1,extenExten:!1,extenPriority:!1,extenApplication:!1}},token:function(e,t){var i=``;if(e.eatSpace())return null;if(t.extenStart)return e.eatWhile(/[^\s]/),i=e.current(),/^=>?$/.test(i)?(t.extenExten=!0,t.extenStart=!1,`strong`):(t.extenStart=!1,e.skipToEnd(),`error`);if(t.extenExten)return t.extenExten=!1,t.extenPriority=!0,e.eatWhile(/[^,]/),t.extenInclude&&=(e.skipToEnd(),t.extenPriority=!1,!1),t.extenSame&&(t.extenPriority=!1,t.extenSame=!1,t.extenApplication=!0),`tag`;if(t.extenPriority)return t.extenPriority=!1,t.extenApplication=!0,e.next(),t.extenSame?null:(e.eatWhile(/[^,]/),`number`);if(t.extenApplication){if(e.eatWhile(/,/),i=e.current(),i===`,`)return null;if(e.eatWhile(/\w/),i=e.current().toLowerCase(),t.extenApplication=!1,n.indexOf(i)!==-1)return`def`}else return r(e,t);return null},languageData:{commentTokens:{line:`;`,block:{open:`;--`,close:`--;`}}}};export{i as asterisk};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=`><+-.,[]`.split(``),t={name:`brainfuck`,startState:function(){return{commentLine:!1,left:0,right:0,commentLoop:!1}},token:function(t,n){if(t.eatSpace())return null;t.sol()&&(n.commentLine=!1);var r=t.next().toString();if(e.indexOf(r)!==-1){if(n.commentLine===!0)return t.eol()&&(n.commentLine=!1),`comment`;if(r===`]`||r===`[`)return r===`[`?n.left++:n.right++,`bracket`;if(r===`+`||r===`-`)return`keyword`;if(r===`<`||r===`>`)return`atom`;if(r===`.`||r===`,`)return`def`}else return n.commentLine=!0,t.eol()&&(n.commentLine=!1),`comment`;t.eol()&&(n.commentLine=!1)}};export{t as brainfuck};
|