apteva 0.4.26 → 0.4.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ActivityPage.41nbye4r.js +3 -0
- package/dist/{ApiDocsPage.3q5x9hhg.js → ApiDocsPage.4smnt8m3.js} +1 -1
- package/dist/{App.9bzz8dqh.js → App.0sbax9et.js} +1 -1
- package/dist/{App.a7h91mxr.js → App.0ws427h8.js} +1 -1
- package/dist/App.4ehxpt48.js +4 -0
- package/dist/App.6q6bar8b.js +4 -0
- package/dist/{App.wnap3h7r.js → App.ca1rz1ph.js} +1 -1
- package/dist/{App.e54ynjf2.js → App.ensa6z0r.js} +1 -1
- package/dist/{App.sb2fg71h.js → App.f8g7tych.js} +1 -1
- package/dist/App.kh7d2xj3.js +267 -0
- package/dist/App.mvtqv6qc.js +20 -0
- package/dist/{App.2prdcxgq.js → App.ncgc9cxy.js} +1 -1
- package/dist/{App.r2c5nw36.js → App.p0fb1pds.js} +1 -1
- package/dist/{App.6ftxk387.js → App.pmaq48sj.js} +1 -1
- package/dist/{App.40azyqz6.js → App.yv87t9m5.js} +1 -1
- package/dist/App.zjmfm8p6.js +4 -0
- package/dist/ConnectionsPage.anb3rv9a.js +3 -0
- package/dist/McpPage.y396h6fy.js +3 -0
- package/dist/SettingsPage.5k6vp396.js +3 -0
- package/dist/SkillsPage.yj3xdsay.js +3 -0
- package/dist/{TasksPage.65dcf4vw.js → TasksPage.sjv0khtv.js} +1 -1
- package/dist/TelemetryPage.2qm4w16r.js +3 -0
- package/dist/TestsPage.zzs4qfj8.js +3 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/channels/telegram.ts +5 -0
- package/src/crypto.ts +13 -4
- package/src/db.ts +25 -2
- package/src/providers.ts +44 -0
- package/src/routes/api/agent-utils.ts +64 -9
- package/src/routes/api/agents.ts +41 -13
- package/src/routes/api/integrations.ts +16 -6
- package/src/routes/api/mcp.ts +7 -0
- package/src/web/components/activity/ActivityPage.tsx +3 -3
- package/src/web/components/agents/AgentCard.tsx +5 -5
- package/src/web/components/agents/AgentPanel.tsx +81 -20
- package/src/web/components/mcp/McpPage.tsx +16 -5
- package/src/web/components/settings/SettingsPage.tsx +279 -30
- package/src/web/context/ProjectContext.tsx +5 -0
- package/src/web/context/TelemetryContext.tsx +14 -0
- package/src/web/types.ts +20 -2
- package/dist/ActivityPage.cycn14ck.js +0 -3
- package/dist/App.0wwyytz2.js +0 -4
- package/dist/App.fq11mvc7.js +0 -4
- package/dist/App.h6k4j1w9.js +0 -4
- package/dist/App.jq5tmjws.js +0 -267
- package/dist/App.k377qek6.js +0 -20
- package/dist/ConnectionsPage.6fyhqfhz.js +0 -3
- package/dist/McpPage.hk2qt1qt.js +0 -3
- package/dist/SettingsPage.gwpx9v7v.js +0 -3
- package/dist/SkillsPage.j5zech2z.js +0 -3
- package/dist/TelemetryPage.07xrbd7k.js +0 -3
- package/dist/TestsPage.q6zfephf.js +0 -3
|
@@ -5,16 +5,17 @@ import { Select } from "../common/Select";
|
|
|
5
5
|
import { useProjects, useAuth, type Project } from "../../context";
|
|
6
6
|
import type { Provider } from "../../types";
|
|
7
7
|
|
|
8
|
-
type SettingsTab = "general" | "providers" | "projects" | "channels" | "api-keys" | "account" | "updates" | "data";
|
|
8
|
+
type SettingsTab = "general" | "providers" | "projects" | "channels" | "api-keys" | "account" | "updates" | "data" | "assistant";
|
|
9
9
|
|
|
10
10
|
export function SettingsPage() {
|
|
11
|
-
const { projectsEnabled } = useProjects();
|
|
11
|
+
const { projectsEnabled, metaAgentEnabled } = useProjects();
|
|
12
12
|
const [activeTab, setActiveTab] = useState<SettingsTab>("general");
|
|
13
13
|
|
|
14
14
|
const tabs: { key: SettingsTab; label: string }[] = [
|
|
15
15
|
{ key: "general", label: "General" },
|
|
16
16
|
{ key: "providers", label: "Providers" },
|
|
17
17
|
...(projectsEnabled ? [{ key: "projects" as SettingsTab, label: "Projects" }] : []),
|
|
18
|
+
...(metaAgentEnabled ? [{ key: "assistant" as SettingsTab, label: "Assistant" }] : []),
|
|
18
19
|
{ key: "channels", label: "Channels" },
|
|
19
20
|
{ key: "api-keys", label: "API Keys" },
|
|
20
21
|
{ key: "account", label: "Account" },
|
|
@@ -68,6 +69,7 @@ export function SettingsPage() {
|
|
|
68
69
|
{activeTab === "account" && <AccountSettings />}
|
|
69
70
|
{activeTab === "updates" && <UpdatesSettings />}
|
|
70
71
|
{activeTab === "data" && <DataSettings />}
|
|
72
|
+
{activeTab === "assistant" && metaAgentEnabled && <AssistantSettings />}
|
|
71
73
|
</div>
|
|
72
74
|
</div>
|
|
73
75
|
);
|
|
@@ -273,8 +275,10 @@ function ProvidersSettings() {
|
|
|
273
275
|
|
|
274
276
|
const llmProviders = providers.filter(p => p.type === "llm");
|
|
275
277
|
const integrations = providers.filter(p => p.type === "integration");
|
|
278
|
+
const browserProviders = providers.filter(p => p.type === "browser");
|
|
276
279
|
const llmConfiguredCount = llmProviders.filter(p => p.hasKey).length;
|
|
277
280
|
const intConfiguredCount = integrations.filter(p => p.hasKey).length;
|
|
281
|
+
const browserConfiguredCount = browserProviders.filter(p => p.hasKey).length;
|
|
278
282
|
|
|
279
283
|
// Auto-dismiss success message after 5 seconds
|
|
280
284
|
useEffect(() => {
|
|
@@ -382,6 +386,44 @@ function ProvidersSettings() {
|
|
|
382
386
|
))}
|
|
383
387
|
</div>
|
|
384
388
|
</div>
|
|
389
|
+
|
|
390
|
+
{/* Browser Providers Section */}
|
|
391
|
+
<div>
|
|
392
|
+
<div className="mb-6">
|
|
393
|
+
<h2 className="text-xl font-semibold mb-1">Browser Providers</h2>
|
|
394
|
+
<p className="text-[#666]">
|
|
395
|
+
Configure browser environments for operator mode (computer use). {browserConfiguredCount} of {browserProviders.length} configured.
|
|
396
|
+
</p>
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
400
|
+
{browserProviders.map(provider => (
|
|
401
|
+
<ProviderKeyCard
|
|
402
|
+
key={provider.id}
|
|
403
|
+
provider={provider}
|
|
404
|
+
isEditing={selectedProvider === provider.id}
|
|
405
|
+
apiKey={apiKey}
|
|
406
|
+
saving={saving}
|
|
407
|
+
testing={testing}
|
|
408
|
+
error={selectedProvider === provider.id ? error : null}
|
|
409
|
+
success={selectedProvider === provider.id ? success : null}
|
|
410
|
+
onStartEdit={() => {
|
|
411
|
+
setSelectedProvider(provider.id);
|
|
412
|
+
setError(null);
|
|
413
|
+
setSuccess(null);
|
|
414
|
+
}}
|
|
415
|
+
onCancelEdit={() => {
|
|
416
|
+
setSelectedProvider(null);
|
|
417
|
+
setApiKey("");
|
|
418
|
+
setError(null);
|
|
419
|
+
}}
|
|
420
|
+
onApiKeyChange={setApiKey}
|
|
421
|
+
onSave={saveKey}
|
|
422
|
+
onDelete={() => deleteKey(provider.id)}
|
|
423
|
+
/>
|
|
424
|
+
))}
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
385
427
|
</div>
|
|
386
428
|
</>
|
|
387
429
|
);
|
|
@@ -899,6 +941,8 @@ function ProviderKeyCard({
|
|
|
899
941
|
onDelete,
|
|
900
942
|
}: ProviderKeyCardProps) {
|
|
901
943
|
const isOllama = provider.id === "ollama";
|
|
944
|
+
const isUrlBased = isOllama || provider.id === "browserengine" || provider.id === "chrome";
|
|
945
|
+
const isBrowser = provider.type === "browser";
|
|
902
946
|
const [ollamaStatus, setOllamaStatus] = React.useState<{ connected: boolean; modelCount?: number } | null>(null);
|
|
903
947
|
|
|
904
948
|
// Check Ollama status when configured
|
|
@@ -919,11 +963,13 @@ function ProviderKeyCard({
|
|
|
919
963
|
<div className="min-w-0">
|
|
920
964
|
<h3 className="font-medium">{provider.name}</h3>
|
|
921
965
|
<p className="text-sm text-[#666] truncate">
|
|
922
|
-
{
|
|
923
|
-
? (provider.description || "
|
|
924
|
-
:
|
|
925
|
-
?
|
|
926
|
-
:
|
|
966
|
+
{isBrowser
|
|
967
|
+
? (provider.description || "Browser automation")
|
|
968
|
+
: provider.type === "integration"
|
|
969
|
+
? (provider.description || "MCP integration")
|
|
970
|
+
: isOllama
|
|
971
|
+
? "Run models locally"
|
|
972
|
+
: `${provider.models.length} models`}
|
|
927
973
|
</p>
|
|
928
974
|
</div>
|
|
929
975
|
{provider.hasKey ? (
|
|
@@ -940,6 +986,8 @@ function ProviderKeyCard({
|
|
|
940
986
|
) : (
|
|
941
987
|
<>Not running</>
|
|
942
988
|
)
|
|
989
|
+
) : isUrlBased ? (
|
|
990
|
+
<><CheckIcon className="w-3 h-3" />Configured</>
|
|
943
991
|
) : (
|
|
944
992
|
<><CheckIcon className="w-3 h-3" />{provider.keyHint}</>
|
|
945
993
|
)}
|
|
@@ -955,18 +1003,26 @@ function ProviderKeyCard({
|
|
|
955
1003
|
{isEditing ? (
|
|
956
1004
|
<div className="space-y-3">
|
|
957
1005
|
<input
|
|
958
|
-
type={
|
|
1006
|
+
type={isUrlBased ? "text" : "password"}
|
|
959
1007
|
value={apiKey}
|
|
960
1008
|
onChange={e => onApiKeyChange(e.target.value)}
|
|
961
1009
|
placeholder={isOllama
|
|
962
1010
|
? "http://localhost:11434"
|
|
963
|
-
: provider.
|
|
1011
|
+
: provider.id === "browserengine"
|
|
1012
|
+
? "http://localhost:8098"
|
|
1013
|
+
: provider.id === "chrome"
|
|
1014
|
+
? "http://localhost:9222"
|
|
1015
|
+
: provider.hasKey ? "Enter new API key..." : "Enter API key..."}
|
|
964
1016
|
autoFocus
|
|
965
1017
|
className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316]"
|
|
966
1018
|
/>
|
|
967
|
-
{
|
|
1019
|
+
{isUrlBased && (
|
|
968
1020
|
<p className="text-xs text-[#666]">
|
|
969
|
-
|
|
1021
|
+
{isOllama
|
|
1022
|
+
? "Enter your Ollama server URL. Default is http://localhost:11434"
|
|
1023
|
+
: provider.id === "browserengine"
|
|
1024
|
+
? "Enter your BrowserEngine service URL (e.g., http://localhost:8098)"
|
|
1025
|
+
: "Enter your Chrome DevTools URL (e.g., http://localhost:9222)"}
|
|
970
1026
|
</p>
|
|
971
1027
|
)}
|
|
972
1028
|
{error && <p className="text-red-400 text-sm">{error}</p>}
|
|
@@ -983,26 +1039,30 @@ function ProviderKeyCard({
|
|
|
983
1039
|
disabled={!apiKey || saving}
|
|
984
1040
|
className="flex-1 px-3 py-1.5 bg-[#f97316] text-black rounded text-sm font-medium disabled:opacity-50"
|
|
985
1041
|
>
|
|
986
|
-
{testing ? "Validating..." : saving ? "Saving..." :
|
|
1042
|
+
{testing ? "Validating..." : saving ? "Saving..." : isUrlBased ? "Connect" : "Save"}
|
|
987
1043
|
</button>
|
|
988
1044
|
</div>
|
|
989
1045
|
</div>
|
|
990
1046
|
) : provider.hasKey ? (
|
|
991
1047
|
<div className="flex items-center justify-between">
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1048
|
+
{provider.docsUrl ? (
|
|
1049
|
+
<a
|
|
1050
|
+
href={provider.docsUrl}
|
|
1051
|
+
target="_blank"
|
|
1052
|
+
rel="noopener noreferrer"
|
|
1053
|
+
className="text-sm text-[#3b82f6] hover:underline"
|
|
1054
|
+
>
|
|
1055
|
+
{isOllama ? "Download Ollama" : "View docs"}
|
|
1056
|
+
</a>
|
|
1057
|
+
) : (
|
|
1058
|
+
<span />
|
|
1059
|
+
)}
|
|
1000
1060
|
<div className="flex items-center gap-3">
|
|
1001
1061
|
<button
|
|
1002
1062
|
onClick={onStartEdit}
|
|
1003
1063
|
className="text-sm text-[#888] hover:text-[#e0e0e0]"
|
|
1004
1064
|
>
|
|
1005
|
-
{
|
|
1065
|
+
{isUrlBased ? "Change URL" : "Update key"}
|
|
1006
1066
|
</button>
|
|
1007
1067
|
<button
|
|
1008
1068
|
onClick={onDelete}
|
|
@@ -1014,19 +1074,23 @@ function ProviderKeyCard({
|
|
|
1014
1074
|
</div>
|
|
1015
1075
|
) : (
|
|
1016
1076
|
<div className="flex items-center justify-between">
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1077
|
+
{provider.docsUrl ? (
|
|
1078
|
+
<a
|
|
1079
|
+
href={provider.docsUrl}
|
|
1080
|
+
target="_blank"
|
|
1081
|
+
rel="noopener noreferrer"
|
|
1082
|
+
className="text-sm text-[#3b82f6] hover:underline"
|
|
1083
|
+
>
|
|
1084
|
+
{isOllama ? "Download Ollama" : isBrowser ? "View docs" : "Get API key"}
|
|
1085
|
+
</a>
|
|
1086
|
+
) : (
|
|
1087
|
+
<span />
|
|
1088
|
+
)}
|
|
1025
1089
|
<button
|
|
1026
1090
|
onClick={onStartEdit}
|
|
1027
1091
|
className="text-sm text-[#f97316] hover:text-[#fb923c]"
|
|
1028
1092
|
>
|
|
1029
|
-
{
|
|
1093
|
+
{isUrlBased ? "Configure" : "+ Add key"}
|
|
1030
1094
|
</button>
|
|
1031
1095
|
</div>
|
|
1032
1096
|
)}
|
|
@@ -2152,3 +2216,188 @@ function ChannelsSettings() {
|
|
|
2152
2216
|
</>
|
|
2153
2217
|
);
|
|
2154
2218
|
}
|
|
2219
|
+
|
|
2220
|
+
function AssistantSettings() {
|
|
2221
|
+
const { authFetch } = useAuth();
|
|
2222
|
+
const [providers, setProviders] = useState<Provider[]>([]);
|
|
2223
|
+
const [provider, setProvider] = useState("");
|
|
2224
|
+
const [model, setModel] = useState("");
|
|
2225
|
+
const [systemPrompt, setSystemPrompt] = useState("");
|
|
2226
|
+
const [status, setStatus] = useState<"running" | "stopped" | "unknown">("unknown");
|
|
2227
|
+
const [loading, setLoading] = useState(true);
|
|
2228
|
+
const [saving, setSaving] = useState(false);
|
|
2229
|
+
const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
|
|
2230
|
+
const [starting, setStarting] = useState(false);
|
|
2231
|
+
|
|
2232
|
+
// Original values for change detection
|
|
2233
|
+
const [original, setOriginal] = useState({ provider: "", model: "", systemPrompt: "" });
|
|
2234
|
+
|
|
2235
|
+
useEffect(() => {
|
|
2236
|
+
const fetchData = async () => {
|
|
2237
|
+
try {
|
|
2238
|
+
const [statusRes, providersRes] = await Promise.all([
|
|
2239
|
+
authFetch("/api/meta-agent/status"),
|
|
2240
|
+
authFetch("/api/providers"),
|
|
2241
|
+
]);
|
|
2242
|
+
const statusData = await statusRes.json();
|
|
2243
|
+
const providersData = await providersRes.json();
|
|
2244
|
+
setProviders((providersData.providers || []).filter((p: Provider) => p.type === "llm" && p.hasKey));
|
|
2245
|
+
|
|
2246
|
+
if (statusData.agent) {
|
|
2247
|
+
const a = statusData.agent;
|
|
2248
|
+
setProvider(a.provider || "");
|
|
2249
|
+
setModel(a.model || "");
|
|
2250
|
+
setSystemPrompt(a.systemPrompt || "");
|
|
2251
|
+
setStatus(a.status || "stopped");
|
|
2252
|
+
setOriginal({ provider: a.provider || "", model: a.model || "", systemPrompt: a.systemPrompt || "" });
|
|
2253
|
+
}
|
|
2254
|
+
} catch {
|
|
2255
|
+
setMessage({ type: "error", text: "Failed to load assistant config" });
|
|
2256
|
+
} finally {
|
|
2257
|
+
setLoading(false);
|
|
2258
|
+
}
|
|
2259
|
+
};
|
|
2260
|
+
fetchData();
|
|
2261
|
+
}, [authFetch]);
|
|
2262
|
+
|
|
2263
|
+
const selectedProvider = providers.find(p => p.id === provider);
|
|
2264
|
+
const models = selectedProvider?.models || [];
|
|
2265
|
+
|
|
2266
|
+
const handleProviderChange = (newProvider: string) => {
|
|
2267
|
+
setProvider(newProvider);
|
|
2268
|
+
const p = providers.find(pr => pr.id === newProvider);
|
|
2269
|
+
const defaultModel = p?.models.find(m => m.recommended)?.value || p?.models[0]?.value || "";
|
|
2270
|
+
setModel(defaultModel);
|
|
2271
|
+
};
|
|
2272
|
+
|
|
2273
|
+
const hasChanges = provider !== original.provider || model !== original.model || systemPrompt !== original.systemPrompt;
|
|
2274
|
+
|
|
2275
|
+
const handleSave = async () => {
|
|
2276
|
+
setSaving(true);
|
|
2277
|
+
setMessage(null);
|
|
2278
|
+
try {
|
|
2279
|
+
const res = await authFetch("/api/agents/apteva-assistant", {
|
|
2280
|
+
method: "PUT",
|
|
2281
|
+
headers: { "Content-Type": "application/json" },
|
|
2282
|
+
body: JSON.stringify({ provider, model, systemPrompt }),
|
|
2283
|
+
});
|
|
2284
|
+
if (res.ok) {
|
|
2285
|
+
setOriginal({ provider, model, systemPrompt });
|
|
2286
|
+
setMessage({ type: "success", text: "Assistant settings saved" });
|
|
2287
|
+
setTimeout(() => setMessage(null), 3000);
|
|
2288
|
+
} else {
|
|
2289
|
+
const data = await res.json().catch(() => ({}));
|
|
2290
|
+
setMessage({ type: "error", text: data.error || "Failed to save" });
|
|
2291
|
+
}
|
|
2292
|
+
} catch {
|
|
2293
|
+
setMessage({ type: "error", text: "Failed to save settings" });
|
|
2294
|
+
} finally {
|
|
2295
|
+
setSaving(false);
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
|
|
2299
|
+
const handleToggle = async () => {
|
|
2300
|
+
setStarting(true);
|
|
2301
|
+
setMessage(null);
|
|
2302
|
+
try {
|
|
2303
|
+
const endpoint = status === "running" ? "/api/meta-agent/stop" : "/api/meta-agent/start";
|
|
2304
|
+
const res = await authFetch(endpoint, { method: "POST" });
|
|
2305
|
+
if (res.ok) {
|
|
2306
|
+
setStatus(status === "running" ? "stopped" : "running");
|
|
2307
|
+
} else {
|
|
2308
|
+
const data = await res.json().catch(() => ({}));
|
|
2309
|
+
setMessage({ type: "error", text: data.error || "Failed to toggle assistant" });
|
|
2310
|
+
}
|
|
2311
|
+
} catch {
|
|
2312
|
+
setMessage({ type: "error", text: "Failed to toggle assistant" });
|
|
2313
|
+
} finally {
|
|
2314
|
+
setStarting(false);
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
|
|
2318
|
+
if (loading) {
|
|
2319
|
+
return <div className="text-[#666]">Loading assistant settings...</div>;
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
return (
|
|
2323
|
+
<div className="max-w-2xl">
|
|
2324
|
+
<h2 className="text-lg font-medium mb-1">Apteva Assistant</h2>
|
|
2325
|
+
<p className="text-sm text-[#666] mb-6">Configure the built-in AI assistant that manages your agents and platform.</p>
|
|
2326
|
+
|
|
2327
|
+
{message && (
|
|
2328
|
+
<div className={`mb-4 px-3 py-2 rounded text-sm ${
|
|
2329
|
+
message.type === "success" ? "bg-green-500/10 text-green-400" : "bg-red-500/10 text-red-400"
|
|
2330
|
+
}`}>
|
|
2331
|
+
{message.text}
|
|
2332
|
+
</div>
|
|
2333
|
+
)}
|
|
2334
|
+
|
|
2335
|
+
{/* Status */}
|
|
2336
|
+
<div className="mb-6 flex items-center gap-3">
|
|
2337
|
+
<span className="text-sm text-[#666]">Status:</span>
|
|
2338
|
+
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
|
2339
|
+
status === "running" ? "bg-[#3b82f6]/20 text-[#3b82f6]" : "bg-[#333] text-[#666]"
|
|
2340
|
+
}`}>
|
|
2341
|
+
{status}
|
|
2342
|
+
</span>
|
|
2343
|
+
<button
|
|
2344
|
+
onClick={handleToggle}
|
|
2345
|
+
disabled={starting}
|
|
2346
|
+
className={`px-3 py-1.5 rounded text-sm font-medium transition ${
|
|
2347
|
+
status === "running"
|
|
2348
|
+
? "bg-[#f97316]/20 text-[#f97316] hover:bg-[#f97316]/30"
|
|
2349
|
+
: "bg-[#3b82f6]/20 text-[#3b82f6] hover:bg-[#3b82f6]/30"
|
|
2350
|
+
} disabled:opacity-50`}
|
|
2351
|
+
>
|
|
2352
|
+
{starting ? "..." : status === "running" ? "Stop" : "Start"}
|
|
2353
|
+
</button>
|
|
2354
|
+
</div>
|
|
2355
|
+
|
|
2356
|
+
{/* Provider */}
|
|
2357
|
+
<div className="mb-4">
|
|
2358
|
+
<label className="block text-sm text-[#666] mb-1">Provider</label>
|
|
2359
|
+
<Select
|
|
2360
|
+
value={provider}
|
|
2361
|
+
onChange={handleProviderChange}
|
|
2362
|
+
options={providers.map(p => ({ value: p.id, label: p.name }))}
|
|
2363
|
+
placeholder="Select provider..."
|
|
2364
|
+
/>
|
|
2365
|
+
</div>
|
|
2366
|
+
|
|
2367
|
+
{/* Model */}
|
|
2368
|
+
<div className="mb-4">
|
|
2369
|
+
<label className="block text-sm text-[#666] mb-1">Model</label>
|
|
2370
|
+
<Select
|
|
2371
|
+
value={model}
|
|
2372
|
+
onChange={setModel}
|
|
2373
|
+
options={models.map(m => ({ value: m.value, label: m.label, recommended: m.recommended }))}
|
|
2374
|
+
placeholder="Select model..."
|
|
2375
|
+
/>
|
|
2376
|
+
</div>
|
|
2377
|
+
|
|
2378
|
+
{/* System Prompt */}
|
|
2379
|
+
<div className="mb-6">
|
|
2380
|
+
<label className="block text-sm text-[#666] mb-1">System Prompt</label>
|
|
2381
|
+
<textarea
|
|
2382
|
+
value={systemPrompt}
|
|
2383
|
+
onChange={e => setSystemPrompt(e.target.value)}
|
|
2384
|
+
rows={12}
|
|
2385
|
+
className="w-full bg-[#111] border border-[#1a1a1a] rounded px-3 py-2 text-sm font-mono focus:outline-none focus:border-[#f97316] resize-y"
|
|
2386
|
+
/>
|
|
2387
|
+
</div>
|
|
2388
|
+
|
|
2389
|
+
{/* Save */}
|
|
2390
|
+
<button
|
|
2391
|
+
onClick={handleSave}
|
|
2392
|
+
disabled={!hasChanges || saving}
|
|
2393
|
+
className="bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 disabled:cursor-not-allowed text-black px-4 py-2 rounded font-medium transition"
|
|
2394
|
+
>
|
|
2395
|
+
{saving ? "Saving..." : "Save Changes"}
|
|
2396
|
+
</button>
|
|
2397
|
+
|
|
2398
|
+
{status === "running" && hasChanges && (
|
|
2399
|
+
<p className="text-xs text-[#666] mt-2">Changes will be applied to the running assistant</p>
|
|
2400
|
+
)}
|
|
2401
|
+
</div>
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
@@ -19,6 +19,7 @@ interface ProjectContextValue {
|
|
|
19
19
|
error: string | null;
|
|
20
20
|
unassignedCount: number;
|
|
21
21
|
projectsEnabled: boolean; // Feature flag
|
|
22
|
+
metaAgentEnabled: boolean; // Feature flag
|
|
22
23
|
setCurrentProjectId: (id: string | null) => void;
|
|
23
24
|
createProject: (data: { name: string; description?: string; color?: string }) => Promise<Project | null>;
|
|
24
25
|
updateProject: (id: string, data: { name?: string; description?: string; color?: string }) => Promise<Project | null>;
|
|
@@ -56,6 +57,7 @@ export function ProjectProvider({ children }: ProjectProviderProps) {
|
|
|
56
57
|
const [error, setError] = useState<string | null>(null);
|
|
57
58
|
const [unassignedCount, setUnassignedCount] = useState(0);
|
|
58
59
|
const [projectsEnabled, setProjectsEnabled] = useState(false);
|
|
60
|
+
const [metaAgentEnabled, setMetaAgentEnabled] = useState(false);
|
|
59
61
|
|
|
60
62
|
// Fetch feature flags on mount
|
|
61
63
|
useEffect(() => {
|
|
@@ -63,9 +65,11 @@ export function ProjectProvider({ children }: ProjectProviderProps) {
|
|
|
63
65
|
.then(res => res.json())
|
|
64
66
|
.then(data => {
|
|
65
67
|
setProjectsEnabled(data.projects === true);
|
|
68
|
+
setMetaAgentEnabled(data.metaAgent === true);
|
|
66
69
|
})
|
|
67
70
|
.catch(() => {
|
|
68
71
|
setProjectsEnabled(false);
|
|
72
|
+
setMetaAgentEnabled(false);
|
|
69
73
|
});
|
|
70
74
|
}, []);
|
|
71
75
|
|
|
@@ -193,6 +197,7 @@ export function ProjectProvider({ children }: ProjectProviderProps) {
|
|
|
193
197
|
error,
|
|
194
198
|
unassignedCount,
|
|
195
199
|
projectsEnabled,
|
|
200
|
+
metaAgentEnabled,
|
|
196
201
|
setCurrentProjectId,
|
|
197
202
|
createProject,
|
|
198
203
|
updateProject,
|
|
@@ -233,6 +233,19 @@ export function useTelemetry(filter?: {
|
|
|
233
233
|
};
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
// Map raw telemetry event types to user-friendly labels
|
|
237
|
+
export function getActivityLabel(type: string): string {
|
|
238
|
+
switch (type) {
|
|
239
|
+
case "llm_request": return "Thinking";
|
|
240
|
+
case "tool_invocation": return "Using tools";
|
|
241
|
+
case "tool_result": return "Using tools";
|
|
242
|
+
case "thread_activity": return "Working";
|
|
243
|
+
case "agent_started": return "Starting";
|
|
244
|
+
case "agent_stopped": return "Stopped";
|
|
245
|
+
default: return "Working";
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
236
249
|
// Hook for agent activity indicator - uses context-level tracking
|
|
237
250
|
export function useAgentActivity(agentId: string) {
|
|
238
251
|
const { activeAgents } = useTelemetryContext();
|
|
@@ -241,6 +254,7 @@ export function useAgentActivity(agentId: string) {
|
|
|
241
254
|
return {
|
|
242
255
|
isActive: !!activity,
|
|
243
256
|
type: activity?.type,
|
|
257
|
+
label: activity ? getActivityLabel(activity.type) : undefined,
|
|
244
258
|
};
|
|
245
259
|
}
|
|
246
260
|
|
package/src/web/types.ts
CHANGED
|
@@ -13,11 +13,19 @@ export interface AgentBuiltinTools {
|
|
|
13
13
|
webFetch: boolean;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
export interface OperatorConfig {
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
browser_provider?: string; // "browserbase" | "steel" | "browserengine" | "chrome"
|
|
19
|
+
display_width?: number;
|
|
20
|
+
display_height?: number;
|
|
21
|
+
max_actions_per_turn?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
export interface AgentFeatures {
|
|
17
25
|
memory: boolean;
|
|
18
26
|
tasks: boolean;
|
|
19
27
|
vision: boolean;
|
|
20
|
-
operator: boolean;
|
|
28
|
+
operator: boolean | OperatorConfig; // Can be boolean for backwards compat or full config
|
|
21
29
|
mcp: boolean;
|
|
22
30
|
realtime: boolean;
|
|
23
31
|
files: boolean;
|
|
@@ -37,6 +45,15 @@ export const DEFAULT_FEATURES: AgentFeatures = {
|
|
|
37
45
|
builtinTools: { webSearch: false, webFetch: false },
|
|
38
46
|
};
|
|
39
47
|
|
|
48
|
+
// Helper to normalize operator feature to OperatorConfig
|
|
49
|
+
export function getOperatorConfig(features: AgentFeatures): OperatorConfig {
|
|
50
|
+
const op = features.operator;
|
|
51
|
+
if (typeof op === "boolean") {
|
|
52
|
+
return { enabled: op };
|
|
53
|
+
}
|
|
54
|
+
return op;
|
|
55
|
+
}
|
|
56
|
+
|
|
40
57
|
// Helper to normalize agents feature to MultiAgentConfig
|
|
41
58
|
export function getMultiAgentConfig(features: AgentFeatures, projectId?: string | null): MultiAgentConfig {
|
|
42
59
|
const agents = features.agents;
|
|
@@ -127,7 +144,7 @@ export interface ProviderModel {
|
|
|
127
144
|
export interface Provider {
|
|
128
145
|
id: string;
|
|
129
146
|
name: string;
|
|
130
|
-
type: "llm" | "integration";
|
|
147
|
+
type: "llm" | "integration" | "browser";
|
|
131
148
|
docsUrl: string;
|
|
132
149
|
description?: string;
|
|
133
150
|
models: ProviderModel[];
|
|
@@ -135,6 +152,7 @@ export interface Provider {
|
|
|
135
152
|
keyHint: string | null;
|
|
136
153
|
isValid: boolean | null;
|
|
137
154
|
configured?: boolean; // for backwards compatibility
|
|
155
|
+
isLocal?: boolean; // Uses URL instead of API key (ollama, browserengine, chrome)
|
|
138
156
|
}
|
|
139
157
|
|
|
140
158
|
export interface OnboardingStatus {
|
package/dist/App.0wwyytz2.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{H as u,I as c,J as E}from"./App.2prdcxgq.js";import{S as A,V as a,W as h,Y as V,Z as x,_ as D,ca as T,fa as j}from"./App.k377qek6.js";var X=A(a(),1);var q=A(h(),1);function Qq({agents:B,loading:Z,onNavigate:L}){let{authFetch:G}=T(),{currentProjectId:J}=j(),{events:$,statusChangeCounter:K}=V(),[H,Q]=X.useState(null),[W,U]=X.useState([]),[O,P]=X.useState([]),I=X.useRef(null),{events:R}=x({category:"TASK"}),N=X.useMemo(()=>{if(J===null)return B;if(J==="unassigned")return B.filter((z)=>!z.projectId);return B.filter((z)=>z.projectId===J)},[B,J]),b=X.useMemo(()=>{return[...N].sort((z,Y)=>{if(z.status==="running"&&Y.status!=="running")return-1;if(Y.status==="running"&&z.status!=="running")return 1;return z.name.localeCompare(Y.name)})},[N]),g=X.useMemo(()=>N.filter((z)=>z.status==="running").length,[N]),f=X.useMemo(()=>new Set(N.map((z)=>z.id)),[N]),d=X.useMemo(()=>{let z=new Map;return N.forEach((Y)=>z.set(Y.id,Y.name)),z},[N]),M=X.useCallback(async()=>{let z=J?`&project_id=${encodeURIComponent(J)}`:"",[Y,C]=await Promise.all([G(`/api/tasks?status=all${z}`).catch(()=>null),G(`/api/telemetry/events?type=thread_activity&limit=50${z}`).catch(()=>null)]);if(Y?.ok){let _=(await Y.json()).tasks||[];_.sort((F,w)=>{let S=F.status==="running"?0:F.status==="pending"?1:F.status==="completed"?2:3,v=w.status==="running"?0:w.status==="pending"?1:w.status==="completed"?2:3;if(S!==v)return S-v;if(S<=1){let r=F.next_run||F.execute_at?new Date(F.next_run||F.execute_at).getTime():1/0,n=w.next_run||w.execute_at?new Date(w.next_run||w.execute_at).getTime():1/0;return r-n}let o=F.completed_at||F.executed_at||F.created_at,i=w.completed_at||w.executed_at||w.created_at;return new Date(i).getTime()-new Date(o).getTime()}),U(_)}if(C?.ok){let p=await C.json();P(p.events||[])}},[G,J]);X.useEffect(()=>{M()},[M,K]),X.useEffect(()=>{if(!R.length)return;let z=R[0];if(!z||z.id===I.current)return;let Y=z.type;if(Y==="task_created"||Y==="task_updated"||Y==="task_deleted")I.current=z.id,M()},[R,M]);let y=X.useMemo(()=>{let z=$.filter((_)=>_.type==="thread_activity"),Y=new Set(z.map((_)=>_.id)),C=[...z];for(let _ of O)if(!Y.has(_.id))C.push(_),Y.add(_.id);let p=C.filter((_)=>f.has(_.agent_id));return p.sort((_,F)=>new Date(F.timestamp).getTime()-new Date(_.timestamp).getTime()),p.slice(0,50)},[$,O,f]);if(Z)return q.jsxDEV("div",{className:"flex-1 flex items-center justify-center text-[#666]",children:"Loading..."},void 0,!1,void 0,this);return q.jsxDEV("div",{className:"flex-1 flex flex-col overflow-hidden",children:[q.jsxDEV("div",{className:"px-6 pt-6 pb-4 shrink-0",children:q.jsxDEV("div",{className:"flex items-center justify-between",children:[q.jsxDEV("h1",{className:"text-xl font-semibold",children:"Activity"},void 0,!1,void 0,this),q.jsxDEV("span",{className:"text-sm text-[#666]",children:[g," of ",N.length," agents running"]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),q.jsxDEV("div",{className:"flex-1 flex min-h-0 overflow-hidden",children:[q.jsxDEV("div",{className:"flex-[2] flex flex-col overflow-hidden border-r border-[#1a1a1a]",children:[q.jsxDEV("div",{className:"px-4 py-2.5 border-b border-[#1a1a1a] shrink-0",children:q.jsxDEV("h3",{className:"text-xs font-semibold text-[#666] uppercase tracking-wider",children:"Agents"},void 0,!1,void 0,this)},void 0,!1,void 0,this),q.jsxDEV("div",{className:"flex-1 overflow-auto px-3 py-2",children:[b.length===0?q.jsxDEV("p",{className:"text-sm text-[#555] px-2 py-4 text-center",children:"No agents found"},void 0,!1,void 0,this):q.jsxDEV("div",{className:"space-y-1",children:b.map((z)=>q.jsxDEV(s,{agent:z,selected:H===z.id,onSelect:()=>Q(H===z.id?null:z.id)},z.id,!1,void 0,this))},void 0,!1,void 0,this),H&&q.jsxDEV(t,{agent:N.find((z)=>z.id===H)||null},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),q.jsxDEV("div",{className:"flex-[3] flex flex-col min-h-0 overflow-hidden border-r border-[#1a1a1a]",children:[q.jsxDEV("div",{className:"px-4 py-2.5 border-b border-[#1a1a1a] flex items-center justify-between shrink-0",children:[q.jsxDEV("h3",{className:"text-xs font-semibold text-[#666] uppercase tracking-wider",children:"Activity Feed"},void 0,!1,void 0,this),q.jsxDEV("span",{className:"text-xs text-[#555]",children:y.length},void 0,!1,void 0,this)]},void 0,!0,void 0,this),q.jsxDEV("div",{className:"flex-1 overflow-auto",children:y.length===0?q.jsxDEV("div",{className:"p-6 text-center text-[#555] text-sm",children:"No activity yet. Agent activity will appear here in real-time."},void 0,!1,void 0,this):q.jsxDEV("div",{className:"divide-y divide-[#1a1a1a]",children:y.map((z)=>q.jsxDEV("div",{className:"px-4 py-2.5 hover:bg-[#111]/50 transition",children:[q.jsxDEV("p",{className:"text-sm truncate",children:z.data?.activity||"Working..."},void 0,!1,void 0,this),q.jsxDEV("div",{className:"flex items-center gap-2 text-[10px] text-[#555] mt-0.5",children:[q.jsxDEV("span",{className:"text-[#666]",children:d.get(z.agent_id)||z.agent_id},void 0,!1,void 0,this),q.jsxDEV("span",{className:"text-[#444]",children:"·"},void 0,!1,void 0,this),q.jsxDEV("span",{children:zq(z.timestamp)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},z.id,!0,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),q.jsxDEV("div",{className:"flex-[3] flex flex-col overflow-hidden",children:[q.jsxDEV("div",{className:"px-4 py-2.5 border-b border-[#1a1a1a] flex items-center justify-between shrink-0",children:[q.jsxDEV("h3",{className:"text-xs font-semibold text-[#666] uppercase tracking-wider",children:"Tasks"},void 0,!1,void 0,this),L&&q.jsxDEV("button",{onClick:()=>L("tasks"),className:"text-xs text-[#3b82f6] hover:text-[#60a5fa]",children:"View All"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),q.jsxDEV("div",{className:"flex-1 overflow-auto px-3 py-3",children:W.length===0?q.jsxDEV("p",{className:"text-sm text-[#555] px-2 py-4 text-center",children:"No tasks yet"},void 0,!1,void 0,this):q.jsxDEV("div",{className:"space-y-2.5",children:W.map((z)=>q.jsxDEV(e,{task:z},`${z.agentId}-${z.id}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function s({agent:B,selected:Z,onSelect:L}){let{isActive:G,type:J}=D(B.id),$=B.status==="running";return q.jsxDEV("button",{onClick:L,className:`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left transition ${Z?"bg-[#f97316]/10 border border-[#f97316]/30":"hover:bg-[#1a1a1a] border border-transparent"}`,children:[q.jsxDEV("span",{className:`w-2.5 h-2.5 rounded-full shrink-0 ${$&&G?"bg-green-400 animate-pulse":$?"bg-[#3b82f6]":"bg-[#444]"}`},void 0,!1,void 0,this),q.jsxDEV("div",{className:"flex-1 min-w-0",children:[q.jsxDEV("div",{className:"flex items-center gap-2",children:[q.jsxDEV("span",{className:`text-sm font-medium truncate ${$?"":"text-[#666]"}`,children:B.name},void 0,!1,void 0,this),q.jsxDEV("span",{className:"text-[10px] text-[#555] shrink-0",children:B.provider},void 0,!1,void 0,this)]},void 0,!0,void 0,this),G&&J?q.jsxDEV("p",{className:"text-[11px] text-green-400 truncate",children:J},void 0,!1,void 0,this):q.jsxDEV("p",{className:`text-[11px] ${$?"text-[#555]":"text-[#444]"}`,children:$?"idle":"stopped"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function t({agent:B}){let{authFetch:Z}=T(),[L,G]=X.useState(""),[J,$]=X.useState(!1),[K,H]=X.useState(null);if(X.useEffect(()=>{G(""),H(null)},[B?.id]),!B)return null;let Q=B.status==="running",W=async()=>{if(!L.trim()||J)return;if(!Q){H("Agent is not running"),setTimeout(()=>H(null),3000);return}$(!0);try{let U=await Z(`/api/agents/${B.id}/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:L,agent_id:B.id})});if(U.ok)H("Sent"),G("");else{let O=await U.json().catch(()=>({}));H(O.error||"Failed")}}catch{H("Failed to send")}finally{$(!1),setTimeout(()=>H(null),3000)}};return q.jsxDEV("div",{className:"mt-2 bg-[#0a0a0a] border border-[#1a1a1a] rounded-lg p-2.5",children:[q.jsxDEV("div",{className:"flex items-center justify-between mb-1.5",children:[q.jsxDEV("span",{className:"text-[10px] text-[#666]",children:["Send to ",q.jsxDEV("span",{className:"text-[#888]",children:B.name},void 0,!1,void 0,this)]},void 0,!0,void 0,this),K&&q.jsxDEV("span",{className:`text-[10px] px-1.5 py-0.5 rounded ${K==="Sent"?"bg-green-500/10 text-green-400":"bg-red-500/10 text-red-400"}`,children:K},void 0,!1,void 0,this)]},void 0,!0,void 0,this),q.jsxDEV("div",{className:"flex gap-1.5",children:[q.jsxDEV("input",{type:"text",value:L,onChange:(U)=>G(U.target.value),onKeyDown:(U)=>U.key==="Enter"&&W(),placeholder:Q?"Command...":"Not running",disabled:J||!Q,autoFocus:!0,className:"flex-1 bg-[#111] border border-[#1a1a1a] rounded px-2 py-1.5 text-xs focus:outline-none focus:border-[#f97316] placeholder-[#444] disabled:opacity-50"},void 0,!1,void 0,this),q.jsxDEV("button",{onClick:W,disabled:J||!L.trim()||!Q,className:"px-2.5 py-1.5 bg-[#f97316]/20 text-[#f97316] rounded text-xs font-medium hover:bg-[#f97316]/30 transition disabled:opacity-30",children:J?"...":"Send"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var l={pending:"bg-yellow-500/20 text-yellow-400",running:"bg-blue-500/20 text-blue-400",completed:"bg-green-500/20 text-green-400",failed:"bg-red-500/20 text-red-400",cancelled:"bg-gray-500/20 text-gray-400"},m=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function e({task:B}){return q.jsxDEV("div",{className:"bg-[#111] border border-[#1a1a1a] rounded-lg p-3 hover:border-[#333] transition",children:[q.jsxDEV("div",{className:"flex items-start justify-between mb-1.5",children:[q.jsxDEV("div",{className:"flex-1 min-w-0",children:[q.jsxDEV("h4",{className:"text-sm font-medium truncate",children:B.title},void 0,!1,void 0,this),q.jsxDEV("p",{className:"text-xs text-[#666]",children:B.agentName},void 0,!1,void 0,this)]},void 0,!0,void 0,this),q.jsxDEV("span",{className:`px-1.5 py-0.5 rounded text-[10px] font-medium shrink-0 ml-2 ${l[B.status]||l.pending}`,children:B.status},void 0,!1,void 0,this)]},void 0,!0,void 0,this),q.jsxDEV("div",{className:"flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-[#555]",children:[q.jsxDEV("span",{className:"flex items-center gap-1",children:[B.type==="recurring"?q.jsxDEV(u,{className:"w-3 h-3"},void 0,!1,void 0,this):B.execute_at?q.jsxDEV(c,{className:"w-3 h-3"},void 0,!1,void 0,this):q.jsxDEV(E,{className:"w-3 h-3"},void 0,!1,void 0,this),B.type==="recurring"&&B.recurrence?qq(B.recurrence):B.type]},void 0,!0,void 0,this),B.next_run&&q.jsxDEV("span",{className:"text-[#f97316]",children:k(B.next_run)},void 0,!1,void 0,this),!B.next_run&&B.execute_at&&q.jsxDEV("span",{className:"text-[#f97316]",children:k(B.execute_at)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function qq(B){try{let Z=B.trim().split(/\s+/);if(Z.length!==5)return B;let[L,G,J,$,K]=Z;if(L.startsWith("*/")&&G==="*"&&J==="*"&&$==="*"&&K==="*"){let Q=parseInt(L.slice(2));return Q===1?"Every min":`Every ${Q}min`}if(L!=="*"&&!L.includes("/")&&G==="*"&&J==="*"&&$==="*"&&K==="*")return"Hourly";if(G.startsWith("*/")&&J==="*"&&$==="*"&&K==="*"){let Q=parseInt(G.slice(2));return Q===1?"Hourly":`Every ${Q}h`}let H=(Q,W)=>{let U=parseInt(Q),O=parseInt(W);if(isNaN(U))return"";let P=U>=12?"PM":"AM";return`${U===0?12:U>12?U-12:U}:${O.toString().padStart(2,"0")} ${P}`};if(G!=="*"&&!G.includes("/")&&J==="*"&&$==="*"){let Q=H(G,L);if(K==="*")return`Daily ${Q}`;let W=K.split(",").map((U)=>m[parseInt(U.trim())]||U);if(W.length===1)return`${W[0]} ${Q}`;return`${W.join(", ")} ${Q}`}return B}catch{return B}}function k(B){let Z=new Date(B),L=new Date,G=Z.getTime()-L.getTime(),J=G>0,$=Math.abs(G),K=Math.floor($/60000),H=Math.floor($/3600000),Q=Z.toLocaleTimeString([],{hour:"numeric",minute:"2-digit"}),W=Z.toDateString()===L.toDateString(),U=new Date(L);U.setDate(U.getDate()+1);let O=Z.toDateString()===U.toDateString();if(W){if(K<1)return"now";if(K<60)return J?`in ${K}m`:`${K}m ago`;return J?`in ${H}h (${Q})`:`${H}h ago`}if(O)return`Tomorrow ${Q}`;return`${m[Z.getDay()]} ${Q}`}function zq(B){let Z=Math.floor((Date.now()-new Date(B).getTime())/1000);if(Z<5)return"just now";if(Z<60)return`${Z}s ago`;let L=Math.floor(Z/60);if(L<60)return`${L}m ago`;let G=Math.floor(L/60);if(G<24)return`${G}h ago`;return`${Math.floor(G/24)}d ago`}
|
|
2
|
-
export{Qq as d};
|
|
3
|
-
|
|
4
|
-
//# debugId=FD9DB9A2941A85B564756E2164756E21
|