heyhank 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +83 -10
- package/bin/cli.ts +7 -7
- package/bin/ctl.ts +42 -42
- package/dist/assets/{AgentsPage-BPhirnCe.js → AgentsPage-B-AAmsMK.js} +3 -3
- package/dist/assets/AssistantPage-BV1Mfwdt.js +2 -0
- package/dist/assets/BusinessPage-tLpNEz19.js +1 -0
- package/dist/assets/{CronManager-DDbz-yiT.js → CronManager-B-K_n3Jg.js} +1 -1
- package/dist/assets/HelpPage-Bhf_j6Xr.js +1 -0
- package/dist/assets/{IntegrationsPage-CrOitCmJ.js → IntegrationsPage-DAMjs9tM.js} +1 -1
- package/dist/assets/JarvisHUD-C_TGXCCn.js +120 -0
- package/dist/assets/MediaPage-C48HTTrt.js +1 -0
- package/dist/assets/MemoryPage-JkC-qtgp.js +1 -0
- package/dist/assets/{PlatformDashboard-Do6F0O2p.js → PlatformDashboard-AUo7tNnE.js} +1 -1
- package/dist/assets/{Playground-Fc5cdc5p.js → Playground-AzNMsRBL.js} +1 -1
- package/dist/assets/{ProcessPanel-CslEiZkI.js → ProcessPanel-DpE_2sX3.js} +1 -1
- package/dist/assets/{PromptsPage-D2EhsdNO.js → PromptsPage-C2RQOs6p.js} +2 -2
- package/dist/assets/RunsPage-B9UOyO79.js +1 -0
- package/dist/assets/{SandboxManager-a1AVI5q2.js → SandboxManager-jHvYjwfh.js} +1 -1
- package/dist/assets/SettingsPage-BBJax6gt.js +51 -0
- package/dist/assets/SkillsMarketplace-IjmjfdjD.js +1 -0
- package/dist/assets/SocialMediaPage-DoPZHhr2.js +10 -0
- package/dist/assets/{TailscalePage-CHiFhZXF.js → TailscalePage-DDEY7ckO.js} +1 -1
- package/dist/assets/TelephonyPage-OPNBZYKt.js +9 -0
- package/dist/assets/{TerminalPage-Drwyrnfd.js → TerminalPage-BjMbHHW3.js} +1 -1
- package/dist/assets/{gemini-live-client-C7rqAW7G.js → gemini-live-client-C70FEtX2.js} +11 -8
- package/dist/assets/{index-CEqZnThB.js → index-BgYM4wXw.js} +94 -93
- package/dist/assets/index-BkjSoVgn.css +32 -0
- package/dist/assets/sw-register-C7NOHtIu.js +1 -0
- package/dist/assets/text-chat-client-BSbLJerZ.js +2 -0
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/package.json +6 -1
- package/server/agent-executor.ts +37 -2
- package/server/agent-store.ts +3 -3
- package/server/agent-types.ts +11 -0
- package/server/assistant-store.ts +232 -6
- package/server/auth-manager.ts +9 -0
- package/server/cache-headers.ts +1 -1
- package/server/calendar-service.ts +10 -0
- package/server/ceo/document-store.ts +129 -0
- package/server/ceo/finance-store.ts +343 -0
- package/server/ceo/kpi-store.ts +208 -0
- package/server/ceo/memory-import.ts +277 -0
- package/server/ceo/news-store.ts +208 -0
- package/server/ceo/template-store.ts +134 -0
- package/server/ceo/time-tracking-store.ts +227 -0
- package/server/claude-auth-monitor.ts +128 -0
- package/server/claude-code-worker.ts +86 -0
- package/server/claude-session-discovery.ts +74 -1
- package/server/cli-launcher.ts +32 -10
- package/server/codex-adapter.ts +2 -2
- package/server/codex-ws-proxy.cjs +1 -1
- package/server/container-manager.ts +4 -4
- package/server/content-intelligence/content-engine.ts +1112 -0
- package/server/content-intelligence/platform-knowledge.ts +870 -0
- package/server/cron-store.ts +3 -3
- package/server/embedding-service.ts +49 -0
- package/server/event-bus-types.ts +13 -0
- package/server/federation/node-store.ts +5 -4
- package/server/fs-utils.ts +28 -1
- package/server/hank-notifications-store.ts +91 -0
- package/server/hank-tool-executor.ts +1835 -0
- package/server/hank-tools.ts +2107 -0
- package/server/image-pull-manager.ts +2 -2
- package/server/index.ts +25 -2
- package/server/llm-providers-streaming.ts +541 -0
- package/server/llm-providers.ts +12 -0
- package/server/marketplace.ts +249 -0
- package/server/mcp-registry.ts +158 -0
- package/server/memory-service.ts +296 -0
- package/server/obsidian-sync.ts +184 -0
- package/server/provider-manager.ts +5 -2
- package/server/provider-registry.ts +12 -0
- package/server/reminder-scheduler.ts +37 -1
- package/server/routes/agent-routes.ts +2 -1
- package/server/routes/assistant-routes.ts +198 -5
- package/server/routes/ceo-finance-kpi-routes.ts +167 -0
- package/server/routes/ceo-news-time-routes.ts +137 -0
- package/server/routes/ceo-routes.ts +99 -0
- package/server/routes/content-routes.ts +116 -0
- package/server/routes/email-routes.ts +147 -0
- package/server/routes/env-routes.ts +3 -3
- package/server/routes/fs-routes.ts +12 -9
- package/server/routes/hank-chat-routes.ts +592 -0
- package/server/routes/llm-routes.ts +12 -0
- package/server/routes/marketplace-routes.ts +63 -0
- package/server/routes/media-routes.ts +1 -1
- package/server/routes/memory-routes.ts +127 -0
- package/server/routes/platform-routes.ts +14 -675
- package/server/routes/sandbox-routes.ts +1 -1
- package/server/routes/settings-routes.ts +51 -1
- package/server/routes/socialmedia-routes.ts +152 -2
- package/server/routes/system-routes.ts +2 -2
- package/server/routes/team-routes.ts +71 -0
- package/server/routes/telephony-routes.ts +98 -18
- package/server/routes.ts +36 -9
- package/server/session-creation-service.ts +2 -2
- package/server/session-orchestrator.ts +54 -2
- package/server/session-types.ts +2 -0
- package/server/settings-manager.ts +50 -2
- package/server/skill-discovery.ts +68 -0
- package/server/socialmedia/adapters/browser-adapter.ts +179 -0
- package/server/socialmedia/adapters/postiz-adapter.ts +291 -14
- package/server/socialmedia/manager.ts +234 -15
- package/server/socialmedia/store.ts +51 -1
- package/server/socialmedia/types.ts +35 -2
- package/server/socialview/browser-manager.ts +150 -0
- package/server/socialview/extractors.ts +1298 -0
- package/server/socialview/image-describe.ts +188 -0
- package/server/socialview/library.ts +119 -0
- package/server/socialview/poster.ts +276 -0
- package/server/socialview/routes.ts +371 -0
- package/server/socialview/style-analyzer.ts +187 -0
- package/server/socialview/style-profiles.ts +67 -0
- package/server/socialview/types.ts +166 -0
- package/server/socialview/vision.ts +127 -0
- package/server/socialview/vnc-manager.ts +110 -0
- package/server/style-injector.ts +135 -0
- package/server/team-service.ts +239 -0
- package/server/team-store.ts +75 -0
- package/server/team-types.ts +52 -0
- package/server/telephony/audio-bridge.ts +281 -35
- package/server/telephony/audio-recorder.ts +132 -0
- package/server/telephony/call-manager.ts +803 -104
- package/server/telephony/call-types.ts +67 -1
- package/server/telephony/esl-client.ts +319 -0
- package/server/telephony/freeswitch-sync.ts +155 -0
- package/server/telephony/phone-utils.ts +63 -0
- package/server/telephony/telephony-store.ts +9 -8
- package/server/url-validator.ts +82 -0
- package/server/vault-markdown.ts +317 -0
- package/server/vault-migration.ts +121 -0
- package/server/vault-store.ts +466 -0
- package/server/vault-watcher.ts +59 -0
- package/server/vector-store.ts +210 -0
- package/server/voice-pipeline/gemini-live-adapter.ts +97 -0
- package/server/voice-pipeline/greeting-cache.ts +200 -0
- package/server/voice-pipeline/manager.ts +249 -0
- package/server/voice-pipeline/pipeline.ts +335 -0
- package/server/voice-pipeline/providers/index.ts +47 -0
- package/server/voice-pipeline/providers/llm-internal.ts +527 -0
- package/server/voice-pipeline/providers/stt-google.ts +157 -0
- package/server/voice-pipeline/providers/tts-google.ts +126 -0
- package/server/voice-pipeline/types.ts +247 -0
- package/server/ws-bridge-types.ts +6 -1
- package/dist/assets/AssistantPage-DJ-cMQfb.js +0 -1
- package/dist/assets/HelpPage-DMfkzERp.js +0 -1
- package/dist/assets/MediaPage-CE5rdvkC.js +0 -1
- package/dist/assets/RunsPage-C5BZF5Rx.js +0 -1
- package/dist/assets/SettingsPage-DirhjQrJ.js +0 -51
- package/dist/assets/SocialMediaPage-DBuM28vD.js +0 -1
- package/dist/assets/TelephonyPage-x0VV0fOo.js +0 -1
- package/dist/assets/index-C8M_PUmX.css +0 -32
- package/dist/assets/sw-register-LSSpj6RU.js +0 -1
- package/server/socialmedia/adapters/ayrshare-adapter.ts +0 -169
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// ─── Google Cloud TTS Provider ───────────────────────────────────────────────
|
|
2
|
+
// Uses the REST API (single synthesize call). For telephony we request LINEAR16
|
|
3
|
+
// at 8000 Hz which can be sent directly to FreeSWITCH without resampling.
|
|
4
|
+
|
|
5
|
+
import { GoogleAuth } from "google-auth-library";
|
|
6
|
+
import type { TTSConfig, TTSProvider, TTSResult } from "../types.js";
|
|
7
|
+
|
|
8
|
+
const TTS_BASE = "https://texttospeech.googleapis.com/v1";
|
|
9
|
+
const SCOPES = ["https://www.googleapis.com/auth/cloud-platform"];
|
|
10
|
+
|
|
11
|
+
function getKeyFile(): string {
|
|
12
|
+
return process.env.GCP_SERVICE_ACCOUNT_KEY
|
|
13
|
+
|| "/opt/agentplatform/gcp-service-account.json";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let cachedAuth: GoogleAuth | null = null;
|
|
17
|
+
async function getToken(): Promise<string> {
|
|
18
|
+
if (!cachedAuth) {
|
|
19
|
+
cachedAuth = new GoogleAuth({ keyFile: getKeyFile(), scopes: SCOPES });
|
|
20
|
+
}
|
|
21
|
+
const client = await cachedAuth.getClient();
|
|
22
|
+
const tok = await client.getAccessToken();
|
|
23
|
+
if (!tok.token) throw new Error("No access token from GoogleAuth");
|
|
24
|
+
return tok.token;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Map our format enum → Google Cloud TTS audioEncoding + sampleRateHertz */
|
|
28
|
+
function mapFormat(format: TTSConfig["format"]): { audioEncoding: string; sampleRateHertz?: number } {
|
|
29
|
+
switch (format) {
|
|
30
|
+
case "PCM_8000":
|
|
31
|
+
return { audioEncoding: "LINEAR16", sampleRateHertz: 8000 };
|
|
32
|
+
case "MP3":
|
|
33
|
+
return { audioEncoding: "MP3" };
|
|
34
|
+
case "OGG_OPUS":
|
|
35
|
+
return { audioEncoding: "OGG_OPUS" };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** PCM_8000 returns audio as a WAV container (~44 byte header). Strip it for FreeSWITCH. */
|
|
40
|
+
function stripWavHeader(audio: Uint8Array): Uint8Array {
|
|
41
|
+
// WAV header is "RIFF....WAVE" - find "data" chunk and skip 8 bytes after it
|
|
42
|
+
if (audio.length < 44) return audio;
|
|
43
|
+
if (audio[0] === 0x52 && audio[1] === 0x49 && audio[2] === 0x46 && audio[3] === 0x46) {
|
|
44
|
+
// RIFF header detected - search for "data" marker
|
|
45
|
+
for (let i = 12; i < Math.min(audio.length - 8, 256); i++) {
|
|
46
|
+
if (audio[i] === 0x64 && audio[i + 1] === 0x61 && audio[i + 2] === 0x74 && audio[i + 3] === 0x61) {
|
|
47
|
+
// Skip "data" (4 bytes) + chunk size (4 bytes) = 8 bytes
|
|
48
|
+
return audio.slice(i + 8);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return audio;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class GoogleTTSProvider implements TTSProvider {
|
|
56
|
+
readonly id = "google" as const;
|
|
57
|
+
|
|
58
|
+
async synthesize(text: string, config: TTSConfig): Promise<TTSResult> {
|
|
59
|
+
const token = await getToken();
|
|
60
|
+
const fmt = mapFormat(config.format);
|
|
61
|
+
|
|
62
|
+
const body = {
|
|
63
|
+
input: { text },
|
|
64
|
+
voice: {
|
|
65
|
+
languageCode: config.language,
|
|
66
|
+
name: config.voice,
|
|
67
|
+
},
|
|
68
|
+
audioConfig: {
|
|
69
|
+
audioEncoding: fmt.audioEncoding,
|
|
70
|
+
...(fmt.sampleRateHertz ? { sampleRateHertz: fmt.sampleRateHertz } : {}),
|
|
71
|
+
speakingRate: config.speakingRate ?? 1.0,
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const res = await fetch(`${TTS_BASE}/text:synthesize`, {
|
|
76
|
+
method: "POST",
|
|
77
|
+
headers: {
|
|
78
|
+
"Authorization": `Bearer ${token}`,
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
},
|
|
81
|
+
body: JSON.stringify(body),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!res.ok) {
|
|
85
|
+
const errText = await res.text();
|
|
86
|
+
throw new Error(`Google TTS error ${res.status}: ${errText.slice(0, 200)}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const data = await res.json() as { audioContent?: string };
|
|
90
|
+
if (!data.audioContent) {
|
|
91
|
+
throw new Error("Google TTS returned no audioContent");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const decoded = Buffer.from(data.audioContent, "base64");
|
|
95
|
+
let audio: Uint8Array = new Uint8Array(decoded.buffer.slice(decoded.byteOffset, decoded.byteOffset + decoded.byteLength));
|
|
96
|
+
if (config.format === "PCM_8000") {
|
|
97
|
+
audio = new Uint8Array(stripWavHeader(audio));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
audio,
|
|
102
|
+
format: config.format,
|
|
103
|
+
voice: config.voice,
|
|
104
|
+
bytes: audio.byteLength,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async listVoices(language: string): Promise<Array<{ name: string; gender?: string; type?: string }>> {
|
|
109
|
+
const token = await getToken();
|
|
110
|
+
const res = await fetch(`${TTS_BASE}/voices?languageCode=${encodeURIComponent(language)}`, {
|
|
111
|
+
headers: { "Authorization": `Bearer ${token}` },
|
|
112
|
+
});
|
|
113
|
+
if (!res.ok) throw new Error(`Google TTS listVoices ${res.status}`);
|
|
114
|
+
const data = await res.json() as {
|
|
115
|
+
voices?: Array<{ name: string; ssmlGender?: string }>;
|
|
116
|
+
};
|
|
117
|
+
return (data.voices || []).map((v) => {
|
|
118
|
+
let type = "Standard";
|
|
119
|
+
if (v.name.includes("Studio")) type = "Studio";
|
|
120
|
+
else if (v.name.includes("Chirp")) type = "Chirp HD";
|
|
121
|
+
else if (v.name.includes("Neural2")) type = "Neural2";
|
|
122
|
+
else if (v.name.includes("Wavenet")) type = "WaveNet";
|
|
123
|
+
return { name: v.name, gender: v.ssmlGender, type };
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// ─── Voice Pipeline Types ─────────────────────────────────────────────────────
|
|
2
|
+
// Provider-agnostic STT → LLM → TTS pipeline as alternative to Gemini Live.
|
|
3
|
+
// Designed for low-latency telephony with pre-rendered greetings.
|
|
4
|
+
|
|
5
|
+
import type { TranscriptEntry, CallState } from "../telephony/call-types.js";
|
|
6
|
+
|
|
7
|
+
// ─── Provider IDs ─────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export type STTProviderId = "google" | "deepgram";
|
|
10
|
+
export type TTSProviderId = "google" | "cartesia" | "elevenlabs";
|
|
11
|
+
/**
|
|
12
|
+
* LLM provider for the voice pipeline — must be an explicit provider-registry id.
|
|
13
|
+
* The chosen provider must be configured & enabled under Settings → Providers.
|
|
14
|
+
*/
|
|
15
|
+
export type LLMProviderId = "anthropic" | "groq" | "openai" | "openrouter" | "mistral" | "deepseek" | "together" | "xai" | "qwen" | "venice" | "moonshot";
|
|
16
|
+
|
|
17
|
+
// ─── Audio formats ────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
/** Telephony audio format from FreeSWITCH (mod_audio_fork) */
|
|
20
|
+
export interface TelephonyAudioFormat {
|
|
21
|
+
encoding: "LINEAR16";
|
|
22
|
+
sampleRateHertz: 8000;
|
|
23
|
+
channels: 1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const TELEPHONY_AUDIO: TelephonyAudioFormat = {
|
|
27
|
+
encoding: "LINEAR16",
|
|
28
|
+
sampleRateHertz: 8000,
|
|
29
|
+
channels: 1,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// ─── STT Provider ─────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
export interface STTConfig {
|
|
35
|
+
language: string; // e.g. "de-DE"
|
|
36
|
+
sampleRateHertz: number; // 8000 for telephony
|
|
37
|
+
interimResults?: boolean; // emit partial results
|
|
38
|
+
/** Provider-specific extras */
|
|
39
|
+
options?: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface STTResult {
|
|
43
|
+
text: string;
|
|
44
|
+
isFinal: boolean;
|
|
45
|
+
/** 0.0 - 1.0 if available */
|
|
46
|
+
confidence?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Streaming STT — caller pushes audio chunks, gets back transcripts */
|
|
50
|
+
export interface STTSession {
|
|
51
|
+
/** Push raw PCM audio (8kHz LINEAR16 from FreeSWITCH) */
|
|
52
|
+
pushAudio(pcm: Buffer | Uint8Array): void;
|
|
53
|
+
/** Stop accepting audio + close stream */
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
/** Subscribe to transcript events */
|
|
56
|
+
onResult(handler: (result: STTResult) => void): void;
|
|
57
|
+
/** Subscribe to errors */
|
|
58
|
+
onError(handler: (err: Error) => void): void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface STTProvider {
|
|
62
|
+
readonly id: STTProviderId;
|
|
63
|
+
start(config: STTConfig): Promise<STTSession>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─── TTS Provider ─────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
export interface TTSConfig {
|
|
69
|
+
voice: string; // e.g. "de-DE-Chirp-HD-D"
|
|
70
|
+
language: string; // e.g. "de-DE"
|
|
71
|
+
/** Output format — telephony needs PCM 8kHz, browser playback prefers MP3 */
|
|
72
|
+
format: "PCM_8000" | "MP3" | "OGG_OPUS";
|
|
73
|
+
speakingRate?: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TTSResult {
|
|
77
|
+
/** Audio bytes in the requested format */
|
|
78
|
+
audio: Uint8Array;
|
|
79
|
+
format: TTSConfig["format"];
|
|
80
|
+
/** Voice that was actually used */
|
|
81
|
+
voice: string;
|
|
82
|
+
/** Total bytes synthesized */
|
|
83
|
+
bytes: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface TTSProvider {
|
|
87
|
+
readonly id: TTSProviderId;
|
|
88
|
+
/** Synthesize text → audio (single call, not streaming) */
|
|
89
|
+
synthesize(text: string, config: TTSConfig): Promise<TTSResult>;
|
|
90
|
+
/** List available voices for a language */
|
|
91
|
+
listVoices(language: string): Promise<Array<{ name: string; gender?: string; type?: string }>>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── LLM Provider ─────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
export interface LLMMessage {
|
|
97
|
+
role: "system" | "user" | "assistant";
|
|
98
|
+
content: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface LLMToolDef {
|
|
102
|
+
name: string;
|
|
103
|
+
description: string;
|
|
104
|
+
/** JSON Schema for tool parameters */
|
|
105
|
+
parameters: Record<string, unknown>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface LLMToolCall {
|
|
109
|
+
id: string;
|
|
110
|
+
name: string;
|
|
111
|
+
args: Record<string, unknown>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface LLMToolResult {
|
|
115
|
+
id: string;
|
|
116
|
+
name: string;
|
|
117
|
+
response: unknown;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface LLMConfig {
|
|
121
|
+
model?: string;
|
|
122
|
+
temperature?: number;
|
|
123
|
+
maxTokens?: number;
|
|
124
|
+
/** Stream tokens (lower latency) */
|
|
125
|
+
stream?: boolean;
|
|
126
|
+
/** Tools the LLM may call */
|
|
127
|
+
tools?: LLMToolDef[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface LLMStreamCallbacks {
|
|
131
|
+
/** Streaming text deltas */
|
|
132
|
+
onChunk: (chunk: string) => void;
|
|
133
|
+
/**
|
|
134
|
+
* Called when the LLM emits one or more tool calls. Must return tool results
|
|
135
|
+
* which the provider will append + continue the conversation with.
|
|
136
|
+
*/
|
|
137
|
+
onToolCalls?: (calls: LLMToolCall[]) => Promise<LLMToolResult[]>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface LLMProvider {
|
|
141
|
+
readonly id: LLMProviderId;
|
|
142
|
+
/** Generate a single response (full text — non-streaming) */
|
|
143
|
+
generate(messages: LLMMessage[], config?: LLMConfig): Promise<{ text: string; ok: boolean; error?: string }>;
|
|
144
|
+
/** Stream tokens one chunk at a time. Emits done() when finished. */
|
|
145
|
+
generateStream?(
|
|
146
|
+
messages: LLMMessage[],
|
|
147
|
+
callbacks: LLMStreamCallbacks,
|
|
148
|
+
config?: LLMConfig,
|
|
149
|
+
): Promise<{ text: string; ok: boolean; error?: string }>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ─── Pipeline Engine ──────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
export type VoiceEngineId = "pipeline" | "gemini-live";
|
|
155
|
+
|
|
156
|
+
export interface VoicePipelineSettings {
|
|
157
|
+
/** Master switch — if false, always use Gemini Live */
|
|
158
|
+
enabled: boolean;
|
|
159
|
+
/** Default engine for new calls */
|
|
160
|
+
engine: VoiceEngineId;
|
|
161
|
+
/** Fall back to Gemini Live if pipeline init fails */
|
|
162
|
+
fallbackToGeminiLive: boolean;
|
|
163
|
+
stt: {
|
|
164
|
+
provider: STTProviderId;
|
|
165
|
+
language: string; // "de-DE"
|
|
166
|
+
};
|
|
167
|
+
tts: {
|
|
168
|
+
provider: TTSProviderId;
|
|
169
|
+
voice: string; // "de-DE-Chirp-HD-D"
|
|
170
|
+
speakingRate?: number;
|
|
171
|
+
};
|
|
172
|
+
llm: {
|
|
173
|
+
provider: LLMProviderId;
|
|
174
|
+
model?: string;
|
|
175
|
+
temperature?: number;
|
|
176
|
+
};
|
|
177
|
+
/** Pre-render greetings on contact create/update for instant playback */
|
|
178
|
+
preRenderGreetings: boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Operator displayed in the greeting (e.g. "Markus Stöger", "ACME GmbH").
|
|
181
|
+
* Empty → Hank introduces itself without naming an operator.
|
|
182
|
+
*/
|
|
183
|
+
operatorName?: string;
|
|
184
|
+
/**
|
|
185
|
+
* Append AI + recording disclosure to the cached greeting.
|
|
186
|
+
* EU AI Act Art. 50 requires AI systems interacting with natural persons to
|
|
187
|
+
* disclose their AI nature; DSGVO Art. 13 requires informing callers about
|
|
188
|
+
* the recording. Defaults to true; only disable if you have an alternative
|
|
189
|
+
* disclosure path (e.g. an IVR announcement before the call connects).
|
|
190
|
+
*/
|
|
191
|
+
legalDisclosureInGreeting?: boolean;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export const DEFAULT_VOICE_PIPELINE_SETTINGS: VoicePipelineSettings = {
|
|
195
|
+
enabled: false, // opt-in initially
|
|
196
|
+
engine: "pipeline",
|
|
197
|
+
fallbackToGeminiLive: true,
|
|
198
|
+
stt: { provider: "google", language: "de-DE" },
|
|
199
|
+
tts: { provider: "google", voice: "de-DE-Chirp-HD-D" },
|
|
200
|
+
llm: { provider: "groq" }, // Best for voice (low TTFT)
|
|
201
|
+
preRenderGreetings: true,
|
|
202
|
+
operatorName: "",
|
|
203
|
+
legalDisclosureInGreeting: true,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// ─── Pipeline Session (per call) ──────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
export interface PipelineSessionConfig {
|
|
209
|
+
callId: string;
|
|
210
|
+
direction: "inbound" | "outbound";
|
|
211
|
+
/** Contact info if known (for personalized greeting) */
|
|
212
|
+
contact?: {
|
|
213
|
+
id: string;
|
|
214
|
+
name: string;
|
|
215
|
+
phone: string;
|
|
216
|
+
} | null;
|
|
217
|
+
/** Phone number of remote party (for caller-ID lookup on inbound) */
|
|
218
|
+
remoteNumber: string;
|
|
219
|
+
/** System prompt for the LLM */
|
|
220
|
+
systemPrompt: string;
|
|
221
|
+
/** Pre-rendered greeting MP3 (PCM 8kHz to send to FreeSWITCH on answer) */
|
|
222
|
+
greetingPcm?: Uint8Array | null;
|
|
223
|
+
/** Hank tools (LLM tool definitions in provider-agnostic JSON Schema form) */
|
|
224
|
+
tools?: LLMToolDef[];
|
|
225
|
+
/** Pipeline settings */
|
|
226
|
+
settings: VoicePipelineSettings;
|
|
227
|
+
/** Callbacks */
|
|
228
|
+
onTranscript: (entry: TranscriptEntry) => void;
|
|
229
|
+
onStatusChange: (status: CallState["status"]) => void;
|
|
230
|
+
onAudioOut: (pcm8k: Uint8Array) => void;
|
|
231
|
+
/** Invoked when the LLM requests one or more tool calls during a turn */
|
|
232
|
+
onToolCall?: (calls: LLMToolCall[]) => Promise<LLMToolResult[]>;
|
|
233
|
+
/** Optional: emitted when the assistant has finished speaking a full turn (audio dispatched) */
|
|
234
|
+
onTurnComplete?: () => void;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface PipelineSession {
|
|
238
|
+
/** Send caller audio (PCM 8kHz) into the pipeline */
|
|
239
|
+
pushAudio(pcm: Buffer | Uint8Array): void;
|
|
240
|
+
/** Send a text message manually (e.g. trigger greeting) */
|
|
241
|
+
sendText(text: string, role?: "user" | "system"): Promise<void>;
|
|
242
|
+
/** Get the cached greeting PCM if ready (for immediate play on call answer) */
|
|
243
|
+
getGreetingPcm(): Uint8Array | null;
|
|
244
|
+
/** Tear down */
|
|
245
|
+
close(): Promise<void>;
|
|
246
|
+
readonly isReady: boolean;
|
|
247
|
+
}
|
|
@@ -47,7 +47,12 @@ export interface TelephonyTranscriptSocketData {
|
|
|
47
47
|
callId: string;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
export
|
|
50
|
+
export interface TelephonyListenSocketData {
|
|
51
|
+
kind: "telephony-listen";
|
|
52
|
+
callId: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type SocketData = CLISocketData | BrowserSocketData | TerminalSocketData | NoVncSocketData | NodeSocketData | TelephonyAudioSocketData | TelephonyTranscriptSocketData | TelephonyListenSocketData;
|
|
51
56
|
|
|
52
57
|
/** Tracks a pending control_request sent to CLI that expects a control_response. */
|
|
53
58
|
export interface PendingControlRequest {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as c,j as e,b as m}from"./index-CEqZnThB.js";const M={high:"text-red-400 bg-red-500/10 border-red-500/20",medium:"text-yellow-400 bg-yellow-500/10 border-yellow-500/20",low:"text-green-400 bg-green-500/10 border-green-500/20"};function z(o){const d=Date.now()-new Date(o).getTime(),n=Math.floor(d/6e4);if(n<1)return"just now";if(n<60)return`${n}m ago`;const l=Math.floor(n/60);if(l<24)return`${l}h ago`;const u=Math.floor(l/24);return u<7?`${u}d ago`:new Date(o).toLocaleDateString()}function V(o){return new Date(o).toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function O(){const[o,d]=c.useState("todos"),[n,l]=c.useState(0);function u(){l(a=>a+1)}const h=[{id:"todos",label:"Todos"},{id:"notes",label:"Notes"},{id:"reminders",label:"Reminders"}];return e.jsxs("div",{className:"h-full flex flex-col bg-cc-bg overflow-hidden",children:[e.jsxs("div",{className:"shrink-0 border-b border-cc-border px-4 sm:px-6 py-4",children:[e.jsx("div",{className:"flex items-center justify-between",children:e.jsxs("div",{children:[e.jsx("h1",{className:"text-lg font-semibold text-cc-fg",children:"Assistant"}),e.jsx("p",{className:"text-xs text-cc-muted mt-0.5",children:"Personal todos, notes & reminders — managed by Gemini Live"})]})}),e.jsx("div",{className:"flex gap-1 mt-3",children:h.map(a=>e.jsx("button",{type:"button",onClick:()=>d(a.id),className:`px-3 py-1.5 text-xs font-medium rounded-md transition-colors cursor-pointer ${o===a.id?"bg-cc-accent text-white":"text-cc-muted hover:text-cc-fg hover:bg-cc-hover"}`,children:a.label},a.id))})]}),e.jsxs("div",{className:"flex-1 overflow-auto",children:[o==="todos"&&e.jsx(H,{refreshKey:n,onRefresh:u}),o==="notes"&&e.jsx(B,{refreshKey:n,onRefresh:u}),o==="reminders"&&e.jsx(P,{refreshKey:n,onRefresh:u})]})]})}function H({refreshKey:o,onRefresh:d}){const[n,l]=c.useState([]),[u,h]=c.useState(!0),[a,E]=c.useState("all"),[i,j]=c.useState(""),[b,p]=c.useState("medium"),[x,v]=c.useState(""),[S,y]=c.useState(!1),[T,g]=c.useState(null),[f,N]=c.useState(""),w=c.useCallback(async()=>{h(!0);try{const t={};a==="active"&&(t.done=!1),a==="done"&&(t.done=!0);const L=await m.listTodos(t);l(L.todos)}catch{}h(!1)},[a]);c.useEffect(()=>{w()},[w,o]);async function k(t){if(t.preventDefault(),!!i.trim()){y(!0);try{await m.addTodo({text:i.trim(),priority:b,category:x.trim()||void 0}),j(""),v(""),d()}catch{}y(!1)}}async function D(t){try{await m.updateTodo(t.id,{done:!t.done}),d()}catch{}}async function s(t){try{await m.deleteTodo(t),d()}catch{}}async function A(t){if(f.trim())try{await m.updateTodo(t,{text:f.trim()}),g(null),d()}catch{}}const r=n.filter(t=>!t.done),C=n.filter(t=>t.done),$=a==="all"?[...r,...C]:n;return e.jsxs("div",{className:"p-4 sm:p-6 space-y-4 max-w-3xl",children:[e.jsxs("form",{onSubmit:k,className:"flex flex-col sm:flex-row gap-2",children:[e.jsx("input",{type:"text",value:i,onChange:t=>j(t.target.value),placeholder:"Add a todo...",className:"flex-1 px-3 py-2 text-sm bg-cc-input border border-cc-border rounded-md text-cc-fg placeholder-cc-muted focus:outline-none focus:border-cc-accent"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("select",{value:b,onChange:t=>p(t.target.value),className:"px-2 py-2 text-xs bg-cc-input border border-cc-border rounded-md text-cc-fg focus:outline-none cursor-pointer",children:[e.jsx("option",{value:"high",children:"High"}),e.jsx("option",{value:"medium",children:"Medium"}),e.jsx("option",{value:"low",children:"Low"})]}),e.jsx("input",{type:"text",value:x,onChange:t=>v(t.target.value),placeholder:"Category",className:"w-24 px-2 py-2 text-xs bg-cc-input border border-cc-border rounded-md text-cc-fg placeholder-cc-muted focus:outline-none"}),e.jsx("button",{type:"submit",disabled:S||!i.trim(),className:"px-4 py-2 text-xs font-medium rounded-md bg-cc-accent text-white hover:bg-cc-accent/80 disabled:opacity-50 cursor-pointer",children:"Add"})]})]}),e.jsx("div",{className:"flex gap-1",children:["all","active","done"].map(t=>e.jsx("button",{type:"button",onClick:()=>E(t),className:`px-2.5 py-1 text-xs rounded-md transition-colors cursor-pointer ${a===t?"bg-cc-hover text-cc-fg":"text-cc-muted hover:text-cc-fg"}`,children:t.charAt(0).toUpperCase()+t.slice(1)},t))}),u?e.jsx("div",{className:"text-sm text-cc-muted py-8 text-center",children:"Loading..."}):$.length===0?e.jsx("div",{className:"text-sm text-cc-muted py-8 text-center",children:a==="done"?"No completed todos":a==="active"?"All done!":"No todos yet. Add one above or ask Gemini Live."}):e.jsx("div",{className:"space-y-1",children:$.map(t=>e.jsxs("div",{className:`group flex items-start gap-3 px-3 py-2.5 rounded-lg border transition-colors ${t.done?"bg-cc-bg border-cc-border/50 opacity-60":"bg-cc-card border-cc-border hover:border-cc-border-hover"}`,children:[e.jsx("button",{type:"button",onClick:()=>D(t),className:`mt-0.5 w-4 h-4 rounded border-2 shrink-0 flex items-center justify-center cursor-pointer transition-colors ${t.done?"bg-cc-accent border-cc-accent":"border-cc-muted hover:border-cc-accent"}`,children:t.done&&e.jsx("svg",{viewBox:"0 0 12 12",className:"w-2.5 h-2.5 text-white",fill:"none",stroke:"currentColor",strokeWidth:"2",children:e.jsx("path",{d:"M2 6l3 3 5-5"})})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[T===t.id?e.jsxs("div",{className:"flex gap-2",children:[e.jsx("input",{type:"text",value:f,onChange:L=>N(L.target.value),onKeyDown:L=>{L.key==="Enter"&&A(t.id),L.key==="Escape"&&g(null)},className:"flex-1 px-2 py-1 text-sm bg-cc-input border border-cc-border rounded text-cc-fg focus:outline-none focus:border-cc-accent",autoFocus:!0}),e.jsx("button",{type:"button",onClick:()=>A(t.id),className:"text-xs text-cc-accent hover:underline cursor-pointer",children:"Save"}),e.jsx("button",{type:"button",onClick:()=>g(null),className:"text-xs text-cc-muted hover:underline cursor-pointer",children:"Cancel"})]}):e.jsx("span",{className:`text-sm ${t.done?"line-through text-cc-muted":"text-cc-fg"}`,onDoubleClick:()=>{g(t.id),N(t.text)},children:t.text}),e.jsxs("div",{className:"flex items-center gap-2 mt-1",children:[e.jsx("span",{className:`text-[10px] px-1.5 py-0.5 rounded border ${M[t.priority]}`,children:t.priority}),t.category&&e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-cc-hover text-cc-muted",children:t.category}),e.jsx("span",{className:"text-[10px] text-cc-muted",children:z(t.createdAt)})]})]}),e.jsx("button",{type:"button",onClick:()=>s(t.id),className:"opacity-0 group-hover:opacity-100 text-cc-muted hover:text-red-400 transition-opacity cursor-pointer p-1",title:"Delete",children:e.jsxs("svg",{viewBox:"0 0 16 16",className:"w-3.5 h-3.5",fill:"currentColor",children:[e.jsx("path",{d:"M5.5 5.5A.5.5 0 016 6v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm2.5 0a.5.5 0 01.5.5v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm3 .5a.5.5 0 00-1 0v6a.5.5 0 001 0V6z"}),e.jsx("path",{fillRule:"evenodd",d:"M14.5 3a1 1 0 01-1 1H13v9a2 2 0 01-2 2H5a2 2 0 01-2-2V4h-.5a1 1 0 010-2H6a1 1 0 011-1h2a1 1 0 011 1h3.5a1 1 0 011 1zM4.118 4L4 4.059V13a1 1 0 001 1h6a1 1 0 001-1V4.059L11.882 4H4.118z"})]})})]},t.id))})]})}function B({refreshKey:o,onRefresh:d}){const[n,l]=c.useState([]),[u,h]=c.useState(!0),[a,E]=c.useState(""),[i,j]=c.useState(null),[b,p]=c.useState(!1),[x,v]=c.useState(""),[S,y]=c.useState(""),[T,g]=c.useState(""),[f,N]=c.useState(!1),w=c.useCallback(async()=>{h(!0);try{const r=await m.listNotes(a||void 0);l(r.notes)}catch{}h(!1)},[a]);c.useEffect(()=>{w()},[w,o]);function k(){j(null),v(""),y(""),g(""),p(!0)}function D(r){j(r),v(r.title),y(r.content),g(r.tags.join(", ")),p(!0)}async function s(){if(!x.trim())return;N(!0);const r=T.split(",").map(C=>C.trim()).filter(Boolean);try{i?await m.updateNote(i.id,{title:x.trim(),content:S,tags:r}):await m.addNote({title:x.trim(),content:S,tags:r}),p(!1),d()}catch{}N(!1)}async function A(r){try{await m.deleteNote(r),(i==null?void 0:i.id)===r&&p(!1),d()}catch{}}return b?e.jsxs("div",{className:"p-4 sm:p-6 max-w-3xl space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("button",{type:"button",onClick:()=>p(!1),className:"text-xs text-cc-muted hover:text-cc-fg cursor-pointer flex items-center gap-1",children:[e.jsx("svg",{viewBox:"0 0 16 16",className:"w-3 h-3",fill:"currentColor",children:e.jsx("path",{fillRule:"evenodd",d:"M11.354 1.646a.5.5 0 010 .708L5.707 8l5.647 5.646a.5.5 0 01-.708.708l-6-6a.5.5 0 010-.708l6-6a.5.5 0 01.708 0z"})}),"Back"]}),e.jsx("span",{className:"text-xs text-cc-muted",children:i?"Edit Note":"New Note"})]}),e.jsx("input",{type:"text",value:x,onChange:r=>v(r.target.value),placeholder:"Title",className:"w-full px-3 py-2 text-sm bg-cc-input border border-cc-border rounded-md text-cc-fg placeholder-cc-muted focus:outline-none focus:border-cc-accent",autoFocus:!0}),e.jsx("textarea",{value:S,onChange:r=>y(r.target.value),placeholder:"Write your note...",rows:12,className:"w-full px-3 py-2 text-sm bg-cc-input border border-cc-border rounded-md text-cc-fg placeholder-cc-muted focus:outline-none focus:border-cc-accent resize-y font-mono"}),e.jsx("input",{type:"text",value:T,onChange:r=>g(r.target.value),placeholder:"Tags (comma separated)",className:"w-full px-2 py-1.5 text-xs bg-cc-input border border-cc-border rounded-md text-cc-fg placeholder-cc-muted focus:outline-none"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("button",{type:"button",onClick:s,disabled:f||!x.trim(),className:"px-4 py-2 text-xs font-medium rounded-md bg-cc-accent text-white hover:bg-cc-accent/80 disabled:opacity-50 cursor-pointer",children:f?"Saving...":"Save"}),e.jsx("button",{type:"button",onClick:()=>p(!1),className:"px-4 py-2 text-xs text-cc-muted hover:text-cc-fg rounded-md hover:bg-cc-hover cursor-pointer",children:"Cancel"})]})]}):e.jsxs("div",{className:"p-4 sm:p-6 space-y-4 max-w-3xl",children:[e.jsxs("div",{className:"flex gap-2",children:[e.jsx("input",{type:"text",value:a,onChange:r=>E(r.target.value),placeholder:"Search notes...",className:"flex-1 px-3 py-2 text-sm bg-cc-input border border-cc-border rounded-md text-cc-fg placeholder-cc-muted focus:outline-none focus:border-cc-accent"}),e.jsx("button",{type:"button",onClick:k,className:"px-4 py-2 text-xs font-medium rounded-md bg-cc-accent text-white hover:bg-cc-accent/80 cursor-pointer",children:"New Note"})]}),u?e.jsx("div",{className:"text-sm text-cc-muted py-8 text-center",children:"Loading..."}):n.length===0?e.jsx("div",{className:"text-sm text-cc-muted py-8 text-center",children:a?"No matching notes":"No notes yet. Create one or ask Gemini Live."}):e.jsx("div",{className:"grid gap-3 sm:grid-cols-2",children:n.map(r=>e.jsxs("div",{className:"group p-4 rounded-lg border border-cc-border bg-cc-card hover:border-cc-border-hover transition-colors cursor-pointer",onClick:()=>D(r),children:[e.jsxs("div",{className:"flex items-start justify-between",children:[e.jsx("h3",{className:"text-sm font-medium text-cc-fg truncate flex-1",children:r.title}),e.jsx("button",{type:"button",onClick:C=>{C.stopPropagation(),A(r.id)},className:"opacity-0 group-hover:opacity-100 text-cc-muted hover:text-red-400 transition-opacity cursor-pointer p-1 -mr-1 -mt-1",title:"Delete",children:e.jsx("svg",{viewBox:"0 0 16 16",className:"w-3 h-3",fill:"currentColor",children:e.jsx("path",{d:"M4.646 4.646a.5.5 0 01.708 0L8 7.293l2.646-2.647a.5.5 0 01.708.708L8.707 8l2.647 2.646a.5.5 0 01-.708.708L8 8.707l-2.646 2.647a.5.5 0 01-.708-.708L7.293 8 4.646 5.354a.5.5 0 010-.708z"})})})]}),r.content&&e.jsx("p",{className:"text-xs text-cc-muted mt-1 line-clamp-3",children:r.content}),e.jsxs("div",{className:"flex items-center gap-2 mt-2",children:[r.tags.map(C=>e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-cc-hover text-cc-muted",children:C},C)),e.jsx("span",{className:"text-[10px] text-cc-muted ml-auto",children:z(r.updatedAt)})]})]},r.id))})]})}function P({refreshKey:o,onRefresh:d}){const[n,l]=c.useState([]),[u,h]=c.useState(!0),[a,E]=c.useState(!1),[i,j]=c.useState(""),[b,p]=c.useState(""),[x,v]=c.useState(""),[S,y]=c.useState(!1),T=c.useCallback(async()=>{h(!0);try{const s=await m.listReminders(a);l(s.reminders)}catch{}h(!1)},[a]);c.useEffect(()=>{T()},[T,o]);async function g(s){if(s.preventDefault(),!(!i.trim()||!b||!x)){y(!0);try{const A=new Date(`${b}T${x}`).toISOString();await m.addReminder({text:i.trim(),triggerAt:A}),j(""),p(""),v(""),d()}catch{}y(!1)}}async function f(s){try{await m.deleteReminder(s),d()}catch{}}const N=new Date,w=n.filter(s=>!s.fired&&new Date(s.triggerAt)>N),k=n.filter(s=>!s.fired&&new Date(s.triggerAt)<=N),D=n.filter(s=>s.fired);return e.jsxs("div",{className:"p-4 sm:p-6 space-y-4 max-w-3xl",children:[e.jsxs("form",{onSubmit:g,className:"flex flex-col sm:flex-row gap-2",children:[e.jsx("input",{type:"text",value:i,onChange:s=>j(s.target.value),placeholder:"Remind me to...",className:"flex-1 px-3 py-2 text-sm bg-cc-input border border-cc-border rounded-md text-cc-fg placeholder-cc-muted focus:outline-none focus:border-cc-accent"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("input",{type:"date",value:b,onChange:s=>p(s.target.value),className:"px-2 py-2 text-xs bg-cc-input border border-cc-border rounded-md text-cc-fg focus:outline-none"}),e.jsx("input",{type:"time",value:x,onChange:s=>v(s.target.value),className:"px-2 py-2 text-xs bg-cc-input border border-cc-border rounded-md text-cc-fg focus:outline-none"}),e.jsx("button",{type:"submit",disabled:S||!i.trim()||!b||!x,className:"px-4 py-2 text-xs font-medium rounded-md bg-cc-accent text-white hover:bg-cc-accent/80 disabled:opacity-50 cursor-pointer",children:"Add"})]})]}),e.jsx("div",{className:"flex items-center gap-2",children:e.jsx("button",{type:"button",onClick:()=>E(!a),className:`text-xs cursor-pointer ${a?"text-cc-accent":"text-cc-muted hover:text-cc-fg"}`,children:a?"Hide fired":"Show all"})}),u?e.jsx("div",{className:"text-sm text-cc-muted py-8 text-center",children:"Loading..."}):n.length===0?e.jsx("div",{className:"text-sm text-cc-muted py-8 text-center",children:"No reminders yet. Add one above or ask Gemini Live."}):e.jsxs("div",{className:"space-y-4",children:[k.length>0&&e.jsxs("div",{children:[e.jsx("h3",{className:"text-xs font-medium text-red-400 mb-2",children:"Overdue"}),e.jsx("div",{className:"space-y-1",children:k.map(s=>e.jsx(R,{reminder:s,onDelete:f,isOverdue:!0},s.id))})]}),w.length>0&&e.jsxs("div",{children:[e.jsx("h3",{className:"text-xs font-medium text-cc-fg mb-2",children:"Upcoming"}),e.jsx("div",{className:"space-y-1",children:w.map(s=>e.jsx(R,{reminder:s,onDelete:f},s.id))})]}),a&&D.length>0&&e.jsxs("div",{children:[e.jsx("h3",{className:"text-xs font-medium text-cc-muted mb-2",children:"Completed"}),e.jsx("div",{className:"space-y-1",children:D.map(s=>e.jsx(R,{reminder:s,onDelete:f,isFired:!0},s.id))})]})]})]})}function R({reminder:o,onDelete:d,isOverdue:n,isFired:l}){return e.jsxs("div",{className:`group flex items-center gap-3 px-3 py-2.5 rounded-lg border transition-colors ${l?"bg-cc-bg border-cc-border/50 opacity-60":n?"bg-red-500/5 border-red-500/20":"bg-cc-card border-cc-border hover:border-cc-border-hover"}`,children:[e.jsx("div",{className:`w-2 h-2 rounded-full shrink-0 ${l?"bg-cc-muted":n?"bg-red-400 animate-pulse":"bg-cc-accent"}`}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("span",{className:`text-sm ${l?"line-through text-cc-muted":"text-cc-fg"}`,children:o.text}),e.jsx("div",{className:"text-[10px] text-cc-muted mt-0.5",children:V(o.triggerAt)})]}),e.jsx("button",{type:"button",onClick:()=>d(o.id),className:"opacity-0 group-hover:opacity-100 text-cc-muted hover:text-red-400 transition-opacity cursor-pointer p-1",title:"Delete",children:e.jsxs("svg",{viewBox:"0 0 16 16",className:"w-3.5 h-3.5",fill:"currentColor",children:[e.jsx("path",{d:"M5.5 5.5A.5.5 0 016 6v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm2.5 0a.5.5 0 01.5.5v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm3 .5a.5.5 0 00-1 0v6a.5.5 0 001 0V6z"}),e.jsx("path",{fillRule:"evenodd",d:"M14.5 3a1 1 0 01-1 1H13v9a2 2 0 01-2 2H5a2 2 0 01-2-2V4h-.5a1 1 0 010-2H6a1 1 0 011-1h2a1 1 0 011 1h3.5a1 1 0 011 1zM4.118 4L4 4.059V13a1 1 0 001 1h6a1 1 0 001-1V4.059L11.882 4H4.118z"})]})})]})}export{O as AssistantPage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{j as e,r as x}from"./index-CEqZnThB.js";function t({id:s,title:a,icon:r,children:c,defaultOpen:d=!1}){const[o,h]=x.useState(d);return e.jsxs("div",{id:s,className:"border border-cc-border rounded-xl overflow-hidden",children:[e.jsxs("button",{onClick:()=>h(!o),className:"w-full flex items-center gap-3 px-5 py-4 text-left hover:bg-cc-hover/50 transition-colors",children:[e.jsx("span",{className:"text-xl shrink-0",children:r}),e.jsx("span",{className:"text-sm font-semibold text-cc-fg flex-1",children:a}),e.jsx("svg",{viewBox:"0 0 16 16",fill:"currentColor",className:`w-4 h-4 text-cc-muted transition-transform ${o?"rotate-180":""}`,children:e.jsx("path",{d:"M4.22 6.22a.75.75 0 011.06 0L8 8.94l2.72-2.72a.75.75 0 111.06 1.06l-3.25 3.25a.75.75 0 01-1.06 0L4.22 7.28a.75.75 0 010-1.06z"})})]}),o&&e.jsxs("div",{className:"px-5 pb-5 text-sm text-cc-muted leading-relaxed space-y-3 border-t border-cc-border/50",children:[e.jsx("div",{className:"pt-4"}),c]})]})}function l({children:s}){return e.jsx("kbd",{className:"inline-block px-1.5 py-0.5 rounded bg-cc-card border border-cc-border text-[11px] font-mono text-cc-fg",children:s})}function i({n:s,children:a}){return e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsx("span",{className:"shrink-0 w-6 h-6 rounded-full bg-cc-primary/20 text-cc-primary text-xs font-bold flex items-center justify-center mt-0.5",children:s}),e.jsx("div",{className:"flex-1",children:a})]})}function n({children:s}){return e.jsx("code",{className:"text-xs bg-cc-card px-1 rounded",children:s})}function m(){const s=[{id:"overview",label:"Overview"},{id:"first-steps",label:"Getting Started"},{id:"gemini-live",label:"Gemini Live"},{id:"agents",label:"Agents"},{id:"sessions",label:"Sessions & Chat"},{id:"media",label:"Media Generation"},{id:"telephony",label:"Telephony"},{id:"federation",label:"Federation"},{id:"sidebar",label:"Sidebar & Navigation"},{id:"platform",label:"Platform Dashboard"},{id:"pwa",label:"PWA & Mobile"},{id:"keyboard",label:"Keyboard Shortcuts"},{id:"tailscale",label:"Tailscale Setup"},{id:"api",label:"API Reference"}];return e.jsx("div",{className:"flex flex-wrap gap-2",children:s.map(a=>e.jsx("a",{href:`#/help?s=${a.id}`,onClick:r=>{var c;r.preventDefault(),(c=document.getElementById(a.id))==null||c.scrollIntoView({behavior:"smooth",block:"start"})},className:"px-3 py-1.5 rounded-lg bg-cc-card border border-cc-border text-xs text-cc-muted hover:text-cc-fg hover:border-cc-primary/50 transition-colors",children:a.label},a.id))})}function p(){return e.jsx("div",{className:"h-full overflow-auto",children:e.jsxs("div",{className:"mx-auto max-w-3xl px-6 py-8 space-y-6 pb-24",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-xl font-bold text-cc-fg",children:"HeyHank Help"}),e.jsx("p",{className:"text-sm text-cc-muted mt-1",children:"Self-hosted AI agent platform with voice assistant, telephony, media generation and multi-node federation."})]}),e.jsx(m,{}),e.jsxs(t,{id:"overview",title:"Overview",icon:"🏠",defaultOpen:!0,children:[e.jsx("p",{children:"HeyHank is your self-hosted AI agent platform. Create custom agents with any LLM backend, talk to them via Gemini Live voice, make phone calls through SIP, generate images and videos, and connect multiple HeyHank instances together."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Key Features"}),e.jsx("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-2 mt-1",children:[{name:"Custom Agents",desc:"Create unlimited agents",color:"text-blue-400"},{name:"Gemini Live",desc:"Voice assistant",color:"text-green-400"},{name:"Telephony",desc:"AI phone calls via SIP",color:"text-purple-400"},{name:"Media Gen",desc:"Images & videos",color:"text-pink-400"},{name:"Federation",desc:"Multi-node mesh",color:"text-yellow-400"},{name:"Multi-LLM",desc:"Claude, Gemini, Ollama",color:"text-orange-400"}].map(s=>e.jsxs("div",{className:"rounded-lg bg-cc-card p-3 border border-cc-border/50",children:[e.jsx("div",{className:`text-xs font-semibold ${s.color}`,children:s.name}),e.jsx("div",{className:"text-[11px] text-cc-muted mt-0.5",children:s.desc})]},s.name))}),e.jsxs("p",{className:"mt-2",children:[e.jsx("strong",{className:"text-cc-fg",children:"LLM Backends:"})," Claude Code CLI (Max subscription or API), Ollama (local, free), Gemini API, Codex CLI — configurable per agent."]})]}),e.jsxs(t,{id:"first-steps",title:"Getting Started",icon:"🚀",children:[e.jsx(i,{n:1,children:e.jsxs("p",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Install"})," — ",e.jsx(n,{children:"npx heyhank"})," or ",e.jsx(n,{children:"bunx heyhank"}),". HeyHank starts on port 3100 by default."]})}),e.jsx(i,{n:2,children:e.jsxs("p",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Log in"})," — Open the URL and enter the auth token shown in the terminal."]})}),e.jsx(i,{n:3,children:e.jsxs("p",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Start a session"})," — Click ",e.jsx("strong",{className:"text-cc-fg",children:"+ New Session"})," in the sidebar. Choose a working directory and backend (Claude/Codex)."]})}),e.jsx(i,{n:4,children:e.jsxs("p",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Chat"})," — Type your message and press ",e.jsx(l,{children:"Enter"}),". The agent responds with text, executes tools, and asks for permission when needed."]})}),e.jsx(i,{n:5,children:e.jsxs("p",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Create an agent"})," — Go to ",e.jsx("strong",{className:"text-cc-fg",children:"Agents"})," in the sidebar, click ",e.jsx("strong",{className:"text-cc-fg",children:"+ New Agent"}),". Give it a name, prompt, model, and working directory."]})}),e.jsx(i,{n:6,children:e.jsxs("p",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Talk to Gemini"})," — Click the microphone button (bottom right) to start a voice conversation with Gemini Live. It can control your agents, make calls, and more."]})})]}),e.jsxs(t,{id:"gemini-live",title:"Gemini Live — Voice Assistant",icon:"🎙",children:[e.jsx("p",{children:"Gemini Live is your always-available voice interface. It uses Google's BidiGenerateContent API for real-time bidirectional audio streaming — you talk, it responds instantly with voice."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"How to use"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:["Click the ",e.jsx("strong",{className:"text-cc-fg",children:"microphone button"})," (floating, bottom right) to start."]}),e.jsxs("li",{children:["A draggable voice chat overlay appears. You can ",e.jsx("strong",{className:"text-cc-fg",children:"minimize"})," it (session stays alive) or ",e.jsx("strong",{className:"text-cc-fg",children:"close"})," it (ends session)."]}),e.jsx("li",{children:"Speak naturally — Gemini responds with voice in real-time."}),e.jsx("li",{children:"Drag the overlay anywhere on screen. On mobile, it stays accessible while you browse."})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"What Gemini can do"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Run agents"}),' — "Start a coding agent on /my/project"']}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Send messages"}),' — "Tell session 3 to run the tests"']}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Create agents"}),' — "Create an agent called Monitor that checks server health"']}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Make phone calls"}),' — "Call +43 1 234 5678 and ask about the appointment" (requires SIP trunk)']}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"List sessions"}),` — "What's running right now?"`]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Monitor agents"}),' — "What is the coding agent doing?"']})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Multimodal input"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Images"})," — Click the image icon to upload photos or documents for Gemini to analyze."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Camera"})," — Click the camera icon to stream your live camera feed. Gemini receives a frame every 2 seconds."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Videos"})," — Upload a video file; HeyHank extracts key frames and sends them to Gemini."]})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Requirements"}),e.jsxs("p",{children:["Requires a ",e.jsx(n,{children:"GEMINI_API_KEY"})," environment variable. Get one free at ",e.jsx("strong",{className:"text-cc-fg",children:"aistudio.google.com"}),"."]})]}),e.jsxs(t,{id:"agents",title:"Agents",icon:"🤖",children:[e.jsx("p",{children:"Agents are the core of HeyHank. Each agent is a configured AI session with its own name, system prompt, LLM model, working directory, and permission mode."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Create an agent"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:["Go to ",e.jsx("strong",{className:"text-cc-fg",children:"Agents"})," in the sidebar."]}),e.jsxs("li",{children:["Click ",e.jsx("strong",{className:"text-cc-fg",children:"+ New Agent"}),"."]}),e.jsx("li",{children:"Configure: name, description, system prompt, model, backend, working directory."}),e.jsx("li",{children:"Optionally set triggers: cron schedule (e.g. run every hour) or webhook."})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Run an agent"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:["Click ",e.jsx("strong",{className:"text-cc-fg",children:"Run"})," on an agent card — starts a new session with the agent's prompt."]}),e.jsx("li",{children:"Some agents may ask for input before they start (configurable)."}),e.jsx("li",{children:'You can also ask Gemini Live: "Run the coding agent" or "Start monitor".'})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Agent examples"}),e.jsx("div",{className:"space-y-1.5 mt-1",children:[{name:"Coding Agent",desc:"Develops features, fixes bugs, writes tests. Works in your project directory with full file and terminal access."},{name:"Monitoring Agent",desc:"Checks server health: CPU, RAM, disk, Docker, PM2. Runs on a cron schedule. Read-only mode recommended."},{name:"Content Agent",desc:"Creates social media posts, generates images with Imagen, produces videos with Veo."},{name:"Personal Agent",desc:"Email management, calendar, reminders. Connects via IMAP/SMTP."}].map(s=>e.jsxs("div",{className:"rounded-lg bg-cc-card p-3 border border-cc-border/50",children:[e.jsx("span",{className:"text-xs font-semibold text-cc-fg",children:s.name}),e.jsx("span",{className:"text-[11px] text-cc-muted ml-2",children:s.desc})]},s.name))}),e.jsx("p",{className:"text-[11px] mt-1",children:"These are just examples — create whatever agents you need for your workflow."})]}),e.jsxs(t,{id:"sessions",title:"Sessions & Chat",icon:"💬",children:[e.jsx("h4",{className:"font-semibold text-cc-fg",children:"Create a new session"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:["Click ",e.jsx("strong",{className:"text-cc-fg",children:"+"})," in the sidebar or ",e.jsx("strong",{className:"text-cc-fg",children:"New Session"})," on the home page."]}),e.jsxs("li",{children:["Choose a ",e.jsx("strong",{className:"text-cc-fg",children:"working directory"})," — this is where the agent operates."]}),e.jsxs("li",{children:["Choose a ",e.jsx("strong",{className:"text-cc-fg",children:"backend"}),": Claude (recommended) or Codex."]}),e.jsxs("li",{children:["Choose the ",e.jsx("strong",{className:"text-cc-fg",children:"permission mode"}),": Accept Edits (default), Plan, Default."]}),e.jsxs("li",{children:["Optional: Choose a ",e.jsx("strong",{className:"text-cc-fg",children:"git branch"})," as starting point."]})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"In the chat"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Composer"})," — Type a message, press ",e.jsx(l,{children:"Enter"})," to send."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"@-Mentions"})," — Type ",e.jsx(n,{children:"@"})," to reference files, folders or prompts."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"/Commands"})," — Type ",e.jsx(n,{children:"/"})," for available commands and skills."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Images"})," — Drag & drop or file upload in the composer."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Permission requests"})," — When the agent wants to use a tool, a yellow banner appears. Click Allow or Deny."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Streaming"})," — Responses are streamed in real time."]})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"Session lifecycle"}),e.jsx("p",{children:"Sessions persist across page reloads and server restarts. Return to any session at any time — the full chat history is preserved."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"Session tabs"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Chat"})," — The main conversation with the agent."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Diff"})," — Git changes the agent has made. Badge = changed file count."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Terminal"})," — Shell terminal within the session."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Processes"})," — Running processes of the session."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Editor"})," — File editor for direct editing."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Browser"})," — Embedded browser for web development."]})]})]}),e.jsxs(t,{id:"media",title:"Media Generation (Image & Video)",icon:"🎨",children:[e.jsx("p",{children:"Generate images and videos via Google AI APIs directly from agent sessions."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Image Generation (Imagen)"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:["Models: ",e.jsx(n,{children:"imagen-4.0-fast-generate-001"})," (fast), ",e.jsx(n,{children:"imagen-4.0-generate-001"})," (quality), ",e.jsx(n,{children:"imagen-4.0-ultra-generate-001"})]}),e.jsx("li",{children:"Aspect ratios: 1:1, 16:9, 9:16, 4:3, 3:4"}),e.jsx("li",{children:"Output: PNG file, saved and accessible via URL"})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Video Generation (Veo)"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:["Models: ",e.jsx(n,{children:"veo-3.1-fast-generate-preview"})," (fast), ",e.jsx(n,{children:"veo-3.1-generate-preview"})," (best quality)"]}),e.jsx("li",{children:"Asynchronous generation — takes 1-3 minutes for a 5-8 second video."}),e.jsx("li",{children:"Output: MP4 file"})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"View media"}),e.jsxs("p",{children:["All generated media is listed at ",e.jsx(n,{children:"/api/media"})," and can be opened directly in the browser."]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Requirements"}),e.jsxs("p",{children:["Requires a ",e.jsx(n,{children:"GEMINI_API_KEY"})," with access to Imagen and Veo models."]})]}),e.jsxs(t,{id:"telephony",title:"Telephony — AI Phone Calls",icon:"📞",children:[e.jsx("p",{children:"HeyHank can make and receive phone calls using Gemini Live as the voice engine. Calls go through a SIP trunk connected to FreeSWITCH, with Gemini handling the conversation."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"How it works"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"FreeSWITCH"})," handles the SIP connection to the phone network."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Gemini Live"})," acts as both speech-to-text, LLM, and text-to-speech — no separate STT/TTS services needed."]}),e.jsx("li",{children:"Audio is bridged in real-time: FreeSWITCH (8kHz PCM) is resampled to Gemini (16kHz) and back."})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Setup"}),e.jsx(i,{n:1,children:e.jsx("p",{children:"Get a SIP trunk from a provider (e.g. peoplefone, sipgate, easybell)."})}),e.jsx(i,{n:2,children:e.jsxs("p",{children:["Go to ",e.jsx("strong",{className:"text-cc-fg",children:"Settings → Telephony"})," and add your SIP trunk credentials."]})}),e.jsx(i,{n:3,children:e.jsx("p",{children:"Start the FreeSWITCH Docker container (included in the HeyHank package)."})}),e.jsx(i,{n:4,children:e.jsx("p",{children:"Test the connection in Settings, then make calls via the Telephony page or Gemini voice command."})}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Making calls"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Telephony page"})," — Dial a number, describe the task, and start the call."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Gemini voice"}),' — "Call +43 1 234 5678 and ask about the delivery"']}),e.jsx("li",{children:"Live transcript is displayed in real-time during the call."}),e.jsx("li",{children:"Call history with summaries is saved automatically."})]})]}),e.jsxs(t,{id:"federation",title:"Federation — Multi-Node",icon:"🌐",children:[e.jsx("p",{children:"Connect multiple HeyHank instances together in a peer-to-peer mesh. Run HeyHank on your laptop, VPS, or both — all nodes see each other's sessions and agents."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"How it works"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:["Each HeyHank instance is a ",e.jsx("strong",{className:"text-cc-fg",children:"node"})," with a unique ID."]}),e.jsx("li",{children:"Nodes connect to each other via WebSocket with shared-secret authentication."}),e.jsx("li",{children:"Sessions and agents are synced automatically — remote sessions appear in your sidebar with a node badge."}),e.jsx("li",{children:"Gemini on any node can see and control sessions across all connected nodes."})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Setup"}),e.jsx(i,{n:1,children:e.jsxs("p",{children:["Go to ",e.jsx("strong",{className:"text-cc-fg",children:"Settings → Federation"})," on both nodes."]})}),e.jsx(i,{n:2,children:e.jsx("p",{children:"Copy the connection URL from Node B."})}),e.jsx(i,{n:3,children:e.jsxs("p",{children:["On Node A, click ",e.jsx("strong",{className:"text-cc-fg",children:"Add Node"})," and paste the URL + shared secret."]})}),e.jsx(i,{n:4,children:e.jsx("p",{children:"Both nodes now see each other's sessions. Gemini can control agents across both."})}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Offline handling"}),e.jsx("p",{children:"If a node goes offline, its sessions are marked as disconnected and disappear after 60 seconds. When the node comes back, auto-reconnect restores everything."})]}),e.jsxs(t,{id:"sidebar",title:"Sidebar — Navigation",icon:"📋",children:[e.jsx("p",{children:"The sidebar is your main navigation. On mobile, open it with the hamburger icon. On desktop, it's always visible."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Upper area: Sessions"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"+ Button"})," — Start a new chat session."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Session list"})," — All active sessions, grouped by project. Click to open."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Right-click / 3-dots"})," — Rename, Archive, Delete."]}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Archived sessions"})," — Collapsible at the bottom, can be restored."]}),e.jsx("li",{children:"Status dots: green = connected, yellow = reconnecting, red = disconnected."}),e.jsx("li",{children:"Badge = open permission requests awaiting confirmation."}),e.jsx("li",{children:"Node badge = session is from a remote federated node."})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"Lower area: Pages"}),e.jsxs("div",{className:"space-y-1",children:[e.jsxs("p",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Workbench:"})," Prompts, Integrations, Terminal"]}),e.jsxs("p",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Workspace:"})," Environments, Agents, Telephony, Platform, Settings, Help"]})]})]}),e.jsxs(t,{id:"platform",title:"Platform Dashboard",icon:"📊",children:[e.jsx("p",{children:"The Platform page shows the overall state of your agent system."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Kill Switch"}),e.jsx("p",{children:"Emergency stop for all agents. Red KILL button stops everything immediately."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Cost Tracking"}),e.jsx("p",{children:"Total costs, token usage (in/out), and per-session cost breakdown with model info."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"LLM Providers"}),e.jsx("p",{children:"Status of all LLM backends: Claude, Gemini, Ollama, Codex. Green = available, yellow = needs API key."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Agent Messages"}),e.jsx("p",{children:"Last 20 inter-agent messages — see how agents communicate with each other."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Shared Context"}),e.jsx("p",{children:"Shared knowledge files accessible by all agents. Click to read, edit to modify."})]}),e.jsxs(t,{id:"pwa",title:"PWA & Mobile",icon:"📱",children:[e.jsx("h4",{className:"font-semibold text-cc-fg",children:"Install as app"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"iPhone/iPad:"}),' Safari → Share → "Add to Home Screen"']}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Android:"}),' Chrome → Menu → "Install app"']}),e.jsxs("li",{children:[e.jsx("strong",{className:"text-cc-fg",children:"Desktop:"})," Chrome/Edge → Address bar → Install icon"]})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Push Notifications"}),e.jsx("p",{children:"HeyHank can send push notifications for agent alerts and monitoring warnings. Enable on first visit when the browser asks for permission."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Mobile tips"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsx("li",{children:"The Gemini Live voice overlay is draggable and collapsible — works well on small screens."}),e.jsx("li",{children:"Use the camera icon in voice chat to stream your phone camera to Gemini."}),e.jsx("li",{children:"The PWA caches static assets for fast loading. Agent interaction requires a connection."})]})]}),e.jsx(t,{id:"keyboard",title:"Keyboard Shortcuts",icon:"⌨",children:e.jsxs("div",{className:"grid grid-cols-[auto_1fr] gap-x-6 gap-y-2",children:[e.jsx(l,{children:"Enter"}),e.jsx("span",{children:"Send message"}),e.jsx(l,{children:"Shift+Enter"}),e.jsx("span",{children:"New line in composer"}),e.jsx(l,{children:"Escape"}),e.jsx("span",{children:"Deny permission request / Interrupt"}),e.jsx(l,{children:"@"}),e.jsx("span",{children:"Reference file/folder/prompt"}),e.jsx(l,{children:"/"}),e.jsx("span",{children:"Commands and skills"}),e.jsx(l,{children:"Ctrl+`"}),e.jsx("span",{children:"Open/close quick terminal"})]})}),e.jsxs(t,{id:"tailscale",title:"Tailscale Setup",icon:"🌐",children:[e.jsx("p",{children:"Tailscale Funnel gives HeyHank a public HTTPS URL without port forwarding, firewall changes, or a reverse proxy. It works by routing traffic through Tailscale's network to your machine."}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"1. Install Tailscale"}),e.jsxs("p",{children:["Download from ",e.jsx("a",{href:"https://tailscale.com/download",target:"_blank",rel:"noopener",className:"text-cc-accent hover:underline",children:"tailscale.com/download"})," or install via CLI:"]}),e.jsx("div",{className:"font-mono text-xs bg-cc-bg rounded p-2 mt-1",children:"curl -fsSL https://tailscale.com/install.sh | sh"}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"2. Connect to your Tailnet"}),e.jsx("div",{className:"font-mono text-xs bg-cc-bg rounded p-2 mt-1",children:"tailscale up"}),e.jsxs("p",{className:"mt-1",children:["Follow the login URL to authenticate. Your machine will get a DNS name like ",e.jsx(n,{children:"machine.tail1234.ts.net"}),"."]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"3. Enable Funnel"}),e.jsxs("p",{children:["HeyHank manages Funnel automatically. Go to ",e.jsx("strong",{children:"Settings"})," and click ",e.jsx("strong",{children:"Enable Tailscale Funnel"}),". This exposes HeyHank over HTTPS at your Tailscale DNS name."]}),e.jsx("p",{className:"mt-1",children:"You can also enable it manually:"}),e.jsxs("div",{className:"font-mono text-xs bg-cc-bg rounded p-2 mt-1",children:["tailscale funnel --bg ","{port}"]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"4. Linux: Operator Mode"}),e.jsx("p",{children:"If running HeyHank as a non-root user on Linux, you need to set operator mode so HeyHank can manage Funnel without sudo:"}),e.jsx("div",{className:"font-mono text-xs bg-cc-bg rounded p-2 mt-1",children:"sudo tailscale set --operator=$USER"}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-3",children:"Troubleshooting"}),e.jsxs("ul",{className:"list-disc pl-5 space-y-1",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"DNS not propagated:"})," After enabling Funnel, DNS may take a few minutes to propagate. HeyHank will show a warning until resolution succeeds."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Operator mode needed:"})," On Linux, if you see permission errors, run ",e.jsx(n,{children:"sudo tailscale set --operator=$USER"})," and restart HeyHank."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Binary not found:"})," Ensure ",e.jsx(n,{children:"tailscale"})," is on your PATH. HeyHank checks for it automatically on startup."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Funnel not working:"})," Make sure Funnel is enabled in your Tailscale admin console under the DNS tab (Access Controls)."]})]})]}),e.jsxs(t,{id:"api",title:"API Reference",icon:"🔧",children:[e.jsxs("p",{children:["All endpoints are available under ",e.jsx(n,{children:"/api/"}),". Authentication via bearer token."]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Sessions"}),e.jsxs("div",{className:"font-mono text-xs space-y-0.5",children:[e.jsx("p",{children:"GET /api/sessions — List all sessions"}),e.jsx("p",{children:"POST /api/sessions — Create new session"}),e.jsx("p",{children:"DELETE /api/sessions/:id — Delete session"})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Agents"}),e.jsxs("div",{className:"font-mono text-xs space-y-0.5",children:[e.jsx("p",{children:"GET /api/agents — List all agents"}),e.jsx("p",{children:"POST /api/agents — Create agent"}),e.jsx("p",{children:"PUT /api/agents/:id — Update agent"}),e.jsx("p",{children:"DELETE /api/agents/:id — Delete agent"}),e.jsx("p",{children:"POST /api/agents/:id/run — Run agent"})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Media"}),e.jsxs("div",{className:"font-mono text-xs space-y-0.5",children:[e.jsx("p",{children:"POST /api/media/generate-image — Generate image"}),e.jsx("p",{children:"POST /api/media/generate-video — Generate video"}),e.jsx("p",{children:"GET /api/media — List all media"}),e.jsx("p",{children:"GET /api/media/file/:filename — Get media file"})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Telephony"}),e.jsxs("div",{className:"font-mono text-xs space-y-0.5",children:[e.jsx("p",{children:"POST /api/telephony/calls — Start a call"}),e.jsx("p",{children:"GET /api/telephony/calls — Active calls"}),e.jsx("p",{children:"DELETE /api/telephony/calls/:id — End call"}),e.jsx("p",{children:"GET /api/telephony/history — Call history"}),e.jsx("p",{children:"GET/PUT /api/telephony/settings — Telephony settings"})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Federation"}),e.jsxs("div",{className:"font-mono text-xs space-y-0.5",children:[e.jsx("p",{children:"GET /api/federation/identity — This node's identity"}),e.jsx("p",{children:"GET /api/federation/nodes — Connected nodes"}),e.jsx("p",{children:"POST /api/federation/nodes — Add node"}),e.jsx("p",{children:"DELETE /api/federation/nodes/:id — Remove node"}),e.jsx("p",{children:"GET /api/federation/remote-sessions — Sessions from other nodes"})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Platform"}),e.jsxs("div",{className:"font-mono text-xs space-y-0.5",children:[e.jsx("p",{children:"GET /api/costs/summary — Cost summary"}),e.jsx("p",{children:"GET/POST /api/kill-switch — Kill switch"}),e.jsx("p",{children:"GET /api/llm/providers — LLM provider status"}),e.jsx("p",{children:"GET /api/shared-context — Shared context files"}),e.jsx("p",{children:"POST /api/llm/chat — Direct LLM call"})]}),e.jsx("h4",{className:"font-semibold text-cc-fg mt-2",children:"Gemini"}),e.jsxs("div",{className:"font-mono text-xs space-y-0.5",children:[e.jsx("p",{children:"GET /gemini/config — Voice assistant config + active sessions"}),e.jsx("p",{children:"POST /gemini/tool-call — Execute a Gemini tool call"})]})]}),e.jsxs("div",{className:"text-center text-xs text-cc-muted pt-4 pb-8 space-y-1",children:[e.jsx("p",{children:"HeyHank — Self-hosted AI Agent Platform"}),e.jsxs("p",{children:["Bugs or feature requests?"," ",e.jsx("a",{href:"https://github.com/heyhank-app/heyhank/issues",target:"_blank",rel:"noopener noreferrer",className:"text-cc-primary hover:underline",children:"Open an issue on GitHub"})]})]})]})})}export{p as HelpPage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as t,b as d,j as e}from"./index-CEqZnThB.js";function m({embedded:r}){const[a,c]=t.useState([]),[i,l]=t.useState(!0);return t.useEffect(()=>{d.listMedia().then(s=>c(s.files)).catch(()=>{}).finally(()=>l(!1))},[]),e.jsx("div",{className:"h-full overflow-auto ",children:e.jsxs("div",{className:"max-w-3xl mx-auto px-4 py-8 sm:px-6",children:[e.jsxs("div",{className:"mb-6",children:[e.jsx("h1",{className:"text-lg font-semibold text-cc-fg",children:"Media"}),e.jsx("p",{className:"text-xs text-cc-muted mt-1",children:"Images and files created by agents and Gemini Live."})]}),i?e.jsx("p",{className:"text-xs text-cc-muted",children:"Loading..."}):a.length===0?e.jsxs("div",{className:"text-center py-16",children:[e.jsxs("svg",{viewBox:"0 0 16 16",fill:"currentColor",className:"w-8 h-8 text-cc-muted/30 mx-auto mb-3",children:[e.jsx("rect",{x:"2",y:"2",width:"12",height:"12",rx:"2"}),e.jsx("circle",{cx:"5.5",cy:"5.5",r:"1"}),e.jsx("path",{d:"M2 11l3-3 2 2 3-4 4 5",strokeLinecap:"round",strokeLinejoin:"round",stroke:"currentColor",strokeWidth:"1",fill:"none"})]}),e.jsx("p",{className:"text-sm text-cc-muted",children:"No media files yet"}),e.jsx("p",{className:"text-xs text-cc-muted mt-1",children:"Generated images from agents and Gemini will appear here."})]}):e.jsx("div",{className:"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3",children:a.map((s,n)=>e.jsxs("a",{href:`/api/media/file/${encodeURIComponent(s.filename)}`,target:"_blank",rel:"noopener noreferrer",className:"group bg-cc-card border border-cc-border rounded-xl overflow-hidden hover:border-cc-primary/40 transition-colors",children:[e.jsx("img",{src:`/api/media/file/${encodeURIComponent(s.filename)}`,alt:s.filename,className:"w-full aspect-square object-cover",loading:"lazy"}),e.jsx("div",{className:"px-2.5 py-2",children:e.jsx("p",{className:"text-[11px] text-cc-muted truncate group-hover:text-cc-fg transition-colors",children:s.filename})})]},n))})]})})}export{m as MediaPage};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r,b as N,j as e}from"./index-CEqZnThB.js";import{t as I}from"./time-ago-B6r_l9u1.js";function g(c){switch(c){case"manual":return"Manual";case"webhook":return"Webhook";case"schedule":return"Schedule";default:return c}}function v(c){switch(c){case"manual":return"bg-blue-500/10 text-blue-600 dark:text-blue-400";case"webhook":return"bg-purple-500/10 text-purple-600 dark:text-purple-400";case"schedule":return"bg-amber-500/10 text-amber-600 dark:text-amber-400";default:return"bg-cc-hover text-cc-muted"}}function E(c){return c.error?{label:"Error",color:"text-cc-error"}:c.success?{label:"Success",color:"text-cc-success"}:c.completedAt?{label:"Unknown",color:"text-cc-muted"}:{label:"Running",color:"text-cc-warning"}}function y(c,x){const l=(x||Date.now())-c;if(l<1e3)return"<1s";if(l<6e4)return`${Math.round(l/1e3)}s`;const u=Math.floor(l/6e4),h=Math.round(l%6e4/1e3);return`${u}m ${h}s`}function D(){const[c,x]=r.useState([]),[m,l]=r.useState([]),[u,h]=r.useState(0),[w,k]=r.useState(!0),[n,A]=r.useState("all"),[i,S]=r.useState("all"),[o,C]=r.useState(""),[s,f]=r.useState(null),p=r.useCallback(async()=>{try{const t={limit:100};n!=="all"&&(t.triggerType=n),i!=="all"&&(t.status=i),o&&(t.agentId=o);const a=await N.listExecutions(t);x(a.executions),h(a.total)}catch(t){console.error("[runs] Failed to fetch executions:",t)}finally{k(!1)}},[n,i,o]),b=r.useCallback(async()=>{try{const t=await N.listAgents();l(t)}catch(t){console.error("[runs] Failed to fetch agents:",t)}},[]);r.useEffect(()=>{b()},[b]),r.useEffect(()=>{p();const t=setInterval(p,5e3);return()=>clearInterval(t)},[p]);const j=t=>{const a=m.find(d=>d.id===t);return(a==null?void 0:a.name)||t};return e.jsxs("div",{className:"h-full flex flex-col bg-cc-bg",children:[e.jsxs("div",{className:"shrink-0 border-b border-cc-border px-6 py-4",children:[e.jsx("h1",{className:"text-lg font-semibold text-cc-fg",children:"Runs"}),e.jsx("p",{className:"text-sm text-cc-muted mt-1",children:"Monitor agent executions across all triggers"})]}),e.jsxs("div",{className:"shrink-0 border-b border-cc-border px-6 py-3 flex items-center gap-4 flex-wrap",children:[e.jsxs("select",{value:o,onChange:t=>C(t.target.value),className:"text-sm rounded-md border border-cc-border bg-cc-input-bg text-cc-fg px-2 py-1","aria-label":"Filter by agent",children:[e.jsx("option",{value:"",children:"All agents"}),m.map(t=>e.jsx("option",{value:t.id,children:t.name},t.id))]}),e.jsx("div",{className:"flex items-center gap-1",children:["all","manual","webhook","schedule"].map(t=>e.jsx("button",{onClick:()=>A(t),className:`text-xs px-2.5 py-1 rounded-full transition-colors ${n===t?"bg-cc-fg text-cc-bg":"bg-cc-hover text-cc-muted hover:text-cc-fg"}`,children:t==="all"?"All triggers":g(t)},t))}),e.jsx("div",{className:"flex items-center gap-1",children:["all","running","success","error"].map(t=>e.jsx("button",{onClick:()=>S(t),className:`text-xs px-2.5 py-1 rounded-full transition-colors ${i===t?"bg-cc-fg text-cc-bg":"bg-cc-hover text-cc-muted hover:text-cc-fg"}`,children:t==="all"?"All statuses":t.charAt(0).toUpperCase()+t.slice(1)},t))}),e.jsxs("span",{className:"text-xs text-cc-muted ml-auto",children:[u," total"]})]}),e.jsx("div",{className:"flex-1 overflow-auto",children:w?e.jsx("div",{className:"text-sm text-cc-muted text-center py-12",children:"Loading..."}):c.length===0?e.jsxs("div",{className:"text-center py-16",children:[e.jsx("div",{className:"mb-3 flex justify-center text-cc-muted",children:e.jsx("svg",{viewBox:"0 0 16 16",fill:"currentColor",className:"w-8 h-8",children:e.jsx("path",{d:"M8 1a7 7 0 100 14A7 7 0 008 1zm-.75 3.5a.75.75 0 011.5 0v3.19l2.03 2.03a.75.75 0 01-1.06 1.06l-2.25-2.25A.75.75 0 017.25 8V4.5z"})})}),e.jsx("p",{className:"text-sm text-cc-muted",children:"No executions found"}),e.jsx("p",{className:"text-xs text-cc-muted mt-1",children:"Run an agent to see executions here"})]}):e.jsxs("table",{className:"w-full text-sm",role:"table",children:[e.jsx("thead",{className:"sticky top-0 bg-cc-card backdrop-blur",children:e.jsxs("tr",{className:"text-left text-xs text-cc-muted uppercase tracking-wider",children:[e.jsx("th",{className:"px-6 py-2 font-medium",children:"Agent"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Trigger"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Status"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Started"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Duration"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Session"})]})}),e.jsx("tbody",{className:"divide-y divide-cc-border/50",children:c.map(t=>{const a=E(t),d=(s==null?void 0:s.sessionId)===t.sessionId;return e.jsxs("tr",{onClick:()=>f(d?null:t),className:`cursor-pointer transition-colors ${d?"bg-cc-active":"hover:bg-cc-hover"}`,children:[e.jsx("td",{className:"px-6 py-3",children:e.jsx("div",{className:"flex items-center gap-2",children:e.jsx("span",{className:"text-cc-fg font-medium",children:j(t.agentId)})})}),e.jsx("td",{className:"px-4 py-3",children:e.jsx("span",{className:`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${v(t.triggerType)}`,children:g(t.triggerType)})}),e.jsx("td",{className:"px-4 py-3",children:e.jsxs("span",{className:`font-medium ${a.color}`,children:[!t.completedAt&&!t.error&&e.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-cc-warning mr-1.5 animate-pulse"}),a.label]})}),e.jsx("td",{className:"px-4 py-3 text-cc-muted",children:I(t.startedAt)}),e.jsx("td",{className:"px-4 py-3 text-cc-muted font-mono text-xs",children:y(t.startedAt,t.completedAt)}),e.jsx("td",{className:"px-4 py-3",children:t.sessionId&&e.jsx("a",{href:`#/session/${t.sessionId}`,onClick:$=>$.stopPropagation(),className:"text-cc-primary hover:text-cc-primary-hover text-xs font-mono underline",children:"Open"})})]},`${t.sessionId}-${t.startedAt}`)})})]})}),s&&e.jsxs("div",{className:"shrink-0 border-t border-cc-border bg-cc-card px-6 py-4",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("h3",{className:"text-sm font-semibold text-cc-fg",children:"Execution Details"}),e.jsx("button",{onClick:()=>f(null),className:"text-cc-muted hover:text-cc-fg text-sm transition-colors","aria-label":"Close details",children:"Close"})]}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4 text-sm",children:[e.jsxs("div",{children:[e.jsx("span",{className:"text-cc-muted",children:"Agent"}),e.jsx("p",{className:"text-cc-fg font-medium",children:j(s.agentId)})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-cc-muted",children:"Trigger"}),e.jsx("p",{className:`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${v(s.triggerType)}`,children:g(s.triggerType)})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-cc-muted",children:"Started"}),e.jsx("p",{className:"text-cc-fg",children:new Date(s.startedAt).toLocaleString()})]}),e.jsxs("div",{children:[e.jsx("span",{className:"text-cc-muted",children:"Duration"}),e.jsx("p",{className:"text-cc-fg font-mono",children:y(s.startedAt,s.completedAt)})]})]}),s.error&&e.jsx("div",{className:"mt-3 p-2 bg-cc-error/10 rounded text-sm text-cc-error font-mono whitespace-pre-wrap",children:s.error}),s.sessionId&&e.jsx("a",{href:`#/session/${s.sessionId}`,className:"mt-3 inline-flex items-center gap-1 text-sm text-cc-primary hover:text-cc-primary-hover",children:"Open session to view live output"})]})]})}export{D as RunsPage};
|