heyhank 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/bin/cli.ts +168 -0
- package/bin/ctl.ts +528 -0
- package/bin/generate-token.ts +28 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/AgentsPage-BPhirnCe.js +7 -0
- package/dist/assets/AssistantPage-DJ-cMQfb.js +1 -0
- package/dist/assets/CronManager-DDbz-yiT.js +1 -0
- package/dist/assets/HelpPage-DMfkzERp.js +1 -0
- package/dist/assets/IntegrationsPage-CrOitCmJ.js +1 -0
- package/dist/assets/MediaPage-CE5rdvkC.js +1 -0
- package/dist/assets/PlatformDashboard-Do6F0O2p.js +1 -0
- package/dist/assets/Playground-Fc5cdc5p.js +109 -0
- package/dist/assets/ProcessPanel-CslEiZkI.js +2 -0
- package/dist/assets/PromptsPage-D2EhsdNO.js +4 -0
- package/dist/assets/RunsPage-C5BZF5Rx.js +1 -0
- package/dist/assets/SandboxManager-a1AVI5q2.js +8 -0
- package/dist/assets/SettingsPage-DirhjQrJ.js +51 -0
- package/dist/assets/SocialMediaPage-DBuM28vD.js +1 -0
- package/dist/assets/TailscalePage-CHiFhZXF.js +1 -0
- package/dist/assets/TelephonyPage-x0VV0fOo.js +1 -0
- package/dist/assets/TerminalPage-Drwyrnfd.js +1 -0
- package/dist/assets/gemini-audio-t-TSU-To.js +17 -0
- package/dist/assets/gemini-live-client-C7rqAW7G.js +166 -0
- package/dist/assets/index-C8M_PUmX.css +32 -0
- package/dist/assets/index-CEqZnThB.js +204 -0
- package/dist/assets/sw-register-LSSpj6RU.js +1 -0
- package/dist/assets/time-ago-B6r_l9u1.js +1 -0
- package/dist/assets/workbox-window.prod.es5-BIl4cyR9.js +2 -0
- package/dist/favicon-32-original.png +0 -0
- package/dist/favicon-32.png +0 -0
- package/dist/favicon.ico +0 -0
- package/dist/favicon.svg +8 -0
- package/dist/fonts/MesloLGSNerdFontMono-Bold.woff2 +0 -0
- package/dist/fonts/MesloLGSNerdFontMono-Regular.woff2 +0 -0
- package/dist/heyhank-mascot-poster.png +0 -0
- package/dist/heyhank-mascot.mp4 +0 -0
- package/dist/heyhank-mascot.webm +0 -0
- package/dist/icon-192-original.png +0 -0
- package/dist/icon-192.png +0 -0
- package/dist/icon-512-original.png +0 -0
- package/dist/icon-512.png +0 -0
- package/dist/index.html +21 -0
- package/dist/logo-192.png +0 -0
- package/dist/logo-512.png +0 -0
- package/dist/logo-codex.svg +14 -0
- package/dist/logo-docker.svg +4 -0
- package/dist/logo-original.png +0 -0
- package/dist/logo.png +0 -0
- package/dist/logo.svg +14 -0
- package/dist/manifest.json +24 -0
- package/dist/push-sw.js +34 -0
- package/dist/sw.js +1 -0
- package/dist/workbox-d2a0910a.js +1 -0
- package/package.json +109 -0
- package/server/agent-cron-migrator.ts +85 -0
- package/server/agent-executor.ts +357 -0
- package/server/agent-store.ts +185 -0
- package/server/agent-timeout.ts +107 -0
- package/server/agent-types.ts +122 -0
- package/server/ai-validation-settings.ts +37 -0
- package/server/ai-validator.ts +181 -0
- package/server/anthropic-provider-migration.ts +48 -0
- package/server/assistant-store.ts +272 -0
- package/server/auth-manager.ts +150 -0
- package/server/auto-approve.ts +153 -0
- package/server/auto-namer.ts +36 -0
- package/server/backend-adapter.ts +54 -0
- package/server/cache-headers.ts +61 -0
- package/server/calendar-service.ts +434 -0
- package/server/claude-adapter.ts +889 -0
- package/server/claude-container-auth.ts +30 -0
- package/server/claude-session-discovery.ts +157 -0
- package/server/claude-session-history.ts +410 -0
- package/server/cli-launcher.ts +1303 -0
- package/server/codex-adapter.ts +3027 -0
- package/server/codex-container-auth.ts +24 -0
- package/server/codex-home.ts +27 -0
- package/server/codex-ws-proxy.cjs +226 -0
- package/server/commands-discovery.ts +81 -0
- package/server/constants.ts +7 -0
- package/server/container-manager.ts +1053 -0
- package/server/cost-tracker.ts +222 -0
- package/server/cron-scheduler.ts +243 -0
- package/server/cron-store.ts +148 -0
- package/server/cron-types.ts +63 -0
- package/server/email-service.ts +354 -0
- package/server/env-manager.ts +161 -0
- package/server/event-bus-types.ts +75 -0
- package/server/event-bus.ts +124 -0
- package/server/execution-store.ts +170 -0
- package/server/federation/node-connection.ts +190 -0
- package/server/federation/node-manager.ts +366 -0
- package/server/federation/node-store.ts +86 -0
- package/server/federation/node-types.ts +121 -0
- package/server/fs-utils.ts +15 -0
- package/server/git-utils.ts +421 -0
- package/server/github-pr.ts +379 -0
- package/server/google-media.ts +342 -0
- package/server/image-pull-manager.ts +279 -0
- package/server/index.ts +491 -0
- package/server/internal-ai.ts +237 -0
- package/server/kill-switch.ts +99 -0
- package/server/llm-providers.ts +342 -0
- package/server/logger.ts +259 -0
- package/server/mcp-registry.ts +401 -0
- package/server/message-bus.ts +271 -0
- package/server/message-delivery.ts +128 -0
- package/server/metrics-collector.ts +350 -0
- package/server/metrics-types.ts +108 -0
- package/server/middleware/managed-auth.ts +195 -0
- package/server/novnc-proxy.ts +99 -0
- package/server/path-resolver.ts +186 -0
- package/server/paths.ts +13 -0
- package/server/pr-poller.ts +162 -0
- package/server/prompt-manager.ts +211 -0
- package/server/protocol/claude-upstream/README.md +19 -0
- package/server/protocol/claude-upstream/sdk.d.ts.txt +1943 -0
- package/server/protocol/codex-upstream/ClientNotification.ts.txt +5 -0
- package/server/protocol/codex-upstream/ClientRequest.ts.txt +60 -0
- package/server/protocol/codex-upstream/README.md +18 -0
- package/server/protocol/codex-upstream/ServerNotification.ts.txt +41 -0
- package/server/protocol/codex-upstream/ServerRequest.ts.txt +16 -0
- package/server/protocol/codex-upstream/v2/DynamicToolCallParams.ts.txt +6 -0
- package/server/protocol/codex-upstream/v2/DynamicToolCallResponse.ts.txt +6 -0
- package/server/protocol-monitor.ts +50 -0
- package/server/provider-manager.ts +111 -0
- package/server/provider-registry.ts +393 -0
- package/server/push-notifications.ts +221 -0
- package/server/recorder.ts +374 -0
- package/server/recording-hub/compat-validator.ts +284 -0
- package/server/recording-hub/diagnostics.ts +299 -0
- package/server/recording-hub/hub-config.ts +19 -0
- package/server/recording-hub/hub-routes.ts +236 -0
- package/server/recording-hub/hub-store.ts +265 -0
- package/server/recording-hub/replay-adapter.ts +207 -0
- package/server/relay-client.ts +320 -0
- package/server/reminder-scheduler.ts +38 -0
- package/server/replay.ts +78 -0
- package/server/routes/agent-routes.ts +264 -0
- package/server/routes/assistant-routes.ts +90 -0
- package/server/routes/cron-routes.ts +103 -0
- package/server/routes/env-routes.ts +95 -0
- package/server/routes/federation-routes.ts +76 -0
- package/server/routes/fs-routes.ts +622 -0
- package/server/routes/git-routes.ts +97 -0
- package/server/routes/llm-routes.ts +166 -0
- package/server/routes/media-routes.ts +135 -0
- package/server/routes/metrics-routes.ts +13 -0
- package/server/routes/platform-routes.ts +1379 -0
- package/server/routes/prompt-routes.ts +67 -0
- package/server/routes/provider-routes.ts +109 -0
- package/server/routes/sandbox-routes.ts +127 -0
- package/server/routes/settings-routes.ts +285 -0
- package/server/routes/skills-routes.ts +100 -0
- package/server/routes/socialmedia-routes.ts +208 -0
- package/server/routes/system-routes.ts +228 -0
- package/server/routes/tailscale-routes.ts +22 -0
- package/server/routes/telephony-routes.ts +259 -0
- package/server/routes.ts +1379 -0
- package/server/sandbox-manager.ts +168 -0
- package/server/service.ts +718 -0
- package/server/session-creation-service.ts +457 -0
- package/server/session-git-info.ts +104 -0
- package/server/session-names.ts +67 -0
- package/server/session-orchestrator.ts +824 -0
- package/server/session-state-machine.ts +207 -0
- package/server/session-store.ts +146 -0
- package/server/session-types.ts +511 -0
- package/server/settings-manager.ts +149 -0
- package/server/shared-context.ts +157 -0
- package/server/socialmedia/adapter.ts +15 -0
- package/server/socialmedia/adapters/ayrshare-adapter.ts +169 -0
- package/server/socialmedia/adapters/buffer-adapter.ts +299 -0
- package/server/socialmedia/adapters/postiz-adapter.ts +298 -0
- package/server/socialmedia/manager.ts +227 -0
- package/server/socialmedia/store.ts +98 -0
- package/server/socialmedia/types.ts +89 -0
- package/server/tailscale-manager.ts +451 -0
- package/server/telephony/audio-bridge.ts +331 -0
- package/server/telephony/call-manager.ts +457 -0
- package/server/telephony/call-types.ts +108 -0
- package/server/telephony/telephony-store.ts +119 -0
- package/server/terminal-manager.ts +240 -0
- package/server/update-checker.ts +192 -0
- package/server/usage-limits.ts +225 -0
- package/server/web-push.d.ts +51 -0
- package/server/worktree-tracker.ts +84 -0
- package/server/ws-auth.ts +41 -0
- package/server/ws-bridge-browser-ingest.ts +72 -0
- package/server/ws-bridge-browser.ts +112 -0
- package/server/ws-bridge-cli-ingest.ts +81 -0
- package/server/ws-bridge-codex.ts +266 -0
- package/server/ws-bridge-controls.ts +20 -0
- package/server/ws-bridge-persist.ts +66 -0
- package/server/ws-bridge-publish.ts +79 -0
- package/server/ws-bridge-replay.ts +61 -0
- package/server/ws-bridge-types.ts +121 -0
- package/server/ws-bridge.ts +1240 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static registry of all known Claude Code CLI providers.
|
|
3
|
+
* Pure data — no I/O, no state.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ProviderEnvField {
|
|
7
|
+
key: string;
|
|
8
|
+
label: string;
|
|
9
|
+
required: boolean;
|
|
10
|
+
secret: boolean;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ProviderDefinition {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
envFields: ProviderEnvField[];
|
|
19
|
+
defaultModel?: string;
|
|
20
|
+
docsUrl?: string;
|
|
21
|
+
cliProviderFlag: string;
|
|
22
|
+
category: "cloud" | "gateway" | "local" | "custom";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const PROVIDER_REGISTRY: ProviderDefinition[] = [
|
|
26
|
+
{
|
|
27
|
+
id: "anthropic",
|
|
28
|
+
name: "Anthropic",
|
|
29
|
+
description: "Claude models via Anthropic API",
|
|
30
|
+
cliProviderFlag: "anthropic",
|
|
31
|
+
category: "cloud",
|
|
32
|
+
defaultModel: "claude-sonnet-4-6",
|
|
33
|
+
docsUrl: "https://docs.anthropic.com/",
|
|
34
|
+
envFields: [
|
|
35
|
+
{ key: "ANTHROPIC_API_KEY", label: "API Key", required: true, secret: true, placeholder: "sk-ant-..." },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "openai",
|
|
40
|
+
name: "OpenAI",
|
|
41
|
+
description: "GPT models via OpenAI API",
|
|
42
|
+
cliProviderFlag: "openai",
|
|
43
|
+
category: "cloud",
|
|
44
|
+
defaultModel: "gpt-4o",
|
|
45
|
+
docsUrl: "https://platform.openai.com/docs",
|
|
46
|
+
envFields: [
|
|
47
|
+
{ key: "OPENAI_API_KEY", label: "API Key", required: true, secret: true, placeholder: "sk-..." },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "google",
|
|
52
|
+
name: "Google",
|
|
53
|
+
description: "Gemini models via Google AI",
|
|
54
|
+
cliProviderFlag: "google",
|
|
55
|
+
category: "cloud",
|
|
56
|
+
defaultModel: "gemini-2.5-pro",
|
|
57
|
+
docsUrl: "https://ai.google.dev/docs",
|
|
58
|
+
envFields: [
|
|
59
|
+
{ key: "GOOGLE_API_KEY", label: "API Key", required: true, secret: true, placeholder: "AIza..." },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "mistral",
|
|
64
|
+
name: "Mistral AI",
|
|
65
|
+
description: "Mistral & Codestral models",
|
|
66
|
+
cliProviderFlag: "mistral",
|
|
67
|
+
category: "cloud",
|
|
68
|
+
defaultModel: "codestral-latest",
|
|
69
|
+
docsUrl: "https://docs.mistral.ai/",
|
|
70
|
+
envFields: [
|
|
71
|
+
{ key: "MISTRAL_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "deepseek",
|
|
76
|
+
name: "DeepSeek",
|
|
77
|
+
description: "DeepSeek coding models",
|
|
78
|
+
cliProviderFlag: "deepseek",
|
|
79
|
+
category: "cloud",
|
|
80
|
+
defaultModel: "deepseek-chat",
|
|
81
|
+
docsUrl: "https://platform.deepseek.com/docs",
|
|
82
|
+
envFields: [
|
|
83
|
+
{ key: "DEEPSEEK_API_KEY", label: "API Key", required: true, secret: true, placeholder: "sk-..." },
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "xai",
|
|
88
|
+
name: "xAI (Grok)",
|
|
89
|
+
description: "Grok models via xAI",
|
|
90
|
+
cliProviderFlag: "xai",
|
|
91
|
+
category: "cloud",
|
|
92
|
+
defaultModel: "grok-3",
|
|
93
|
+
docsUrl: "https://docs.x.ai/",
|
|
94
|
+
envFields: [
|
|
95
|
+
{ key: "XAI_API_KEY", label: "API Key", required: true, secret: true, placeholder: "xai-..." },
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: "openrouter",
|
|
100
|
+
name: "OpenRouter",
|
|
101
|
+
description: "Access hundreds of models through one API",
|
|
102
|
+
cliProviderFlag: "openrouter",
|
|
103
|
+
category: "gateway",
|
|
104
|
+
docsUrl: "https://openrouter.ai/docs",
|
|
105
|
+
envFields: [
|
|
106
|
+
{ key: "OPENROUTER_API_KEY", label: "API Key", required: true, secret: true, placeholder: "sk-or-..." },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "together",
|
|
111
|
+
name: "Together AI",
|
|
112
|
+
description: "Open-source models hosted by Together",
|
|
113
|
+
cliProviderFlag: "together",
|
|
114
|
+
category: "cloud",
|
|
115
|
+
docsUrl: "https://docs.together.ai/",
|
|
116
|
+
envFields: [
|
|
117
|
+
{ key: "TOGETHER_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
id: "ollama",
|
|
122
|
+
name: "Ollama",
|
|
123
|
+
description: "Run models locally with Ollama",
|
|
124
|
+
cliProviderFlag: "ollama",
|
|
125
|
+
category: "local",
|
|
126
|
+
defaultModel: "llama3.1",
|
|
127
|
+
docsUrl: "https://ollama.com/",
|
|
128
|
+
envFields: [
|
|
129
|
+
{ key: "OLLAMA_BASE_URL", label: "Base URL", required: false, secret: false, placeholder: "http://localhost:11434" },
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: "huggingface",
|
|
134
|
+
name: "Hugging Face",
|
|
135
|
+
description: "Models via Hugging Face Inference API",
|
|
136
|
+
cliProviderFlag: "huggingface",
|
|
137
|
+
category: "cloud",
|
|
138
|
+
docsUrl: "https://huggingface.co/docs/api-inference",
|
|
139
|
+
envFields: [
|
|
140
|
+
{ key: "HF_TOKEN", label: "API Token", required: true, secret: true, placeholder: "hf_..." },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "copilot",
|
|
145
|
+
name: "Copilot",
|
|
146
|
+
description: "GitHub Copilot as provider",
|
|
147
|
+
cliProviderFlag: "copilot",
|
|
148
|
+
category: "cloud",
|
|
149
|
+
docsUrl: "https://docs.github.com/en/copilot",
|
|
150
|
+
envFields: [
|
|
151
|
+
{ key: "GITHUB_TOKEN", label: "GitHub Token", required: true, secret: true, placeholder: "ghp_..." },
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "cloudflare",
|
|
156
|
+
name: "Cloudflare AI Gateway",
|
|
157
|
+
description: "Route through Cloudflare AI Gateway",
|
|
158
|
+
cliProviderFlag: "cloudflare",
|
|
159
|
+
category: "gateway",
|
|
160
|
+
docsUrl: "https://developers.cloudflare.com/ai-gateway/",
|
|
161
|
+
envFields: [
|
|
162
|
+
{ key: "CLOUDFLARE_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
163
|
+
{ key: "CLOUDFLARE_ACCOUNT_ID", label: "Account ID", required: true, secret: false, placeholder: "..." },
|
|
164
|
+
{ key: "CLOUDFLARE_GATEWAY_ID", label: "Gateway ID", required: true, secret: false, placeholder: "..." },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: "litellm",
|
|
169
|
+
name: "LiteLLM",
|
|
170
|
+
description: "Unified proxy for 100+ LLM providers",
|
|
171
|
+
cliProviderFlag: "litellm",
|
|
172
|
+
category: "gateway",
|
|
173
|
+
docsUrl: "https://docs.litellm.ai/",
|
|
174
|
+
envFields: [
|
|
175
|
+
{ key: "LITELLM_API_KEY", label: "API Key", required: false, secret: true, placeholder: "sk-..." },
|
|
176
|
+
{ key: "LITELLM_BASE_URL", label: "Base URL", required: true, secret: false, placeholder: "http://localhost:4000" },
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: "byteplus",
|
|
181
|
+
name: "BytePlus",
|
|
182
|
+
description: "BytePlus ModelArk models",
|
|
183
|
+
cliProviderFlag: "byteplus",
|
|
184
|
+
category: "cloud",
|
|
185
|
+
envFields: [
|
|
186
|
+
{ key: "BYTEPLUS_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "chutes",
|
|
191
|
+
name: "Chutes",
|
|
192
|
+
description: "Chutes AI inference platform",
|
|
193
|
+
cliProviderFlag: "chutes",
|
|
194
|
+
category: "cloud",
|
|
195
|
+
envFields: [
|
|
196
|
+
{ key: "CHUTES_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: "kilo",
|
|
201
|
+
name: "Kilo Gateway",
|
|
202
|
+
description: "Kilo AI Gateway",
|
|
203
|
+
cliProviderFlag: "kilo",
|
|
204
|
+
category: "gateway",
|
|
205
|
+
envFields: [
|
|
206
|
+
{ key: "KILO_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
207
|
+
{ key: "KILO_BASE_URL", label: "Base URL", required: false, secret: false, placeholder: "https://api.kilo.ai" },
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: "kimi",
|
|
212
|
+
name: "Kimi Code",
|
|
213
|
+
description: "Moonshot AI Kimi coding models",
|
|
214
|
+
cliProviderFlag: "kimi",
|
|
215
|
+
category: "cloud",
|
|
216
|
+
defaultModel: "kimi-k2.5",
|
|
217
|
+
envFields: [
|
|
218
|
+
{ key: "MOONSHOT_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
219
|
+
],
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: "minimax",
|
|
223
|
+
name: "MiniMax",
|
|
224
|
+
description: "MiniMax AI models",
|
|
225
|
+
cliProviderFlag: "minimax",
|
|
226
|
+
category: "cloud",
|
|
227
|
+
envFields: [
|
|
228
|
+
{ key: "MINIMAX_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
id: "moonshot",
|
|
233
|
+
name: "Moonshot AI",
|
|
234
|
+
description: "Moonshot AI (Kimi K2.5)",
|
|
235
|
+
cliProviderFlag: "moonshot",
|
|
236
|
+
category: "cloud",
|
|
237
|
+
defaultModel: "kimi-k2.5",
|
|
238
|
+
envFields: [
|
|
239
|
+
{ key: "MOONSHOT_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
id: "opencode",
|
|
244
|
+
name: "OpenCode",
|
|
245
|
+
description: "OpenCode provider",
|
|
246
|
+
cliProviderFlag: "opencode",
|
|
247
|
+
category: "cloud",
|
|
248
|
+
envFields: [
|
|
249
|
+
{ key: "OPENCODE_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: "qianfan",
|
|
254
|
+
name: "Qianfan",
|
|
255
|
+
description: "Baidu Qianfan platform",
|
|
256
|
+
cliProviderFlag: "qianfan",
|
|
257
|
+
category: "cloud",
|
|
258
|
+
envFields: [
|
|
259
|
+
{ key: "QIANFAN_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
260
|
+
],
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: "qwen",
|
|
264
|
+
name: "Qwen",
|
|
265
|
+
description: "Qwen models",
|
|
266
|
+
cliProviderFlag: "qwen",
|
|
267
|
+
category: "cloud",
|
|
268
|
+
defaultModel: "qwen-max",
|
|
269
|
+
envFields: [
|
|
270
|
+
{ key: "DASHSCOPE_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
271
|
+
],
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
id: "qwen-alibaba",
|
|
275
|
+
name: "Qwen (Alibaba Cloud)",
|
|
276
|
+
description: "Qwen via Alibaba Cloud Model Studio",
|
|
277
|
+
cliProviderFlag: "qwen-alibaba",
|
|
278
|
+
category: "cloud",
|
|
279
|
+
envFields: [
|
|
280
|
+
{ key: "ALIBABA_CLOUD_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: "sglang",
|
|
285
|
+
name: "SGLang",
|
|
286
|
+
description: "SGLang inference server",
|
|
287
|
+
cliProviderFlag: "sglang",
|
|
288
|
+
category: "local",
|
|
289
|
+
envFields: [
|
|
290
|
+
{ key: "SGLANG_BASE_URL", label: "Base URL", required: true, secret: false, placeholder: "http://localhost:30000" },
|
|
291
|
+
{ key: "SGLANG_API_KEY", label: "API Key", required: false, secret: true, placeholder: "..." },
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: "synthetic",
|
|
296
|
+
name: "Synthetic",
|
|
297
|
+
description: "Synthetic AI provider",
|
|
298
|
+
cliProviderFlag: "synthetic",
|
|
299
|
+
category: "cloud",
|
|
300
|
+
envFields: [
|
|
301
|
+
{ key: "SYNTHETIC_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
id: "venice",
|
|
306
|
+
name: "Venice AI",
|
|
307
|
+
description: "Venice AI privacy-focused inference",
|
|
308
|
+
cliProviderFlag: "venice",
|
|
309
|
+
category: "cloud",
|
|
310
|
+
docsUrl: "https://docs.venice.ai/",
|
|
311
|
+
envFields: [
|
|
312
|
+
{ key: "VENICE_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
id: "vercel",
|
|
317
|
+
name: "Vercel AI Gateway",
|
|
318
|
+
description: "Route through Vercel AI Gateway",
|
|
319
|
+
cliProviderFlag: "vercel",
|
|
320
|
+
category: "gateway",
|
|
321
|
+
docsUrl: "https://vercel.com/docs/ai-gateway",
|
|
322
|
+
envFields: [
|
|
323
|
+
{ key: "VERCEL_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
324
|
+
],
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
id: "vllm",
|
|
328
|
+
name: "vLLM",
|
|
329
|
+
description: "Self-hosted vLLM inference server",
|
|
330
|
+
cliProviderFlag: "vllm",
|
|
331
|
+
category: "local",
|
|
332
|
+
envFields: [
|
|
333
|
+
{ key: "VLLM_BASE_URL", label: "Base URL", required: true, secret: false, placeholder: "http://localhost:8000" },
|
|
334
|
+
{ key: "VLLM_API_KEY", label: "API Key", required: false, secret: true, placeholder: "..." },
|
|
335
|
+
],
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
id: "volcano",
|
|
339
|
+
name: "Volcano Engine",
|
|
340
|
+
description: "ByteDance Volcano Engine",
|
|
341
|
+
cliProviderFlag: "volcano",
|
|
342
|
+
category: "cloud",
|
|
343
|
+
envFields: [
|
|
344
|
+
{ key: "VOLC_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
id: "xiaomi",
|
|
349
|
+
name: "Xiaomi",
|
|
350
|
+
description: "Xiaomi AI models",
|
|
351
|
+
cliProviderFlag: "xiaomi",
|
|
352
|
+
category: "cloud",
|
|
353
|
+
envFields: [
|
|
354
|
+
{ key: "XIAOMI_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
355
|
+
],
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
id: "zai",
|
|
359
|
+
name: "Z.AI",
|
|
360
|
+
description: "GLM Coding Plan — Global & CN",
|
|
361
|
+
cliProviderFlag: "zai",
|
|
362
|
+
category: "cloud",
|
|
363
|
+
envFields: [
|
|
364
|
+
{ key: "ZAI_API_KEY", label: "API Key", required: true, secret: true, placeholder: "..." },
|
|
365
|
+
],
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
id: "custom",
|
|
369
|
+
name: "Custom Provider",
|
|
370
|
+
description: "Any OpenAI-compatible API endpoint",
|
|
371
|
+
cliProviderFlag: "custom",
|
|
372
|
+
category: "custom",
|
|
373
|
+
envFields: [
|
|
374
|
+
{ key: "CUSTOM_API_KEY", label: "API Key", required: false, secret: true, placeholder: "sk-..." },
|
|
375
|
+
{ key: "CUSTOM_BASE_URL", label: "Base URL", required: true, secret: false, placeholder: "https://your-api.com/v1" },
|
|
376
|
+
{ key: "CUSTOM_MODEL", label: "Model Name", required: false, secret: false, placeholder: "model-name" },
|
|
377
|
+
],
|
|
378
|
+
},
|
|
379
|
+
];
|
|
380
|
+
|
|
381
|
+
const registryMap = new Map(PROVIDER_REGISTRY.map((p) => [p.id, p]));
|
|
382
|
+
|
|
383
|
+
export function getProviderById(id: string): ProviderDefinition | undefined {
|
|
384
|
+
return registryMap.get(id);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function getAllProviders(): ProviderDefinition[] {
|
|
388
|
+
return PROVIDER_REGISTRY;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function getProvidersByCategory(category: ProviderDefinition["category"]): ProviderDefinition[] {
|
|
392
|
+
return PROVIDER_REGISTRY.filter((p) => p.category === category);
|
|
393
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// ─── Web Push Notifications ──────────────────────────────────────────────────
|
|
2
|
+
// VAPID-based push notifications for agent alerts
|
|
3
|
+
|
|
4
|
+
import webpush from "web-push";
|
|
5
|
+
import {
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { join, dirname } from "node:path";
|
|
12
|
+
import { HEYHANK_HOME } from "./paths.js";
|
|
13
|
+
|
|
14
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export interface PushSubscriptionData {
|
|
17
|
+
endpoint: string;
|
|
18
|
+
keys: {
|
|
19
|
+
p256dh: string;
|
|
20
|
+
auth: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface VapidKeys {
|
|
25
|
+
publicKey: string;
|
|
26
|
+
privateKey: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
const VAPID_FILE = join(HEYHANK_HOME, "vapid-keys.json");
|
|
32
|
+
const SUBSCRIPTIONS_FILE = join(HEYHANK_HOME, "push-subscriptions.json");
|
|
33
|
+
|
|
34
|
+
// ─── VAPID Key Management ────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
let vapidKeys: VapidKeys | null = null;
|
|
37
|
+
|
|
38
|
+
function loadOrGenerateVapidKeys(): VapidKeys {
|
|
39
|
+
if (vapidKeys) return vapidKeys;
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
if (existsSync(VAPID_FILE)) {
|
|
43
|
+
const raw = readFileSync(VAPID_FILE, "utf-8");
|
|
44
|
+
vapidKeys = JSON.parse(raw) as VapidKeys;
|
|
45
|
+
webpush.setVapidDetails(
|
|
46
|
+
"mailto:noreply@heyhank.dev",
|
|
47
|
+
vapidKeys.publicKey,
|
|
48
|
+
vapidKeys.privateKey,
|
|
49
|
+
);
|
|
50
|
+
console.log("[push] Loaded existing VAPID keys from disk");
|
|
51
|
+
return vapidKeys;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Generate new keys
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Generate proper ECDSA P-256 VAPID keys using web-push
|
|
58
|
+
const generated = webpush.generateVAPIDKeys();
|
|
59
|
+
const keys: VapidKeys = {
|
|
60
|
+
publicKey: generated.publicKey,
|
|
61
|
+
privateKey: generated.privateKey,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
mkdirSync(dirname(VAPID_FILE), { recursive: true });
|
|
66
|
+
writeFileSync(VAPID_FILE, JSON.stringify(keys, null, 2), { mode: 0o600 });
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error("[push] Failed to persist VAPID keys:", err);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
webpush.setVapidDetails(
|
|
72
|
+
"mailto:noreply@heyhank.dev",
|
|
73
|
+
keys.publicKey,
|
|
74
|
+
keys.privateKey,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
vapidKeys = keys;
|
|
78
|
+
console.log("[push] Generated new VAPID keys");
|
|
79
|
+
return keys;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Get the public VAPID key (for browser subscription). */
|
|
83
|
+
export function getPublicVapidKey(): string {
|
|
84
|
+
return loadOrGenerateVapidKeys().publicKey;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── Subscription Management ─────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
let subscriptions: PushSubscriptionData[] = [];
|
|
90
|
+
|
|
91
|
+
function loadSubscriptions(): void {
|
|
92
|
+
try {
|
|
93
|
+
if (existsSync(SUBSCRIPTIONS_FILE)) {
|
|
94
|
+
const raw = readFileSync(SUBSCRIPTIONS_FILE, "utf-8");
|
|
95
|
+
subscriptions = JSON.parse(raw);
|
|
96
|
+
}
|
|
97
|
+
} catch {
|
|
98
|
+
subscriptions = [];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function saveSubscriptions(): void {
|
|
103
|
+
try {
|
|
104
|
+
mkdirSync(dirname(SUBSCRIPTIONS_FILE), { recursive: true });
|
|
105
|
+
writeFileSync(
|
|
106
|
+
SUBSCRIPTIONS_FILE,
|
|
107
|
+
JSON.stringify(subscriptions, null, 2),
|
|
108
|
+
"utf-8",
|
|
109
|
+
);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error("[push] Failed to save subscriptions:", err);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Add a push subscription. */
|
|
116
|
+
export function addSubscription(sub: PushSubscriptionData): void {
|
|
117
|
+
// Deduplicate by endpoint
|
|
118
|
+
subscriptions = subscriptions.filter((s) => s.endpoint !== sub.endpoint);
|
|
119
|
+
subscriptions.push(sub);
|
|
120
|
+
saveSubscriptions();
|
|
121
|
+
console.log(`[push] Subscription added (total: ${subscriptions.length})`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Remove all subscriptions. */
|
|
125
|
+
export function clearSubscriptions(): void {
|
|
126
|
+
subscriptions = [];
|
|
127
|
+
saveSubscriptions();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Get subscription count. */
|
|
131
|
+
export function getSubscriptionCount(): number {
|
|
132
|
+
return subscriptions.length;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Send Notifications ──────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Send a push notification to all subscribed browsers.
|
|
139
|
+
* Uses the web-push library for proper VAPID-signed Web Push protocol.
|
|
140
|
+
*/
|
|
141
|
+
export async function sendNotification(
|
|
142
|
+
title: string,
|
|
143
|
+
body: string,
|
|
144
|
+
options?: {
|
|
145
|
+
icon?: string;
|
|
146
|
+
badge?: string;
|
|
147
|
+
tag?: string;
|
|
148
|
+
url?: string;
|
|
149
|
+
data?: Record<string, unknown>;
|
|
150
|
+
},
|
|
151
|
+
): Promise<{ sent: number; failed: number }> {
|
|
152
|
+
const payload = JSON.stringify({
|
|
153
|
+
title,
|
|
154
|
+
body,
|
|
155
|
+
icon: options?.icon || "/icon-192.png",
|
|
156
|
+
badge: options?.badge || "/icon-192.png",
|
|
157
|
+
tag: options?.tag,
|
|
158
|
+
data: {
|
|
159
|
+
url: options?.url || "/",
|
|
160
|
+
...options?.data,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
let sent = 0;
|
|
165
|
+
let failed = 0;
|
|
166
|
+
const failedEndpoints: string[] = [];
|
|
167
|
+
|
|
168
|
+
for (const sub of subscriptions) {
|
|
169
|
+
try {
|
|
170
|
+
await webpush.sendNotification(
|
|
171
|
+
{
|
|
172
|
+
endpoint: sub.endpoint,
|
|
173
|
+
keys: sub.keys,
|
|
174
|
+
},
|
|
175
|
+
payload,
|
|
176
|
+
{ TTL: 86400 },
|
|
177
|
+
);
|
|
178
|
+
sent++;
|
|
179
|
+
} catch (err: unknown) {
|
|
180
|
+
const statusCode = (err as { statusCode?: number }).statusCode;
|
|
181
|
+
if (statusCode === 410 || statusCode === 404) {
|
|
182
|
+
// Gone or Not Found — subscription expired
|
|
183
|
+
failedEndpoints.push(sub.endpoint);
|
|
184
|
+
}
|
|
185
|
+
failed++;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Remove expired subscriptions
|
|
190
|
+
if (failedEndpoints.length > 0) {
|
|
191
|
+
subscriptions = subscriptions.filter(
|
|
192
|
+
(s) => !failedEndpoints.includes(s.endpoint),
|
|
193
|
+
);
|
|
194
|
+
saveSubscriptions();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return { sent, failed };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Send an agent alert notification. */
|
|
201
|
+
export async function notifyAgentAlert(
|
|
202
|
+
agentName: string,
|
|
203
|
+
message: string,
|
|
204
|
+
severity: "info" | "warning" | "error" = "info",
|
|
205
|
+
): Promise<void> {
|
|
206
|
+
const icons: Record<string, string> = {
|
|
207
|
+
info: "\u2139\uFE0F",
|
|
208
|
+
warning: "\u26A0\uFE0F",
|
|
209
|
+
error: "\uD83D\uDEA8",
|
|
210
|
+
};
|
|
211
|
+
await sendNotification(
|
|
212
|
+
`${icons[severity]} ${agentName}`,
|
|
213
|
+
message,
|
|
214
|
+
{ tag: `agent-${agentName}` },
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ─── Initialize ──────────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
loadSubscriptions();
|
|
221
|
+
loadOrGenerateVapidKeys();
|