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,259 @@
|
|
|
1
|
+
// ─── Telephony Routes ─────────────────────────────────────────────────────────
|
|
2
|
+
// REST API for managing phone calls via FreeSWITCH + Gemini Live.
|
|
3
|
+
|
|
4
|
+
import type { Hono } from "hono";
|
|
5
|
+
import { callManager } from "../telephony/call-manager.js";
|
|
6
|
+
import * as store from "../telephony/telephony-store.js";
|
|
7
|
+
import type { CallConfig, SipTrunkConfig, TelephonyContact } from "../telephony/call-types.js";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
|
|
10
|
+
export function registerTelephonyRoutes(api: Hono): void {
|
|
11
|
+
// ─── Calls ───────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
/** Start a new outbound call */
|
|
14
|
+
api.post("/telephony/calls", async (c) => {
|
|
15
|
+
try {
|
|
16
|
+
const body = await c.req.json() as CallConfig;
|
|
17
|
+
|
|
18
|
+
if (!body.phone) {
|
|
19
|
+
return c.json({ error: "phone is required (E.164 format, e.g. +4366412345)" }, 400);
|
|
20
|
+
}
|
|
21
|
+
if (!body.prompt) {
|
|
22
|
+
return c.json({ error: "prompt is required (task for the AI)" }, 400);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Normalize phone number
|
|
26
|
+
let phone = body.phone.replace(/\s/g, "");
|
|
27
|
+
if (!phone.startsWith("+")) {
|
|
28
|
+
// Assume Austrian number if no country code
|
|
29
|
+
if (phone.startsWith("0")) phone = "+43" + phone.slice(1);
|
|
30
|
+
else phone = "+" + phone;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const callState = await callManager.startCall({
|
|
34
|
+
...body,
|
|
35
|
+
phone,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return c.json(callState);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
const msg = err instanceof Error ? err.message : "Failed to start call";
|
|
41
|
+
return c.json({ error: msg }, 500);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/** List active calls */
|
|
46
|
+
api.get("/telephony/calls", (c) => {
|
|
47
|
+
const active = callManager.getActiveCalls();
|
|
48
|
+
return c.json({ calls: active });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/** Get call details (active or from history) */
|
|
52
|
+
api.get("/telephony/calls/:id", (c) => {
|
|
53
|
+
const id = c.req.param("id");
|
|
54
|
+
const active = callManager.getCallState(id);
|
|
55
|
+
if (active) return c.json(active);
|
|
56
|
+
|
|
57
|
+
const stored = store.getCall(id);
|
|
58
|
+
if (stored) return c.json(stored);
|
|
59
|
+
|
|
60
|
+
return c.json({ error: "Call not found" }, 404);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/** End/hangup an active call */
|
|
64
|
+
api.delete("/telephony/calls/:id", async (c) => {
|
|
65
|
+
const id = c.req.param("id");
|
|
66
|
+
try {
|
|
67
|
+
const result = await callManager.endCall(id);
|
|
68
|
+
if (!result) return c.json({ error: "Call not found or already ended" }, 404);
|
|
69
|
+
return c.json(result);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to end call" }, 500);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/** Call history */
|
|
76
|
+
api.get("/telephony/history", (c) => {
|
|
77
|
+
const limit = parseInt(c.req.query("limit") || "50", 10);
|
|
78
|
+
const calls = store.listCalls(limit);
|
|
79
|
+
return c.json({ calls });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ─── Settings ────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
/** Get telephony settings */
|
|
85
|
+
api.get("/telephony/settings", (c) => {
|
|
86
|
+
const settings = store.getSettings();
|
|
87
|
+
// Don't expose passwords in API response
|
|
88
|
+
const safe = {
|
|
89
|
+
...settings,
|
|
90
|
+
freeswitch: {
|
|
91
|
+
...settings.freeswitch,
|
|
92
|
+
eslPassword: settings.freeswitch.eslPassword ? "***" : "",
|
|
93
|
+
},
|
|
94
|
+
trunks: settings.trunks.map((t) => ({
|
|
95
|
+
...t,
|
|
96
|
+
password: t.password ? "***" : "",
|
|
97
|
+
})),
|
|
98
|
+
};
|
|
99
|
+
return c.json(safe);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/** Update telephony settings */
|
|
103
|
+
api.put("/telephony/settings", async (c) => {
|
|
104
|
+
try {
|
|
105
|
+
const body = await c.req.json();
|
|
106
|
+
const current = store.getSettings();
|
|
107
|
+
|
|
108
|
+
// Merge settings, preserving passwords if masked
|
|
109
|
+
const updated = { ...current, ...body };
|
|
110
|
+
|
|
111
|
+
// Restore passwords if they come back as "***"
|
|
112
|
+
if (updated.freeswitch?.eslPassword === "***") {
|
|
113
|
+
updated.freeswitch.eslPassword = current.freeswitch.eslPassword;
|
|
114
|
+
}
|
|
115
|
+
if (updated.trunks) {
|
|
116
|
+
updated.trunks = updated.trunks.map((t: SipTrunkConfig, i: number) => {
|
|
117
|
+
if (t.password === "***" && current.trunks[i]) {
|
|
118
|
+
return { ...t, password: current.trunks[i].password };
|
|
119
|
+
}
|
|
120
|
+
return t;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
store.saveSettings(updated);
|
|
125
|
+
return c.json({ success: true });
|
|
126
|
+
} catch (err) {
|
|
127
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to save settings" }, 500);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ─── SIP Trunks ──────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
/** Add a SIP trunk */
|
|
134
|
+
api.post("/telephony/trunks", async (c) => {
|
|
135
|
+
try {
|
|
136
|
+
const body = await c.req.json() as Omit<SipTrunkConfig, "id">;
|
|
137
|
+
if (!body.name || !body.username || !body.password || !body.server) {
|
|
138
|
+
return c.json({ error: "name, username, password, and server are required" }, 400);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const settings = store.getSettings();
|
|
142
|
+
const trunk: SipTrunkConfig = {
|
|
143
|
+
...body,
|
|
144
|
+
id: randomUUID(),
|
|
145
|
+
enabled: body.enabled ?? true,
|
|
146
|
+
};
|
|
147
|
+
settings.trunks.push(trunk);
|
|
148
|
+
if (!settings.defaultTrunkId) settings.defaultTrunkId = trunk.id;
|
|
149
|
+
store.saveSettings(settings);
|
|
150
|
+
|
|
151
|
+
return c.json(trunk);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to add trunk" }, 500);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/** Remove a SIP trunk */
|
|
158
|
+
api.delete("/telephony/trunks/:id", (c) => {
|
|
159
|
+
const id = c.req.param("id");
|
|
160
|
+
const settings = store.getSettings();
|
|
161
|
+
settings.trunks = settings.trunks.filter((t) => t.id !== id);
|
|
162
|
+
if (settings.defaultTrunkId === id) {
|
|
163
|
+
settings.defaultTrunkId = settings.trunks[0]?.id || null;
|
|
164
|
+
}
|
|
165
|
+
store.saveSettings(settings);
|
|
166
|
+
return c.json({ success: true });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// ─── Contacts ──────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
/** List all contacts */
|
|
172
|
+
api.get("/telephony/contacts", (c) => {
|
|
173
|
+
return c.json({ contacts: store.getContacts() });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
/** Add a contact */
|
|
177
|
+
api.post("/telephony/contacts", async (c) => {
|
|
178
|
+
try {
|
|
179
|
+
const body = await c.req.json() as Omit<TelephonyContact, "id">;
|
|
180
|
+
if (!body.name?.trim() || !body.phone?.trim()) {
|
|
181
|
+
return c.json({ error: "name and phone are required" }, 400);
|
|
182
|
+
}
|
|
183
|
+
let phone = body.phone.replace(/\s/g, "");
|
|
184
|
+
if (!phone.startsWith("+")) {
|
|
185
|
+
if (phone.startsWith("0")) phone = "+43" + phone.slice(1);
|
|
186
|
+
else phone = "+" + phone;
|
|
187
|
+
}
|
|
188
|
+
const contact: TelephonyContact = {
|
|
189
|
+
id: randomUUID(),
|
|
190
|
+
name: body.name.trim(),
|
|
191
|
+
phone,
|
|
192
|
+
notes: body.notes?.trim() || undefined,
|
|
193
|
+
};
|
|
194
|
+
store.addContact(contact);
|
|
195
|
+
return c.json(contact);
|
|
196
|
+
} catch (err) {
|
|
197
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to add contact" }, 500);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
/** Update a contact */
|
|
202
|
+
api.put("/telephony/contacts/:id", async (c) => {
|
|
203
|
+
try {
|
|
204
|
+
const id = c.req.param("id");
|
|
205
|
+
const body = await c.req.json() as Partial<Omit<TelephonyContact, "id">>;
|
|
206
|
+
if (body.phone) {
|
|
207
|
+
let phone = body.phone.replace(/\s/g, "");
|
|
208
|
+
if (!phone.startsWith("+")) {
|
|
209
|
+
if (phone.startsWith("0")) phone = "+43" + phone.slice(1);
|
|
210
|
+
else phone = "+" + phone;
|
|
211
|
+
}
|
|
212
|
+
body.phone = phone;
|
|
213
|
+
}
|
|
214
|
+
const updated = store.updateContact(id, body);
|
|
215
|
+
if (!updated) return c.json({ error: "Contact not found" }, 404);
|
|
216
|
+
return c.json(updated);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to update contact" }, 500);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
/** Delete a contact */
|
|
223
|
+
api.delete("/telephony/contacts/:id", (c) => {
|
|
224
|
+
const id = c.req.param("id");
|
|
225
|
+
const deleted = store.deleteContact(id);
|
|
226
|
+
if (!deleted) return c.json({ error: "Contact not found" }, 404);
|
|
227
|
+
return c.json({ success: true });
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
/** Test FreeSWITCH ESL connection */
|
|
231
|
+
api.post("/telephony/test-connection", async (c) => {
|
|
232
|
+
const settings = store.getSettings();
|
|
233
|
+
const { eslHost, eslPort, eslPassword } = settings.freeswitch;
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const eslUrl = `http://${eslHost}:${eslPort}/api`;
|
|
237
|
+
const res = await fetch(eslUrl, {
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers: {
|
|
240
|
+
"Content-Type": "text/plain",
|
|
241
|
+
"Authorization": `Basic ${btoa(`freeswitch:${eslPassword}`)}`,
|
|
242
|
+
},
|
|
243
|
+
body: "status",
|
|
244
|
+
signal: AbortSignal.timeout(5000),
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (res.ok) {
|
|
248
|
+
const text = await res.text();
|
|
249
|
+
return c.json({ connected: true, status: text.trim().slice(0, 200) });
|
|
250
|
+
}
|
|
251
|
+
return c.json({ connected: false, error: `HTTP ${res.status}` });
|
|
252
|
+
} catch (err) {
|
|
253
|
+
return c.json({
|
|
254
|
+
connected: false,
|
|
255
|
+
error: err instanceof Error ? err.message : "Connection failed",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|