@cef-ai/cli 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -1247
- package/dist/build/duration.d.ts +22 -0
- package/dist/build/duration.d.ts.map +1 -0
- package/dist/build/duration.js +44 -0
- package/dist/build/duration.js.map +1 -0
- package/dist/build/esbuild-plugin.d.ts +32 -0
- package/dist/build/esbuild-plugin.d.ts.map +1 -0
- package/dist/build/esbuild-plugin.js +53 -0
- package/dist/build/esbuild-plugin.js.map +1 -0
- package/dist/build/lints.d.ts +55 -0
- package/dist/build/lints.d.ts.map +1 -0
- package/dist/build/lints.js +214 -0
- package/dist/build/lints.js.map +1 -0
- package/dist/build/load-config.d.ts +114 -0
- package/dist/build/load-config.d.ts.map +1 -0
- package/dist/build/load-config.js +150 -0
- package/dist/build/load-config.js.map +1 -0
- package/dist/build/manifest.d.ts +156 -0
- package/dist/build/manifest.d.ts.map +1 -0
- package/dist/build/manifest.js +111 -0
- package/dist/build/manifest.js.map +1 -0
- package/dist/build/targeting.d.ts +15 -0
- package/dist/build/targeting.d.ts.map +1 -0
- package/dist/build/targeting.js +69 -0
- package/dist/build/targeting.js.map +1 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +107 -62
- package/dist/cli.js.map +1 -0
- package/dist/commands/build.d.ts +41 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +252 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/inspect.d.ts +49 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +135 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/typegen.d.ts +36 -0
- package/dist/commands/typegen.d.ts.map +1 -0
- package/dist/commands/typegen.js +210 -0
- package/dist/commands/typegen.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +12 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +9 -0
- package/dist/lib.js.map +1 -0
- package/dist/typegen/augment.d.ts +57 -0
- package/dist/typegen/augment.d.ts.map +1 -0
- package/dist/typegen/augment.js +87 -0
- package/dist/typegen/augment.js.map +1 -0
- package/dist/typegen/fetch.d.ts +30 -0
- package/dist/typegen/fetch.d.ts.map +1 -0
- package/dist/typegen/fetch.js +38 -0
- package/dist/typegen/fetch.js.map +1 -0
- package/dist/typegen/lockfile.d.ts +42 -0
- package/dist/typegen/lockfile.d.ts.map +1 -0
- package/dist/typegen/lockfile.js +58 -0
- package/dist/typegen/lockfile.js.map +1 -0
- package/dist/typegen/schema-to-ts.d.ts +21 -0
- package/dist/typegen/schema-to-ts.d.ts.map +1 -0
- package/dist/typegen/schema-to-ts.js +29 -0
- package/dist/typegen/schema-to-ts.js.map +1 -0
- package/package.json +20 -58
- package/dist/commands/auth/index.d.ts +0 -2
- package/dist/commands/auth/index.js +0 -162
- package/dist/commands/deploy/clone.d.ts +0 -13
- package/dist/commands/deploy/clone.js +0 -143
- package/dist/commands/deploy/delete.d.ts +0 -14
- package/dist/commands/deploy/delete.js +0 -134
- package/dist/commands/deploy/deploy.d.ts +0 -24
- package/dist/commands/deploy/deploy.js +0 -654
- package/dist/commands/deploy/index.d.ts +0 -2
- package/dist/commands/deploy/index.js +0 -216
- package/dist/commands/deploy/status.d.ts +0 -14
- package/dist/commands/deploy/status.js +0 -61
- package/dist/commands/dev/index.d.ts +0 -2
- package/dist/commands/dev/index.js +0 -134
- package/dist/commands/dev/server.d.ts +0 -14
- package/dist/commands/dev/server.js +0 -195
- package/dist/commands/dev/static/app.js +0 -633
- package/dist/commands/dev/static/index.html +0 -205
- package/dist/commands/dev/static/static/app.js +0 -633
- package/dist/commands/dev/static/static/index.html +0 -205
- package/dist/commands/dev/static/static/style.css +0 -184
- package/dist/commands/dev/static/style.css +0 -184
- package/dist/commands/playground/index.d.ts +0 -2
- package/dist/commands/playground/index.js +0 -75
- package/dist/commands/playground/test.d.ts +0 -15
- package/dist/commands/playground/test.js +0 -166
- package/dist/commands/service/index.d.ts +0 -2
- package/dist/commands/service/index.js +0 -75
- package/dist/lib/api.d.ts +0 -146
- package/dist/lib/api.js +0 -330
- package/dist/lib/config.d.ts +0 -12
- package/dist/lib/config.js +0 -145
- package/dist/lib/credentials.d.ts +0 -19
- package/dist/lib/credentials.js +0 -68
- package/dist/lib/runtime/agent-router.d.ts +0 -12
- package/dist/lib/runtime/agent-router.js +0 -67
- package/dist/lib/runtime/context-builder.d.ts +0 -27
- package/dist/lib/runtime/context-builder.js +0 -166
- package/dist/lib/runtime/cubby-manager.d.ts +0 -20
- package/dist/lib/runtime/cubby-manager.js +0 -162
- package/dist/lib/runtime/index.d.ts +0 -51
- package/dist/lib/runtime/index.js +0 -181
- package/dist/lib/runtime/log-collector.d.ts +0 -17
- package/dist/lib/runtime/log-collector.js +0 -61
- package/dist/lib/runtime/stream-manager.d.ts +0 -10
- package/dist/lib/runtime/stream-manager.js +0 -77
- package/dist/lib/runtime/types.d.ts +0 -76
- package/dist/lib/runtime/types.js +0 -2
- package/dist/lib/transpile.d.ts +0 -1
- package/dist/lib/transpile.js +0 -9
- package/dist/lib/typecheck.d.ts +0 -31
- package/dist/lib/typecheck.js +0 -175
- package/dist/lib/types.d.ts +0 -246
- package/dist/lib/types.js +0 -2
- package/dist/lib/utils.d.ts +0 -4
- package/dist/lib/utils.js +0 -33
- package/example/.env.example +0 -6
- package/example/agents/object-detection/tasks/yolo.ts +0 -28
- package/example/agents/parking-violation-detector/tasks/detect.ts +0 -326
- package/example/cef.config.yaml +0 -91
- package/example/engagements/engagement.ts +0 -902
|
@@ -1,633 +0,0 @@
|
|
|
1
|
-
(function(){
|
|
2
|
-
const API='';
|
|
3
|
-
let topology=null,activeEngagement=null,seqNum=0,ws=null,logFilter='',blastPayloads=null,blastAbort=false,batchRunning=false,batchAbort=false;
|
|
4
|
-
|
|
5
|
-
async function api(method,path,body){
|
|
6
|
-
const opts={method,headers:{'Content-Type':'application/json'}};
|
|
7
|
-
if(body!==undefined)opts.body=JSON.stringify(body);
|
|
8
|
-
const res=await fetch(API+path,opts);return res.json();
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// WebSocket
|
|
12
|
-
function connectWs(){
|
|
13
|
-
const proto=location.protocol==='https:'?'wss:':'ws:';
|
|
14
|
-
ws=new WebSocket(proto+'//'+location.host+'/ws');
|
|
15
|
-
ws.onopen=()=>{$('status-dot').className='status-dot ok';$('status-pill').innerHTML='<span class="status-dot ok" id="status-dot"></span>'+location.host};
|
|
16
|
-
ws.onclose=()=>{$('status-dot').className='status-dot err';$('status-pill').innerHTML='<span class="status-dot err" id="status-dot"></span>reconnecting';setTimeout(connectWs,2000)};
|
|
17
|
-
ws.onmessage=(e)=>{const m=JSON.parse(e.data);if(m.type==='log')appendLog(m.data);if(m.type==='trace')appendTrace(m.data)};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Logs
|
|
21
|
-
function appendLog(entry){
|
|
22
|
-
const c=$('log-container'),d=document.createElement('div');
|
|
23
|
-
d.className='log-line'+(entry.level!=='log'?' '+entry.level:'');
|
|
24
|
-
const ts=new Date(entry.timestamp).toISOString().slice(11,23);
|
|
25
|
-
const ag=entry.agentAlias?(entry.agentAlias+(entry.taskAlias?'.'+entry.taskAlias:'')):'';
|
|
26
|
-
const pf=ag?'<span class="agent">['+ag+']</span> ':'';
|
|
27
|
-
const args=entry.args.map(a=>typeof a==='string'?a:JSON.stringify(a)).join(' ');
|
|
28
|
-
d.innerHTML='<span class="ts">'+ts+'</span>'+pf+esc(args);
|
|
29
|
-
if(logFilter&&!args.toLowerCase().includes(logFilter))d.style.display='none';
|
|
30
|
-
c.appendChild(d);while(c.children.length>500)c.removeChild(c.firstChild);
|
|
31
|
-
c.scrollTop=c.scrollHeight;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Flow
|
|
35
|
-
function appendTrace(ev){
|
|
36
|
-
const c=$('flow-container'),d=document.createElement('div');
|
|
37
|
-
d.className='flow-entry';
|
|
38
|
-
const ts=new Date(ev.timestamp).toISOString().slice(11,23);
|
|
39
|
-
let ind='',det='',cls='';
|
|
40
|
-
switch(ev.type){
|
|
41
|
-
case'engagement:start':cls='fe-eng';det=ev.callee||'';break;
|
|
42
|
-
case'agent:call':ind=' ';cls='fe-agent';det=ev.callee+(ev.caller?' <span class="fe-dim">\u2190 '+esc(ev.caller)+'</span>':'');break;
|
|
43
|
-
case'agent:return':ind=' ';cls=ev.success?'fe-ok':'fe-err';det=ev.callee+' <span class="fe-dim">'+(ev.durationMs||0).toFixed(1)+'ms</span>'+(ev.success?'':' <span class="fe-err">'+esc(ev.error||'FAILED')+'</span>');break;
|
|
44
|
-
case'cubby:query':ind=' ';cls='fe-cubby';det=(ev.cubbyAlias||'')+' <span class="fe-dim">'+esc(ev.sql||'')+'</span>';break;
|
|
45
|
-
case'cubby:exec':ind=' ';cls='fe-cubby';det=(ev.cubbyAlias||'')+' <span class="fe-dim">'+esc(ev.sql||'')+'</span>'+(ev.rowsAffected!=null?' <span class="fe-dim">('+ev.rowsAffected+')</span>':'');break;
|
|
46
|
-
}
|
|
47
|
-
d.innerHTML=ind+'<span class="fe-time">'+ts+'</span> <span class="fe-type '+cls+'">'+esc(ev.type)+'</span> '+det;
|
|
48
|
-
c.appendChild(d);c.scrollTop=c.scrollHeight;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Sidebar
|
|
52
|
-
async function loadTopology(){topology=await api('GET','/api/topology');renderSidebar();updateEmptyStats()}
|
|
53
|
-
function updateEmptyStats(){
|
|
54
|
-
if(!topology)return;
|
|
55
|
-
var el=$('empty-stats');if(!el)return;
|
|
56
|
-
el.innerHTML=
|
|
57
|
-
'<div class="stat-card"><div class="stat-num">'+topology.engagements.length+'</div><div class="stat-label">Engagements</div></div>'+
|
|
58
|
-
'<div class="stat-card"><div class="stat-num">'+topology.agents.reduce(function(s,a){return s+a.tasks.length},0)+'</div><div class="stat-label">Agent Tasks</div></div>'+
|
|
59
|
-
'<div class="stat-card"><div class="stat-num">'+topology.cubbies.length+'</div><div class="stat-label">Cubbies</div></div>';
|
|
60
|
-
}
|
|
61
|
-
function renderSidebar(){
|
|
62
|
-
const el=$('sidebar');let h='';
|
|
63
|
-
if(topology.engagements.length){
|
|
64
|
-
h+='<div class="s-label">▶ Engagements</div>';
|
|
65
|
-
for(const e of topology.engagements)h+='<div class="s-item" data-type="engagement" data-name="'+ea(e.name)+'"><span class="s-dot s-dot-eng"></span>'+esc(e.name)+'</div>';
|
|
66
|
-
}
|
|
67
|
-
if(topology.agents.length){
|
|
68
|
-
h+='<div class="s-label">⚙ Agents</div>';
|
|
69
|
-
for(const a of topology.agents)for(const t of a.tasks)
|
|
70
|
-
h+='<div class="s-item" data-type="agent" data-alias="'+ea(a.alias)+'" data-task="'+ea(t.alias)+'"><span class="s-dot s-dot-agent"></span>'+esc(a.alias)+'.<span class="s-task">'+esc(t.alias)+'</span></div>';
|
|
71
|
-
}
|
|
72
|
-
if(topology.cubbies.length){
|
|
73
|
-
h+='<div class="s-label">▤ Cubbies</div>';
|
|
74
|
-
for(const c of topology.cubbies)h+='<div class="s-item" data-type="cubby" data-alias="'+ea(c.alias)+'"><span class="s-dot s-dot-cubby"></span>'+esc(c.alias)+'</div>';
|
|
75
|
-
const graphCubbies=topology.cubbies.filter(c=>c.alias);
|
|
76
|
-
if(graphCubbies.length)h+='<div class="s-item" data-type="graph-viz" style="margin-top:4px"><span class="s-dot" style="background:#22c55e"></span> Graph Visualizer</div>';
|
|
77
|
-
}
|
|
78
|
-
el.innerHTML=h;
|
|
79
|
-
el.querySelectorAll('.s-item').forEach(item=>{
|
|
80
|
-
item.addEventListener('click',()=>{
|
|
81
|
-
el.querySelectorAll('.s-item').forEach(i=>i.classList.remove('active'));
|
|
82
|
-
item.classList.add('active');
|
|
83
|
-
const t=item.dataset.type;
|
|
84
|
-
if(t==='engagement')showEngagement(item.dataset.name);
|
|
85
|
-
else if(t==='agent')showAgent(item.dataset.alias,item.dataset.task);
|
|
86
|
-
else if(t==='cubby')showCubby(item.dataset.alias);
|
|
87
|
-
else if(t==='graph-viz'){const gc=topology.cubbies[0];if(gc){showCubby(gc.alias);setTimeout(()=>{const tab=document.querySelector('#panel-cubby .tab[data-tab="graph"]');if(tab)tab.click();},50);}}
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Panels
|
|
93
|
-
function hideAll(){document.querySelectorAll('.panel').forEach(p=>p.classList.remove('active'));$('empty-state').style.display='none'}
|
|
94
|
-
|
|
95
|
-
function showEngagement(name){
|
|
96
|
-
hideAll();$('panel-engagement').classList.add('active');
|
|
97
|
-
$('eng-name').textContent=name;$('eng-result').innerHTML='';
|
|
98
|
-
const ec=topology.engagements.find(e=>e.name===name),params=ec?.parameters;
|
|
99
|
-
$('eng-desc').textContent=ec?.description||'';
|
|
100
|
-
if(params&¶ms.properties){
|
|
101
|
-
const s=schemaToSample(params);
|
|
102
|
-
if('streamId' in s&&!s.streamId)s.streamId=name.toLowerCase().replace(/\s+/g,'-')+'-stream';
|
|
103
|
-
$('eng-payload').value=JSON.stringify(s,null,2);
|
|
104
|
-
}else{$('eng-payload').value=JSON.stringify({streamId:name.toLowerCase().replace(/\s+/g,'-')+'-stream'},null,2)}
|
|
105
|
-
$('eng-schema-hints').innerHTML=renderSchemaHints(params);
|
|
106
|
-
if(activeEngagement&&activeEngagement.name===name){$('eng-stream-area').style.display='';$('eng-stop').style.display='';$('eng-start').style.display='none'}
|
|
107
|
-
else{$('eng-stream-area').style.display='none';$('eng-stop').style.display='none';$('eng-start').style.display=''}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function showAgent(alias,task){
|
|
111
|
-
hideAll();$('panel-agent').classList.add('active');
|
|
112
|
-
const lbl=$('agent-label');lbl.textContent=alias+'.'+task;lbl.dataset.alias=alias;lbl.dataset.task=task;
|
|
113
|
-
$('agent-result').innerHTML='';
|
|
114
|
-
const ac=topology.agents.find(a=>a.alias===alias),tc=ac?.tasks.find(t=>t.alias===task);
|
|
115
|
-
const params=tc?.parameters,returns=tc?.returns;
|
|
116
|
-
$('agent-desc').textContent=tc?.description||ac?.description||'';
|
|
117
|
-
$('agent-payload').value=params&¶ms.properties?JSON.stringify(schemaToSample(params),null,2):'{}';
|
|
118
|
-
$('agent-schema-hints').innerHTML=renderSchemaHints(params,returns);
|
|
119
|
-
var bb=$('agent-batch');
|
|
120
|
-
if(tc&&tc.batchSql){bb.style.display='';bb.dataset.batchSql=tc.batchSql;bb.dataset.batchCubby=tc.batchCubby||'';}
|
|
121
|
-
else{bb.style.display='none';}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function showCubby(alias){
|
|
125
|
-
hideAll();$('panel-cubby').classList.add('active');
|
|
126
|
-
const lbl=$('cubby-label');lbl.textContent=alias;lbl.dataset.alias=alias;
|
|
127
|
-
document.querySelectorAll('#panel-cubby .tab').forEach(t=>t.classList.remove('active'));
|
|
128
|
-
document.querySelector('#panel-cubby .tab[data-tab="browse"]').classList.add('active');
|
|
129
|
-
$('cubby-browse').style.display='';$('cubby-sql').style.display='none';$('cubby-graph').style.display='none';
|
|
130
|
-
$('graph-tab').style.display='';
|
|
131
|
-
$('panel-cubby').classList.add('panel-wide');
|
|
132
|
-
qtRefreshSelect(alias);
|
|
133
|
-
loadCubbyBrowse(alias);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function renderSchemaHints(params,returns){
|
|
137
|
-
const hp=params&¶ms.properties&&Object.keys(params.properties).length>0;
|
|
138
|
-
const hr=returns&&returns.properties&&Object.keys(returns.properties).length>0;
|
|
139
|
-
if(!hp&&!hr)return'';
|
|
140
|
-
let h='<div class="schema-pair">';
|
|
141
|
-
if(hp){
|
|
142
|
-
h+='<div class="schema-card"><div class="st-title">Input</div>';
|
|
143
|
-
for(const[k,v]of Object.entries(params.properties)){
|
|
144
|
-
const req=(params.required||[]).includes(k)?'<span class="sr">required</span>':'';
|
|
145
|
-
h+='<div class="schema-row"><span class="sk">'+esc(k)+'</span><span class="st">'+esc(v.type||'any')+'</span>'+req;
|
|
146
|
-
if(v.description)h+=' <span class="sd">'+esc(v.description)+'</span>';
|
|
147
|
-
h+='</div>';
|
|
148
|
-
}
|
|
149
|
-
h+='</div>';
|
|
150
|
-
}
|
|
151
|
-
if(hr){
|
|
152
|
-
h+='<div class="schema-card"><div class="st-title">Output</div>';
|
|
153
|
-
for(const[k,v]of Object.entries(returns.properties))
|
|
154
|
-
h+='<div class="schema-row"><span class="sk">'+esc(k)+'</span><span class="st">'+esc(v.type||'any')+'</span></div>';
|
|
155
|
-
h+='</div>';
|
|
156
|
-
}
|
|
157
|
-
return h+'</div>';
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function schemaToSample(s){
|
|
161
|
-
if(!s||!s.properties)return{};const o={};
|
|
162
|
-
for(const[k,v]of Object.entries(s.properties)){
|
|
163
|
-
if(v.type==='string')o[k]='';else if(v.type==='number')o[k]=0;
|
|
164
|
-
else if(v.type==='boolean')o[k]=false;else if(v.type==='array')o[k]=[];
|
|
165
|
-
else if(v.type==='object')o[k]={};else o[k]=null;
|
|
166
|
-
}return o;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Engagement actions
|
|
170
|
-
$('eng-file-blast').addEventListener('change',function(){
|
|
171
|
-
if(!this.files||!this.files[0]){blastPayloads=null;return;}
|
|
172
|
-
var reader=new FileReader();
|
|
173
|
-
reader.onload=function(e){
|
|
174
|
-
try{
|
|
175
|
-
var parsed=JSON.parse(e.target.result);
|
|
176
|
-
blastPayloads=Array.isArray(parsed)?parsed:[parsed];
|
|
177
|
-
$('eng-result').innerHTML='<div class="success-banner">Loaded '+blastPayloads.length+' payloads from file</div>';
|
|
178
|
-
$('eng-repeat').value=''+blastPayloads.length;
|
|
179
|
-
}catch(err){blastPayloads=null;$('eng-result').innerHTML='<div class="error-banner">Invalid JSON: '+esc(err.message||String(err))+'</div>';}
|
|
180
|
-
};
|
|
181
|
-
reader.readAsText(this.files[0]);
|
|
182
|
-
});
|
|
183
|
-
$('eng-start').addEventListener('click',async function(){
|
|
184
|
-
const name=$('eng-name').textContent;
|
|
185
|
-
const delay=parseInt($('eng-delay').value)||0;
|
|
186
|
-
let payloads;
|
|
187
|
-
if(blastPayloads){
|
|
188
|
-
payloads=blastPayloads;
|
|
189
|
-
}else{
|
|
190
|
-
let base;try{base=JSON.parse($('eng-payload').value)}catch{base={}}
|
|
191
|
-
const n=Math.max(1,parseInt($('eng-repeat').value)||1);
|
|
192
|
-
payloads=Array.from({length:n},function(){return Object.assign({},base);});
|
|
193
|
-
}
|
|
194
|
-
blastAbort=false;
|
|
195
|
-
this.style.display='none';$('eng-stop').style.display='';
|
|
196
|
-
let ok=0,errors=0;
|
|
197
|
-
for(var i=0;i<payloads.length;i++){
|
|
198
|
-
if(blastAbort)break;
|
|
199
|
-
var payload=Object.assign({},payloads[i]);
|
|
200
|
-
payload.streamId=name.toLowerCase().replace(/\s+/g,'-')+'-'+i+'-'+Date.now();
|
|
201
|
-
$('eng-result').innerHTML='<div class="success-banner">Firing '+(i+1)+'/'+payloads.length+'...</div>';
|
|
202
|
-
try{
|
|
203
|
-
const r=await api('POST','/api/trigger',{engagement:name,payload});
|
|
204
|
-
if(r.error){errors++;}else{ok++;if(payloads.length===1){activeEngagement={executionId:r.executionId,streamId:sid,name};seqNum=0;$('eng-stream-area').style.display='';}}
|
|
205
|
-
}catch(e){errors++;}
|
|
206
|
-
if(delay&&i<payloads.length-1)await new Promise(function(r){setTimeout(r,delay);});
|
|
207
|
-
}
|
|
208
|
-
var msg=blastAbort?'Aborted after '+(i)+'/'+payloads.length+' \u2014 '+ok+' ok':'Done \u2014 '+ok+'/'+payloads.length+' ok'+(errors?' ('+errors+' errors)':'');
|
|
209
|
-
$('eng-result').innerHTML='<div class="'+(errors||blastAbort?'error-banner':'success-banner')+'">'+esc(msg)+'</div>';
|
|
210
|
-
$('eng-stop').style.display='none';this.style.display='';
|
|
211
|
-
});
|
|
212
|
-
$('eng-stop').addEventListener('click',function(){
|
|
213
|
-
blastAbort=true;activeEngagement=null;$('eng-stream-area').style.display='none';this.style.display='none';
|
|
214
|
-
$('eng-start').style.display='';
|
|
215
|
-
});
|
|
216
|
-
// Stream push tab switching
|
|
217
|
-
document.querySelectorAll('[data-stab]').forEach(tab=>{
|
|
218
|
-
tab.addEventListener('click',()=>{
|
|
219
|
-
document.querySelectorAll('[data-stab]').forEach(t=>t.classList.remove('active'));tab.classList.add('active');
|
|
220
|
-
$('stream-single').style.display=tab.dataset.stab==='single'?'':'none';
|
|
221
|
-
$('stream-bulk').style.display=tab.dataset.stab==='bulk'?'':'none';
|
|
222
|
-
$('stream-file').style.display=tab.dataset.stab==='file'?'':'none';
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Helper: push one packet to active stream
|
|
227
|
-
async function pushOne(payloadStr){
|
|
228
|
-
if(!activeEngagement)return;
|
|
229
|
-
seqNum++;
|
|
230
|
-
await api('POST','/api/streams/'+encodeURIComponent(activeEngagement.streamId)+'/push',{data:{sequenceNum:seqNum,payload:payloadStr}});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Helper: parse lines from text (JSON Lines or JSON array)
|
|
234
|
-
function parseLines(text){
|
|
235
|
-
const trimmed=text.trim();
|
|
236
|
-
if(trimmed.startsWith('[')){try{const arr=JSON.parse(trimmed);if(Array.isArray(arr))return arr.map(o=>JSON.stringify(o))}catch{}}
|
|
237
|
-
return trimmed.split('\n').map(l=>l.trim()).filter(l=>l&&!l.startsWith('//'));
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
let bulkAbort=false;
|
|
241
|
-
|
|
242
|
-
// Helper: send lines with delay and progress
|
|
243
|
-
async function sendLines(lines,delayMs,progressEl,stopBtn,sendBtn){
|
|
244
|
-
bulkAbort=false;stopBtn.style.display='';sendBtn.style.display='none';
|
|
245
|
-
progressEl.innerHTML='<div class="progress-bar"><div class="fill" style="width:0%"></div></div><div class="progress-text">0 / '+lines.length+'</div>';
|
|
246
|
-
const bar=progressEl.querySelector('.fill'),txt=progressEl.querySelector('.progress-text');
|
|
247
|
-
let sent=0;
|
|
248
|
-
for(const line of lines){
|
|
249
|
-
if(bulkAbort)break;
|
|
250
|
-
try{await pushOne(line);sent++}catch(e){progressEl.innerHTML+='<div class="error-banner">Failed at '+sent+': '+esc(e.message)+'</div>';break}
|
|
251
|
-
const pct=Math.round(sent/lines.length*100);
|
|
252
|
-
bar.style.width=pct+'%';txt.textContent=sent+' / '+lines.length;
|
|
253
|
-
if(delayMs>0&&sent<lines.length)await new Promise(r=>setTimeout(r,delayMs));
|
|
254
|
-
}
|
|
255
|
-
stopBtn.style.display='none';sendBtn.style.display='';
|
|
256
|
-
if(!bulkAbort)$('eng-push-result').innerHTML='<div class="success-banner">Sent '+sent+' packets</div>';
|
|
257
|
-
else $('eng-push-result').innerHTML='<div class="success-banner">Stopped after '+sent+' / '+lines.length+'</div>';
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Single push
|
|
261
|
-
$('eng-push').addEventListener('click',async function(){
|
|
262
|
-
if(!activeEngagement)return;this.textContent='...';
|
|
263
|
-
try{await pushOne($('eng-stream-payload').value);
|
|
264
|
-
$('eng-push-result').innerHTML='<div class="success-banner">Pushed <span class="fe-dim">seq '+seqNum+'</span></div>';
|
|
265
|
-
}catch(e){$('eng-push-result').innerHTML='<div class="error-banner">'+esc(e.message)+'</div>'}
|
|
266
|
-
this.textContent='Push';
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
// Bulk send
|
|
270
|
-
$('eng-bulk-send').addEventListener('click',async function(){
|
|
271
|
-
if(!activeEngagement)return;
|
|
272
|
-
const lines=parseLines($('eng-bulk-payload').value);
|
|
273
|
-
if(!lines.length)return;
|
|
274
|
-
const delay=parseInt($('eng-bulk-delay').value)||0;
|
|
275
|
-
await sendLines(lines,delay,$('eng-bulk-progress'),$('eng-bulk-stop'),this);
|
|
276
|
-
});
|
|
277
|
-
$('eng-bulk-stop').addEventListener('click',()=>{bulkAbort=true});
|
|
278
|
-
|
|
279
|
-
// File upload
|
|
280
|
-
let fileLines=[];
|
|
281
|
-
$('file-drop-zone').addEventListener('click',()=>$('eng-file-input').click());
|
|
282
|
-
$('file-drop-zone').addEventListener('dragover',(e)=>{e.preventDefault();e.currentTarget.classList.add('drag-over')});
|
|
283
|
-
$('file-drop-zone').addEventListener('dragleave',(e)=>{e.currentTarget.classList.remove('drag-over')});
|
|
284
|
-
$('file-drop-zone').addEventListener('drop',(e)=>{e.preventDefault();e.currentTarget.classList.remove('drag-over');if(e.dataTransfer.files.length)handleFile(e.dataTransfer.files[0])});
|
|
285
|
-
$('eng-file-input').addEventListener('change',(e)=>{if(e.target.files.length)handleFile(e.target.files[0])});
|
|
286
|
-
|
|
287
|
-
function handleFile(file){
|
|
288
|
-
const reader=new FileReader();
|
|
289
|
-
reader.onload=(e)=>{
|
|
290
|
-
fileLines=parseLines(e.target.result);
|
|
291
|
-
$('file-info').textContent=file.name+' ('+fileLines.length+' events)';
|
|
292
|
-
$('eng-file-send').disabled=false;
|
|
293
|
-
};
|
|
294
|
-
reader.readAsText(file);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
$('eng-file-send').addEventListener('click',async function(){
|
|
298
|
-
if(!activeEngagement||!fileLines.length)return;
|
|
299
|
-
const delay=parseInt($('eng-file-delay').value)||0;
|
|
300
|
-
await sendLines(fileLines,delay,$('eng-file-progress'),$('eng-file-stop'),this);
|
|
301
|
-
});
|
|
302
|
-
$('eng-file-stop').addEventListener('click',()=>{bulkAbort=true});
|
|
303
|
-
|
|
304
|
-
// Agent actions
|
|
305
|
-
$('agent-call').addEventListener('click',async function(){
|
|
306
|
-
const lbl=$('agent-label'),alias=lbl.dataset.alias,task=lbl.dataset.task;
|
|
307
|
-
let payload;try{payload=JSON.parse($('agent-payload').value)}catch{payload={}}
|
|
308
|
-
this.textContent='...';
|
|
309
|
-
try{
|
|
310
|
-
const r=await api('POST','/api/agents/'+alias+'/'+task,{payload});
|
|
311
|
-
if(r.error&&!r.success){$('agent-result').innerHTML='<div class="error-banner">'+esc(r.error)+'</div>'}
|
|
312
|
-
else{$('agent-result').innerHTML='<div class="result"><pre>'+esc(JSON.stringify(r.result,null,2))+'</pre></div><div class="result-meta"><span>'+(r.durationMs||0).toFixed(1)+'ms</span><span class="badge '+(r.success?'badge-ok':'badge-err')+'">'+(r.success?'ok':'error')+'</span></div>'}
|
|
313
|
-
}catch(e){$('agent-result').innerHTML='<div class="error-banner">'+esc(e.message)+'</div>'}
|
|
314
|
-
this.textContent='Call';
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
$('agent-batch').addEventListener('click',async function(){
|
|
318
|
-
if(batchRunning){batchAbort=true;return;}
|
|
319
|
-
const lbl=$('agent-label'),alias=lbl.dataset.alias,task=lbl.dataset.task;
|
|
320
|
-
const batchSql=this.dataset.batchSql,batchCubby=this.dataset.batchCubby;
|
|
321
|
-
if(!batchSql||!batchCubby){$('agent-result').innerHTML='<div class="error-banner">No batch config on this task</div>';return;}
|
|
322
|
-
$('agent-result').innerHTML='<div class="success-banner">Querying '+esc(batchCubby)+'...</div>';
|
|
323
|
-
var r=await api('POST','/api/cubbies/'+batchCubby+'/query',{sql:batchSql,params:[]});
|
|
324
|
-
if(r.error){$('agent-result').innerHTML='<div class="error-banner">Query failed: '+esc(r.error)+'</div>';return;}
|
|
325
|
-
var rows=r.rows||[];
|
|
326
|
-
if(!rows.length){$('agent-result').innerHTML='<div class="success-banner">No rows matched \u2014 nothing to run</div>';return;}
|
|
327
|
-
const ac=topology.agents.find(function(a){return a.alias===alias;}),tc=ac&&ac.tasks.find(function(t){return t.alias===task;});
|
|
328
|
-
const paramKeys=tc&&tc.parameters&&tc.parameters.properties?Object.keys(tc.parameters.properties):[];
|
|
329
|
-
batchRunning=true;batchAbort=false;
|
|
330
|
-
this.textContent='Stop';this.classList.replace('btn-secondary','btn-danger');
|
|
331
|
-
var ok=0,errors=0,i=0;
|
|
332
|
-
for(;i<rows.length;i++){
|
|
333
|
-
if(batchAbort)break;
|
|
334
|
-
$('agent-result').innerHTML='<div class="success-banner">Running '+(i+1)+'/'+rows.length+' \u2014 '+ok+' ok'+( errors?' '+errors+' err':'')+'</div>';
|
|
335
|
-
var payload={};
|
|
336
|
-
for(var j=0;j<paramKeys.length&&j<rows[i].length;j++)payload[paramKeys[j]]=rows[i][j];
|
|
337
|
-
try{
|
|
338
|
-
var res=await api('POST','/api/agents/'+alias+'/'+task,{payload});
|
|
339
|
-
if(res.error&&!res.success)errors++;else ok++;
|
|
340
|
-
}catch(e){errors++;}
|
|
341
|
-
await new Promise(function(r){setTimeout(r,300);});
|
|
342
|
-
}
|
|
343
|
-
batchRunning=false;
|
|
344
|
-
this.textContent='Run Batch';this.classList.replace('btn-danger','btn-secondary');
|
|
345
|
-
var msg=batchAbort?'Stopped after '+i+'/'+rows.length+' \u2014 '+ok+' ok':'Done \u2014 '+ok+'/'+rows.length+' ok'+(errors?' ('+errors+' errors)':'');
|
|
346
|
-
$('agent-result').innerHTML='<div class="'+(errors||batchAbort?'error-banner':'success-banner')+'">'+esc(msg)+'</div>';
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// Cubby tabs
|
|
350
|
-
document.querySelectorAll('#panel-cubby .tab').forEach(tab=>{
|
|
351
|
-
tab.addEventListener('click',()=>{
|
|
352
|
-
document.querySelectorAll('#panel-cubby .tab').forEach(t=>t.classList.remove('active'));tab.classList.add('active');
|
|
353
|
-
$('cubby-browse').style.display=tab.dataset.tab==='browse'?'':'none';
|
|
354
|
-
$('cubby-sql').style.display=tab.dataset.tab==='sql'?'':'none';
|
|
355
|
-
$('cubby-graph').style.display=tab.dataset.tab==='graph'?'':'none';
|
|
356
|
-
if(tab.dataset.tab==='graph'){$('panel-cubby').classList.add('panel-wide');loadCubbyGraph($('cubby-label').dataset.alias);}
|
|
357
|
-
else if(tab.dataset.tab==='sql'){$('panel-cubby').classList.add('panel-wide');}
|
|
358
|
-
else $('panel-cubby').classList.remove('panel-wide');
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
// Graph tab
|
|
363
|
-
const GRAPH_NODE_COLORS={
|
|
364
|
-
artifact:{bg:'#6366f1',border:'#818cf8'},
|
|
365
|
-
work:{bg:'#f59e0b',border:'#fbbf24'},
|
|
366
|
-
operator:{bg:'#22c55e',border:'#4ade80'},
|
|
367
|
-
eval:{bg:'#ec4899',border:'#f472b6'},
|
|
368
|
-
artifact_map:{bg:'#52525b',border:'#71717a'},
|
|
369
|
-
};
|
|
370
|
-
let graphNet=null,graphVisNodes=null,hiddenTypes=new Set();
|
|
371
|
-
|
|
372
|
-
async function loadCubbyGraph(alias){
|
|
373
|
-
const canvas=$('graph-canvas');
|
|
374
|
-
canvas.innerHTML='<div class="empty">Loading graph...</div>';
|
|
375
|
-
$('graph-detail').style.display='none';
|
|
376
|
-
$('graph-stats').textContent='';
|
|
377
|
-
try{
|
|
378
|
-
const [nr,er]=await Promise.all([
|
|
379
|
-
api('POST','/api/cubbies/'+alias+'/query',{sql:"SELECT id,version,type,ref,created_at,attribute FROM nodes"}),
|
|
380
|
-
api('POST','/api/cubbies/'+alias+'/query',{sql:"SELECT id,src,type,dst FROM edges"}),
|
|
381
|
-
]);
|
|
382
|
-
if(nr.error){canvas.innerHTML='<div class="empty">This cubby has no nodes table \u2014 graph view requires nodes + edges tables</div>';return}
|
|
383
|
-
const rows=nr.rows||[],erows=er.rows||[];
|
|
384
|
-
if(!rows.length){canvas.innerHTML='<div class="empty">No nodes yet \u2014 run an ingest first</div>';return}
|
|
385
|
-
|
|
386
|
-
// Build vis nodes
|
|
387
|
-
const visNodes=[],versionedNode={},latestVer={},nodeVisId={};
|
|
388
|
-
let idx=1;
|
|
389
|
-
for(const r of rows){
|
|
390
|
-
const[id,ver,type,ref,created_at,attr]=r;
|
|
391
|
-
const col=GRAPH_NODE_COLORS[type]||GRAPH_NODE_COLORS.artifact_map;
|
|
392
|
-
const a=safeJ(attr);
|
|
393
|
-
const label=type==='artifact'?(a.title||ref||id).slice(0,22)+'\nv'+ver
|
|
394
|
-
:type==='operator'?(a.name||ref||'operator').slice(0,18)
|
|
395
|
-
:type==='work'?(a.type||'work')+'\nv'+ver
|
|
396
|
-
:(type+' v'+ver);
|
|
397
|
-
const shape=type==='operator'?'diamond':type==='work'?'box':'ellipse';
|
|
398
|
-
visNodes.push({id:idx,label,
|
|
399
|
-
color:{background:col.bg,border:col.border,highlight:{background:col.border,border:'#fff'}},
|
|
400
|
-
font:{color:'#fff',size:11,face:"'SF Mono','Fira Code',monospace",multi:false},
|
|
401
|
-
shape,borderWidth:2,
|
|
402
|
-
_raw:{id,ver,type,ref,created_at,attr}});
|
|
403
|
-
nodeVisId[id+'|'+ver]=idx;
|
|
404
|
-
if(!versionedNode[id])versionedNode[id]={};
|
|
405
|
-
versionedNode[id][ver]=idx;
|
|
406
|
-
if(!latestVer[id]||parseInt(ver)>parseInt(latestVer[id].ver))latestVer[id]={ver,visId:idx};
|
|
407
|
-
idx++;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Build vis edges
|
|
411
|
-
const nodeVer={};
|
|
412
|
-
for(const r of rows)nodeVer[r[0]]=r[1];
|
|
413
|
-
|
|
414
|
-
const visEdges=[];
|
|
415
|
-
for(const r of erows){
|
|
416
|
-
const[eid,src,type,dst]=r;
|
|
417
|
-
const sVisId=latestVer[src]?.visId;
|
|
418
|
-
const srcVersion=nodeVer[src];
|
|
419
|
-
const dVisId=(versionedNode[dst]&&srcVersion&&versionedNode[dst][srcVersion])
|
|
420
|
-
?versionedNode[dst][srcVersion]
|
|
421
|
-
:latestVer[dst]?.visId;
|
|
422
|
-
if(!sVisId||!dVisId)continue;
|
|
423
|
-
visEdges.push({id:eid,from:sVisId,to:dVisId,label:type,
|
|
424
|
-
font:{size:9,color:'#52525b',align:'middle'},
|
|
425
|
-
color:{color:'#3f3f46',highlight:'#818cf8'},
|
|
426
|
-
arrows:{to:{enabled:true,scaleFactor:0.5}},
|
|
427
|
-
smooth:{type:'curvedCW',roundness:0.2}});
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Synthetic PREV_VERSION dashed edges
|
|
431
|
-
for(const[nodeId,verMap]of Object.entries(versionedNode)){
|
|
432
|
-
const ntype=rows.find(function(r){return r[0]===nodeId;})?.[2];
|
|
433
|
-
if(ntype!=='artifact')continue;
|
|
434
|
-
const versions=Object.keys(verMap).map(Number).sort(function(a,b){return a-b;});
|
|
435
|
-
for(var vi=1;vi<versions.length;vi++){
|
|
436
|
-
const fromVisId=verMap[String(versions[vi-1])],toVisId=verMap[String(versions[vi])];
|
|
437
|
-
if(!fromVisId||!toVisId)continue;
|
|
438
|
-
visEdges.push({id:nodeId+'|PREV|v'+versions[vi],from:fromVisId,to:toVisId,
|
|
439
|
-
label:'',dashes:true,
|
|
440
|
-
font:{size:8,color:'#3f3f46',align:'middle'},
|
|
441
|
-
color:{color:'#3f3f46',highlight:'#52525b'},
|
|
442
|
-
arrows:{to:{enabled:true,scaleFactor:0.3}},
|
|
443
|
-
smooth:{type:'continuous'}});
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Count node types for legend
|
|
448
|
-
const typeCounts={};
|
|
449
|
-
for(const n of visNodes)typeCounts[n._raw.type]=(typeCounts[n._raw.type]||0)+1;
|
|
450
|
-
|
|
451
|
-
$('graph-stats').innerHTML=visNodes.length+' nodes \u00b7 '+visEdges.length+' edges';
|
|
452
|
-
|
|
453
|
-
// Legend toggles
|
|
454
|
-
var legendHtml='<div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:8px">';
|
|
455
|
-
for(const[type,col]of Object.entries(GRAPH_NODE_COLORS)){
|
|
456
|
-
if(!typeCounts[type])continue;
|
|
457
|
-
const isHidden=hiddenTypes.has(type);
|
|
458
|
-
legendHtml+='<button onclick="window.graphToggleType(\''+type+'\')" style="display:flex;align-items:center;gap:5px;background:'+(isHidden?'var(--surface2)':col.bg+'22')+';border:1px solid '+(isHidden?'var(--border)':col.bg+'66')+';color:'+(isHidden?'var(--text3)':col.bg)+';border-radius:4px;padding:2px 8px;font-size:10px;cursor:pointer">'
|
|
459
|
-
+'<span style="width:8px;height:8px;border-radius:50%;background:'+(isHidden?'var(--border)':col.bg)+';display:inline-block"></span>'+esc(type)+' <span style="opacity:.6">'+typeCounts[type]+'</span></button>';
|
|
460
|
-
}
|
|
461
|
-
legendHtml+='</div>';
|
|
462
|
-
$('graph-stats').innerHTML+= legendHtml;
|
|
463
|
-
|
|
464
|
-
canvas.innerHTML='';
|
|
465
|
-
if(graphNet)graphNet.destroy();
|
|
466
|
-
graphVisNodes=new window.vis.DataSet(visNodes.map(function(n){return Object.assign({},n,{hidden:hiddenTypes.has(n._raw.type)});}));
|
|
467
|
-
graphNet=new window.vis.Network(canvas,
|
|
468
|
-
{nodes:graphVisNodes,edges:new window.vis.DataSet(visEdges)},
|
|
469
|
-
{physics:{barnesHut:{gravitationalConstant:-12000,springLength:200,springConstant:0.02,damping:0.15,centralGravity:0.05},stabilization:{iterations:200}},
|
|
470
|
-
interaction:{hover:true},layout:{improvedLayout:rows.length<80}});
|
|
471
|
-
|
|
472
|
-
graphNet.on('click',(p)=>{
|
|
473
|
-
if(!p.nodes.length){$('graph-detail').style.display='none';return}
|
|
474
|
-
const n=visNodes.find(v=>v.id===p.nodes[0]);if(!n)return;
|
|
475
|
-
const{id,ver,type,ref,created_at,attr}=n._raw,a=safeJ(attr);
|
|
476
|
-
const col=GRAPH_NODE_COLORS[type]||GRAPH_NODE_COLORS.artifact_map;
|
|
477
|
-
$('gd-type-badge').textContent=type;
|
|
478
|
-
$('gd-type-badge').style.cssText='background:'+col.bg+'22;color:'+col.bg+';border:1px solid '+col.bg+'44';
|
|
479
|
-
const rows=[['id',id],['version',ver],['ref',ref],['created_at',created_at],
|
|
480
|
-
...Object.entries(a).filter(([k])=>k!=='segments'&&k!=='embedding').map(([k,v])=>[k,typeof v==='object'?JSON.stringify(v):String(v??'')])
|
|
481
|
-
].filter(([,v])=>v&&v!=='null');
|
|
482
|
-
if(a.segments?.length)rows.push(['segments',a.segments.length+' blocks']);
|
|
483
|
-
$('gd-attrs').innerHTML=rows.map(([k,v])=>'<div class="gd-row"><span class="gd-key">'+esc(k)+'</span><span class="gd-val">'+esc(String(v))+'</span></div>').join('');
|
|
484
|
-
$('graph-detail').style.display='';
|
|
485
|
-
});
|
|
486
|
-
}catch(e){canvas.innerHTML='<div class="error-banner">'+esc(e.message||String(e))+'</div>'}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
window.loadCubbyGraph=loadCubbyGraph;
|
|
490
|
-
window.graphToggleType=function(type){
|
|
491
|
-
if(hiddenTypes.has(type))hiddenTypes.delete(type);else hiddenTypes.add(type);
|
|
492
|
-
if(!graphVisNodes)return;
|
|
493
|
-
graphVisNodes.update(graphVisNodes.get().filter(function(n){return n._raw&&n._raw.type===type;}).map(function(n){return{id:n.id,hidden:hiddenTypes.has(type)};}));
|
|
494
|
-
document.querySelectorAll('#graph-stats button').forEach(function(btn){
|
|
495
|
-
var t=btn.textContent.trim().split(' ')[0];
|
|
496
|
-
var col=GRAPH_NODE_COLORS[t];if(!col)return;
|
|
497
|
-
var hidden=hiddenTypes.has(t);
|
|
498
|
-
btn.style.background=hidden?'var(--surface2)':col.bg+'22';
|
|
499
|
-
btn.style.borderColor=hidden?'var(--border)':col.bg+'66';
|
|
500
|
-
btn.style.color=hidden?'var(--text3)':col.bg;
|
|
501
|
-
var dot=btn.querySelector('span');if(dot)dot.style.background=hidden?'var(--border)':col.bg;
|
|
502
|
-
});
|
|
503
|
-
};
|
|
504
|
-
function safeJ(s){try{return JSON.parse(s)}catch{return{}}}
|
|
505
|
-
async function loadCubbyBrowse(alias){
|
|
506
|
-
const el=$('cubby-tables');el.innerHTML='<div class="empty">Loading...</div>';
|
|
507
|
-
try{
|
|
508
|
-
const t=await api('POST','/api/cubbies/'+alias+'/query',{sql:"SELECT name FROM sqlite_master WHERE type='table' AND name != 'schema_migrations'"});
|
|
509
|
-
if(!t.rows||!t.rows.length){el.innerHTML='<div class="empty">No tables</div>';return}
|
|
510
|
-
let h='';
|
|
511
|
-
for(const row of t.rows){
|
|
512
|
-
const tn=row[0],cr=await api('POST','/api/cubbies/'+alias+'/query',{sql:'SELECT COUNT(*) FROM "'+tn+'"'});
|
|
513
|
-
h+='<div class="cubby-table-item" onclick="browseCubbyTable(\''+ea(alias)+'\',\''+ea(tn)+'\')">';
|
|
514
|
-
h+='<span class="cti-name">'+esc(tn)+'</span><span class="cti-count">'+(cr.rows?.[0]?.[0]??'?')+' rows</span></div>';
|
|
515
|
-
}
|
|
516
|
-
el.innerHTML=h;
|
|
517
|
-
}catch(e){el.innerHTML='<div class="error-banner">'+esc(e.message)+'</div>'}
|
|
518
|
-
}
|
|
519
|
-
window.browseCubbyTable=async function(alias,table){
|
|
520
|
-
const el=$('cubby-tables');
|
|
521
|
-
try{const r=await api('POST','/api/cubbies/'+alias+'/query',{sql:'SELECT * FROM "'+table+'" LIMIT 100'});
|
|
522
|
-
el.innerHTML='<div style="display:flex;gap:8px;justify-content:flex-end;margin-bottom:8px">'
|
|
523
|
-
+'<button class="btn-ghost" onclick="loadCubbyBrowse(\''+ea(alias)+'\')">\u2190 Back</button>'
|
|
524
|
-
+'<button class="btn-danger" style="font-size:11px;padding:5px 12px" onclick="clearCubbyTable(\''+ea(alias)+'\',\''+ea(table)+'\')">Clear table</button>'
|
|
525
|
-
+'</div>'+renderTable(r);
|
|
526
|
-
}catch(e){el.innerHTML='<div class="error-banner">'+esc(e.message)+'</div>'}
|
|
527
|
-
};
|
|
528
|
-
window.clearCubbyTable=async function(alias,table){
|
|
529
|
-
if(!confirm('Delete all rows from "'+table+'"?'))return;
|
|
530
|
-
try{
|
|
531
|
-
await api('POST','/api/cubbies/'+alias+'/exec',{sql:'DELETE FROM "'+table+'"'});
|
|
532
|
-
window.browseCubbyTable(alias,table);
|
|
533
|
-
}catch(e){alert('Error: '+e.message);}
|
|
534
|
-
};
|
|
535
|
-
window.loadCubbyBrowse=loadCubbyBrowse;
|
|
536
|
-
|
|
537
|
-
$('cubby-run').addEventListener('click',async function(){
|
|
538
|
-
const alias=$('cubby-label').dataset.alias,sql=$('cubby-sql-input').value.trim();if(!sql)return;
|
|
539
|
-
const isW=/^\s*(INSERT|UPDATE|DELETE|CREATE|DROP|ALTER)/i.test(sql),ep=isW?'exec':'query';
|
|
540
|
-
this.textContent='...';
|
|
541
|
-
try{const r=await api('POST','/api/cubbies/'+alias+'/'+ep,{sql});
|
|
542
|
-
if(r.error){$('cubby-sql-result').innerHTML='<div class="error-banner">'+esc(r.error)+'</div>'}
|
|
543
|
-
else if(r.columns){$('cubby-sql-result').innerHTML=renderTable(r)+'<div class="result-meta"><span>'+r.meta.rowsRead+' rows</span><span>'+(r.meta.duration*1000).toFixed(2)+'ms</span></div>'}
|
|
544
|
-
else{$('cubby-sql-result').innerHTML='<div class="success-banner">'+(r.rowsAffected??0)+' rows affected</div>'}
|
|
545
|
-
}catch(e){$('cubby-sql-result').innerHTML='<div class="error-banner">'+esc(e.message)+'</div>'}
|
|
546
|
-
this.textContent='Run';
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
// Query template manager (stored in cubby saved_queries table)
|
|
550
|
-
async function qtRefreshSelect(alias){
|
|
551
|
-
var sel=$('query-select'),prev=sel.value;
|
|
552
|
-
try{
|
|
553
|
-
var r=await api('POST','/api/cubbies/'+alias+'/query',{sql:'SELECT name FROM saved_queries ORDER BY name',params:[]});
|
|
554
|
-
sel.innerHTML='<option value="">\u2014 select a saved query \u2014</option>';
|
|
555
|
-
(r.rows||[]).forEach(function(row){var o=document.createElement('option');o.value=row[0];o.textContent=row[0];sel.appendChild(o);});
|
|
556
|
-
sel.value=prev||'';
|
|
557
|
-
$('query-delete').style.display=sel.value?'':'none';
|
|
558
|
-
}catch(e){}
|
|
559
|
-
}
|
|
560
|
-
$('query-select').addEventListener('change',async function(){
|
|
561
|
-
var alias=$('cubby-label').dataset.alias,name=this.value;
|
|
562
|
-
$('query-delete').style.display=name?'':'none';
|
|
563
|
-
if(!name)return;
|
|
564
|
-
try{
|
|
565
|
-
var r=await api('POST','/api/cubbies/'+alias+'/query',{sql:'SELECT sql_text FROM saved_queries WHERE name=?',params:[name]});
|
|
566
|
-
if(r.rows&&r.rows[0]){$('cubby-sql-input').value=r.rows[0][0];$('query-name').value=name;}
|
|
567
|
-
}catch(e){}
|
|
568
|
-
});
|
|
569
|
-
$('query-save').addEventListener('click',async function(){
|
|
570
|
-
var alias=$('cubby-label').dataset.alias,name=$('query-name').value.trim(),sql=$('cubby-sql-input').value.trim();
|
|
571
|
-
if(!name||!sql)return;
|
|
572
|
-
try{
|
|
573
|
-
await api('POST','/api/cubbies/'+alias+'/exec',{sql:'INSERT OR REPLACE INTO saved_queries(name,sql_text) VALUES(?,?)',params:[name,sql]});
|
|
574
|
-
await qtRefreshSelect(alias);$('query-select').value=name;$('query-delete').style.display='';
|
|
575
|
-
}catch(e){}
|
|
576
|
-
});
|
|
577
|
-
$('query-delete').addEventListener('click',async function(){
|
|
578
|
-
var alias=$('cubby-label').dataset.alias,name=$('query-select').value;if(!name)return;
|
|
579
|
-
try{
|
|
580
|
-
await api('POST','/api/cubbies/'+alias+'/exec',{sql:'DELETE FROM saved_queries WHERE name=?',params:[name]});
|
|
581
|
-
await qtRefreshSelect(alias);$('query-name').value='';
|
|
582
|
-
}catch(e){}
|
|
583
|
-
});
|
|
584
|
-
|
|
585
|
-
// Bottom panel: tabs, resize, collapse
|
|
586
|
-
document.querySelectorAll('.btab').forEach(tab=>{
|
|
587
|
-
tab.addEventListener('click',()=>{
|
|
588
|
-
document.querySelectorAll('.btab').forEach(t=>t.classList.remove('active'));tab.classList.add('active');
|
|
589
|
-
$('log-container').style.display=tab.dataset.btab==='logs'?'':'none';
|
|
590
|
-
$('flow-container').style.display=tab.dataset.btab==='flow'?'':'none';
|
|
591
|
-
});
|
|
592
|
-
});
|
|
593
|
-
$('log-filter').addEventListener('input',function(){
|
|
594
|
-
logFilter=this.value.toLowerCase();
|
|
595
|
-
$('log-container').querySelectorAll('.log-line').forEach(l=>{l.style.display=(!logFilter||l.textContent.toLowerCase().includes(logFilter))?'':'none'});
|
|
596
|
-
});
|
|
597
|
-
$('btn-clear').addEventListener('click',()=>{$('log-container').innerHTML='';$('flow-container').innerHTML=''});
|
|
598
|
-
$('btn-collapse').addEventListener('click',function(){
|
|
599
|
-
const b=$('bottom');
|
|
600
|
-
if(b.classList.toggle('collapsed')){this.innerHTML='▲';this.title='Expand'}
|
|
601
|
-
else{this.innerHTML='▼';this.title='Collapse'}
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
// Resize handle drag
|
|
605
|
-
(function(){
|
|
606
|
-
const handle=$('resize-handle'),bottom=$('bottom');
|
|
607
|
-
let startY,startH;
|
|
608
|
-
handle.addEventListener('mousedown',(e)=>{
|
|
609
|
-
e.preventDefault();startY=e.clientY;startH=bottom.offsetHeight;
|
|
610
|
-
const onMove=(e)=>{const h=Math.max(40,Math.min(window.innerHeight*0.7,startH-(e.clientY-startY)));bottom.style.height=h+'px';bottom.classList.remove('collapsed')};
|
|
611
|
-
const onUp=()=>{document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp)};
|
|
612
|
-
document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);
|
|
613
|
-
});
|
|
614
|
-
})();
|
|
615
|
-
|
|
616
|
-
// Tab in textareas
|
|
617
|
-
document.querySelectorAll('textarea').forEach(ta=>{
|
|
618
|
-
ta.addEventListener('keydown',function(e){if(e.key==='Tab'){e.preventDefault();const s=this.selectionStart;this.value=this.value.substring(0,s)+' '+this.value.substring(this.selectionEnd);this.selectionStart=this.selectionEnd=s+2}});
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
// Helpers
|
|
622
|
-
function $(id){return document.getElementById(id)}
|
|
623
|
-
function renderTable(r){
|
|
624
|
-
if(!r.columns||!r.rows)return'';let h='<div class="table-wrap"><table><thead><tr>';
|
|
625
|
-
for(const c of r.columns)h+='<th>'+esc(c)+'</th>';h+='</tr></thead><tbody>';
|
|
626
|
-
for(const row of r.rows){h+='<tr>';for(const cell of row)h+='<td>'+esc(cell==null?'NULL':String(cell))+'</td>';h+='</tr>'}
|
|
627
|
-
return h+'</tbody></table></div>';
|
|
628
|
-
}
|
|
629
|
-
function esc(s){if(s==null)return'';return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')}
|
|
630
|
-
function ea(s){return String(s).replace(/'/g,"\\'").replace(/"/g,'"')}
|
|
631
|
-
|
|
632
|
-
loadTopology();connectWs();
|
|
633
|
-
})();
|