@ai2aim.ai/hivemind-sdk 1.0.3 → 1.0.5
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 +15 -25
- package/dist/cli-config.d.ts +0 -1
- package/dist/cli-config.d.ts.map +1 -1
- package/dist/cli-config.js +0 -8
- package/dist/cli-config.js.map +1 -1
- package/dist/cli.js +23 -6
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +487 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +599 -67
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +397 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/web.d.ts +20 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +971 -0
- package/dist/web.js.map +1 -0
- package/package.json +2 -3
package/dist/web.js
ADDED
|
@@ -0,0 +1,971 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Embedded web dashboard for the Hivemind SDK.
|
|
4
|
+
*
|
|
5
|
+
* `hivemind start-web` launches a local HTTP server that serves an
|
|
6
|
+
* interactive single-page application built with Vue 3 + Tailwind (CDN).
|
|
7
|
+
* The app is organized around **flows** — multi-step interactive wizards
|
|
8
|
+
* that chain SDK methods together — rather than raw endpoint forms.
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.startWebServer = startWebServer;
|
|
15
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
16
|
+
/* ------------------------------------------------------------------ */
|
|
17
|
+
/* HTML template */
|
|
18
|
+
/* ------------------------------------------------------------------ */
|
|
19
|
+
function buildDashboardHtml(config) {
|
|
20
|
+
const esc = (s) => s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"');
|
|
21
|
+
return /* html */ `<!DOCTYPE html>
|
|
22
|
+
<html lang="en">
|
|
23
|
+
<head>
|
|
24
|
+
<meta charset="utf-8"/>
|
|
25
|
+
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
26
|
+
<title>Hivemind — Interactive Dashboard</title>
|
|
27
|
+
<script src="https://cdn.tailwindcss.com"><\/script>
|
|
28
|
+
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"><\/script>
|
|
29
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css"/>
|
|
30
|
+
<script>
|
|
31
|
+
tailwind.config={theme:{extend:{colors:{brand:'#6c63ff',brand2:'#4fc3f7',surface:'#1a1d27',bg:'#0f1117',border:'#2a2d3a'}}}}
|
|
32
|
+
<\/script>
|
|
33
|
+
<style>
|
|
34
|
+
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif}
|
|
35
|
+
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:#0f1117}::-webkit-scrollbar-thumb{background:#3a3d4a;border-radius:3px}
|
|
36
|
+
.fade-enter-active,.fade-leave-active{transition:opacity .2s ease}
|
|
37
|
+
.fade-enter-from,.fade-leave-to{opacity:0}
|
|
38
|
+
</style>
|
|
39
|
+
</head>
|
|
40
|
+
<body class="bg-bg text-gray-200 h-screen overflow-hidden">
|
|
41
|
+
|
|
42
|
+
<div id="app">
|
|
43
|
+
<div class="flex h-screen">
|
|
44
|
+
<!-- Sidebar -->
|
|
45
|
+
<aside class="w-64 min-w-[256px] bg-surface border-r border-border flex flex-col">
|
|
46
|
+
<div class="px-5 pt-5 pb-2">
|
|
47
|
+
<div class="flex items-center gap-2 text-xl font-bold text-white">
|
|
48
|
+
<span class="mdi mdi-brain text-brand text-2xl"></span> Hivemind
|
|
49
|
+
</div>
|
|
50
|
+
<div class="text-xs text-gray-500 mt-1">Interactive Dashboard</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="px-5 py-2">
|
|
53
|
+
<div @click="currentView='settings'" class="flex items-center gap-2 text-xs cursor-pointer rounded-lg px-3 py-2 transition"
|
|
54
|
+
:class="health.ok?'bg-green-900/30 text-green-400 hover:bg-green-900/40':'bg-red-900/30 text-red-400 hover:bg-red-900/40'">
|
|
55
|
+
<span class="mdi" :class="health.ok?'mdi-check-circle':'mdi-alert-circle'"></span>
|
|
56
|
+
<span>{{health.text}}</span>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<nav class="flex-1 overflow-y-auto px-3 mt-2">
|
|
60
|
+
<div v-for="section in navSections" :key="section.name" class="mb-3">
|
|
61
|
+
<div class="text-[10px] uppercase tracking-widest text-gray-500 font-bold px-3 py-1">{{section.name}}</div>
|
|
62
|
+
<div v-for="item in section.items" :key="item.view"
|
|
63
|
+
@click="currentView=item.view"
|
|
64
|
+
class="flex items-center gap-3 px-3 py-2 rounded-lg text-sm cursor-pointer transition"
|
|
65
|
+
:class="currentView===item.view?'bg-brand/20 text-white':'text-gray-400 hover:bg-white/5 hover:text-gray-200'">
|
|
66
|
+
<span class="mdi text-lg" :class="item.icon"></span>
|
|
67
|
+
<span>{{item.label}}</span>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</nav>
|
|
71
|
+
<div class="px-5 py-3 border-t border-border text-[10px] text-gray-600">SDK v1.0.4</div>
|
|
72
|
+
</aside>
|
|
73
|
+
|
|
74
|
+
<!-- Main -->
|
|
75
|
+
<main class="flex-1 flex flex-col overflow-hidden">
|
|
76
|
+
<header class="h-12 bg-surface border-b border-border flex items-center px-5 gap-3">
|
|
77
|
+
<input v-model="search" placeholder="Search flows and actions…"
|
|
78
|
+
class="flex-1 bg-bg border border-border rounded-lg px-3 py-1.5 text-sm text-gray-300 placeholder-gray-600 focus:outline-none focus:border-brand/50"/>
|
|
79
|
+
<button @click="currentView='quick'" class="text-xs bg-brand/20 text-brand px-3 py-1.5 rounded-lg hover:bg-brand/30 transition">
|
|
80
|
+
<span class="mdi mdi-lightning-bolt"></span> Quick Actions
|
|
81
|
+
</button>
|
|
82
|
+
</header>
|
|
83
|
+
|
|
84
|
+
<div class="flex-1 overflow-y-auto p-6">
|
|
85
|
+
<transition name="fade" mode="out-in">
|
|
86
|
+
|
|
87
|
+
<!-- HOME -->
|
|
88
|
+
<div v-if="currentView==='home'" key="home">
|
|
89
|
+
<h1 class="text-2xl font-bold text-white mb-1">Welcome to Hivemind</h1>
|
|
90
|
+
<p class="text-gray-400 text-sm mb-8">Choose a flow to get started, or use Quick Actions for individual operations.</p>
|
|
91
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
92
|
+
<div v-for="flow in flows" :key="flow.view" @click="currentView=flow.view"
|
|
93
|
+
class="bg-surface border border-border rounded-xl p-5 cursor-pointer hover:border-brand/50 hover:shadow-lg hover:shadow-brand/5 transition group">
|
|
94
|
+
<div class="flex items-center gap-3 mb-3">
|
|
95
|
+
<div class="w-10 h-10 rounded-lg flex items-center justify-center text-xl" :class="flow.color">
|
|
96
|
+
<span class="mdi" :class="flow.icon"></span>
|
|
97
|
+
</div>
|
|
98
|
+
<div>
|
|
99
|
+
<div class="font-semibold text-white group-hover:text-brand transition">{{flow.title}}</div>
|
|
100
|
+
<div class="text-xs text-gray-500">{{flow.steps}} steps</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
<p class="text-xs text-gray-400 leading-relaxed">{{flow.desc}}</p>
|
|
104
|
+
<div class="mt-3 flex flex-wrap gap-1">
|
|
105
|
+
<span v-for="tag in flow.tags" class="text-[10px] bg-white/5 text-gray-500 px-2 py-0.5 rounded-full">{{tag}}</span>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<!-- SETTINGS -->
|
|
112
|
+
<div v-if="currentView==='settings'" key="settings">
|
|
113
|
+
<h1 class="text-2xl font-bold text-white mb-1">Connection Settings</h1>
|
|
114
|
+
<p class="text-gray-400 text-sm mb-6">Configure your API connection.</p>
|
|
115
|
+
<div class="max-w-lg space-y-4">
|
|
116
|
+
<div v-for="s in settingsFields" :key="s.key">
|
|
117
|
+
<label class="text-xs text-gray-500 uppercase tracking-wide">{{s.label}}</label>
|
|
118
|
+
<input v-model="cfg[s.key]" :type="s.secret?'password':'text'" :placeholder="s.placeholder"
|
|
119
|
+
class="mt-1 w-full bg-bg border border-border rounded-lg px-3 py-2 text-sm text-gray-300 focus:outline-none focus:border-brand/50"/>
|
|
120
|
+
</div>
|
|
121
|
+
<button @click="saveSettings" class="bg-brand text-white px-6 py-2 rounded-lg text-sm font-semibold hover:bg-brand/80 transition">Save & Test</button>
|
|
122
|
+
<div v-if="settingsMsg" class="text-sm mt-2" :class="settingsMsgOk?'text-green-400':'text-red-400'">{{settingsMsg}}</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- QUICK ACTIONS -->
|
|
127
|
+
<div v-if="currentView==='quick'" key="quick">
|
|
128
|
+
<h1 class="text-2xl font-bold text-white mb-1">Quick Actions</h1>
|
|
129
|
+
<p class="text-gray-400 text-sm mb-6">Run any individual API call.</p>
|
|
130
|
+
<div class="flex flex-wrap gap-2 mb-6">
|
|
131
|
+
<button v-for="cat in quickCategories" :key="cat" @click="quickCat=cat"
|
|
132
|
+
class="text-xs px-3 py-1.5 rounded-full transition"
|
|
133
|
+
:class="quickCat===cat?'bg-brand text-white':'bg-white/5 text-gray-400 hover:bg-white/10'">{{cat}}</button>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="space-y-3">
|
|
136
|
+
<div v-for="action in filteredQuickActions" :key="action.id" class="bg-surface border border-border rounded-xl overflow-hidden">
|
|
137
|
+
<div @click="action.open=!action.open" class="flex items-center gap-3 px-4 py-3 cursor-pointer hover:bg-white/5 transition">
|
|
138
|
+
<span class="text-xs font-mono px-2 py-0.5 rounded font-bold" :class="methodColor(action.method)">{{action.method}}</span>
|
|
139
|
+
<span class="text-sm text-gray-300 flex-1">{{action.title}}</span>
|
|
140
|
+
<span class="text-xs text-gray-600 font-mono">{{action.path}}</span>
|
|
141
|
+
<span class="mdi text-gray-500" :class="action.open?'mdi-chevron-up':'mdi-chevron-down'"></span>
|
|
142
|
+
</div>
|
|
143
|
+
<div v-if="action.open" class="border-t border-border px-4 py-4">
|
|
144
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-4" v-if="action.fields.length">
|
|
145
|
+
<div v-for="f in action.fields" :key="f.key">
|
|
146
|
+
<label class="text-xs text-gray-500">{{f.label}}</label>
|
|
147
|
+
<textarea v-if="f.type==='textarea'" v-model="action.values[f.key]" :placeholder="f.placeholder||''" rows="3"
|
|
148
|
+
class="mt-1 w-full bg-bg border border-border rounded-lg px-3 py-1.5 text-sm text-gray-300 focus:outline-none focus:border-brand/50 resize-y"></textarea>
|
|
149
|
+
<input v-else v-model="action.values[f.key]" :type="f.type||'text'" :placeholder="f.placeholder||''"
|
|
150
|
+
class="mt-1 w-full bg-bg border border-border rounded-lg px-3 py-1.5 text-sm text-gray-300 focus:outline-none focus:border-brand/50"/>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
<button @click="runQuickAction(action)" class="bg-brand text-white px-4 py-1.5 rounded-lg text-sm font-semibold hover:bg-brand/80 transition" :disabled="action.loading">
|
|
154
|
+
<span v-if="action.loading" class="mdi mdi-loading mdi-spin mr-1"></span>{{action.loading?'Running…':'Execute'}}
|
|
155
|
+
</button>
|
|
156
|
+
<div v-if="action.result!==null" class="mt-3 bg-bg border rounded-lg p-3 text-xs font-mono overflow-x-auto max-h-80 overflow-y-auto whitespace-pre-wrap"
|
|
157
|
+
:class="action.resultOk?'border-green-800 text-green-300':'border-red-800 text-red-300'">{{action.result}}</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<!-- FLOW: Organization Setup -->
|
|
164
|
+
<div v-if="currentView==='flow-org'" key="flow-org">
|
|
165
|
+
<flow-header title="Organization Setup" desc="Create and configure an organization, then generate its API key." :step="orgFlow.step" :steps="orgFlow.steps"></flow-header>
|
|
166
|
+
<div v-if="orgFlow.step===0" class="max-w-lg">
|
|
167
|
+
<step-card title="Create Organization" subtitle="Provision a new org.">
|
|
168
|
+
<text-field label="Org ID" v-model="orgFlow.orgId" placeholder="my-org-1"/>
|
|
169
|
+
<text-field label="Name" v-model="orgFlow.name" placeholder="Acme Corporation"/>
|
|
170
|
+
<select-field label="Tier" v-model="orgFlow.tier" :options="['free','standard','premium','enterprise']"/>
|
|
171
|
+
<template #actions><flow-btn @click="orgFlowCreate" :loading="orgFlow.loading">Create Organization</flow-btn></template>
|
|
172
|
+
</step-card>
|
|
173
|
+
</div>
|
|
174
|
+
<div v-if="orgFlow.step===1" class="max-w-lg">
|
|
175
|
+
<step-card title="Organization Created!" subtitle="Save the API key below.">
|
|
176
|
+
<div class="bg-green-900/20 border border-green-800 rounded-lg p-4 mb-4">
|
|
177
|
+
<div class="text-xs text-green-400 mb-1">API Key</div>
|
|
178
|
+
<div class="font-mono text-sm text-green-300 break-all select-all">{{orgFlow.apiKey}}</div>
|
|
179
|
+
</div>
|
|
180
|
+
<kv-display :items="orgFlow.orgData"/>
|
|
181
|
+
<template #actions>
|
|
182
|
+
<flow-btn variant="secondary" @click="copyText(orgFlow.apiKey)">Copy Key</flow-btn>
|
|
183
|
+
<flow-btn @click="orgFlow.step=2">Continue → Verify</flow-btn>
|
|
184
|
+
</template>
|
|
185
|
+
</step-card>
|
|
186
|
+
</div>
|
|
187
|
+
<div v-if="orgFlow.step===2" class="max-w-lg">
|
|
188
|
+
<step-card title="Verify Organization" subtitle="Retrieve the org to confirm setup.">
|
|
189
|
+
<text-field label="Org ID" v-model="orgFlow.orgId" disabled/>
|
|
190
|
+
<template #actions><flow-btn @click="orgFlowVerify" :loading="orgFlow.loading">Fetch Details</flow-btn></template>
|
|
191
|
+
<kv-display v-if="orgFlow.verifyData" :items="orgFlow.verifyData" class="mt-4"/>
|
|
192
|
+
</step-card>
|
|
193
|
+
</div>
|
|
194
|
+
<div v-if="orgFlow.step===3" class="max-w-lg">
|
|
195
|
+
<step-card title="Rotate API Key" subtitle="Optionally rotate the API key.">
|
|
196
|
+
<text-field label="Org ID" v-model="orgFlow.orgId" disabled/>
|
|
197
|
+
<template #actions>
|
|
198
|
+
<flow-btn variant="warn" @click="orgFlowRotate" :loading="orgFlow.loading">Rotate Key</flow-btn>
|
|
199
|
+
<flow-btn variant="secondary" @click="currentView='home'">Done</flow-btn>
|
|
200
|
+
</template>
|
|
201
|
+
<div v-if="orgFlow.newKey" class="mt-4 bg-yellow-900/20 border border-yellow-800 rounded-lg p-4">
|
|
202
|
+
<div class="text-xs text-yellow-400 mb-1">New API Key</div>
|
|
203
|
+
<div class="font-mono text-sm text-yellow-300 break-all select-all">{{orgFlow.newKey}}</div>
|
|
204
|
+
</div>
|
|
205
|
+
</step-card>
|
|
206
|
+
</div>
|
|
207
|
+
<flow-result :result="orgFlow.lastResult"/>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<!-- FLOW: Document Pipeline -->
|
|
211
|
+
<div v-if="currentView==='flow-docs'" key="flow-docs">
|
|
212
|
+
<flow-header title="Document Pipeline" desc="Upload documents, query with RAG, and data-chat." :step="docFlow.step" :steps="docFlow.steps"></flow-header>
|
|
213
|
+
<div v-if="docFlow.step===0" class="max-w-lg">
|
|
214
|
+
<step-card title="Select Organization" subtitle="Which org owns these documents?">
|
|
215
|
+
<text-field label="Org ID" v-model="docFlow.orgId" placeholder="my-org-1"/>
|
|
216
|
+
<template #actions><flow-btn @click="docFlow.step=1">Next →</flow-btn></template>
|
|
217
|
+
</step-card>
|
|
218
|
+
</div>
|
|
219
|
+
<div v-if="docFlow.step===1" class="max-w-lg">
|
|
220
|
+
<step-card title="Upload Document" subtitle="Upload a file to be processed.">
|
|
221
|
+
<text-field label="Org ID" v-model="docFlow.orgId" disabled/>
|
|
222
|
+
<div class="mt-3"><label class="text-xs text-gray-500 uppercase tracking-wide">File</label>
|
|
223
|
+
<input type="file" id="docFileInput" class="mt-1 w-full text-sm text-gray-400 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-brand/20 file:text-brand hover:file:bg-brand/30"/>
|
|
224
|
+
</div>
|
|
225
|
+
<template #actions><flow-btn @click="docFlowUpload" :loading="docFlow.loading">Upload</flow-btn></template>
|
|
226
|
+
<div v-if="docFlow.uploadResult" class="mt-4 bg-green-900/20 border border-green-800 rounded-lg p-3 text-sm text-green-300">Document uploaded: {{docFlow.uploadResult.document_id||'OK'}}</div>
|
|
227
|
+
</step-card>
|
|
228
|
+
</div>
|
|
229
|
+
<div v-if="docFlow.step===2" class="max-w-lg">
|
|
230
|
+
<step-card title="RAG Query" subtitle="Ask questions against uploaded documents.">
|
|
231
|
+
<text-field label="Org ID" v-model="docFlow.orgId" disabled/>
|
|
232
|
+
<text-area label="Your Question" v-model="docFlow.query" placeholder="What are the key findings?"/>
|
|
233
|
+
<div class="grid grid-cols-2 gap-3 mt-3">
|
|
234
|
+
<select-field label="Model" v-model="docFlow.model" :options="['gemini-pro','gemini-pro-vision','gemini-ultra']"/>
|
|
235
|
+
<select-field label="Strategy" v-model="docFlow.strategy" :options="['hybrid','semantic','keyword']"/>
|
|
236
|
+
</div>
|
|
237
|
+
<template #actions><flow-btn @click="docFlowQuery" :loading="docFlow.loading">Ask</flow-btn></template>
|
|
238
|
+
<div v-if="docFlow.answer" class="mt-4 bg-surface border border-border rounded-lg p-4">
|
|
239
|
+
<div class="text-xs text-brand mb-2">AI Answer</div>
|
|
240
|
+
<div class="text-sm text-gray-300 leading-relaxed whitespace-pre-wrap">{{docFlow.answer}}</div>
|
|
241
|
+
</div>
|
|
242
|
+
</step-card>
|
|
243
|
+
</div>
|
|
244
|
+
<div v-if="docFlow.step===3" class="max-w-2xl">
|
|
245
|
+
<step-card title="Data Chat" subtitle="Conversational interaction with your data.">
|
|
246
|
+
<text-field label="Org ID" v-model="docFlow.orgId" disabled/>
|
|
247
|
+
<div class="space-y-2 mb-3 max-h-60 overflow-y-auto bg-bg rounded-lg p-3 border border-border">
|
|
248
|
+
<div v-if="!docFlow.chatHistory.length" class="text-sm text-gray-500 italic text-center py-8">Start a conversation</div>
|
|
249
|
+
<div v-for="(msg,i) in docFlow.chatHistory" :key="i" class="p-3 rounded-lg text-sm"
|
|
250
|
+
:class="msg.role==='user'?'bg-brand/10 text-gray-300 ml-8':'bg-surface border border-border text-gray-300 mr-8'">
|
|
251
|
+
<div class="text-[10px] text-gray-500 mb-1">{{msg.role==='user'?'You':'Hivemind'}}</div>{{msg.text}}
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
<div class="flex gap-2">
|
|
255
|
+
<input v-model="docFlow.chatInput" @keydown.enter="docFlowChat" placeholder="Type your message…"
|
|
256
|
+
class="flex-1 bg-bg border border-border rounded-lg px-3 py-2 text-sm text-gray-300 focus:outline-none focus:border-brand/50"/>
|
|
257
|
+
<flow-btn @click="docFlowChat" :loading="docFlow.loading" size="sm">Send</flow-btn>
|
|
258
|
+
</div>
|
|
259
|
+
</step-card>
|
|
260
|
+
</div>
|
|
261
|
+
<flow-result :result="docFlow.lastResult"/>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<!-- FLOW: Content Generation -->
|
|
265
|
+
<div v-if="currentView==='flow-gen'" key="flow-gen">
|
|
266
|
+
<flow-header title="Content Generation" desc="Generate videos or images, then track jobs." :step="genFlow.step" :steps="genFlow.steps"></flow-header>
|
|
267
|
+
<div v-if="genFlow.step===0" class="max-w-lg">
|
|
268
|
+
<step-card title="Choose Type" subtitle="What kind of content?">
|
|
269
|
+
<text-field label="Org ID" v-model="genFlow.orgId" placeholder="my-org-1"/>
|
|
270
|
+
<div class="grid grid-cols-2 gap-3 mt-4">
|
|
271
|
+
<div v-for="t in ['video','image']" :key="t" @click="genFlow.type=t"
|
|
272
|
+
class="bg-bg border rounded-xl p-4 cursor-pointer transition text-center"
|
|
273
|
+
:class="genFlow.type===t?'border-brand bg-brand/10':'border-border hover:border-gray-600'">
|
|
274
|
+
<span class="mdi text-3xl" :class="t==='video'?'mdi-video':'mdi-image'" :style="{color:genFlow.type===t?'#6c63ff':'#6b7280'}"></span>
|
|
275
|
+
<div class="text-sm mt-2" :class="genFlow.type===t?'text-white':'text-gray-400'">{{t}}</div>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
<template #actions><flow-btn @click="genFlow.step=1" :disabled="!genFlow.type">Next →</flow-btn></template>
|
|
279
|
+
</step-card>
|
|
280
|
+
</div>
|
|
281
|
+
<div v-if="genFlow.step===1" class="max-w-lg">
|
|
282
|
+
<step-card :title="'Generate '+(genFlow.type==='video'?'Video':'Image')" subtitle="Describe what you want.">
|
|
283
|
+
<text-area label="Prompt" v-model="genFlow.prompt" placeholder="A cinematic drone shot over snowy mountains"/>
|
|
284
|
+
<div class="grid grid-cols-2 gap-3 mt-3">
|
|
285
|
+
<select-field label="Aspect Ratio" v-model="genFlow.aspectRatio" :options="['16:9','1:1','9:16','4:3']"/>
|
|
286
|
+
<text-field v-if="genFlow.type==='video'" label="Duration (sec)" v-model="genFlow.duration" placeholder="8"/>
|
|
287
|
+
<text-field v-else label="Num Images" v-model="genFlow.numImages" placeholder="1"/>
|
|
288
|
+
</div>
|
|
289
|
+
<text-field label="Style (optional)" v-model="genFlow.style" placeholder="cinematic, anime…"/>
|
|
290
|
+
<template #actions><flow-btn @click="genFlowSubmit" :loading="genFlow.loading">Generate</flow-btn></template>
|
|
291
|
+
</step-card>
|
|
292
|
+
</div>
|
|
293
|
+
<div v-if="genFlow.step===2" class="max-w-lg">
|
|
294
|
+
<step-card title="Job Submitted" subtitle="Track generation progress.">
|
|
295
|
+
<div class="bg-surface border border-border rounded-lg p-4">
|
|
296
|
+
<div class="flex items-center gap-3 mb-3">
|
|
297
|
+
<span class="mdi text-xl" :class="genFlow.jobStatus==='completed'?'mdi-check-circle text-green-400':genFlow.jobStatus==='failed'?'mdi-alert-circle text-red-400':'mdi-loading mdi-spin text-brand'"></span>
|
|
298
|
+
<div>
|
|
299
|
+
<div class="text-sm text-white">Job: {{genFlow.jobId}}</div>
|
|
300
|
+
<div class="text-xs text-gray-500">Status: <span class="font-semibold" :class="genFlow.jobStatus==='completed'?'text-green-400':genFlow.jobStatus==='failed'?'text-red-400':'text-yellow-400'">{{genFlow.jobStatus}}</span></div>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
<div v-if="genFlow.jobResult" class="bg-bg rounded-lg p-3 text-xs font-mono whitespace-pre-wrap text-gray-400 max-h-60 overflow-y-auto">{{JSON.stringify(genFlow.jobResult,null,2)}}</div>
|
|
304
|
+
</div>
|
|
305
|
+
<template #actions>
|
|
306
|
+
<flow-btn variant="secondary" @click="genFlowPoll" :loading="genFlow.loading">Refresh Status</flow-btn>
|
|
307
|
+
<flow-btn variant="secondary" @click="genFlow.step=0">Generate Another</flow-btn>
|
|
308
|
+
</template>
|
|
309
|
+
</step-card>
|
|
310
|
+
</div>
|
|
311
|
+
<flow-result :result="genFlow.lastResult"/>
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
<!-- FLOW: Blueprint → Document -->
|
|
315
|
+
<div v-if="currentView==='flow-blueprint'" key="flow-blueprint">
|
|
316
|
+
<flow-header title="Blueprint → Document" desc="Create a blueprint, publish, create document, AI-fill sections, workflow." :step="bpFlow.step" :steps="bpFlow.steps"></flow-header>
|
|
317
|
+
<div v-if="bpFlow.step===0" class="max-w-lg">
|
|
318
|
+
<step-card title="Create Blueprint" subtitle="Define a document template.">
|
|
319
|
+
<text-field label="Org ID" v-model="bpFlow.orgId" placeholder="my-org-1"/>
|
|
320
|
+
<text-field label="Name" v-model="bpFlow.name" placeholder="Quarterly Report Template"/>
|
|
321
|
+
<text-field label="Category" v-model="bpFlow.category" placeholder="Reports"/>
|
|
322
|
+
<text-area label="Description" v-model="bpFlow.description" placeholder="Template for quarterly reports"/>
|
|
323
|
+
<text-area label="Sections (JSON)" v-model="bpFlow.sectionsJson" placeholder='[{"title":"Executive Summary","required":true}]'/>
|
|
324
|
+
<template #actions><flow-btn @click="bpFlowCreate" :loading="bpFlow.loading">Create Blueprint</flow-btn></template>
|
|
325
|
+
</step-card>
|
|
326
|
+
</div>
|
|
327
|
+
<div v-if="bpFlow.step===1" class="max-w-lg">
|
|
328
|
+
<step-card title="Blueprint Created" subtitle="Now publish it.">
|
|
329
|
+
<kv-display :items="{id:bpFlow.blueprintId,name:bpFlow.name,status:'draft'}"/>
|
|
330
|
+
<template #actions><flow-btn @click="bpFlowPublish" :loading="bpFlow.loading">Publish Blueprint</flow-btn></template>
|
|
331
|
+
</step-card>
|
|
332
|
+
</div>
|
|
333
|
+
<div v-if="bpFlow.step===2" class="max-w-lg">
|
|
334
|
+
<step-card title="Create Document" subtitle="Create a managed document from this blueprint.">
|
|
335
|
+
<text-field label="Document Title" v-model="bpFlow.docTitle" placeholder="Q1 2026 Report"/>
|
|
336
|
+
<text-area label="Description" v-model="bpFlow.docDesc" placeholder="First quarter report"/>
|
|
337
|
+
<template #actions><flow-btn @click="bpFlowCreateDoc" :loading="bpFlow.loading">Create Document</flow-btn></template>
|
|
338
|
+
</step-card>
|
|
339
|
+
</div>
|
|
340
|
+
<div v-if="bpFlow.step===3" class="max-w-lg">
|
|
341
|
+
<step-card title="AI Generate Sections" subtitle="Fill document sections with AI.">
|
|
342
|
+
<div class="text-xs text-gray-500 mb-2">Document: {{bpFlow.docId}}</div>
|
|
343
|
+
<div v-for="sec in bpFlow.docSections" :key="sec.id" class="flex items-center gap-3 bg-bg rounded-lg p-3 mb-2 border border-border">
|
|
344
|
+
<span class="mdi" :class="sec.generated?'mdi-check-circle text-green-400':'mdi-circle-outline text-gray-600'"></span>
|
|
345
|
+
<span class="flex-1 text-sm text-gray-300">{{sec.title||sec.id}}</span>
|
|
346
|
+
<button @click="bpFlowGenSection(sec)" class="text-xs bg-brand/20 text-brand px-3 py-1 rounded-lg hover:bg-brand/30" :disabled="sec.loading">
|
|
347
|
+
{{sec.loading?'Generating…':sec.generated?'Regenerate':'AI Generate'}}
|
|
348
|
+
</button>
|
|
349
|
+
</div>
|
|
350
|
+
<text-field label="Section ID" v-model="bpFlow.manualSectionId" placeholder="section-1"/>
|
|
351
|
+
<text-area label="Context (JSON)" v-model="bpFlow.genContext" placeholder='{"quarter":"Q1"}'/>
|
|
352
|
+
<template #actions>
|
|
353
|
+
<flow-btn @click="bpFlowGenManual" :loading="bpFlow.loading">Generate Section</flow-btn>
|
|
354
|
+
<flow-btn variant="secondary" @click="bpFlow.step=4">Next → Workflow</flow-btn>
|
|
355
|
+
</template>
|
|
356
|
+
</step-card>
|
|
357
|
+
</div>
|
|
358
|
+
<div v-if="bpFlow.step===4" class="max-w-lg">
|
|
359
|
+
<step-card title="Submit for Review" subtitle="Submit the document through the approval workflow.">
|
|
360
|
+
<text-field label="Document ID" v-model="bpFlow.docId" disabled/>
|
|
361
|
+
<select-field label="Action" v-model="bpFlow.wfAction" :options="['submit','approve','reject','request_changes']"/>
|
|
362
|
+
<text-area label="Comment" v-model="bpFlow.wfComment" placeholder="Ready for review"/>
|
|
363
|
+
<template #actions>
|
|
364
|
+
<flow-btn @click="bpFlowWorkflow" :loading="bpFlow.loading">Execute</flow-btn>
|
|
365
|
+
<flow-btn variant="secondary" @click="currentView='home'">Done</flow-btn>
|
|
366
|
+
</template>
|
|
367
|
+
</step-card>
|
|
368
|
+
</div>
|
|
369
|
+
<flow-result :result="bpFlow.lastResult"/>
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
<!-- FLOW: Jira -->
|
|
373
|
+
<div v-if="currentView==='flow-jira'" key="flow-jira">
|
|
374
|
+
<flow-header title="Jira Integration" desc="Connect Jira, check status, sync projects." :step="jiraFlow.step" :steps="jiraFlow.steps"></flow-header>
|
|
375
|
+
<div v-if="jiraFlow.step===0" class="max-w-lg">
|
|
376
|
+
<step-card title="Connect Jira" subtitle="Enter your Atlassian credentials.">
|
|
377
|
+
<text-field label="Org ID" v-model="jiraFlow.orgId" placeholder="my-org-1"/>
|
|
378
|
+
<text-field label="Jira Site URL" v-model="jiraFlow.siteUrl" placeholder="https://yoursite.atlassian.net"/>
|
|
379
|
+
<text-field label="Email" v-model="jiraFlow.email" placeholder="user@company.com"/>
|
|
380
|
+
<text-field label="API Token" v-model="jiraFlow.apiToken" placeholder="Atlassian API token" type="password"/>
|
|
381
|
+
<template #actions><flow-btn @click="jiraFlowConnect" :loading="jiraFlow.loading">Connect</flow-btn></template>
|
|
382
|
+
</step-card>
|
|
383
|
+
</div>
|
|
384
|
+
<div v-if="jiraFlow.step===1" class="max-w-lg">
|
|
385
|
+
<step-card title="Connection Status" subtitle="Verify integration health.">
|
|
386
|
+
<kv-display v-if="jiraFlow.statusData" :items="jiraFlow.statusData"/>
|
|
387
|
+
<template #actions>
|
|
388
|
+
<flow-btn variant="secondary" @click="jiraFlowStatus" :loading="jiraFlow.loading">Refresh</flow-btn>
|
|
389
|
+
<flow-btn @click="jiraFlow.step=2">Next → Sync</flow-btn>
|
|
390
|
+
</template>
|
|
391
|
+
</step-card>
|
|
392
|
+
</div>
|
|
393
|
+
<div v-if="jiraFlow.step===2" class="max-w-lg">
|
|
394
|
+
<step-card title="Sync Projects" subtitle="Import issues from Jira projects.">
|
|
395
|
+
<text-field label="Project Keys (comma-separated)" v-model="jiraFlow.projectKeys" placeholder="PROJ, TEAM"/>
|
|
396
|
+
<template #actions><flow-btn @click="jiraFlowSync" :loading="jiraFlow.loading">Start Sync</flow-btn></template>
|
|
397
|
+
</step-card>
|
|
398
|
+
</div>
|
|
399
|
+
<div v-if="jiraFlow.step===3" class="max-w-lg">
|
|
400
|
+
<step-card title="Manage" subtitle="Disconnect Jira when no longer needed.">
|
|
401
|
+
<template #actions>
|
|
402
|
+
<flow-btn variant="danger" @click="jiraFlowDisconnect" :loading="jiraFlow.loading">Disconnect</flow-btn>
|
|
403
|
+
<flow-btn variant="secondary" @click="currentView='home'">Done</flow-btn>
|
|
404
|
+
</template>
|
|
405
|
+
</step-card>
|
|
406
|
+
</div>
|
|
407
|
+
<flow-result :result="jiraFlow.lastResult"/>
|
|
408
|
+
</div>
|
|
409
|
+
|
|
410
|
+
<!-- FLOW: Scraping -->
|
|
411
|
+
<div v-if="currentView==='flow-scrape'" key="flow-scrape">
|
|
412
|
+
<flow-header title="Web Scraping" desc="Submit URLs, monitor progress, retrieve results." :step="scrapeFlow.step" :steps="scrapeFlow.steps"></flow-header>
|
|
413
|
+
<div v-if="scrapeFlow.step===0" class="max-w-lg">
|
|
414
|
+
<step-card title="Configure & Submit" subtitle="Enter URLs to scrape.">
|
|
415
|
+
<text-area label="URLs (one per line)" v-model="scrapeFlow.urls" placeholder="https://example.com"/>
|
|
416
|
+
<template #actions>
|
|
417
|
+
<flow-btn @click="scrapeFlowSubmit" :loading="scrapeFlow.loading">Start Scraping</flow-btn>
|
|
418
|
+
<flow-btn variant="secondary" @click="scrapeFlowConfig" :loading="scrapeFlow.loading2">View Config</flow-btn>
|
|
419
|
+
</template>
|
|
420
|
+
<div v-if="scrapeFlow.configData" class="mt-4 bg-bg rounded-lg p-3 text-xs font-mono whitespace-pre-wrap text-gray-400">{{JSON.stringify(scrapeFlow.configData,null,2)}}</div>
|
|
421
|
+
</step-card>
|
|
422
|
+
</div>
|
|
423
|
+
<div v-if="scrapeFlow.step===1" class="max-w-lg">
|
|
424
|
+
<step-card title="Monitoring" subtitle="Track scraping progress.">
|
|
425
|
+
<div class="bg-surface border border-border rounded-lg p-4">
|
|
426
|
+
<div class="flex items-center gap-3 mb-3">
|
|
427
|
+
<span class="mdi text-xl" :class="scrapeFlow.status==='completed'?'mdi-check-circle text-green-400':scrapeFlow.status==='failed'?'mdi-alert-circle text-red-400':'mdi-loading mdi-spin text-brand'"></span>
|
|
428
|
+
<div>
|
|
429
|
+
<div class="text-sm text-white">Task: {{scrapeFlow.taskId}}</div>
|
|
430
|
+
<div class="text-xs text-gray-500">Status: <span class="font-semibold" :class="scrapeFlow.status==='completed'?'text-green-400':scrapeFlow.status==='failed'?'text-red-400':'text-yellow-400'">{{scrapeFlow.status}}</span></div>
|
|
431
|
+
</div>
|
|
432
|
+
</div>
|
|
433
|
+
<div v-if="scrapeFlow.taskResult" class="bg-bg rounded-lg p-3 text-xs font-mono whitespace-pre-wrap text-gray-400 max-h-80 overflow-y-auto">{{JSON.stringify(scrapeFlow.taskResult,null,2)}}</div>
|
|
434
|
+
</div>
|
|
435
|
+
<template #actions>
|
|
436
|
+
<flow-btn variant="secondary" @click="scrapeFlowPoll" :loading="scrapeFlow.loading">Refresh</flow-btn>
|
|
437
|
+
<flow-btn variant="secondary" @click="scrapeFlow.step=0">Scrape More</flow-btn>
|
|
438
|
+
</template>
|
|
439
|
+
</step-card>
|
|
440
|
+
</div>
|
|
441
|
+
<flow-result :result="scrapeFlow.lastResult"/>
|
|
442
|
+
</div>
|
|
443
|
+
|
|
444
|
+
<!-- FLOW: AI Agents -->
|
|
445
|
+
<div v-if="currentView==='flow-agents'" key="flow-agents">
|
|
446
|
+
<flow-header title="AI Agents" desc="Create agents and chat with them." :step="agentFlow.step" :steps="agentFlow.steps"></flow-header>
|
|
447
|
+
<div v-if="agentFlow.step===0" class="max-w-lg">
|
|
448
|
+
<step-card title="Create Agent" subtitle="Configure a custom AI agent.">
|
|
449
|
+
<text-field label="Org ID" v-model="agentFlow.orgId" placeholder="my-org-1"/>
|
|
450
|
+
<text-field label="Name" v-model="agentFlow.name" placeholder="Support Bot"/>
|
|
451
|
+
<text-area label="Instructions" v-model="agentFlow.instructions" placeholder="You are a helpful agent…"/>
|
|
452
|
+
<text-area label="Tools (JSON)" v-model="agentFlow.tools" placeholder='["rag_search"]'/>
|
|
453
|
+
<template #actions>
|
|
454
|
+
<flow-btn @click="agentFlowCreate" :loading="agentFlow.loading">Create Agent</flow-btn>
|
|
455
|
+
<flow-btn variant="secondary" @click="agentFlow.step=1">Skip → Chat</flow-btn>
|
|
456
|
+
</template>
|
|
457
|
+
</step-card>
|
|
458
|
+
</div>
|
|
459
|
+
<div v-if="agentFlow.step===1" class="max-w-2xl">
|
|
460
|
+
<step-card title="Chat with Agent" subtitle="Interactive conversation.">
|
|
461
|
+
<div class="flex gap-3 mb-4">
|
|
462
|
+
<text-field label="Org ID" v-model="agentFlow.orgId" placeholder="my-org-1" class="flex-1"/>
|
|
463
|
+
<select-field label="Agent Type" v-model="agentFlow.agentType" :options="['auto','rag','data','custom']" class="flex-1"/>
|
|
464
|
+
</div>
|
|
465
|
+
<div class="space-y-2 mb-3 max-h-80 overflow-y-auto bg-bg rounded-lg p-3 border border-border">
|
|
466
|
+
<div v-if="!agentFlow.chatHistory.length" class="text-sm text-gray-500 italic text-center py-8">Start a conversation</div>
|
|
467
|
+
<div v-for="(msg,i) in agentFlow.chatHistory" :key="i" class="p-3 rounded-lg text-sm"
|
|
468
|
+
:class="msg.role==='user'?'bg-brand/10 text-gray-300 ml-12':'bg-surface border border-border text-gray-300 mr-12'">
|
|
469
|
+
<div class="text-[10px] text-gray-500 mb-1 font-semibold">{{msg.role==='user'?'You':'Agent'}}</div>
|
|
470
|
+
<div class="whitespace-pre-wrap">{{msg.text}}</div>
|
|
471
|
+
</div>
|
|
472
|
+
</div>
|
|
473
|
+
<div class="flex gap-2">
|
|
474
|
+
<input v-model="agentFlow.chatInput" @keydown.enter="agentFlowQuery" placeholder="Ask something…"
|
|
475
|
+
class="flex-1 bg-bg border border-border rounded-lg px-3 py-2 text-sm text-gray-300 focus:outline-none focus:border-brand/50"/>
|
|
476
|
+
<flow-btn @click="agentFlowQuery" :loading="agentFlow.loading" size="sm">Send</flow-btn>
|
|
477
|
+
</div>
|
|
478
|
+
</step-card>
|
|
479
|
+
</div>
|
|
480
|
+
<flow-result :result="agentFlow.lastResult"/>
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
<!-- FLOW: Analytics & ML -->
|
|
484
|
+
<div v-if="currentView==='flow-analytics'" key="flow-analytics">
|
|
485
|
+
<flow-header title="Analytics & ML" desc="Train models, predict, and forecast." :step="mlFlow.step" :steps="mlFlow.steps"></flow-header>
|
|
486
|
+
<div v-if="mlFlow.step===0" class="max-w-lg">
|
|
487
|
+
<step-card title="Train Model" subtitle="Provide training data.">
|
|
488
|
+
<text-field label="Org ID" v-model="mlFlow.orgId" placeholder="my-org-1"/>
|
|
489
|
+
<text-field label="Target Column" v-model="mlFlow.target" placeholder="churn"/>
|
|
490
|
+
<text-area label="Feature Columns (JSON)" v-model="mlFlow.features" placeholder='["tenure","charges"]'/>
|
|
491
|
+
<text-area label="Training Rows (JSON)" v-model="mlFlow.rows" placeholder='[{"tenure":12,"charges":50,"churn":0}]'/>
|
|
492
|
+
<template #actions>
|
|
493
|
+
<flow-btn @click="mlFlowTrain" :loading="mlFlow.loading">Train</flow-btn>
|
|
494
|
+
<flow-btn variant="secondary" @click="mlFlow.step=1">Skip → Predict</flow-btn>
|
|
495
|
+
</template>
|
|
496
|
+
</step-card>
|
|
497
|
+
</div>
|
|
498
|
+
<div v-if="mlFlow.step===1" class="max-w-lg">
|
|
499
|
+
<step-card title="Predict" subtitle="Use a trained model.">
|
|
500
|
+
<text-field label="Org ID" v-model="mlFlow.orgId"/>
|
|
501
|
+
<text-field label="Model ID" v-model="mlFlow.modelId" placeholder="model-xxx"/>
|
|
502
|
+
<text-area label="Instances (JSON)" v-model="mlFlow.instances" placeholder='[{"tenure":5}]'/>
|
|
503
|
+
<template #actions>
|
|
504
|
+
<flow-btn @click="mlFlowPredict" :loading="mlFlow.loading">Predict</flow-btn>
|
|
505
|
+
<flow-btn variant="secondary" @click="mlFlow.step=2">Next → Forecast</flow-btn>
|
|
506
|
+
</template>
|
|
507
|
+
</step-card>
|
|
508
|
+
</div>
|
|
509
|
+
<div v-if="mlFlow.step===2" class="max-w-lg">
|
|
510
|
+
<step-card title="Forecast" subtitle="Time-series forecasting.">
|
|
511
|
+
<text-field label="Org ID" v-model="mlFlow.orgId"/>
|
|
512
|
+
<text-area label="Series (JSON)" v-model="mlFlow.series" placeholder="[100,120,115,130]"/>
|
|
513
|
+
<text-field label="Horizon" v-model="mlFlow.horizon" placeholder="30"/>
|
|
514
|
+
<template #actions><flow-btn @click="mlFlowForecast" :loading="mlFlow.loading">Forecast</flow-btn></template>
|
|
515
|
+
</step-card>
|
|
516
|
+
</div>
|
|
517
|
+
<flow-result :result="mlFlow.lastResult"/>
|
|
518
|
+
</div>
|
|
519
|
+
|
|
520
|
+
<!-- FLOW: Audio -->
|
|
521
|
+
<div v-if="currentView==='flow-audio'" key="flow-audio">
|
|
522
|
+
<flow-header title="Audio Processing" desc="Transcribe and synthesize speech." :step="audioFlow.step" :steps="audioFlow.steps"></flow-header>
|
|
523
|
+
<div v-if="audioFlow.step===0" class="max-w-lg">
|
|
524
|
+
<step-card title="Transcribe" subtitle="Audio to text.">
|
|
525
|
+
<text-field label="Org ID" v-model="audioFlow.orgId" placeholder="my-org-1"/>
|
|
526
|
+
<text-area label="Audio Text / URL" v-model="audioFlow.audioText" placeholder="Content to process"/>
|
|
527
|
+
<select-field label="Language" v-model="audioFlow.lang" :options="['en-US','en-GB','es-ES','fr-FR','de-DE','ja-JP','zh-CN']"/>
|
|
528
|
+
<template #actions>
|
|
529
|
+
<flow-btn @click="audioFlowTranscribe" :loading="audioFlow.loading">Transcribe</flow-btn>
|
|
530
|
+
<flow-btn variant="secondary" @click="audioFlow.step=1">Skip → Synthesize</flow-btn>
|
|
531
|
+
</template>
|
|
532
|
+
</step-card>
|
|
533
|
+
</div>
|
|
534
|
+
<div v-if="audioFlow.step===1" class="max-w-lg">
|
|
535
|
+
<step-card title="Synthesize Speech" subtitle="Text to audio.">
|
|
536
|
+
<text-field label="Org ID" v-model="audioFlow.orgId"/>
|
|
537
|
+
<text-area label="Text" v-model="audioFlow.synthText" placeholder="Hello! Welcome to Hivemind."/>
|
|
538
|
+
<text-field label="Voice" v-model="audioFlow.voice" placeholder="en-US-Neural2-J"/>
|
|
539
|
+
<template #actions><flow-btn @click="audioFlowSynthesize" :loading="audioFlow.loading">Synthesize</flow-btn></template>
|
|
540
|
+
</step-card>
|
|
541
|
+
</div>
|
|
542
|
+
<flow-result :result="audioFlow.lastResult"/>
|
|
543
|
+
</div>
|
|
544
|
+
|
|
545
|
+
<!-- FLOW: Billing & Audit -->
|
|
546
|
+
<div v-if="currentView==='flow-billing'" key="flow-billing">
|
|
547
|
+
<h1 class="text-2xl font-bold text-white mb-1">Billing & Audit</h1>
|
|
548
|
+
<p class="text-gray-400 text-sm mb-6">View usage, invoices, and audit trails.</p>
|
|
549
|
+
<div class="max-w-lg space-y-4">
|
|
550
|
+
<step-card title="Usage" subtitle="View usage for an org.">
|
|
551
|
+
<div class="flex gap-3"><text-field label="Org ID" v-model="billingFlow.orgId" class="flex-1"/><text-field label="Month" v-model="billingFlow.month" placeholder="2026-03" class="flex-1"/></div>
|
|
552
|
+
<template #actions><flow-btn @click="billingFlowUsage" :loading="billingFlow.loadingU">Get Usage</flow-btn></template>
|
|
553
|
+
<div v-if="billingFlow.usageData" class="mt-4 bg-bg rounded-lg p-3 text-xs font-mono whitespace-pre-wrap text-gray-400 max-h-60 overflow-y-auto">{{JSON.stringify(billingFlow.usageData,null,2)}}</div>
|
|
554
|
+
</step-card>
|
|
555
|
+
<step-card title="Invoice" subtitle="Retrieve invoice details.">
|
|
556
|
+
<div class="flex gap-3"><text-field label="Org ID" v-model="billingFlow.orgId" class="flex-1"/><text-field label="Month" v-model="billingFlow.month" class="flex-1"/></div>
|
|
557
|
+
<template #actions><flow-btn @click="billingFlowInvoice" :loading="billingFlow.loadingI">Get Invoice</flow-btn></template>
|
|
558
|
+
<div v-if="billingFlow.invoiceData" class="mt-4 bg-bg rounded-lg p-3 text-xs font-mono whitespace-pre-wrap text-gray-400 max-h-60 overflow-y-auto">{{JSON.stringify(billingFlow.invoiceData,null,2)}}</div>
|
|
559
|
+
</step-card>
|
|
560
|
+
<step-card title="Audit Logs" subtitle="View audit trail.">
|
|
561
|
+
<text-field label="Org ID" v-model="billingFlow.orgId"/>
|
|
562
|
+
<template #actions><flow-btn @click="billingFlowAudit" :loading="billingFlow.loadingA">Get Logs</flow-btn></template>
|
|
563
|
+
<div v-if="billingFlow.auditData" class="mt-4 bg-bg rounded-lg p-3 text-xs font-mono whitespace-pre-wrap text-gray-400 max-h-60 overflow-y-auto">{{JSON.stringify(billingFlow.auditData,null,2)}}</div>
|
|
564
|
+
</step-card>
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
|
|
568
|
+
<!-- FLOW: Admin -->
|
|
569
|
+
<div v-if="currentView==='flow-admin'" key="flow-admin">
|
|
570
|
+
<h1 class="text-2xl font-bold text-white mb-1">Admin Dashboard</h1>
|
|
571
|
+
<p class="text-gray-400 text-sm mb-6">System overview and controls.</p>
|
|
572
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6">
|
|
573
|
+
<div v-for="w in adminWidgets" :key="w.id" class="bg-surface border border-border rounded-xl p-4 hover:border-brand/30 transition">
|
|
574
|
+
<div class="flex items-center justify-between mb-3">
|
|
575
|
+
<div class="flex items-center gap-2"><span class="mdi text-lg" :class="w.icon+' '+w.color"></span><span class="text-sm font-semibold text-white">{{w.title}}</span></div>
|
|
576
|
+
<button @click="adminFetch(w)" class="text-xs text-gray-500 hover:text-brand"><span class="mdi" :class="w.loading?'mdi-loading mdi-spin':'mdi-refresh'"></span></button>
|
|
577
|
+
</div>
|
|
578
|
+
<div v-if="w.data" class="bg-bg rounded-lg p-3 text-xs font-mono whitespace-pre-wrap text-gray-400 max-h-48 overflow-y-auto">{{JSON.stringify(w.data,null,2)}}</div>
|
|
579
|
+
<div v-else class="text-xs text-gray-600 italic">Click refresh to load</div>
|
|
580
|
+
</div>
|
|
581
|
+
</div>
|
|
582
|
+
<h2 class="text-lg font-bold text-white mb-3">Actions</h2>
|
|
583
|
+
<div class="flex flex-wrap gap-2 mb-4">
|
|
584
|
+
<button v-for="act in adminActions" :key="act.id" @click="act.open=!act.open"
|
|
585
|
+
class="text-xs px-3 py-1.5 rounded-lg transition"
|
|
586
|
+
:class="act.open?'bg-brand text-white':'bg-surface border border-border text-gray-400 hover:border-gray-600'">
|
|
587
|
+
<span class="mdi" :class="act.icon"></span> {{act.title}}
|
|
588
|
+
</button>
|
|
589
|
+
</div>
|
|
590
|
+
<div v-for="act in adminActions.filter(a=>a.open)" :key="'a-'+act.id" class="bg-surface border border-border rounded-xl p-4 mb-3 max-w-lg">
|
|
591
|
+
<div class="text-sm font-semibold text-white mb-3">{{act.title}}</div>
|
|
592
|
+
<div class="space-y-3 mb-4">
|
|
593
|
+
<div v-for="f in act.fields" :key="f.key"><label class="text-xs text-gray-500">{{f.label}}</label>
|
|
594
|
+
<input v-model="act.values[f.key]" :type="f.type||'text'" :placeholder="f.placeholder||''"
|
|
595
|
+
class="mt-1 w-full bg-bg border border-border rounded-lg px-3 py-1.5 text-sm text-gray-300 focus:outline-none focus:border-brand/50"/>
|
|
596
|
+
</div>
|
|
597
|
+
</div>
|
|
598
|
+
<button @click="adminRunAction(act)" class="bg-brand text-white px-4 py-1.5 rounded-lg text-sm font-semibold hover:bg-brand/80 transition" :disabled="act.loading">{{act.loading?'Running…':'Execute'}}</button>
|
|
599
|
+
<div v-if="act.result" class="mt-3 bg-bg rounded-lg p-3 text-xs font-mono whitespace-pre-wrap text-gray-400 max-h-48 overflow-y-auto border" :class="act.resultOk?'border-green-800':'border-red-800'">{{act.result}}</div>
|
|
600
|
+
</div>
|
|
601
|
+
</div>
|
|
602
|
+
|
|
603
|
+
</transition>
|
|
604
|
+
</div>
|
|
605
|
+
</main>
|
|
606
|
+
</div>
|
|
607
|
+
|
|
608
|
+
<!-- Toast -->
|
|
609
|
+
<transition name="fade">
|
|
610
|
+
<div v-if="toast.show" class="fixed bottom-5 right-5 bg-surface border border-brand rounded-xl px-5 py-3 text-sm shadow-xl shadow-black/30 z-50 flex items-center gap-2">
|
|
611
|
+
<span class="mdi" :class="toast.ok?'mdi-check-circle text-green-400':'mdi-alert-circle text-red-400'"></span>{{toast.msg}}
|
|
612
|
+
</div>
|
|
613
|
+
</transition>
|
|
614
|
+
</div>
|
|
615
|
+
|
|
616
|
+
<script>
|
|
617
|
+
const {createApp,ref,reactive,computed,onMounted}=Vue;
|
|
618
|
+
|
|
619
|
+
createApp({
|
|
620
|
+
setup(){
|
|
621
|
+
const cfg=reactive({baseUrl:'${esc(config.baseUrl)}',apiPrefix:'${esc(config.apiPrefix)}',apiKey:'${esc(config.apiKey)}',adminKey:'${esc(config.adminKey)}',webhookSecret:'${esc(config.webhookSecret)}',employeeId:'${esc(config.employeeId)}'});
|
|
622
|
+
const settingsFields=[
|
|
623
|
+
{key:'baseUrl',label:'Base URL',placeholder:'https://api.example.com'},
|
|
624
|
+
{key:'apiPrefix',label:'API Prefix',placeholder:'/v1'},
|
|
625
|
+
{key:'apiKey',label:'API Key',placeholder:'vtx_org_xxx',secret:true},
|
|
626
|
+
{key:'adminKey',label:'Admin Key',placeholder:'admin-key',secret:true},
|
|
627
|
+
{key:'webhookSecret',label:'Webhook Secret',placeholder:'whsec_xxx',secret:true},
|
|
628
|
+
{key:'employeeId',label:'Employee ID',placeholder:'emp-001'},
|
|
629
|
+
];
|
|
630
|
+
const settingsMsg=ref('');const settingsMsgOk=ref(false);
|
|
631
|
+
|
|
632
|
+
async function api(method,path,body,extra={}){
|
|
633
|
+
const url=cfg.baseUrl.replace(/\\/+$/,'')+cfg.apiPrefix+path;
|
|
634
|
+
const h={...extra};
|
|
635
|
+
if(cfg.apiKey)h['Authorization']='Bearer '+cfg.apiKey;
|
|
636
|
+
if(cfg.adminKey)h['X-Admin-Key']=cfg.adminKey;
|
|
637
|
+
if(cfg.webhookSecret)h['X-Webhook-Secret']=cfg.webhookSecret;
|
|
638
|
+
if(cfg.employeeId)h['X-Employee-Id']=cfg.employeeId;
|
|
639
|
+
const opts={method,headers:h};
|
|
640
|
+
if(body!==undefined&&body!==null&&!(body instanceof FormData)){h['Content-Type']='application/json';opts.body=JSON.stringify(body);}
|
|
641
|
+
else if(body instanceof FormData){opts.body=body;}
|
|
642
|
+
const res=await fetch(url,opts);const text=await res.text();
|
|
643
|
+
let json;try{json=JSON.parse(text);}catch{json=text;}
|
|
644
|
+
return{ok:res.ok,status:res.status,data:json};
|
|
645
|
+
}
|
|
646
|
+
function tryJson(s){try{return JSON.parse(s);}catch{return s;}}
|
|
647
|
+
|
|
648
|
+
const toast=reactive({show:false,msg:'',ok:true});
|
|
649
|
+
function showToast(m,ok=true){toast.msg=m;toast.ok=ok;toast.show=true;setTimeout(()=>toast.show=false,3000);}
|
|
650
|
+
function copyText(t){navigator.clipboard.writeText(t);showToast('Copied!');}
|
|
651
|
+
|
|
652
|
+
const health=reactive({ok:false,text:'Checking…'});
|
|
653
|
+
async function checkHealth(){try{const r=await api('GET','/health');if(r.ok){health.ok=true;health.text=(r.data.service||'API')+' — '+r.data.status;}else{health.ok=false;health.text='Error '+r.status;}}catch{health.ok=false;health.text='Unreachable';}}
|
|
654
|
+
async function saveSettings(){settingsMsg.value='Testing…';await checkHealth();if(health.ok){settingsMsg.value='Connected!';settingsMsgOk.value=true;showToast('Connected!');}else{settingsMsg.value='Failed: '+health.text;settingsMsgOk.value=false;showToast('Failed',false);}}
|
|
655
|
+
|
|
656
|
+
const currentView=ref('home');const search=ref('');
|
|
657
|
+
|
|
658
|
+
const navSections=[
|
|
659
|
+
{name:'Home',items:[{view:'home',label:'Dashboard',icon:'mdi-view-dashboard'},{view:'settings',label:'Settings',icon:'mdi-cog'},{view:'quick',label:'Quick Actions',icon:'mdi-lightning-bolt'}]},
|
|
660
|
+
{name:'Flows',items:[
|
|
661
|
+
{view:'flow-org',label:'Organization Setup',icon:'mdi-domain'},
|
|
662
|
+
{view:'flow-docs',label:'Document Pipeline',icon:'mdi-file-document'},
|
|
663
|
+
{view:'flow-gen',label:'Content Generation',icon:'mdi-creation'},
|
|
664
|
+
{view:'flow-blueprint',label:'Blueprint → Document',icon:'mdi-file-tree'},
|
|
665
|
+
{view:'flow-agents',label:'AI Agents',icon:'mdi-robot'},
|
|
666
|
+
{view:'flow-jira',label:'Jira Integration',icon:'mdi-jira'},
|
|
667
|
+
{view:'flow-scrape',label:'Web Scraping',icon:'mdi-web'},
|
|
668
|
+
{view:'flow-analytics',label:'Analytics & ML',icon:'mdi-chart-line'},
|
|
669
|
+
{view:'flow-audio',label:'Audio Processing',icon:'mdi-microphone'},
|
|
670
|
+
]},
|
|
671
|
+
{name:'Management',items:[{view:'flow-billing',label:'Billing & Audit',icon:'mdi-cash-register'},{view:'flow-admin',label:'Admin Dashboard',icon:'mdi-shield-crown'}]},
|
|
672
|
+
];
|
|
673
|
+
|
|
674
|
+
const flows=[
|
|
675
|
+
{view:'flow-org',title:'Organization Setup',icon:'mdi-domain',color:'bg-blue-500/20 text-blue-400',steps:4,desc:'Create org, provision API keys, configure settings.',tags:['create','api-key','verify','rotate']},
|
|
676
|
+
{view:'flow-docs',title:'Document Pipeline',icon:'mdi-file-document',color:'bg-emerald-500/20 text-emerald-400',steps:4,desc:'Upload docs, RAG query, data chat.',tags:['upload','RAG','query','data-chat']},
|
|
677
|
+
{view:'flow-gen',title:'Content Generation',icon:'mdi-creation',color:'bg-purple-500/20 text-purple-400',steps:3,desc:'Generate videos/images, track jobs.',tags:['video','image','job-tracking']},
|
|
678
|
+
{view:'flow-blueprint',title:'Blueprint → Document',icon:'mdi-file-tree',color:'bg-amber-500/20 text-amber-400',steps:5,desc:'Create blueprints, generate documents, AI-fill sections, workflow.',tags:['blueprint','sections','AI-generate','workflow']},
|
|
679
|
+
{view:'flow-agents',title:'AI Agents',icon:'mdi-robot',color:'bg-pink-500/20 text-pink-400',steps:2,desc:'Create custom agents and chat in real-time.',tags:['custom-agent','chat','tools']},
|
|
680
|
+
{view:'flow-jira',title:'Jira Integration',icon:'mdi-jira',color:'bg-sky-500/20 text-sky-400',steps:4,desc:'Connect, verify, sync projects, manage.',tags:['connect','sync','disconnect']},
|
|
681
|
+
{view:'flow-scrape',title:'Web Scraping',icon:'mdi-web',color:'bg-teal-500/20 text-teal-400',steps:2,desc:'Submit URLs, monitor progress, collect results.',tags:['URLs','monitor','results']},
|
|
682
|
+
{view:'flow-analytics',title:'Analytics & ML',icon:'mdi-chart-line',color:'bg-orange-500/20 text-orange-400',steps:3,desc:'Train ML models, predict, forecast.',tags:['train','predict','forecast']},
|
|
683
|
+
{view:'flow-audio',title:'Audio Processing',icon:'mdi-microphone',color:'bg-rose-500/20 text-rose-400',steps:2,desc:'Transcribe audio, synthesize speech.',tags:['transcribe','synthesize']},
|
|
684
|
+
];
|
|
685
|
+
|
|
686
|
+
// Quick Actions
|
|
687
|
+
const quickCat=ref('All');
|
|
688
|
+
const quickCategories=['All','Organization','Documents','Generation','Agents','Audio','Analytics','Billing','Audit','Scraping','Webhooks','Jira','Blueprints','Managed Docs','Admin'];
|
|
689
|
+
function qf(key,label,placeholder,type){return{key,label,placeholder,type:type||'text'};}
|
|
690
|
+
const orgF=qf('orgId','Org ID','my-org-1');
|
|
691
|
+
|
|
692
|
+
const quickActions=reactive([
|
|
693
|
+
{id:'q0',cat:'Organization',method:'GET',title:'Health Check',path:'/health',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/health')},
|
|
694
|
+
{id:'q1',cat:'Organization',method:'POST',title:'Create Organization',path:'/organizations',fields:[orgF,qf('name','Name','Acme'),qf('tier','Tier','standard')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations',{org_id:v.orgId,name:v.name,tier:v.tier||'standard',settings:{}})},
|
|
695
|
+
{id:'q2',cat:'Organization',method:'GET',title:'Get Organization',path:'/organizations/{orgId}',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId)},
|
|
696
|
+
{id:'q3',cat:'Organization',method:'POST',title:'Rotate API Key',path:'/organizations/{orgId}/api-keys/rotate',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/api-keys/rotate')},
|
|
697
|
+
{id:'q4',cat:'Organization',method:'POST',title:'Suspend Org',path:'/organizations/{orgId}/suspend',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/suspend')},
|
|
698
|
+
{id:'q5',cat:'Organization',method:'POST',title:'Reactivate Org',path:'/organizations/{orgId}/reactivate',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/reactivate')},
|
|
699
|
+
{id:'q6',cat:'Organization',method:'DELETE',title:'Delete Organization',path:'/organizations/{orgId}',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('DELETE','/organizations/'+v.orgId)},
|
|
700
|
+
{id:'q7',cat:'Documents',method:'POST',title:'RAG Query',path:'/organizations/{orgId}/query',fields:[orgF,qf('query','Query','key findings?'),qf('model','Model','gemini-pro'),qf('strategy','Strategy','hybrid')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/query',{query:v.query,model_type:v.model||'gemini-pro',retrieval_strategy:v.strategy||'hybrid'})},
|
|
701
|
+
{id:'q8',cat:'Documents',method:'POST',title:'Data Chat',path:'/organizations/{orgId}/data-chat',fields:[orgF,qf('question','Question','active users?')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/data-chat',{question:v.question})},
|
|
702
|
+
{id:'q9',cat:'Generation',method:'POST',title:'Generate Video',path:'/organizations/{orgId}/generate/video',fields:[orgF,qf('prompt','Prompt','Drone flyover'),qf('duration','Duration','8'),qf('aspect','Aspect','16:9')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/generate/video',{prompt:v.prompt,duration:parseInt(v.duration||'8'),aspect_ratio:v.aspect||'16:9'})},
|
|
703
|
+
{id:'q10',cat:'Generation',method:'POST',title:'Generate Image',path:'/organizations/{orgId}/generate/image',fields:[orgF,qf('prompt','Prompt','Futuristic city'),qf('aspect','Aspect','1:1'),qf('num','Num','1')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/generate/image',{prompt:v.prompt,aspect_ratio:v.aspect||'1:1',num_images:parseInt(v.num||'1')})},
|
|
704
|
+
{id:'q11',cat:'Generation',method:'GET',title:'Job Status',path:'/organizations/{orgId}/jobs/{jobId}',fields:[orgF,qf('jobId','Job ID','job-xxx')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/jobs/'+v.jobId)},
|
|
705
|
+
{id:'q12',cat:'Agents',method:'POST',title:'Create Agent',path:'/organizations/{orgId}/agents/custom',fields:[orgF,qf('name','Name','Bot'),qf('instructions','Instructions','','textarea'),qf('tools','Tools','["rag_search"]')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/agents/custom',{name:v.name,instructions:v.instructions,tools:tryJson(v.tools)||[]})},
|
|
706
|
+
{id:'q13',cat:'Agents',method:'POST',title:'Query Agent',path:'/organizations/{orgId}/agents/query',fields:[orgF,qf('query','Query','Help me'),qf('agent_type','Type','auto')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/agents/query',{query:v.query,agent_type:v.agent_type||'auto'})},
|
|
707
|
+
{id:'q14',cat:'Audio',method:'POST',title:'Transcribe',path:'/organizations/{orgId}/audio/transcribe',fields:[orgF,qf('audio','Audio','...'),qf('lang','Language','en-US')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/audio/transcribe',{audio_text:v.audio,language_code:v.lang||'en-US'})},
|
|
708
|
+
{id:'q15',cat:'Audio',method:'POST',title:'Synthesize',path:'/organizations/{orgId}/audio/synthesize',fields:[orgF,qf('text','Text','Hello'),qf('voice','Voice','en-US-Neural2-J')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/audio/synthesize',{text:v.text,voice_name:v.voice||'en-US-Neural2-J'})},
|
|
709
|
+
{id:'q16',cat:'Analytics',method:'POST',title:'Train Model',path:'/organizations/{orgId}/analytics/train',fields:[orgF,qf('target','Target','churn'),qf('features','Features','["tenure"]'),qf('rows','Rows','[]','textarea')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/analytics/train',{target_column:v.target,feature_columns:tryJson(v.features)||[],rows:tryJson(v.rows)||[]})},
|
|
710
|
+
{id:'q17',cat:'Analytics',method:'POST',title:'Predict',path:'/organizations/{orgId}/analytics/predict',fields:[orgF,qf('model_id','Model ID','model-xxx'),qf('instances','Instances','[]','textarea')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/analytics/predict',{model_id:v.model_id,instances:tryJson(v.instances)||[]})},
|
|
711
|
+
{id:'q18',cat:'Analytics',method:'POST',title:'Forecast',path:'/organizations/{orgId}/analytics/forecast',fields:[orgF,qf('series','Series','[1,2,3]'),qf('horizon','Horizon','30')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/analytics/forecast',{series:tryJson(v.series)||[],horizon:parseInt(v.horizon||'30')})},
|
|
712
|
+
{id:'q19',cat:'Billing',method:'GET',title:'Usage',path:'/organizations/{orgId}/usage',fields:[orgF,qf('month','Month','2026-03')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/usage'+(v.month?'?month='+v.month:''))},
|
|
713
|
+
{id:'q20',cat:'Billing',method:'GET',title:'Invoice',path:'/organizations/{orgId}/invoice',fields:[orgF,qf('month','Month','')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/invoice'+(v.month?'?month='+v.month:''))},
|
|
714
|
+
{id:'q21',cat:'Audit',method:'GET',title:'Audit Logs',path:'/organizations/{orgId}/audit',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/audit')},
|
|
715
|
+
{id:'q22',cat:'Scraping',method:'GET',title:'Scraping Config',path:'/scraping/config',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/scraping/config')},
|
|
716
|
+
{id:'q23',cat:'Scraping',method:'POST',title:'Submit Scrape',path:'/scraping/scrape',fields:[qf('urls','URLs (comma)','https://example.com')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/scraping/scrape',{urls:(v.urls||'').split(',').map(u=>u.trim()).filter(Boolean)})},
|
|
717
|
+
{id:'q24',cat:'Scraping',method:'GET',title:'Scrape Status',path:'/scraping/status/{taskId}',fields:[qf('taskId','Task ID','task-xxx')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/scraping/status/'+v.taskId)},
|
|
718
|
+
{id:'q25',cat:'Webhooks',method:'POST',title:'Video Webhook',path:'/webhook/video-complete',fields:[qf('job_id','Job ID','job-1'),orgF,qf('status','Status','completed')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/webhook/video-complete',{job_id:v.job_id,org_id:v.orgId,status:v.status})},
|
|
719
|
+
{id:'q26',cat:'Webhooks',method:'POST',title:'Document Webhook',path:'/webhook/document-processed',fields:[qf('document_id','Doc ID','doc-1'),orgF,qf('status','Status','processed')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/webhook/document-processed',{document_id:v.document_id,org_id:v.orgId,status:v.status})},
|
|
720
|
+
{id:'q27',cat:'Webhooks',method:'POST',title:'Jira Webhook',path:'/webhook/jira',fields:[qf('payload','Payload','{}','textarea')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/webhook/jira',tryJson(v.payload)||{})},
|
|
721
|
+
{id:'q28',cat:'Jira',method:'POST',title:'Connect Jira',path:'/organizations/{orgId}/integrations/jira/connect',fields:[orgF,qf('site_url','Site URL','https://x.atlassian.net'),qf('email','Email','user@co.com'),qf('api_token','Token','','password')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/integrations/jira/connect',{site_url:v.site_url,email:v.email,api_token:v.api_token})},
|
|
722
|
+
{id:'q29',cat:'Jira',method:'GET',title:'Jira Status',path:'/organizations/{orgId}/integrations/jira/status',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/integrations/jira/status')},
|
|
723
|
+
{id:'q30',cat:'Jira',method:'DELETE',title:'Disconnect Jira',path:'/organizations/{orgId}/integrations/jira',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('DELETE','/organizations/'+v.orgId+'/integrations/jira')},
|
|
724
|
+
{id:'q31',cat:'Jira',method:'POST',title:'Sync Jira',path:'/organizations/{orgId}/integrations/jira/sync',fields:[orgF,qf('keys','Project Keys','["PROJ"]')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/integrations/jira/sync',{project_keys:tryJson(v.keys)||[]})},
|
|
725
|
+
{id:'q32',cat:'Blueprints',method:'POST',title:'Create Blueprint',path:'/organizations/{orgId}/blueprints',fields:[orgF,qf('name','Name','Template'),qf('category','Category','HR'),qf('sections','Sections','[]','textarea')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/blueprints',{name:v.name,category:v.category,sections:tryJson(v.sections)||[],default_workflow:'standard_approval'})},
|
|
726
|
+
{id:'q33',cat:'Blueprints',method:'GET',title:'List Blueprints',path:'/organizations/{orgId}/blueprints',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/blueprints')},
|
|
727
|
+
{id:'q34',cat:'Blueprints',method:'GET',title:'Get Blueprint',path:'/organizations/{orgId}/blueprints/{bpId}',fields:[orgF,qf('bpId','Blueprint ID','bp-xxx')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/blueprints/'+v.bpId)},
|
|
728
|
+
{id:'q35',cat:'Blueprints',method:'POST',title:'Publish Blueprint',path:'/organizations/{orgId}/blueprints/{bpId}/publish',fields:[orgF,qf('bpId','BP ID','bp-xxx')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/blueprints/'+v.bpId+'/publish',{})},
|
|
729
|
+
{id:'q36',cat:'Blueprints',method:'POST',title:'Archive Blueprint',path:'/organizations/{orgId}/blueprints/{bpId}/archive',fields:[orgF,qf('bpId','BP ID','bp-xxx')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/blueprints/'+v.bpId+'/archive',{})},
|
|
730
|
+
{id:'q37',cat:'Blueprints',method:'DELETE',title:'Delete Blueprint',path:'/organizations/{orgId}/blueprints/{bpId}',fields:[orgF,qf('bpId','BP ID','bp-xxx')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('DELETE','/organizations/'+v.orgId+'/blueprints/'+v.bpId)},
|
|
731
|
+
{id:'q38',cat:'Managed Docs',method:'POST',title:'Create from Blueprint',path:'/organizations/{orgId}/documents/create',fields:[orgF,qf('bpId','BP ID','bp-xxx'),qf('title','Title','Q1 Report')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/documents/create',{blueprint_id:v.bpId,title:v.title})},
|
|
732
|
+
{id:'q39',cat:'Managed Docs',method:'GET',title:'List Managed Docs',path:'/organizations/{orgId}/documents/managed',fields:[orgF],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/documents/managed')},
|
|
733
|
+
{id:'q40',cat:'Managed Docs',method:'GET',title:'Get Managed Doc',path:'/organizations/{orgId}/documents/managed/{docId}',fields:[orgF,qf('docId','Doc ID','doc-xxx')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('GET','/organizations/'+v.orgId+'/documents/managed/'+v.docId)},
|
|
734
|
+
{id:'q41',cat:'Managed Docs',method:'PATCH',title:'Update Section',path:'/organizations/{orgId}/documents/managed/{docId}/sections',fields:[orgF,qf('docId','Doc ID','doc-xxx'),qf('section_id','Section','sec-1'),qf('content','Content','','textarea')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('PATCH','/organizations/'+v.orgId+'/documents/managed/'+v.docId+'/sections',{section_id:v.section_id,content:v.content})},
|
|
735
|
+
{id:'q42',cat:'Managed Docs',method:'POST',title:'AI Generate Section',path:'/organizations/{orgId}/documents/managed/{docId}/generate',fields:[orgF,qf('docId','Doc ID','doc-xxx'),qf('section_id','Section','sec-1'),qf('context','Context','{}')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/documents/managed/'+v.docId+'/generate',{section_id:v.section_id,context:tryJson(v.context)||{}})},
|
|
736
|
+
{id:'q43',cat:'Managed Docs',method:'POST',title:'Workflow Action',path:'/organizations/{orgId}/documents/managed/{docId}/workflow',fields:[orgF,qf('docId','Doc ID','doc-xxx'),qf('action','Action','submit'),qf('comment','Comment','')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/organizations/'+v.orgId+'/documents/managed/'+v.docId+'/workflow',{action:v.action,comment:v.comment})},
|
|
737
|
+
{id:'q44',cat:'Managed Docs',method:'DELETE',title:'Delete Document',path:'/organizations/{orgId}/documents/managed/{docId}',fields:[orgF,qf('docId','Doc ID','doc-xxx')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('DELETE','/organizations/'+v.orgId+'/documents/managed/'+v.docId)},
|
|
738
|
+
{id:'q45',cat:'Admin',method:'POST',title:'Admin Login',path:'/admin/login',fields:[qf('key','Admin Key','','password')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('POST','/admin/login',{admin_key:v.key})},
|
|
739
|
+
{id:'q46',cat:'Admin',method:'GET',title:'Config',path:'/admin/config',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/config')},
|
|
740
|
+
{id:'q47',cat:'Admin',method:'GET',title:'System Info',path:'/admin/system',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/system')},
|
|
741
|
+
{id:'q48',cat:'Admin',method:'GET',title:'All Orgs',path:'/admin/organizations',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/organizations')},
|
|
742
|
+
{id:'q49',cat:'Admin',method:'GET',title:'All Jobs',path:'/admin/jobs',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/jobs')},
|
|
743
|
+
{id:'q50',cat:'Admin',method:'GET',title:'Feature Flags',path:'/admin/feature-flags',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/feature-flags')},
|
|
744
|
+
{id:'q51',cat:'Admin',method:'GET',title:'Rate Limits',path:'/admin/rate-limits',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/rate-limits')},
|
|
745
|
+
{id:'q52',cat:'Admin',method:'GET',title:'Cache Stats',path:'/admin/cache',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/cache')},
|
|
746
|
+
{id:'q53',cat:'Admin',method:'GET',title:'Tiers',path:'/admin/tiers',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/tiers')},
|
|
747
|
+
{id:'q54',cat:'Admin',method:'GET',title:'Pricing',path:'/admin/pricing',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/pricing')},
|
|
748
|
+
{id:'q55',cat:'Admin',method:'GET',title:'CORS',path:'/admin/cors',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/cors')},
|
|
749
|
+
{id:'q56',cat:'Admin',method:'GET',title:'Traffic Inbound',path:'/admin/traffic/inbound',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/traffic/inbound')},
|
|
750
|
+
{id:'q57',cat:'Admin',method:'GET',title:'Traffic Outbound',path:'/admin/traffic/outbound',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/traffic/outbound')},
|
|
751
|
+
{id:'q58',cat:'Admin',method:'GET',title:'Log Level',path:'/admin/logging/level',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/logging/level')},
|
|
752
|
+
{id:'q59',cat:'Admin',method:'GET',title:'Loggers',path:'/admin/logging/loggers',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/logging/loggers')},
|
|
753
|
+
{id:'q60',cat:'Admin',method:'GET',title:'Maintenance',path:'/admin/maintenance',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/maintenance')},
|
|
754
|
+
{id:'q61',cat:'Admin',method:'GET',title:'Admin Audit',path:'/admin/audit',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('GET','/admin/audit')},
|
|
755
|
+
{id:'q62',cat:'Admin',method:'POST',title:'Flush Cache',path:'/admin/cache/flush',fields:[],values:{},open:false,loading:false,result:null,resultOk:false,fn:()=>api('POST','/admin/cache/flush')},
|
|
756
|
+
{id:'q63',cat:'Admin',method:'PUT',title:'Update Setting',path:'/admin/settings',fields:[qf('key','Key','log_level'),qf('value','Value','DEBUG')],values:{},open:false,loading:false,result:null,resultOk:false,fn:v=>api('PUT','/admin/settings',{key:v.key,value:v.value})},
|
|
757
|
+
]);
|
|
758
|
+
|
|
759
|
+
const filteredQuickActions=computed(()=>{
|
|
760
|
+
let l=quickActions;
|
|
761
|
+
if(quickCat.value!=='All')l=l.filter(a=>a.cat===quickCat.value);
|
|
762
|
+
if(search.value){const q=search.value.toLowerCase();l=l.filter(a=>a.title.toLowerCase().includes(q)||a.path.toLowerCase().includes(q)||a.cat.toLowerCase().includes(q));}
|
|
763
|
+
return l;
|
|
764
|
+
});
|
|
765
|
+
function methodColor(m){return{GET:'bg-green-900/40 text-green-400',POST:'bg-blue-900/40 text-blue-400',PUT:'bg-yellow-900/40 text-yellow-400',PATCH:'bg-orange-900/40 text-orange-400',DELETE:'bg-red-900/40 text-red-400'}[m]||'bg-gray-900/40 text-gray-400';}
|
|
766
|
+
async function runQuickAction(a){a.loading=true;a.result=null;try{const r=await a.fn(a.values);a.resultOk=r.ok;a.result=JSON.stringify(r.data,null,2);}catch(e){a.resultOk=false;a.result='Error: '+e.message;}a.loading=false;}
|
|
767
|
+
|
|
768
|
+
// ── Org Flow ──
|
|
769
|
+
const orgFlow=reactive({step:0,steps:['Create','API Key','Verify','Rotate'],orgId:'',name:'',tier:'standard',loading:false,apiKey:'',orgData:null,verifyData:null,newKey:'',lastResult:null});
|
|
770
|
+
async function orgFlowCreate(){orgFlow.loading=true;try{const r=await api('POST','/organizations',{org_id:orgFlow.orgId,name:orgFlow.name,tier:orgFlow.tier,settings:{}});orgFlow.lastResult=r;if(r.ok){orgFlow.apiKey=r.data.api_key||'';orgFlow.orgData=r.data.organization||r.data;orgFlow.step=1;showToast('Org created!');}else showToast('Failed: '+(r.data.detail||r.status),false);}catch(e){showToast(e.message,false);}orgFlow.loading=false;}
|
|
771
|
+
async function orgFlowVerify(){orgFlow.loading=true;try{const r=await api('GET','/organizations/'+orgFlow.orgId);orgFlow.lastResult=r;if(r.ok){orgFlow.verifyData=r.data;orgFlow.step=3;showToast('Verified!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}orgFlow.loading=false;}
|
|
772
|
+
async function orgFlowRotate(){orgFlow.loading=true;try{const r=await api('POST','/organizations/'+orgFlow.orgId+'/api-keys/rotate');orgFlow.lastResult=r;if(r.ok){orgFlow.newKey=r.data.api_key||JSON.stringify(r.data);showToast('Rotated!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}orgFlow.loading=false;}
|
|
773
|
+
|
|
774
|
+
// ── Doc Flow ──
|
|
775
|
+
const docFlow=reactive({step:0,steps:['Select Org','Upload','Query','Data Chat'],orgId:'',loading:false,uploadResult:null,query:'',model:'gemini-pro',strategy:'hybrid',answer:'',chatHistory:[],chatInput:'',lastResult:null});
|
|
776
|
+
async function docFlowUpload(){const el=document.getElementById('docFileInput');if(!el||!el.files.length){showToast('Select a file',false);return;}docFlow.loading=true;try{const form=new FormData();form.append('file',el.files[0]);const r=await api('POST','/organizations/'+docFlow.orgId+'/documents/upload',form);docFlow.lastResult=r;if(r.ok){docFlow.uploadResult=r.data;docFlow.step=2;showToast('Uploaded!');}else showToast('Upload failed',false);}catch(e){showToast(e.message,false);}docFlow.loading=false;}
|
|
777
|
+
async function docFlowQuery(){docFlow.loading=true;try{const r=await api('POST','/organizations/'+docFlow.orgId+'/query',{query:docFlow.query,model_type:docFlow.model,retrieval_strategy:docFlow.strategy});docFlow.lastResult=r;if(r.ok){docFlow.answer=r.data.answer||JSON.stringify(r.data,null,2);showToast('Answer received!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}docFlow.loading=false;}
|
|
778
|
+
async function docFlowChat(){if(!docFlow.chatInput.trim())return;const msg=docFlow.chatInput;docFlow.chatInput='';docFlow.chatHistory.push({role:'user',text:msg});docFlow.loading=true;try{const r=await api('POST','/organizations/'+docFlow.orgId+'/data-chat',{question:msg});docFlow.lastResult=r;docFlow.chatHistory.push({role:'ai',text:r.ok?(r.data.answer||JSON.stringify(r.data)):'Error: '+r.status});}catch(e){docFlow.chatHistory.push({role:'ai',text:'Error: '+e.message});}docFlow.loading=false;}
|
|
779
|
+
|
|
780
|
+
// ── Gen Flow ──
|
|
781
|
+
const genFlow=reactive({step:0,steps:['Type','Configure','Track'],orgId:'',type:'',prompt:'',aspectRatio:'16:9',duration:'8',numImages:'1',style:'',loading:false,jobId:'',jobStatus:'',jobResult:null,lastResult:null});
|
|
782
|
+
async function genFlowSubmit(){genFlow.loading=true;try{const body=genFlow.type==='video'?{prompt:genFlow.prompt,duration:parseInt(genFlow.duration||'8'),aspect_ratio:genFlow.aspectRatio,style:genFlow.style||undefined}:{prompt:genFlow.prompt,aspect_ratio:genFlow.aspectRatio,num_images:parseInt(genFlow.numImages||'1'),style:genFlow.style||undefined};const r=await api('POST','/organizations/'+genFlow.orgId+'/generate/'+genFlow.type,body);genFlow.lastResult=r;if(r.ok){genFlow.jobId=r.data.job_id||'';genFlow.jobStatus=r.data.status||'queued';genFlow.step=2;showToast('Submitted!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}genFlow.loading=false;}
|
|
783
|
+
async function genFlowPoll(){genFlow.loading=true;try{const r=await api('GET','/organizations/'+genFlow.orgId+'/jobs/'+genFlow.jobId);genFlow.lastResult=r;if(r.ok){genFlow.jobStatus=r.data.status||'unknown';genFlow.jobResult=r.data;}}catch(e){showToast(e.message,false);}genFlow.loading=false;}
|
|
784
|
+
|
|
785
|
+
// ── Blueprint Flow ──
|
|
786
|
+
const bpFlow=reactive({step:0,steps:['Create BP','Publish','Create Doc','AI Generate','Workflow'],orgId:'',name:'',category:'',description:'',sectionsJson:'',loading:false,blueprintId:'',docTitle:'',docDesc:'',docId:'',docSections:[],manualSectionId:'',genContext:'{}',wfAction:'submit',wfComment:'',lastResult:null});
|
|
787
|
+
async function bpFlowCreate(){bpFlow.loading=true;try{const r=await api('POST','/organizations/'+bpFlow.orgId+'/blueprints',{name:bpFlow.name,category:bpFlow.category,description:bpFlow.description,sections:tryJson(bpFlow.sectionsJson)||[],default_workflow:'standard_approval'});bpFlow.lastResult=r;if(r.ok){bpFlow.blueprintId=r.data.id||r.data.blueprint_id||'';bpFlow.step=1;showToast('Blueprint created!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}bpFlow.loading=false;}
|
|
788
|
+
async function bpFlowPublish(){bpFlow.loading=true;try{const r=await api('POST','/organizations/'+bpFlow.orgId+'/blueprints/'+bpFlow.blueprintId+'/publish',{});bpFlow.lastResult=r;if(r.ok){bpFlow.step=2;showToast('Published!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}bpFlow.loading=false;}
|
|
789
|
+
async function bpFlowCreateDoc(){bpFlow.loading=true;try{const r=await api('POST','/organizations/'+bpFlow.orgId+'/documents/create',{blueprint_id:bpFlow.blueprintId,title:bpFlow.docTitle,description:bpFlow.docDesc});bpFlow.lastResult=r;if(r.ok){bpFlow.docId=r.data.id||r.data.document_id||'';bpFlow.docSections=(r.data.sections||[]).map(s=>({...s,id:s.id||s.section_id,generated:false,loading:false}));bpFlow.step=3;showToast('Document created!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}bpFlow.loading=false;}
|
|
790
|
+
async function bpFlowGenSection(sec){sec.loading=true;try{const r=await api('POST','/organizations/'+bpFlow.orgId+'/documents/managed/'+bpFlow.docId+'/generate',{section_id:sec.id,context:tryJson(bpFlow.genContext)||{}});bpFlow.lastResult=r;if(r.ok){sec.generated=true;showToast('Generated!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}sec.loading=false;}
|
|
791
|
+
async function bpFlowGenManual(){bpFlow.loading=true;try{const r=await api('POST','/organizations/'+bpFlow.orgId+'/documents/managed/'+bpFlow.docId+'/generate',{section_id:bpFlow.manualSectionId,context:tryJson(bpFlow.genContext)||{}});bpFlow.lastResult=r;if(r.ok)showToast('Generated!');else showToast('Failed',false);}catch(e){showToast(e.message,false);}bpFlow.loading=false;}
|
|
792
|
+
async function bpFlowWorkflow(){bpFlow.loading=true;try{const r=await api('POST','/organizations/'+bpFlow.orgId+'/documents/managed/'+bpFlow.docId+'/workflow',{action:bpFlow.wfAction,comment:bpFlow.wfComment});bpFlow.lastResult=r;if(r.ok)showToast('Workflow executed!');else showToast('Failed',false);}catch(e){showToast(e.message,false);}bpFlow.loading=false;}
|
|
793
|
+
|
|
794
|
+
// ── Jira Flow ──
|
|
795
|
+
const jiraFlow=reactive({step:0,steps:['Connect','Status','Sync','Manage'],orgId:'',siteUrl:'',email:'',apiToken:'',projectKeys:'',loading:false,statusData:null,lastResult:null});
|
|
796
|
+
async function jiraFlowConnect(){jiraFlow.loading=true;try{const r=await api('POST','/organizations/'+jiraFlow.orgId+'/integrations/jira/connect',{site_url:jiraFlow.siteUrl,email:jiraFlow.email,api_token:jiraFlow.apiToken});jiraFlow.lastResult=r;if(r.ok){jiraFlow.step=1;await jiraFlowStatus();showToast('Connected!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}jiraFlow.loading=false;}
|
|
797
|
+
async function jiraFlowStatus(){jiraFlow.loading=true;try{const r=await api('GET','/organizations/'+jiraFlow.orgId+'/integrations/jira/status');jiraFlow.lastResult=r;if(r.ok)jiraFlow.statusData=r.data;}catch(e){showToast(e.message,false);}jiraFlow.loading=false;}
|
|
798
|
+
async function jiraFlowSync(){jiraFlow.loading=true;try{const keys=jiraFlow.projectKeys.split(',').map(k=>k.trim()).filter(Boolean);const r=await api('POST','/organizations/'+jiraFlow.orgId+'/integrations/jira/sync',{project_keys:keys});jiraFlow.lastResult=r;if(r.ok){jiraFlow.step=3;showToast('Sync started!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}jiraFlow.loading=false;}
|
|
799
|
+
async function jiraFlowDisconnect(){jiraFlow.loading=true;try{const r=await api('DELETE','/organizations/'+jiraFlow.orgId+'/integrations/jira');jiraFlow.lastResult=r;if(r.ok){jiraFlow.step=0;showToast('Disconnected');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}jiraFlow.loading=false;}
|
|
800
|
+
|
|
801
|
+
// ── Scrape Flow ──
|
|
802
|
+
const scrapeFlow=reactive({step:0,steps:['Submit','Monitor'],urls:'',loading:false,loading2:false,configData:null,taskId:'',status:'',taskResult:null,lastResult:null});
|
|
803
|
+
async function scrapeFlowConfig(){scrapeFlow.loading2=true;try{const r=await api('GET','/scraping/config');scrapeFlow.configData=r.data;}catch{}scrapeFlow.loading2=false;}
|
|
804
|
+
async function scrapeFlowSubmit(){scrapeFlow.loading=true;try{const urls=scrapeFlow.urls.split('\\n').map(u=>u.trim()).filter(Boolean);const r=await api('POST','/scraping/scrape',{urls});scrapeFlow.lastResult=r;if(r.ok){scrapeFlow.taskId=r.data.task_id||'';scrapeFlow.status=r.data.status||'queued';scrapeFlow.step=1;showToast('Submitted!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}scrapeFlow.loading=false;}
|
|
805
|
+
async function scrapeFlowPoll(){scrapeFlow.loading=true;try{const r=await api('GET','/scraping/status/'+scrapeFlow.taskId);scrapeFlow.lastResult=r;if(r.ok){scrapeFlow.status=r.data.status||'unknown';scrapeFlow.taskResult=r.data;}}catch(e){showToast(e.message,false);}scrapeFlow.loading=false;}
|
|
806
|
+
|
|
807
|
+
// ── Agent Flow ──
|
|
808
|
+
const agentFlow=reactive({step:0,steps:['Create','Chat'],orgId:'',name:'',instructions:'',tools:'',agentType:'auto',chatHistory:[],chatInput:'',loading:false,lastResult:null});
|
|
809
|
+
async function agentFlowCreate(){agentFlow.loading=true;try{const r=await api('POST','/organizations/'+agentFlow.orgId+'/agents/custom',{name:agentFlow.name,instructions:agentFlow.instructions,tools:tryJson(agentFlow.tools)||[]});agentFlow.lastResult=r;if(r.ok){agentFlow.step=1;showToast('Agent created!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}agentFlow.loading=false;}
|
|
810
|
+
async function agentFlowQuery(){if(!agentFlow.chatInput.trim())return;const msg=agentFlow.chatInput;agentFlow.chatInput='';agentFlow.chatHistory.push({role:'user',text:msg});agentFlow.loading=true;try{const r=await api('POST','/organizations/'+agentFlow.orgId+'/agents/query',{query:msg,agent_type:agentFlow.agentType||'auto'});agentFlow.lastResult=r;agentFlow.chatHistory.push({role:'ai',text:r.ok?(r.data.response||r.data.answer||JSON.stringify(r.data)):'Error: '+r.status});}catch(e){agentFlow.chatHistory.push({role:'ai',text:'Error: '+e.message});}agentFlow.loading=false;}
|
|
811
|
+
|
|
812
|
+
// ── ML Flow ──
|
|
813
|
+
const mlFlow=reactive({step:0,steps:['Train','Predict','Forecast'],orgId:'',target:'',features:'',rows:'',modelId:'',instances:'',series:'',horizon:'30',loading:false,lastResult:null});
|
|
814
|
+
async function mlFlowTrain(){mlFlow.loading=true;try{const r=await api('POST','/organizations/'+mlFlow.orgId+'/analytics/train',{target_column:mlFlow.target,feature_columns:tryJson(mlFlow.features)||[],rows:tryJson(mlFlow.rows)||[]});mlFlow.lastResult=r;if(r.ok){mlFlow.modelId=r.data.model_id||'';mlFlow.step=1;showToast('Trained!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}mlFlow.loading=false;}
|
|
815
|
+
async function mlFlowPredict(){mlFlow.loading=true;try{const r=await api('POST','/organizations/'+mlFlow.orgId+'/analytics/predict',{model_id:mlFlow.modelId,instances:tryJson(mlFlow.instances)||[]});mlFlow.lastResult=r;if(r.ok)showToast('Predicted!');else showToast('Failed',false);}catch(e){showToast(e.message,false);}mlFlow.loading=false;}
|
|
816
|
+
async function mlFlowForecast(){mlFlow.loading=true;try{const r=await api('POST','/organizations/'+mlFlow.orgId+'/analytics/forecast',{series:tryJson(mlFlow.series)||[],horizon:parseInt(mlFlow.horizon||'30')});mlFlow.lastResult=r;if(r.ok)showToast('Forecast ready!');else showToast('Failed',false);}catch(e){showToast(e.message,false);}mlFlow.loading=false;}
|
|
817
|
+
|
|
818
|
+
// ── Audio Flow ──
|
|
819
|
+
const audioFlow=reactive({step:0,steps:['Transcribe','Synthesize'],orgId:'',audioText:'',lang:'en-US',synthText:'',voice:'en-US-Neural2-J',loading:false,lastResult:null});
|
|
820
|
+
async function audioFlowTranscribe(){audioFlow.loading=true;try{const r=await api('POST','/organizations/'+audioFlow.orgId+'/audio/transcribe',{audio_text:audioFlow.audioText,language_code:audioFlow.lang});audioFlow.lastResult=r;if(r.ok){audioFlow.step=1;showToast('Transcribed!');}else showToast('Failed',false);}catch(e){showToast(e.message,false);}audioFlow.loading=false;}
|
|
821
|
+
async function audioFlowSynthesize(){audioFlow.loading=true;try{const r=await api('POST','/organizations/'+audioFlow.orgId+'/audio/synthesize',{text:audioFlow.synthText,voice_name:audioFlow.voice});audioFlow.lastResult=r;if(r.ok)showToast('Synthesized!');else showToast('Failed',false);}catch(e){showToast(e.message,false);}audioFlow.loading=false;}
|
|
822
|
+
|
|
823
|
+
// ── Billing Flow ──
|
|
824
|
+
const billingFlow=reactive({orgId:'',month:'',loadingU:false,loadingI:false,loadingA:false,usageData:null,invoiceData:null,auditData:null});
|
|
825
|
+
async function billingFlowUsage(){billingFlow.loadingU=true;try{const r=await api('GET','/organizations/'+billingFlow.orgId+'/usage'+(billingFlow.month?'?month='+billingFlow.month:''));billingFlow.usageData=r.data;}catch{}billingFlow.loadingU=false;}
|
|
826
|
+
async function billingFlowInvoice(){billingFlow.loadingI=true;try{const r=await api('GET','/organizations/'+billingFlow.orgId+'/invoice'+(billingFlow.month?'?month='+billingFlow.month:''));billingFlow.invoiceData=r.data;}catch{}billingFlow.loadingI=false;}
|
|
827
|
+
async function billingFlowAudit(){billingFlow.loadingA=true;try{const r=await api('GET','/organizations/'+billingFlow.orgId+'/audit');billingFlow.auditData=r.data;}catch{}billingFlow.loadingA=false;}
|
|
828
|
+
|
|
829
|
+
// ── Admin ──
|
|
830
|
+
const adminWidgets=reactive([
|
|
831
|
+
{id:'sys',title:'System',icon:'mdi-server',color:'text-blue-400',endpoint:'/admin/system',loading:false,data:null},
|
|
832
|
+
{id:'cfg',title:'Config',icon:'mdi-cog',color:'text-purple-400',endpoint:'/admin/config',loading:false,data:null},
|
|
833
|
+
{id:'trf',title:'Traffic',icon:'mdi-swap-vertical',color:'text-green-400',endpoint:'/admin/traffic/inbound',loading:false,data:null},
|
|
834
|
+
{id:'cch',title:'Cache',icon:'mdi-database',color:'text-amber-400',endpoint:'/admin/cache',loading:false,data:null},
|
|
835
|
+
{id:'flg',title:'Feature Flags',icon:'mdi-flag',color:'text-pink-400',endpoint:'/admin/feature-flags',loading:false,data:null},
|
|
836
|
+
{id:'mnt',title:'Maintenance',icon:'mdi-wrench',color:'text-orange-400',endpoint:'/admin/maintenance',loading:false,data:null},
|
|
837
|
+
{id:'trs',title:'Tiers',icon:'mdi-layers',color:'text-cyan-400',endpoint:'/admin/tiers',loading:false,data:null},
|
|
838
|
+
{id:'prc',title:'Pricing',icon:'mdi-tag',color:'text-emerald-400',endpoint:'/admin/pricing',loading:false,data:null},
|
|
839
|
+
{id:'rts',title:'Rate Limits',icon:'mdi-speedometer',color:'text-red-400',endpoint:'/admin/rate-limits',loading:false,data:null},
|
|
840
|
+
]);
|
|
841
|
+
async function adminFetch(w){w.loading=true;try{const r=await api('GET',w.endpoint);w.data=r.data;}catch(e){w.data={error:e.message};}w.loading=false;}
|
|
842
|
+
|
|
843
|
+
const adminActions=reactive([
|
|
844
|
+
{id:'a1',title:'Update Setting',icon:'mdi-pencil',open:false,loading:false,result:null,resultOk:false,fields:[qf('key','Key','log_level'),qf('value','Value','DEBUG')],values:{},fn:v=>api('PUT','/admin/settings',{key:v.key,value:v.value})},
|
|
845
|
+
{id:'a2',title:'Set Log Level',icon:'mdi-format-list-bulleted',open:false,loading:false,result:null,resultOk:false,fields:[qf('level','Level','DEBUG')],values:{},fn:v=>api('PUT','/admin/logging/level',{level:v.level})},
|
|
846
|
+
{id:'a3',title:'Update CORS',icon:'mdi-web',open:false,loading:false,result:null,resultOk:false,fields:[qf('origins','Allow Origins','*')],values:{},fn:v=>api('PUT','/admin/cors',{allow_origins:v.origins})},
|
|
847
|
+
{id:'a4',title:'Maintenance Mode',icon:'mdi-wrench',open:false,loading:false,result:null,resultOk:false,fields:[qf('enabled','Enabled','true'),qf('message','Message','')],values:{},fn:v=>api('PUT','/admin/maintenance',{enabled:v.enabled==='true',message:v.message||undefined})},
|
|
848
|
+
{id:'a5',title:'Flush Cache',icon:'mdi-delete-sweep',open:false,loading:false,result:null,resultOk:false,fields:[],values:{},fn:()=>api('POST','/admin/cache/flush')},
|
|
849
|
+
{id:'a6',title:'Set Feature Flag',icon:'mdi-flag',open:false,loading:false,result:null,resultOk:false,fields:[qf('key','Flag Key','new_feature'),qf('enabled','Enabled','true')],values:{},fn:v=>api('PUT','/admin/feature-flags',{key:v.key,enabled:v.enabled==='true'})},
|
|
850
|
+
]);
|
|
851
|
+
async function adminRunAction(act){act.loading=true;act.result=null;try{const r=await act.fn(act.values);act.resultOk=r.ok;act.result=JSON.stringify(r.data,null,2);}catch(e){act.resultOk=false;act.result='Error: '+e.message;}act.loading=false;}
|
|
852
|
+
|
|
853
|
+
onMounted(()=>{checkHealth();});
|
|
854
|
+
|
|
855
|
+
return{cfg,settingsFields,settingsMsg,settingsMsgOk,saveSettings,health,toast,showToast,copyText,currentView,search,navSections,flows,
|
|
856
|
+
quickCat,quickCategories,quickActions,filteredQuickActions,methodColor,runQuickAction,
|
|
857
|
+
orgFlow,orgFlowCreate,orgFlowVerify,orgFlowRotate,
|
|
858
|
+
docFlow,docFlowUpload,docFlowQuery,docFlowChat,
|
|
859
|
+
genFlow,genFlowSubmit,genFlowPoll,
|
|
860
|
+
bpFlow,bpFlowCreate,bpFlowPublish,bpFlowCreateDoc,bpFlowGenSection,bpFlowGenManual,bpFlowWorkflow,
|
|
861
|
+
jiraFlow,jiraFlowConnect,jiraFlowStatus,jiraFlowSync,jiraFlowDisconnect,
|
|
862
|
+
scrapeFlow,scrapeFlowConfig,scrapeFlowSubmit,scrapeFlowPoll,
|
|
863
|
+
agentFlow,agentFlowCreate,agentFlowQuery,
|
|
864
|
+
mlFlow,mlFlowTrain,mlFlowPredict,mlFlowForecast,
|
|
865
|
+
audioFlow,audioFlowTranscribe,audioFlowSynthesize,
|
|
866
|
+
billingFlow,billingFlowUsage,billingFlowInvoice,billingFlowAudit,
|
|
867
|
+
adminWidgets,adminFetch,adminActions,adminRunAction};
|
|
868
|
+
}
|
|
869
|
+
})
|
|
870
|
+
.component('flow-header',{props:['title','desc','step','steps'],template:\`
|
|
871
|
+
<div class="mb-6">
|
|
872
|
+
<h1 class="text-2xl font-bold text-white mb-1">{{title}}</h1>
|
|
873
|
+
<p class="text-gray-400 text-sm mb-4">{{desc}}</p>
|
|
874
|
+
<div class="flex items-center gap-1 mb-2">
|
|
875
|
+
<template v-for="(s,i) in steps" :key="i">
|
|
876
|
+
<div class="flex items-center gap-1">
|
|
877
|
+
<div class="w-7 h-7 rounded-full flex items-center justify-center text-xs font-bold transition-all"
|
|
878
|
+
:class="i<step?'bg-green-500 text-white':i===step?'bg-brand text-white ring-2 ring-brand/30':'bg-gray-700 text-gray-500'">
|
|
879
|
+
<span v-if="i<step" class="mdi mdi-check"></span><span v-else>{{i+1}}</span>
|
|
880
|
+
</div>
|
|
881
|
+
<span class="text-xs hidden sm:inline" :class="i===step?'text-white font-semibold':'text-gray-500'">{{s}}</span>
|
|
882
|
+
</div>
|
|
883
|
+
<div v-if="i<steps.length-1" class="flex-1 h-px mx-1" :class="i<step?'bg-green-500':'bg-gray-700'"></div>
|
|
884
|
+
</template>
|
|
885
|
+
</div>
|
|
886
|
+
</div>\`})
|
|
887
|
+
.component('step-card',{props:['title','subtitle'],template:\`
|
|
888
|
+
<div class="bg-surface border border-border rounded-xl p-6 mb-4">
|
|
889
|
+
<h3 class="text-lg font-semibold text-white mb-1">{{title}}</h3>
|
|
890
|
+
<p class="text-xs text-gray-500 mb-4">{{subtitle}}</p>
|
|
891
|
+
<slot></slot>
|
|
892
|
+
<div class="flex gap-2 mt-4" v-if="$slots.actions"><slot name="actions"></slot></div>
|
|
893
|
+
</div>\`})
|
|
894
|
+
.component('text-field',{props:['label','modelValue','placeholder','disabled','type'],emits:['update:modelValue'],template:\`
|
|
895
|
+
<div class="mt-2">
|
|
896
|
+
<label class="text-xs text-gray-500 uppercase tracking-wide">{{label}}</label>
|
|
897
|
+
<input :value="modelValue" @input="$emit('update:modelValue',$event.target.value)" :type="type||'text'" :placeholder="placeholder" :disabled="disabled"
|
|
898
|
+
class="mt-1 w-full bg-bg border border-border rounded-lg px-3 py-2 text-sm text-gray-300 focus:outline-none focus:border-brand/50 disabled:opacity-50"/>
|
|
899
|
+
</div>\`})
|
|
900
|
+
.component('text-area',{props:['label','modelValue','placeholder'],emits:['update:modelValue'],template:\`
|
|
901
|
+
<div class="mt-2">
|
|
902
|
+
<label class="text-xs text-gray-500 uppercase tracking-wide">{{label}}</label>
|
|
903
|
+
<textarea :value="modelValue" @input="$emit('update:modelValue',$event.target.value)" :placeholder="placeholder" rows="3"
|
|
904
|
+
class="mt-1 w-full bg-bg border border-border rounded-lg px-3 py-2 text-sm text-gray-300 focus:outline-none focus:border-brand/50 resize-y"></textarea>
|
|
905
|
+
</div>\`})
|
|
906
|
+
.component('select-field',{props:['label','modelValue','options'],emits:['update:modelValue'],template:\`
|
|
907
|
+
<div class="mt-2">
|
|
908
|
+
<label class="text-xs text-gray-500 uppercase tracking-wide">{{label}}</label>
|
|
909
|
+
<select :value="modelValue" @change="$emit('update:modelValue',$event.target.value)"
|
|
910
|
+
class="mt-1 w-full bg-bg border border-border rounded-lg px-3 py-2 text-sm text-gray-300 focus:outline-none focus:border-brand/50">
|
|
911
|
+
<option v-for="o in options" :key="o" :value="o">{{o}}</option>
|
|
912
|
+
</select>
|
|
913
|
+
</div>\`})
|
|
914
|
+
.component('flow-btn',{props:{variant:{default:'primary'},loading:{default:false},disabled:{default:false},size:{default:'md'}},template:\`
|
|
915
|
+
<button @click="$emit('click')" :disabled="loading||disabled"
|
|
916
|
+
class="rounded-lg font-semibold transition disabled:opacity-50"
|
|
917
|
+
:class="[variant==='primary'?'bg-brand text-white hover:bg-brand/80':variant==='secondary'?'bg-white/5 text-gray-300 border border-border hover:bg-white/10':variant==='danger'?'bg-red-600 text-white hover:bg-red-700':variant==='warn'?'bg-amber-600 text-white hover:bg-amber-700':'',size==='sm'?'px-3 py-1.5 text-xs':'px-5 py-2 text-sm']">
|
|
918
|
+
<span v-if="loading" class="mdi mdi-loading mdi-spin mr-1"></span><slot></slot>
|
|
919
|
+
</button>\`})
|
|
920
|
+
.component('kv-display',{props:['items'],template:\`
|
|
921
|
+
<div class="bg-bg rounded-lg p-3 border border-border">
|
|
922
|
+
<div v-for="(v,k) in items" :key="k" class="flex justify-between py-1 text-xs border-b border-border/50 last:border-0">
|
|
923
|
+
<span class="text-gray-500">{{k}}</span>
|
|
924
|
+
<span class="text-gray-300 font-mono text-right max-w-[60%] truncate" :title="String(v)">{{typeof v==='object'?JSON.stringify(v):v}}</span>
|
|
925
|
+
</div>
|
|
926
|
+
</div>\`})
|
|
927
|
+
.component('flow-result',{props:['result'],template:\`
|
|
928
|
+
<details v-if="result" class="mt-4 max-w-lg">
|
|
929
|
+
<summary class="text-xs text-gray-500 cursor-pointer hover:text-gray-400">Raw API Response</summary>
|
|
930
|
+
<div class="mt-2 bg-bg rounded-lg p-3 border text-xs font-mono whitespace-pre-wrap max-h-60 overflow-y-auto"
|
|
931
|
+
:class="result.ok?'border-green-800 text-green-300':'border-red-800 text-red-300'">{{JSON.stringify(result.data,null,2)}}</div>
|
|
932
|
+
</details>\`})
|
|
933
|
+
.mount('#app');
|
|
934
|
+
<\/script>
|
|
935
|
+
</body>
|
|
936
|
+
</html>`;
|
|
937
|
+
}
|
|
938
|
+
function startWebServer(opts) {
|
|
939
|
+
const html = buildDashboardHtml({
|
|
940
|
+
baseUrl: opts.baseUrl,
|
|
941
|
+
apiPrefix: opts.apiPrefix,
|
|
942
|
+
apiKey: opts.apiKey,
|
|
943
|
+
adminKey: opts.adminKey,
|
|
944
|
+
webhookSecret: opts.webhookSecret,
|
|
945
|
+
employeeId: opts.employeeId,
|
|
946
|
+
});
|
|
947
|
+
const server = node_http_1.default.createServer((_req, res) => {
|
|
948
|
+
res.writeHead(200, {
|
|
949
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
950
|
+
"Cache-Control": "no-cache",
|
|
951
|
+
});
|
|
952
|
+
res.end(html);
|
|
953
|
+
});
|
|
954
|
+
server.listen(opts.port, "127.0.0.1", () => {
|
|
955
|
+
const addr = server.address();
|
|
956
|
+
const url = `http://127.0.0.1:${addr.port}`;
|
|
957
|
+
console.log(`\n Hivemind Dashboard running at ${url}\n`);
|
|
958
|
+
console.log(" Press Ctrl+C to stop.\n");
|
|
959
|
+
if (opts.open) {
|
|
960
|
+
const { exec } = require("node:child_process");
|
|
961
|
+
const cmd = process.platform === "darwin"
|
|
962
|
+
? `open "${url}"`
|
|
963
|
+
: process.platform === "win32"
|
|
964
|
+
? `start "" "${url}"`
|
|
965
|
+
: `xdg-open "${url}"`;
|
|
966
|
+
exec(cmd, () => { });
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
``;
|
|
971
|
+
//# sourceMappingURL=web.js.map
|