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.
Files changed (199) hide show
  1. package/README.md +40 -0
  2. package/bin/cli.ts +168 -0
  3. package/bin/ctl.ts +528 -0
  4. package/bin/generate-token.ts +28 -0
  5. package/dist/apple-touch-icon.png +0 -0
  6. package/dist/assets/AgentsPage-BPhirnCe.js +7 -0
  7. package/dist/assets/AssistantPage-DJ-cMQfb.js +1 -0
  8. package/dist/assets/CronManager-DDbz-yiT.js +1 -0
  9. package/dist/assets/HelpPage-DMfkzERp.js +1 -0
  10. package/dist/assets/IntegrationsPage-CrOitCmJ.js +1 -0
  11. package/dist/assets/MediaPage-CE5rdvkC.js +1 -0
  12. package/dist/assets/PlatformDashboard-Do6F0O2p.js +1 -0
  13. package/dist/assets/Playground-Fc5cdc5p.js +109 -0
  14. package/dist/assets/ProcessPanel-CslEiZkI.js +2 -0
  15. package/dist/assets/PromptsPage-D2EhsdNO.js +4 -0
  16. package/dist/assets/RunsPage-C5BZF5Rx.js +1 -0
  17. package/dist/assets/SandboxManager-a1AVI5q2.js +8 -0
  18. package/dist/assets/SettingsPage-DirhjQrJ.js +51 -0
  19. package/dist/assets/SocialMediaPage-DBuM28vD.js +1 -0
  20. package/dist/assets/TailscalePage-CHiFhZXF.js +1 -0
  21. package/dist/assets/TelephonyPage-x0VV0fOo.js +1 -0
  22. package/dist/assets/TerminalPage-Drwyrnfd.js +1 -0
  23. package/dist/assets/gemini-audio-t-TSU-To.js +17 -0
  24. package/dist/assets/gemini-live-client-C7rqAW7G.js +166 -0
  25. package/dist/assets/index-C8M_PUmX.css +32 -0
  26. package/dist/assets/index-CEqZnThB.js +204 -0
  27. package/dist/assets/sw-register-LSSpj6RU.js +1 -0
  28. package/dist/assets/time-ago-B6r_l9u1.js +1 -0
  29. package/dist/assets/workbox-window.prod.es5-BIl4cyR9.js +2 -0
  30. package/dist/favicon-32-original.png +0 -0
  31. package/dist/favicon-32.png +0 -0
  32. package/dist/favicon.ico +0 -0
  33. package/dist/favicon.svg +8 -0
  34. package/dist/fonts/MesloLGSNerdFontMono-Bold.woff2 +0 -0
  35. package/dist/fonts/MesloLGSNerdFontMono-Regular.woff2 +0 -0
  36. package/dist/heyhank-mascot-poster.png +0 -0
  37. package/dist/heyhank-mascot.mp4 +0 -0
  38. package/dist/heyhank-mascot.webm +0 -0
  39. package/dist/icon-192-original.png +0 -0
  40. package/dist/icon-192.png +0 -0
  41. package/dist/icon-512-original.png +0 -0
  42. package/dist/icon-512.png +0 -0
  43. package/dist/index.html +21 -0
  44. package/dist/logo-192.png +0 -0
  45. package/dist/logo-512.png +0 -0
  46. package/dist/logo-codex.svg +14 -0
  47. package/dist/logo-docker.svg +4 -0
  48. package/dist/logo-original.png +0 -0
  49. package/dist/logo.png +0 -0
  50. package/dist/logo.svg +14 -0
  51. package/dist/manifest.json +24 -0
  52. package/dist/push-sw.js +34 -0
  53. package/dist/sw.js +1 -0
  54. package/dist/workbox-d2a0910a.js +1 -0
  55. package/package.json +109 -0
  56. package/server/agent-cron-migrator.ts +85 -0
  57. package/server/agent-executor.ts +357 -0
  58. package/server/agent-store.ts +185 -0
  59. package/server/agent-timeout.ts +107 -0
  60. package/server/agent-types.ts +122 -0
  61. package/server/ai-validation-settings.ts +37 -0
  62. package/server/ai-validator.ts +181 -0
  63. package/server/anthropic-provider-migration.ts +48 -0
  64. package/server/assistant-store.ts +272 -0
  65. package/server/auth-manager.ts +150 -0
  66. package/server/auto-approve.ts +153 -0
  67. package/server/auto-namer.ts +36 -0
  68. package/server/backend-adapter.ts +54 -0
  69. package/server/cache-headers.ts +61 -0
  70. package/server/calendar-service.ts +434 -0
  71. package/server/claude-adapter.ts +889 -0
  72. package/server/claude-container-auth.ts +30 -0
  73. package/server/claude-session-discovery.ts +157 -0
  74. package/server/claude-session-history.ts +410 -0
  75. package/server/cli-launcher.ts +1303 -0
  76. package/server/codex-adapter.ts +3027 -0
  77. package/server/codex-container-auth.ts +24 -0
  78. package/server/codex-home.ts +27 -0
  79. package/server/codex-ws-proxy.cjs +226 -0
  80. package/server/commands-discovery.ts +81 -0
  81. package/server/constants.ts +7 -0
  82. package/server/container-manager.ts +1053 -0
  83. package/server/cost-tracker.ts +222 -0
  84. package/server/cron-scheduler.ts +243 -0
  85. package/server/cron-store.ts +148 -0
  86. package/server/cron-types.ts +63 -0
  87. package/server/email-service.ts +354 -0
  88. package/server/env-manager.ts +161 -0
  89. package/server/event-bus-types.ts +75 -0
  90. package/server/event-bus.ts +124 -0
  91. package/server/execution-store.ts +170 -0
  92. package/server/federation/node-connection.ts +190 -0
  93. package/server/federation/node-manager.ts +366 -0
  94. package/server/federation/node-store.ts +86 -0
  95. package/server/federation/node-types.ts +121 -0
  96. package/server/fs-utils.ts +15 -0
  97. package/server/git-utils.ts +421 -0
  98. package/server/github-pr.ts +379 -0
  99. package/server/google-media.ts +342 -0
  100. package/server/image-pull-manager.ts +279 -0
  101. package/server/index.ts +491 -0
  102. package/server/internal-ai.ts +237 -0
  103. package/server/kill-switch.ts +99 -0
  104. package/server/llm-providers.ts +342 -0
  105. package/server/logger.ts +259 -0
  106. package/server/mcp-registry.ts +401 -0
  107. package/server/message-bus.ts +271 -0
  108. package/server/message-delivery.ts +128 -0
  109. package/server/metrics-collector.ts +350 -0
  110. package/server/metrics-types.ts +108 -0
  111. package/server/middleware/managed-auth.ts +195 -0
  112. package/server/novnc-proxy.ts +99 -0
  113. package/server/path-resolver.ts +186 -0
  114. package/server/paths.ts +13 -0
  115. package/server/pr-poller.ts +162 -0
  116. package/server/prompt-manager.ts +211 -0
  117. package/server/protocol/claude-upstream/README.md +19 -0
  118. package/server/protocol/claude-upstream/sdk.d.ts.txt +1943 -0
  119. package/server/protocol/codex-upstream/ClientNotification.ts.txt +5 -0
  120. package/server/protocol/codex-upstream/ClientRequest.ts.txt +60 -0
  121. package/server/protocol/codex-upstream/README.md +18 -0
  122. package/server/protocol/codex-upstream/ServerNotification.ts.txt +41 -0
  123. package/server/protocol/codex-upstream/ServerRequest.ts.txt +16 -0
  124. package/server/protocol/codex-upstream/v2/DynamicToolCallParams.ts.txt +6 -0
  125. package/server/protocol/codex-upstream/v2/DynamicToolCallResponse.ts.txt +6 -0
  126. package/server/protocol-monitor.ts +50 -0
  127. package/server/provider-manager.ts +111 -0
  128. package/server/provider-registry.ts +393 -0
  129. package/server/push-notifications.ts +221 -0
  130. package/server/recorder.ts +374 -0
  131. package/server/recording-hub/compat-validator.ts +284 -0
  132. package/server/recording-hub/diagnostics.ts +299 -0
  133. package/server/recording-hub/hub-config.ts +19 -0
  134. package/server/recording-hub/hub-routes.ts +236 -0
  135. package/server/recording-hub/hub-store.ts +265 -0
  136. package/server/recording-hub/replay-adapter.ts +207 -0
  137. package/server/relay-client.ts +320 -0
  138. package/server/reminder-scheduler.ts +38 -0
  139. package/server/replay.ts +78 -0
  140. package/server/routes/agent-routes.ts +264 -0
  141. package/server/routes/assistant-routes.ts +90 -0
  142. package/server/routes/cron-routes.ts +103 -0
  143. package/server/routes/env-routes.ts +95 -0
  144. package/server/routes/federation-routes.ts +76 -0
  145. package/server/routes/fs-routes.ts +622 -0
  146. package/server/routes/git-routes.ts +97 -0
  147. package/server/routes/llm-routes.ts +166 -0
  148. package/server/routes/media-routes.ts +135 -0
  149. package/server/routes/metrics-routes.ts +13 -0
  150. package/server/routes/platform-routes.ts +1379 -0
  151. package/server/routes/prompt-routes.ts +67 -0
  152. package/server/routes/provider-routes.ts +109 -0
  153. package/server/routes/sandbox-routes.ts +127 -0
  154. package/server/routes/settings-routes.ts +285 -0
  155. package/server/routes/skills-routes.ts +100 -0
  156. package/server/routes/socialmedia-routes.ts +208 -0
  157. package/server/routes/system-routes.ts +228 -0
  158. package/server/routes/tailscale-routes.ts +22 -0
  159. package/server/routes/telephony-routes.ts +259 -0
  160. package/server/routes.ts +1379 -0
  161. package/server/sandbox-manager.ts +168 -0
  162. package/server/service.ts +718 -0
  163. package/server/session-creation-service.ts +457 -0
  164. package/server/session-git-info.ts +104 -0
  165. package/server/session-names.ts +67 -0
  166. package/server/session-orchestrator.ts +824 -0
  167. package/server/session-state-machine.ts +207 -0
  168. package/server/session-store.ts +146 -0
  169. package/server/session-types.ts +511 -0
  170. package/server/settings-manager.ts +149 -0
  171. package/server/shared-context.ts +157 -0
  172. package/server/socialmedia/adapter.ts +15 -0
  173. package/server/socialmedia/adapters/ayrshare-adapter.ts +169 -0
  174. package/server/socialmedia/adapters/buffer-adapter.ts +299 -0
  175. package/server/socialmedia/adapters/postiz-adapter.ts +298 -0
  176. package/server/socialmedia/manager.ts +227 -0
  177. package/server/socialmedia/store.ts +98 -0
  178. package/server/socialmedia/types.ts +89 -0
  179. package/server/tailscale-manager.ts +451 -0
  180. package/server/telephony/audio-bridge.ts +331 -0
  181. package/server/telephony/call-manager.ts +457 -0
  182. package/server/telephony/call-types.ts +108 -0
  183. package/server/telephony/telephony-store.ts +119 -0
  184. package/server/terminal-manager.ts +240 -0
  185. package/server/update-checker.ts +192 -0
  186. package/server/usage-limits.ts +225 -0
  187. package/server/web-push.d.ts +51 -0
  188. package/server/worktree-tracker.ts +84 -0
  189. package/server/ws-auth.ts +41 -0
  190. package/server/ws-bridge-browser-ingest.ts +72 -0
  191. package/server/ws-bridge-browser.ts +112 -0
  192. package/server/ws-bridge-cli-ingest.ts +81 -0
  193. package/server/ws-bridge-codex.ts +266 -0
  194. package/server/ws-bridge-controls.ts +20 -0
  195. package/server/ws-bridge-persist.ts +66 -0
  196. package/server/ws-bridge-publish.ts +79 -0
  197. package/server/ws-bridge-replay.ts +61 -0
  198. package/server/ws-bridge-types.ts +121 -0
  199. 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
+ }