@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.17.0-fork.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/CHANGELOG.md +67 -0
- package/dist/src/agent-execution.js +36 -51
- package/dist/src/background-widget.d.ts +56 -0
- package/dist/src/background-widget.js +22 -15
- package/dist/src/decoders.js +2 -1
- package/dist/src/host-navigator.d.ts +18 -0
- package/dist/src/host-navigator.js +71 -13
- package/dist/src/host-view.d.ts +16 -1
- package/dist/src/host-view.js +109 -42
- package/dist/src/host.js +57 -24
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +867 -545
- package/dist/src/index.js.map +3 -3
- package/dist/src/registry.js +2 -1
- package/dist/src/roles.d.ts +70 -0
- package/dist/src/roles.js +325 -0
- package/dist/src/settings.d.ts +13 -0
- package/dist/src/settings.js +253 -0
- package/dist/src/trajectory.d.ts +29 -4
- package/dist/src/trajectory.js +58 -3
- package/dist/src/types.d.ts +0 -1
- package/dist/src/utils.d.ts +2 -1
- package/dist/src/utils.js +10 -0
- package/dist/src/validation.d.ts +5 -17
- package/dist/src/validation.js +9 -458
- package/dist/subagents/index.js +1132 -694
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +4 -0
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +2 -1
- package/dist/subagents/src/manager.js +29 -10
- package/dist/subagents/src/navigator.js +396 -247
- package/dist/subagents/src/view.d.ts +23 -2
- package/dist/subagents/src/view.js +96 -30
- package/dist/trajectory/assets/index.html +347 -231
- package/dist/trajectory/assets/prism.min.js +13 -0
- package/dist/trajectory/index.js +377 -257
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +347 -231
- package/dist/trajectory/src/assets/prism.min.js +13 -0
- package/dist/trajectory/src/export.js +4 -2
- package/dist/trajectory/src/index.d.ts +2 -0
- package/dist/trajectory/src/index.js +143 -42
- package/dist/trajectory/src/server.js +134 -9
- package/dist/trajectory/src/server.js.map +1 -1
- package/package.json +2 -1
- package/src/agent-execution.ts +36 -42
- package/src/background-widget.ts +25 -18
- package/src/decoders.ts +2 -1
- package/src/host-navigator.ts +58 -14
- package/src/host-view.ts +93 -37
- package/src/host.ts +51 -21
- package/src/index.ts +3 -1
- package/src/registry.ts +2 -1
- package/src/roles.ts +326 -0
- package/src/settings.ts +206 -0
- package/src/trajectory.ts +76 -7
- package/src/types.ts +1 -1
- package/src/utils.ts +8 -1
- package/src/validation.ts +12 -356
- package/subagents/README.md +1 -1
- package/subagents/src/contracts.ts +4 -0
- package/subagents/src/index.ts +3 -2
- package/subagents/src/manager.ts +28 -10
- package/subagents/src/navigator.ts +336 -180
- package/subagents/src/view.ts +97 -28
- package/trajectory/src/assets/index.html +347 -231
- package/trajectory/src/assets/prism.min.js +13 -0
- package/trajectory/src/export.ts +4 -2
- package/trajectory/src/index.ts +118 -38
- package/trajectory/src/server.ts +112 -12
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prism v1.30.0 (core, clike, javascript, typescript, json, bash, diff, python) - https://prismjs.com
|
|
3
|
+
* Copyright (c) 2012 Lea Verou. (MIT Licensed)
|
|
4
|
+
*/
|
|
5
|
+
window.Prism = window.Prism || {}; window.Prism.manual = true;
|
|
6
|
+
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case"Object":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)&&(r[l]=e(n[l],t));return r;case"Array":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return"none"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,"gi"),""),e.classList.add("language-"+t)},currentScript:function(){if("undefined"==typeof document)return null;if(document.currentScript&&"SCRIPT"===document.currentScript.tagName)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName("script");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r="no-"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)&&(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u&&n!=e&&(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);"Object"!==u||i[l(s)]?"Array"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};a.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run("before-all-elements-highlight",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o&&"pre"===o.nodeName.toLowerCase()&&a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run("before-insert",s),s.element.innerHTML=s.highlightedCode,a.hooks.run("after-highlight",s),a.hooks.run("complete",s),r&&r.call(s.element)}if(a.hooks.run("before-sanity-check",s),(o=s.element.parentElement)&&"pre"===o.nodeName.toLowerCase()&&!o.hasAttribute("tabindex")&&o.setAttribute("tabindex","0"),!s.code)return a.hooks.run("complete",s),void(r&&r.call(s.element));if(a.hooks.run("before-highlight",s),s.grammar)if(t&&e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run("before-tokenize",r),!r.grammar)throw new Error('The language "'+r.language+'" has no grammar.');return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run("after-tokenize",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t&&t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d<h.length;++d){if(g&&g.cause==f+","+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y&&!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+"g")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail&&!(g&&A>=g.reach);A+=w.value.length,w=w.next){var P=w.value;if(n.length>e.length)return;if(!(P instanceof i)){var E,S=1;if(y){if(!(E=l(b,A,e,m))||E.index>=e.length)break;var L=E.index,O=E.index+E[0].length,C=A;for(C+=w.value.length;L>=C;)C+=(w=w.next).value.length;if(A=C-=w.value.length,w.value instanceof i)continue;for(var j=w;j!==n.tail&&(C<O||"string"==typeof j.value);j=j.next)S++,C+=j.value.length;S--,P=e.slice(A,C),E.index-=A}else if(!(E=l(b,0,P,m)))continue;L=E.index;var N=E[0],_=P.slice(0,L),M=P.slice(L+N.length),W=A+P.length;g&&W>g.reach&&(g.reach=W);var I=w.prev;if(_&&(I=u(n,I,_),A+=_.length),c(n,I,S),w=u(n,I,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),S>1){var T={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,T),g&&T.reach>g.reach&&(g.reach=T.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if("string"==typeof n)return n;if(Array.isArray(n)){var r="";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:"span",classes:["token",n.type],attributes:{},language:t},l=n.alias;l&&(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=" "+s+'="'+(i.attributes[s]||"").replace(/"/g,""")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+o+">"+i.content+"</"+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
|
|
7
|
+
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
|
|
8
|
+
Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
|
|
9
|
+
!function(e){e.languages.typescript=e.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var s=e.languages.extend("typescript",{});delete s["class-name"],e.languages.typescript["class-name"].inside=s,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:s}}}}),e.languages.ts=e.languages.typescript}(Prism);
|
|
10
|
+
Prism.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},Prism.languages.webmanifest=Prism.languages.json;
|
|
11
|
+
!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",a={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},n={bash:a,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?:\.\w+)*(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},parameter:{pattern:/(^|\s)-{1,2}(?:\w+:[+-]?)?\w+(?:\.\w+)*(?=[=\s]|$)/,alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:n},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:a}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:n.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:n.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cargo|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|java|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|sysctl|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},a.inside=e.languages.bash;for(var s=["comment","function-name","for-or-select","assign-left","parameter","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=n.variable[1].inside,i=0;i<s.length;i++)o[s[i]]=e.languages.bash[s[i]];e.languages.sh=e.languages.bash,e.languages.shell=e.languages.bash}(Prism);
|
|
12
|
+
!function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var n={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(n).forEach((function(a){var i=n[a],r=[];/^\w+$/.test(a)||r.push(/\w+/.exec(a)[0]),"diff"===a&&r.push("bold"),e.languages.diff[a]={pattern:RegExp("^(?:["+i+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:r,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(a)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:n})}(Prism);
|
|
13
|
+
Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern://,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},Prism.languages.python["string-interpolation"].inside.interpolation.inside.rest=Prism.languages.python,Prism.languages.py=Prism.languages.python;
|
|
@@ -22,15 +22,16 @@ export async function exportTrajectoryRunHtml(options) {
|
|
|
22
22
|
if (!run)
|
|
23
23
|
throw new Error(`Workflow run ${options.runId} was not found for session ${options.sessionId}`);
|
|
24
24
|
const assets = new URL("./assets/", import.meta.url);
|
|
25
|
-
const [html, marked, morphdom, favicon] = await Promise.all([
|
|
25
|
+
const [html, marked, morphdom, prism, favicon] = await Promise.all([
|
|
26
26
|
readFile(new URL("index.html", assets), "utf8"),
|
|
27
27
|
readFile(new URL("marked.min.js", assets)),
|
|
28
28
|
readFile(new URL("morphdom.min.js", assets)),
|
|
29
|
+
readFile(new URL("prism.min.js", assets)),
|
|
29
30
|
readFile(new URL("favicon.png", assets)),
|
|
30
31
|
]);
|
|
31
32
|
const state = {
|
|
32
33
|
type: "state",
|
|
33
|
-
publishers: [{ id: `export-${options.runId}`, title: `run ${options.runId.slice(0, 8)}`, cwd: options.cwd, sessionId: options.sessionId,
|
|
34
|
+
publishers: [{ id: `export-${options.runId}`, title: `run ${options.runId.slice(0, 8)}`, cwd: options.cwd, sessionId: options.sessionId, connected: true, runs: [run], subagents: [] }],
|
|
34
35
|
updatedAt: Date.now(),
|
|
35
36
|
};
|
|
36
37
|
// Base64 data URLs avoid </script> escaping issues inside inlined sources; JSON gets the standard \u003c escape.
|
|
@@ -39,6 +40,7 @@ export async function exportTrajectoryRunHtml(options) {
|
|
|
39
40
|
output = inlineAsset(output, '<link rel="icon" type="image/png" href="./favicon.png" />', `<link rel="icon" type="image/png" href="data:image/png;base64,${favicon.toString("base64")}" />`);
|
|
40
41
|
output = inlineAsset(output, '<script src="./marked.min.js"></script>', script(marked));
|
|
41
42
|
output = inlineAsset(output, '<script src="./morphdom.min.js"></script>', script(morphdom));
|
|
43
|
+
output = inlineAsset(output, '<script src="./prism.min.js"></script>', script(prism));
|
|
42
44
|
output = inlineAsset(output, '<body data-view="run">', `<body data-view="run">\n <script>window.__PIEWF_STATIC__ = ${JSON.stringify(state).replace(/</g, "\\u003c")};</script>`);
|
|
43
45
|
return output;
|
|
44
46
|
}
|
|
@@ -13,6 +13,8 @@ export type TrajectoryController = {
|
|
|
13
13
|
export declare function trajectoryServerPath(moduleDirectory?: string): string;
|
|
14
14
|
export declare function trajectoryUrl(port: number): string;
|
|
15
15
|
export declare function openTrajectoryUrl(url: string): void;
|
|
16
|
+
type LiveStateRecord = Record<string, unknown>;
|
|
17
|
+
export declare function minimalStatePublisher(publisher: LiveStateRecord): LiveStateRecord;
|
|
16
18
|
export declare function createTrajectoryController(agentDir: string): TrajectoryController;
|
|
17
19
|
export { DEFAULT_TRAJECTORY_PORT, TRAJECTORY_IDLE_EXIT_MS, TRAJECTORY_LOCK_NAME };
|
|
18
20
|
export { exportTrajectoryRunHtml, shareTrajectoryRun, type TrajectoryExportOptions, type TrajectoryShareOptions, type TrajectoryShareResult } from "./export.js";
|
|
@@ -29,15 +29,22 @@ async function trajectoryFingerprint(serverPath) {
|
|
|
29
29
|
function publisherId(cwd, sessionId) { return createHash("sha256").update(`${cwd}\n${sessionId}`).digest("hex").slice(0, 16); }
|
|
30
30
|
function trajectoryPort(value) { return positiveInteger(value) && value <= 65535 ? value : DEFAULT_TRAJECTORY_PORT; }
|
|
31
31
|
function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }
|
|
32
|
-
|
|
32
|
+
// Servers older than the identity fields answer `{ ok: true }` alone, so every field is optional.
|
|
33
|
+
async function serverHealth(port) {
|
|
33
34
|
try {
|
|
34
35
|
const response = await fetch(`http://127.0.0.1:${String(port)}/health`, { signal: AbortSignal.timeout(300) });
|
|
35
|
-
|
|
36
|
+
if (!response.ok)
|
|
37
|
+
return undefined;
|
|
38
|
+
const body = await response.json().catch(() => undefined);
|
|
39
|
+
if (!object(body))
|
|
40
|
+
return {};
|
|
41
|
+
return { ...(positiveInteger(body.pid) ? { pid: body.pid } : {}), ...(typeof body.fingerprint === "string" ? { fingerprint: body.fingerprint } : {}), ...(positiveInteger(body.startedAt) ? { startedAt: body.startedAt } : {}) };
|
|
36
42
|
}
|
|
37
43
|
catch {
|
|
38
|
-
return
|
|
44
|
+
return undefined;
|
|
39
45
|
}
|
|
40
46
|
}
|
|
47
|
+
async function serverHealthy(port) { return await serverHealth(port) !== undefined; }
|
|
41
48
|
function signalProcess(pid, signal) {
|
|
42
49
|
try {
|
|
43
50
|
process.kill(pid, signal);
|
|
@@ -82,17 +89,22 @@ async function readLock(path) {
|
|
|
82
89
|
return undefined;
|
|
83
90
|
}
|
|
84
91
|
}
|
|
85
|
-
|
|
92
|
+
// Only a server reporting this fingerprint counts: an older one still bound to the port would otherwise be adopted forever.
|
|
93
|
+
async function waitForServer(port, fingerprint) {
|
|
94
|
+
let foreign = false;
|
|
86
95
|
for (let attempt = 0; attempt < 60; attempt += 1) {
|
|
87
|
-
|
|
96
|
+
const health = await serverHealth(port);
|
|
97
|
+
if (health?.fingerprint === fingerprint)
|
|
88
98
|
return;
|
|
99
|
+
foreign = health !== undefined;
|
|
89
100
|
await delay(50);
|
|
90
101
|
}
|
|
91
|
-
throw new Error(`Trajectory server did not start on port ${String(port)}`);
|
|
102
|
+
throw new Error(foreign ? `Trajectory port ${String(port)} is held by another Trajectory server; stop it and retry` : `Trajectory server did not start on port ${String(port)}`);
|
|
92
103
|
}
|
|
93
104
|
async function resolveExistingServer(lockPath, existing, fingerprint) {
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
const health = await serverHealth(existing.port);
|
|
106
|
+
if (health) {
|
|
107
|
+
if (existing.fingerprint === fingerprint && health.fingerprint === fingerprint)
|
|
96
108
|
return existing;
|
|
97
109
|
await stopStaleServer(existing);
|
|
98
110
|
await rm(lockPath, { force: true });
|
|
@@ -100,7 +112,7 @@ async function resolveExistingServer(lockPath, existing, fingerprint) {
|
|
|
100
112
|
}
|
|
101
113
|
if (await processAlive(existing.pid, existing.startedAt)) {
|
|
102
114
|
try {
|
|
103
|
-
await waitForServer(existing.port);
|
|
115
|
+
await waitForServer(existing.port, fingerprint);
|
|
104
116
|
return existing;
|
|
105
117
|
}
|
|
106
118
|
catch {
|
|
@@ -148,12 +160,16 @@ async function ensureTrajectoryServer(agentDir, configuredPort) {
|
|
|
148
160
|
finally {
|
|
149
161
|
await lockHandle?.close();
|
|
150
162
|
}
|
|
163
|
+
// A server can answer on the port without owning the lock, e.g. one whose lock was lost; it reports its pid, so replace it before spawning.
|
|
164
|
+
const occupant = await serverHealth(configuredPort);
|
|
165
|
+
if (occupant?.pid !== undefined && occupant.fingerprint !== fingerprint)
|
|
166
|
+
await stopStaleServer({ pid: occupant.pid, port: configuredPort, ...(occupant.startedAt === undefined ? {} : { startedAt: occupant.startedAt }) });
|
|
151
167
|
try {
|
|
152
168
|
// NOTE: under a Bun-compiled pi binary process.execPath is the pi CLI, and Bun's node:http never writes the WebSocket 101 upgrade (oven-sh/bun#28157), so the server must run on a real node from PATH.
|
|
153
169
|
const child = spawn(process.versions.bun ? "node" : process.execPath, [serverPath, "--port", String(configuredPort), "--lock", lockPath, "--fingerprint", fingerprint], { detached: true, stdio: "ignore" });
|
|
154
170
|
const startupError = new Promise((_resolve, reject) => { child.once("error", reject); });
|
|
155
171
|
child.unref();
|
|
156
|
-
await Promise.race([waitForServer(configuredPort), startupError]);
|
|
172
|
+
await Promise.race([waitForServer(configuredPort, fingerprint), startupError]);
|
|
157
173
|
return { port: configuredPort };
|
|
158
174
|
}
|
|
159
175
|
catch (error) {
|
|
@@ -181,6 +197,9 @@ const MAX_FRAME_BYTES = 32 * 1024 * 1024;
|
|
|
181
197
|
const MAX_LIVE_STATE_BYTES = MAX_FRAME_BYTES - 1024;
|
|
182
198
|
const MAX_TRANSCRIPT_REQUESTS = 64;
|
|
183
199
|
const TRANSCRIPT_REQUEST_TIMEOUT_MS = 10_000;
|
|
200
|
+
const ACTIVE_POLL_MS = 1_000;
|
|
201
|
+
// Nobody is watching, so the publisher stops reading run state every second.
|
|
202
|
+
const IDLE_POLL_MS = 10_000;
|
|
184
203
|
const RECONNECT_INITIAL_DELAY_MS = 100;
|
|
185
204
|
const RECONNECT_MAX_DELAY_MS = 5_000;
|
|
186
205
|
function isTimingEntry(value) { return object(value) && value.type === "custom" && value.customType === "pi-workflows:tool-timing"; }
|
|
@@ -196,52 +215,109 @@ const MAX_LIVE_OBJECT_KEYS = 64;
|
|
|
196
215
|
const LIVE_METADATA_ARRAY_KEYS = new Set(["agents", "runs", "subagents"]);
|
|
197
216
|
const LIVE_TRUNCATABLE_ARRAY_KEYS = new Set(["events", "phaseHistory"]);
|
|
198
217
|
const LIVE_METADATA_OBJECT_KEYS = new Set(["transcripts"]);
|
|
218
|
+
function liveValueWillBeBounded(value, key = "", depth = 0) {
|
|
219
|
+
if (typeof value === "string")
|
|
220
|
+
return Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES;
|
|
221
|
+
if (typeof value !== "object" || value === null)
|
|
222
|
+
return false;
|
|
223
|
+
if (depth >= 12)
|
|
224
|
+
return true;
|
|
225
|
+
if (Array.isArray(value)) {
|
|
226
|
+
const maxEntries = LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
|
|
227
|
+
if (key === "attemptDetails" && value.length > 8 || !LIVE_METADATA_ARRAY_KEYS.has(key) && value.length > maxEntries)
|
|
228
|
+
return true;
|
|
229
|
+
return value.some((entry) => liveValueWillBeBounded(entry, "", depth + 1));
|
|
230
|
+
}
|
|
231
|
+
if (!object(value))
|
|
232
|
+
return false;
|
|
233
|
+
const properties = LIVE_METADATA_OBJECT_KEYS.has(key) ? Object.keys(value) : Object.keys(value).sort().slice(0, MAX_LIVE_OBJECT_KEYS);
|
|
234
|
+
if (!LIVE_METADATA_OBJECT_KEYS.has(key) && Object.keys(value).length > MAX_LIVE_OBJECT_KEYS)
|
|
235
|
+
return true;
|
|
236
|
+
return properties.some((property) => liveValueWillBeBounded(value[property], property, depth + 1));
|
|
237
|
+
}
|
|
238
|
+
const MAX_LIVE_TIMING_BYTES = 64 * 1024;
|
|
239
|
+
/** Keeps the newest timing entries: dropping the oldest leaves a live agent's gantt growing instead of frozen. */
|
|
199
240
|
function boundedTiming(value) {
|
|
200
241
|
const entries = Array.isArray(value) ? value.filter(isTimingEntry) : [];
|
|
201
242
|
const retained = [];
|
|
202
243
|
let bytes = 2;
|
|
203
|
-
for (
|
|
244
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
204
245
|
let serialized;
|
|
205
246
|
try {
|
|
206
|
-
serialized = JSON.stringify(
|
|
247
|
+
serialized = JSON.stringify(entries[index]);
|
|
207
248
|
}
|
|
208
249
|
catch {
|
|
209
250
|
continue;
|
|
210
251
|
}
|
|
211
252
|
const nextBytes = bytes + (retained.length ? 1 : 0) + Buffer.byteLength(serialized);
|
|
212
|
-
if (nextBytes >=
|
|
253
|
+
if (nextBytes >= MAX_LIVE_TIMING_BYTES)
|
|
213
254
|
break;
|
|
214
|
-
retained.push(
|
|
255
|
+
retained.push(entries[index]);
|
|
215
256
|
bytes = nextBytes;
|
|
216
257
|
}
|
|
217
|
-
return retained;
|
|
258
|
+
return retained.reverse();
|
|
218
259
|
}
|
|
219
|
-
function boundedLiveValue(value, key = "", depth = 0) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (
|
|
260
|
+
function boundedLiveValue(value, key = "", depth = 0, bounds) {
|
|
261
|
+
const pathLength = bounds?.path.length ?? 0;
|
|
262
|
+
if (bounds && key)
|
|
263
|
+
bounds.path.push(key);
|
|
264
|
+
const argsValue = bounds !== undefined && bounds.path.length >= 3 && bounds.path[0] === "runs" && bounds.path[1] === "snapshot" && bounds.path[2] === "args";
|
|
265
|
+
const markArgsTruncated = () => { if (argsValue && bounds.runIndex !== undefined)
|
|
266
|
+
bounds.argsTruncatedRuns.add(bounds.runIndex); };
|
|
267
|
+
try {
|
|
268
|
+
if (typeof value === "string") {
|
|
269
|
+
if (argsValue && Buffer.byteLength(value) > MAX_LIVE_STRING_BYTES)
|
|
270
|
+
markArgsTruncated();
|
|
271
|
+
return boundedString(value, MAX_LIVE_STRING_BYTES);
|
|
272
|
+
}
|
|
273
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null)
|
|
274
|
+
return value;
|
|
275
|
+
if (depth >= 12) {
|
|
276
|
+
markArgsTruncated();
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
if (key === "timing" && !argsValue)
|
|
280
|
+
return boundedTiming(value);
|
|
281
|
+
if (key === "output" && object(value) && value.status === "available") {
|
|
282
|
+
const boundedOutput = boundedLiveValue(value.value, "value", depth + 1, bounds);
|
|
283
|
+
return liveValueWillBeBounded(value.value, "value", depth + 1) ? { status: "truncated", kind: "result", bytes: value.bytes } : { ...value, value: boundedOutput };
|
|
284
|
+
}
|
|
285
|
+
if (Array.isArray(value)) {
|
|
286
|
+
const array = value;
|
|
287
|
+
const maxEntries = !argsValue && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
|
|
288
|
+
const entries = !argsValue && key === "attemptDetails" ? array.slice(-8) : !argsValue && LIVE_METADATA_ARRAY_KEYS.has(key) ? array : array.length <= maxEntries ? array : [...array.slice(0, 32), ...array.slice(-maxEntries + 32)];
|
|
289
|
+
if (entries.length !== array.length)
|
|
290
|
+
markArgsTruncated();
|
|
291
|
+
const publisherRuns = bounds?.path.length === 1 && bounds.path[0] === "runs";
|
|
292
|
+
const bounded = entries.map((entry, index) => {
|
|
293
|
+
const previousRunIndex = bounds?.runIndex;
|
|
294
|
+
if (publisherRuns)
|
|
295
|
+
bounds.runIndex = index;
|
|
296
|
+
const result = boundedLiveValue(entry, "", depth + 1, bounds);
|
|
297
|
+
if (bounds)
|
|
298
|
+
bounds.runIndex = previousRunIndex;
|
|
299
|
+
return result;
|
|
300
|
+
});
|
|
301
|
+
if (entries.length !== array.length && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key))
|
|
302
|
+
bounded.push({ type: "trajectory:truncated", field: key, omitted: array.length - entries.length });
|
|
303
|
+
return bounded;
|
|
304
|
+
}
|
|
305
|
+
if (object(value)) {
|
|
306
|
+
const result = {};
|
|
307
|
+
const keys = Object.keys(value).sort();
|
|
308
|
+
const properties = !argsValue && LIVE_METADATA_OBJECT_KEYS.has(key) ? keys : keys.slice(0, MAX_LIVE_OBJECT_KEYS);
|
|
309
|
+
if (properties.length !== keys.length)
|
|
310
|
+
markArgsTruncated();
|
|
311
|
+
for (const property of properties)
|
|
312
|
+
result[property] = boundedLiveValue(value[property], property, depth + 1, bounds);
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
225
315
|
return undefined;
|
|
226
|
-
if (key === "timing")
|
|
227
|
-
return boundedTiming(value);
|
|
228
|
-
if (Array.isArray(value)) {
|
|
229
|
-
const array = value;
|
|
230
|
-
const maxEntries = LIVE_TRUNCATABLE_ARRAY_KEYS.has(key) ? MAX_LIVE_ARRAY_ENTRIES - 1 : MAX_LIVE_ARRAY_ENTRIES;
|
|
231
|
-
const entries = key === "attemptDetails" ? array.slice(-8) : LIVE_METADATA_ARRAY_KEYS.has(key) ? array : array.length <= maxEntries ? array : [...array.slice(0, 32), ...array.slice(-maxEntries + 32)];
|
|
232
|
-
const bounded = entries.map((entry) => boundedLiveValue(entry, "", depth + 1));
|
|
233
|
-
if (entries.length !== array.length && LIVE_TRUNCATABLE_ARRAY_KEYS.has(key))
|
|
234
|
-
bounded.push({ type: "trajectory:truncated", field: key, omitted: array.length - entries.length });
|
|
235
|
-
return bounded;
|
|
236
316
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
for (const property of properties)
|
|
241
|
-
result[property] = boundedLiveValue(value[property], property, depth + 1);
|
|
242
|
-
return result;
|
|
317
|
+
finally {
|
|
318
|
+
if (bounds)
|
|
319
|
+
bounds.path.length = pathLength;
|
|
243
320
|
}
|
|
244
|
-
return undefined;
|
|
245
321
|
}
|
|
246
322
|
function transcriptRevisionProjection(value, depth = 0) {
|
|
247
323
|
if (depth >= 8)
|
|
@@ -293,8 +369,21 @@ function projectPublisher(metadata, publisher, revisions) {
|
|
|
293
369
|
const publisherId = typeof publisher.id === "string" ? publisher.id : "";
|
|
294
370
|
return { ...publisher, runs: metadata.runs.map((run) => projectRun(run, `${publisherId}\t${run.run.id}`, revisions)), subagents: metadata.subagents.map((subagent) => projectSubagent(subagent, `${publisherId}\tsubagent\t${subagent.id}`, revisions)) };
|
|
295
371
|
}
|
|
296
|
-
function minimalStatePublisher(publisher) {
|
|
297
|
-
|
|
372
|
+
export function minimalStatePublisher(publisher) {
|
|
373
|
+
const bounds = { argsTruncatedRuns: new Set(), path: [], runIndex: undefined };
|
|
374
|
+
const bounded = boundedLiveValue(publisher, "", 0, bounds) ?? {};
|
|
375
|
+
const sourceRuns = Array.isArray(publisher.runs) ? publisher.runs : [];
|
|
376
|
+
const boundedRuns = Array.isArray(bounded.runs) ? bounded.runs : [];
|
|
377
|
+
if (!sourceRuns.length || !boundedRuns.length)
|
|
378
|
+
return bounded;
|
|
379
|
+
const runs = boundedRuns.map((run, index) => {
|
|
380
|
+
const sourceRun = sourceRuns[index];
|
|
381
|
+
const sourceSnapshot = object(sourceRun) && object(sourceRun.snapshot) ? sourceRun.snapshot : undefined;
|
|
382
|
+
const boundedSnapshot = object(run) && object(run.snapshot) ? run.snapshot : undefined;
|
|
383
|
+
const argsUnavailable = sourceSnapshot !== undefined && sourceSnapshot.args !== undefined && sourceSnapshot.args !== null && (boundedSnapshot === undefined || !Object.prototype.hasOwnProperty.call(boundedSnapshot, "args"));
|
|
384
|
+
return (bounds.argsTruncatedRuns.has(index) || argsUnavailable) && object(run) ? { ...run, snapshotArgsTruncated: true } : run;
|
|
385
|
+
});
|
|
386
|
+
return { ...bounded, runs };
|
|
298
387
|
}
|
|
299
388
|
function publisherStateFrame(publisher, runs, subagents, truncated = false) {
|
|
300
389
|
const publisherSummary = { ...publisher };
|
|
@@ -365,11 +454,12 @@ export function createTrajectoryController(agentDir) {
|
|
|
365
454
|
} };
|
|
366
455
|
const clearTranscriptRequests = () => { for (const request of pendingTranscripts.values())
|
|
367
456
|
clearTimeout(request.timer); pendingTranscripts.clear(); };
|
|
457
|
+
let viewers = 0;
|
|
368
458
|
const startPolling = () => { stopPolling(); pollTimer = setInterval(() => { void sendState().catch((error) => { if (!closing)
|
|
369
|
-
console.error(`Trajectory state publish failed: ${errorText(error)}`); }); },
|
|
459
|
+
console.error(`Trajectory state publish failed: ${errorText(error)}`); }); }, viewers > 0 ? ACTIVE_POLL_MS : IDLE_POLL_MS); pollTimer.unref(); };
|
|
370
460
|
let stateLoad;
|
|
371
461
|
let lastState;
|
|
372
|
-
const publisherValue = (input) => ({ id: publisherId(input.cwd, input.sessionId), title: `session ${input.sessionId.slice(0, 8)}`, cwd: input.cwd, sessionId: input.sessionId,
|
|
462
|
+
const publisherValue = (input) => ({ id: publisherId(input.cwd, input.sessionId), title: `session ${input.sessionId.slice(0, 8)}`, cwd: input.cwd, sessionId: input.sessionId, connected: true });
|
|
373
463
|
const sendState = async () => {
|
|
374
464
|
const activeSocket = socket;
|
|
375
465
|
if (activeSocket !== undefined && stateLoad?.socket === activeSocket)
|
|
@@ -547,6 +637,17 @@ export function createTrajectoryController(agentDir) {
|
|
|
547
637
|
established = false;
|
|
548
638
|
return;
|
|
549
639
|
}
|
|
640
|
+
if (message.type === "publisher:viewers" && typeof message.count === "number") {
|
|
641
|
+
const watched = viewers > 0;
|
|
642
|
+
viewers = message.count;
|
|
643
|
+
if (watched === viewers > 0)
|
|
644
|
+
return;
|
|
645
|
+
if (socket === next && !closing)
|
|
646
|
+
startPolling();
|
|
647
|
+
if (viewers > 0)
|
|
648
|
+
void sendState().catch(() => undefined);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
550
651
|
if (message.type === "publisher:transcript" && typeof message.requestId === "string" && typeof message.publisherId === "string" && (typeof message.runId === "string" && typeof message.agentId === "string" || typeof message.subagentId === "string")) {
|
|
551
652
|
handleTranscriptRequest(next, generation, message, input);
|
|
552
653
|
return;
|
|
@@ -1943,6 +1943,68 @@ function compactSubagent(subagent) {
|
|
|
1943
1943
|
const record = subagent;
|
|
1944
1944
|
return { ...record, transcript: compactTranscript(record.transcript) };
|
|
1945
1945
|
}
|
|
1946
|
+
function withoutTiming(run) {
|
|
1947
|
+
if (!run || typeof run !== "object" || Array.isArray(run)) return run;
|
|
1948
|
+
const record = run;
|
|
1949
|
+
if (!record.transcripts || typeof record.transcripts !== "object" || Array.isArray(record.transcripts)) return run;
|
|
1950
|
+
const transcripts = {};
|
|
1951
|
+
for (const [id, entry] of Object.entries(record.transcripts)) transcripts[id] = entry && typeof entry === "object" && !Array.isArray(entry) ? { ...entry, timing: [] } : entry;
|
|
1952
|
+
return { ...record, transcripts };
|
|
1953
|
+
}
|
|
1954
|
+
function knownTiming(run, prefix, sent, mark) {
|
|
1955
|
+
if (!run || typeof run !== "object" || Array.isArray(run)) return run;
|
|
1956
|
+
const record = run;
|
|
1957
|
+
if (!record.transcripts || typeof record.transcripts !== "object" || Array.isArray(record.transcripts)) return run;
|
|
1958
|
+
const transcripts = {};
|
|
1959
|
+
for (const [id, entry] of Object.entries(record.transcripts)) {
|
|
1960
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
1961
|
+
transcripts[id] = entry;
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
const value = entry;
|
|
1965
|
+
if (typeof value.revision !== "number" || !Array.isArray(value.timing)) {
|
|
1966
|
+
transcripts[id] = entry;
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
if (sent.get(`${prefix} ${id}`) === value.revision) {
|
|
1970
|
+
const rest = { ...value };
|
|
1971
|
+
delete rest.timing;
|
|
1972
|
+
transcripts[id] = rest;
|
|
1973
|
+
continue;
|
|
1974
|
+
}
|
|
1975
|
+
mark(`${prefix} ${id}`, value.revision);
|
|
1976
|
+
transcripts[id] = entry;
|
|
1977
|
+
}
|
|
1978
|
+
return { ...record, transcripts };
|
|
1979
|
+
}
|
|
1980
|
+
function forgetTiming(run, prefix, sent) {
|
|
1981
|
+
if (!run || typeof run !== "object" || Array.isArray(run)) return;
|
|
1982
|
+
const record = run;
|
|
1983
|
+
if (!record.transcripts || typeof record.transcripts !== "object" || Array.isArray(record.transcripts)) return;
|
|
1984
|
+
for (const id of Object.keys(record.transcripts)) sent.delete(`${prefix} ${id}`);
|
|
1985
|
+
}
|
|
1986
|
+
function focusedState(state, focus, sent, mark = () => {
|
|
1987
|
+
}) {
|
|
1988
|
+
return {
|
|
1989
|
+
...state,
|
|
1990
|
+
publishers: state.publishers.map((publisher) => {
|
|
1991
|
+
if (!publisher || typeof publisher !== "object" || Array.isArray(publisher)) return publisher;
|
|
1992
|
+
const value = publisher;
|
|
1993
|
+
const runs = Array.isArray(value.runs) ? value.runs : void 0;
|
|
1994
|
+
if (!runs) return publisher;
|
|
1995
|
+
const focusedPublisher = focus !== void 0 && value.id === focus.publisherId;
|
|
1996
|
+
return { ...value, runs: runs.map((run) => {
|
|
1997
|
+
const id = run && typeof run === "object" && !Array.isArray(run) ? run.run?.id : void 0;
|
|
1998
|
+
const prefix = `${String(value.id)} ${String(id)}`;
|
|
1999
|
+
if (!focusedPublisher || id !== focus.runId) {
|
|
2000
|
+
if (sent) forgetTiming(run, prefix, sent);
|
|
2001
|
+
return withoutTiming(run);
|
|
2002
|
+
}
|
|
2003
|
+
return sent ? knownTiming(run, prefix, sent, mark) : run;
|
|
2004
|
+
}) };
|
|
2005
|
+
})
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
1946
2008
|
function compactPublishers(publishers) {
|
|
1947
2009
|
return publishers.map((publisher) => {
|
|
1948
2010
|
if (!publisher || typeof publisher !== "object" || Array.isArray(publisher)) return publisher;
|
|
@@ -1981,7 +2043,7 @@ function minimalPublisher(publisher) {
|
|
|
1981
2043
|
function tinyPublisher(publisher) {
|
|
1982
2044
|
if (!publisher || typeof publisher !== "object" || Array.isArray(publisher)) return { id: "publisher" };
|
|
1983
2045
|
const value = publisher;
|
|
1984
|
-
return { id: typeof value.id === "string" ? value.id.slice(0, 200) : "publisher", title: typeof value.title === "string" ? value.title.slice(0, 200) : void 0, cwd: typeof value.cwd === "string" ? value.cwd.slice(0, 1024) : void 0, sessionId: typeof value.sessionId === "string" ? value.sessionId.slice(0, 200) : void 0,
|
|
2046
|
+
return { id: typeof value.id === "string" ? value.id.slice(0, 200) : "publisher", title: typeof value.title === "string" ? value.title.slice(0, 200) : void 0, cwd: typeof value.cwd === "string" ? value.cwd.slice(0, 1024) : void 0, sessionId: typeof value.sessionId === "string" ? value.sessionId.slice(0, 200) : void 0, connected: value.connected === true };
|
|
1985
2047
|
}
|
|
1986
2048
|
function encodePublisherList(publishers, updatedAt, maxBytes, initial, truncated, projector, fallback) {
|
|
1987
2049
|
const prefix = `{"type":"state","publishers":[`;
|
|
@@ -2062,14 +2124,19 @@ function frame(payload, maxBytes) {
|
|
|
2062
2124
|
function isState(value) {
|
|
2063
2125
|
return Boolean(value && typeof value === "object" && !Array.isArray(value) && value.type === "state");
|
|
2064
2126
|
}
|
|
2065
|
-
function writeFrame(client, packet, state) {
|
|
2127
|
+
function writeFrame(client, packet, state, onDelivered) {
|
|
2066
2128
|
if (state && client.backpressured) {
|
|
2067
2129
|
client.pendingState = packet;
|
|
2130
|
+
client.pendingDeliver = onDelivered;
|
|
2068
2131
|
return;
|
|
2069
2132
|
}
|
|
2070
2133
|
try {
|
|
2071
|
-
if (state)
|
|
2134
|
+
if (state) {
|
|
2135
|
+
client.pendingState = void 0;
|
|
2136
|
+
client.pendingDeliver = void 0;
|
|
2137
|
+
}
|
|
2072
2138
|
if (!client.socket.write(packet)) client.backpressured = true;
|
|
2139
|
+
onDelivered?.();
|
|
2073
2140
|
} catch {
|
|
2074
2141
|
client.socket.destroy();
|
|
2075
2142
|
}
|
|
@@ -2145,6 +2212,7 @@ function authorized(request, port) {
|
|
|
2145
2212
|
function createTrajectoryServer(port, lockPath, options = {}) {
|
|
2146
2213
|
const maxFrameBytes = options.maxFrameBytes ?? MAX_FRAME_BYTES;
|
|
2147
2214
|
const serverFingerprint = options.fingerprint ?? "";
|
|
2215
|
+
let startedAt = 0;
|
|
2148
2216
|
const clients = /* @__PURE__ */ new Set();
|
|
2149
2217
|
const publishers = /* @__PURE__ */ new Map();
|
|
2150
2218
|
let latest = { type: "state", publishers: [], updatedAt: Date.now(), initial: true };
|
|
@@ -2216,8 +2284,21 @@ function createTrajectoryServer(port, lockPath, options = {}) {
|
|
|
2216
2284
|
const broadcast = (value) => {
|
|
2217
2285
|
if (isState(value)) {
|
|
2218
2286
|
try {
|
|
2219
|
-
|
|
2220
|
-
for (const client of clients)
|
|
2287
|
+
let shared;
|
|
2288
|
+
for (const client of clients) {
|
|
2289
|
+
if (client.kind !== "browser") continue;
|
|
2290
|
+
if (client.focusAware !== true) {
|
|
2291
|
+
shared ??= frame(encodeState(value, maxFrameBytes), maxFrameBytes);
|
|
2292
|
+
writeFrame(client, shared, true);
|
|
2293
|
+
continue;
|
|
2294
|
+
}
|
|
2295
|
+
const sent = client.sentTiming ??= /* @__PURE__ */ new Map();
|
|
2296
|
+
const marks = [];
|
|
2297
|
+
const stateFrame = frame(encodeState(focusedState(value, client.focus, sent, (key, revision) => marks.push([key, revision])), maxFrameBytes), maxFrameBytes);
|
|
2298
|
+
writeFrame(client, stateFrame, true, () => {
|
|
2299
|
+
for (const [key, revision] of marks) sent.set(key, revision);
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2221
2302
|
} catch {
|
|
2222
2303
|
for (const client of clients) if (client.kind === "browser") client.socket.destroy();
|
|
2223
2304
|
}
|
|
@@ -2225,6 +2306,13 @@ function createTrajectoryServer(port, lockPath, options = {}) {
|
|
|
2225
2306
|
}
|
|
2226
2307
|
for (const client of clients) if (client.kind === "browser") emit(client, value);
|
|
2227
2308
|
};
|
|
2309
|
+
let viewers = 0;
|
|
2310
|
+
const notifyViewers = () => {
|
|
2311
|
+
const count = [...clients].filter((candidate) => candidate.kind === "browser").length;
|
|
2312
|
+
if (count === viewers) return;
|
|
2313
|
+
viewers = count;
|
|
2314
|
+
for (const candidate of clients) if (candidate.kind === "publisher") emit(candidate, { type: "publisher:viewers", count });
|
|
2315
|
+
};
|
|
2228
2316
|
const publishState = () => {
|
|
2229
2317
|
const values = [...publishers.values()].map(({ value, generation }) => ({ ...value, generation }));
|
|
2230
2318
|
latest = { type: "state", publishers: values, updatedAt: Date.now() };
|
|
@@ -2233,6 +2321,7 @@ function createTrajectoryServer(port, lockPath, options = {}) {
|
|
|
2233
2321
|
const disconnect = (client) => {
|
|
2234
2322
|
clearPending(client, client.kind === "publisher" ? "Publisher is disconnected" : "Trajectory browser disconnected");
|
|
2235
2323
|
clients.delete(client);
|
|
2324
|
+
if (client.kind === "browser") notifyViewers();
|
|
2236
2325
|
if (client.kind === "publisher" && client.publisherId && publishers.get(client.publisherId)?.client === client) {
|
|
2237
2326
|
publishers.delete(client.publisherId);
|
|
2238
2327
|
publishState();
|
|
@@ -2259,6 +2348,26 @@ function createTrajectoryServer(port, lockPath, options = {}) {
|
|
|
2259
2348
|
if (message.type === "ui:attach") {
|
|
2260
2349
|
client.kind = "browser";
|
|
2261
2350
|
emit(client, latest);
|
|
2351
|
+
notifyViewers();
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
if (message.type === "ui:focus" && client.kind === "browser") {
|
|
2355
|
+
const publisherId = typeof message.publisherId === "string" ? message.publisherId : void 0;
|
|
2356
|
+
const runId = typeof message.runId === "string" ? message.runId : void 0;
|
|
2357
|
+
client.focusAware = true;
|
|
2358
|
+
client.sentTiming = /* @__PURE__ */ new Map();
|
|
2359
|
+
if (publisherId !== void 0 && runId !== void 0) client.focus = { publisherId, runId };
|
|
2360
|
+
else delete client.focus;
|
|
2361
|
+
const sent = client.sentTiming;
|
|
2362
|
+
const marks = [];
|
|
2363
|
+
const focused = focusedState(latest, client.focus, sent, (key, revision) => marks.push([key, revision]));
|
|
2364
|
+
try {
|
|
2365
|
+
writeFrame(client, frame(encodeState(focused, maxFrameBytes), maxFrameBytes), true, () => {
|
|
2366
|
+
for (const [key, revision] of marks) sent.set(key, revision);
|
|
2367
|
+
});
|
|
2368
|
+
} catch {
|
|
2369
|
+
client.socket.destroy();
|
|
2370
|
+
}
|
|
2262
2371
|
return;
|
|
2263
2372
|
}
|
|
2264
2373
|
if (client.kind === "publisher" && message.type === "publisher:attach" && typeof message.publisherId === "string") {
|
|
@@ -2273,6 +2382,7 @@ function createTrajectoryServer(port, lockPath, options = {}) {
|
|
|
2273
2382
|
}
|
|
2274
2383
|
const generation = (previous?.generation ?? 0) + 1;
|
|
2275
2384
|
publishers.set(message.publisherId, { client, value: { ...previous?.value ?? { id: message.publisherId }, id: message.publisherId, connected: true }, generation });
|
|
2385
|
+
emit(client, { type: "publisher:viewers", count: viewers });
|
|
2276
2386
|
publishState();
|
|
2277
2387
|
return;
|
|
2278
2388
|
}
|
|
@@ -2409,7 +2519,7 @@ function createTrajectoryServer(port, lockPath, options = {}) {
|
|
|
2409
2519
|
}
|
|
2410
2520
|
const path2 = url.pathname;
|
|
2411
2521
|
if (request.method === "GET" && path2 === "/health") {
|
|
2412
|
-
writeJson(response, 200, { ok: true });
|
|
2522
|
+
writeJson(response, 200, { ok: true, pid: process.pid, fingerprint: serverFingerprint, startedAt });
|
|
2413
2523
|
return;
|
|
2414
2524
|
}
|
|
2415
2525
|
if (request.method === "GET" && (path2 === "/" || path2 === "/index.html")) {
|
|
@@ -2439,6 +2549,15 @@ function createTrajectoryServer(port, lockPath, options = {}) {
|
|
|
2439
2549
|
});
|
|
2440
2550
|
return;
|
|
2441
2551
|
}
|
|
2552
|
+
if (request.method === "GET" && path2 === "/prism.min.js") {
|
|
2553
|
+
void readFile(new URL("./assets/prism.min.js", import.meta.url)).then((script) => {
|
|
2554
|
+
response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "content-length": script.byteLength, "cache-control": "no-store" });
|
|
2555
|
+
response.end(script);
|
|
2556
|
+
}).catch(() => {
|
|
2557
|
+
writeJson(response, 500, { error: "Trajectory syntax highlighting library is unavailable" });
|
|
2558
|
+
});
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2442
2561
|
if (request.method === "GET" && (path2 === "/favicon.png" || path2 === "/favicon.ico")) {
|
|
2443
2562
|
void readFile(new URL("./assets/favicon.png", import.meta.url)).then((icon) => {
|
|
2444
2563
|
response.writeHead(200, { "content-type": "image/png", "content-length": icon.byteLength, "cache-control": "no-store" });
|
|
@@ -2470,12 +2589,14 @@ Connection: Upgrade\r
|
|
|
2470
2589
|
Sec-WebSocket-Accept: ${accept}\r
|
|
2471
2590
|
\r
|
|
2472
2591
|
`);
|
|
2473
|
-
const client = { socket, kind: "publisher", buffer: Buffer.alloc(0), pendingState: void 0, backpressured: false, superseded: false };
|
|
2592
|
+
const client = { socket, kind: "publisher", buffer: Buffer.alloc(0), pendingState: void 0, pendingDeliver: void 0, backpressured: false, superseded: false };
|
|
2474
2593
|
socket.on("drain", () => {
|
|
2475
2594
|
const pendingState = client.pendingState;
|
|
2595
|
+
const pendingDeliver = client.pendingDeliver;
|
|
2476
2596
|
client.pendingState = void 0;
|
|
2597
|
+
client.pendingDeliver = void 0;
|
|
2477
2598
|
client.backpressured = false;
|
|
2478
|
-
if (pendingState !== void 0) writeFrame(client, pendingState, true);
|
|
2599
|
+
if (pendingState !== void 0) writeFrame(client, pendingState, true, pendingDeliver);
|
|
2479
2600
|
});
|
|
2480
2601
|
clients.add(client);
|
|
2481
2602
|
socket.on("data", (chunk) => {
|
|
@@ -2492,9 +2613,13 @@ Sec-WebSocket-Accept: ${accept}\r
|
|
|
2492
2613
|
socket.on("error", () => {
|
|
2493
2614
|
disconnect(client);
|
|
2494
2615
|
});
|
|
2616
|
+
socket.on("end", () => {
|
|
2617
|
+
socket.destroy();
|
|
2618
|
+
});
|
|
2495
2619
|
});
|
|
2496
2620
|
server.once("listening", () => {
|
|
2497
|
-
|
|
2621
|
+
startedAt = Date.now();
|
|
2622
|
+
void writeFile(lockPath, `${JSON.stringify({ pid: process.pid, port, fingerprint: serverFingerprint, startedAt })}
|
|
2498
2623
|
`, { mode: 384 }).catch(() => {
|
|
2499
2624
|
process.exitCode = 1;
|
|
2500
2625
|
});
|