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,511 @@
|
|
|
1
|
+
// Types for the WebSocket bridge between Claude Code CLI and the browser
|
|
2
|
+
|
|
3
|
+
import type { SessionPhase } from "./session-state-machine.js";
|
|
4
|
+
|
|
5
|
+
// ─── CLI Message Types (NDJSON from Claude Code CLI) ──────────────────────────
|
|
6
|
+
|
|
7
|
+
export interface CLISystemInitMessage {
|
|
8
|
+
type: "system";
|
|
9
|
+
subtype: "init";
|
|
10
|
+
cwd: string;
|
|
11
|
+
session_id: string;
|
|
12
|
+
tools: string[];
|
|
13
|
+
mcp_servers: { name: string; status: string }[];
|
|
14
|
+
model: string;
|
|
15
|
+
permissionMode: string;
|
|
16
|
+
apiKeySource: string;
|
|
17
|
+
claude_code_version: string;
|
|
18
|
+
slash_commands: string[];
|
|
19
|
+
agents?: string[];
|
|
20
|
+
skills?: string[];
|
|
21
|
+
output_style: string;
|
|
22
|
+
uuid: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CLISystemStatusMessage {
|
|
26
|
+
type: "system";
|
|
27
|
+
subtype: "status";
|
|
28
|
+
status: "compacting" | null;
|
|
29
|
+
permissionMode?: string;
|
|
30
|
+
uuid: string;
|
|
31
|
+
session_id: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CLICompactBoundaryMessage {
|
|
35
|
+
type: "system";
|
|
36
|
+
subtype: "compact_boundary";
|
|
37
|
+
compact_metadata: {
|
|
38
|
+
trigger: "manual" | "auto";
|
|
39
|
+
pre_tokens: number;
|
|
40
|
+
};
|
|
41
|
+
uuid: string;
|
|
42
|
+
session_id: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface CLITaskNotificationMessage {
|
|
46
|
+
type: "system";
|
|
47
|
+
subtype: "task_notification";
|
|
48
|
+
task_id: string;
|
|
49
|
+
status: "completed" | "failed" | "stopped";
|
|
50
|
+
output_file: string;
|
|
51
|
+
summary: string;
|
|
52
|
+
uuid: string;
|
|
53
|
+
session_id: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CLIFilesPersistedMessage {
|
|
57
|
+
type: "system";
|
|
58
|
+
subtype: "files_persisted";
|
|
59
|
+
files: { filename: string; file_id: string }[];
|
|
60
|
+
failed: { filename: string; error: string }[];
|
|
61
|
+
processed_at: string;
|
|
62
|
+
uuid: string;
|
|
63
|
+
session_id: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CLIHookStartedMessage {
|
|
67
|
+
type: "system";
|
|
68
|
+
subtype: "hook_started";
|
|
69
|
+
hook_id: string;
|
|
70
|
+
hook_name: string;
|
|
71
|
+
hook_event: string;
|
|
72
|
+
uuid: string;
|
|
73
|
+
session_id: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface CLIHookProgressMessage {
|
|
77
|
+
type: "system";
|
|
78
|
+
subtype: "hook_progress";
|
|
79
|
+
hook_id: string;
|
|
80
|
+
hook_name: string;
|
|
81
|
+
hook_event: string;
|
|
82
|
+
stdout: string;
|
|
83
|
+
stderr: string;
|
|
84
|
+
output: string;
|
|
85
|
+
uuid: string;
|
|
86
|
+
session_id: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface CLIHookResponseMessage {
|
|
90
|
+
type: "system";
|
|
91
|
+
subtype: "hook_response";
|
|
92
|
+
hook_id: string;
|
|
93
|
+
hook_name: string;
|
|
94
|
+
hook_event: string;
|
|
95
|
+
output: string;
|
|
96
|
+
stdout: string;
|
|
97
|
+
stderr: string;
|
|
98
|
+
exit_code?: number;
|
|
99
|
+
outcome: "success" | "error" | "cancelled";
|
|
100
|
+
uuid: string;
|
|
101
|
+
session_id: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type CLISystemMessage =
|
|
105
|
+
| CLISystemInitMessage
|
|
106
|
+
| CLISystemStatusMessage
|
|
107
|
+
| CLICompactBoundaryMessage
|
|
108
|
+
| CLITaskNotificationMessage
|
|
109
|
+
| CLIFilesPersistedMessage
|
|
110
|
+
| CLIHookStartedMessage
|
|
111
|
+
| CLIHookProgressMessage
|
|
112
|
+
| CLIHookResponseMessage;
|
|
113
|
+
|
|
114
|
+
export interface CLIAssistantMessage {
|
|
115
|
+
type: "assistant";
|
|
116
|
+
message: {
|
|
117
|
+
id: string;
|
|
118
|
+
type: "message";
|
|
119
|
+
role: "assistant";
|
|
120
|
+
model: string;
|
|
121
|
+
content: ContentBlock[];
|
|
122
|
+
stop_reason: string | null;
|
|
123
|
+
usage: {
|
|
124
|
+
input_tokens: number;
|
|
125
|
+
output_tokens: number;
|
|
126
|
+
cache_creation_input_tokens: number;
|
|
127
|
+
cache_read_input_tokens: number;
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
parent_tool_use_id: string | null;
|
|
131
|
+
error?: string;
|
|
132
|
+
uuid: string;
|
|
133
|
+
session_id: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface CLIResultMessage {
|
|
137
|
+
type: "result";
|
|
138
|
+
subtype: "success" | "error_during_execution" | "error_max_turns" | "error_max_budget_usd" | "error_max_structured_output_retries";
|
|
139
|
+
is_error: boolean;
|
|
140
|
+
result?: string;
|
|
141
|
+
errors?: string[];
|
|
142
|
+
duration_ms: number;
|
|
143
|
+
duration_api_ms: number;
|
|
144
|
+
num_turns: number;
|
|
145
|
+
total_cost_usd: number;
|
|
146
|
+
stop_reason: string | null;
|
|
147
|
+
usage: {
|
|
148
|
+
input_tokens: number;
|
|
149
|
+
output_tokens: number;
|
|
150
|
+
cache_creation_input_tokens: number;
|
|
151
|
+
cache_read_input_tokens: number;
|
|
152
|
+
};
|
|
153
|
+
modelUsage?: Record<string, {
|
|
154
|
+
inputTokens: number;
|
|
155
|
+
outputTokens: number;
|
|
156
|
+
cacheReadInputTokens: number;
|
|
157
|
+
cacheCreationInputTokens: number;
|
|
158
|
+
contextWindow: number;
|
|
159
|
+
maxOutputTokens: number;
|
|
160
|
+
costUSD: number;
|
|
161
|
+
}>;
|
|
162
|
+
total_lines_added?: number;
|
|
163
|
+
total_lines_removed?: number;
|
|
164
|
+
uuid: string;
|
|
165
|
+
session_id: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface CLIStreamEventMessage {
|
|
169
|
+
type: "stream_event";
|
|
170
|
+
event: unknown;
|
|
171
|
+
parent_tool_use_id: string | null;
|
|
172
|
+
uuid: string;
|
|
173
|
+
session_id: string;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface CLIToolProgressMessage {
|
|
177
|
+
type: "tool_progress";
|
|
178
|
+
tool_use_id: string;
|
|
179
|
+
tool_name: string;
|
|
180
|
+
parent_tool_use_id: string | null;
|
|
181
|
+
elapsed_time_seconds: number;
|
|
182
|
+
uuid: string;
|
|
183
|
+
session_id: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface CLIToolUseSummaryMessage {
|
|
187
|
+
type: "tool_use_summary";
|
|
188
|
+
summary: string;
|
|
189
|
+
preceding_tool_use_ids: string[];
|
|
190
|
+
uuid: string;
|
|
191
|
+
session_id: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface CLIControlRequestMessage {
|
|
195
|
+
type: "control_request";
|
|
196
|
+
request_id: string;
|
|
197
|
+
request: {
|
|
198
|
+
subtype: "can_use_tool";
|
|
199
|
+
tool_name: string;
|
|
200
|
+
input: Record<string, unknown>;
|
|
201
|
+
permission_suggestions?: PermissionUpdate[];
|
|
202
|
+
description?: string;
|
|
203
|
+
tool_use_id: string;
|
|
204
|
+
agent_id?: string;
|
|
205
|
+
title?: string;
|
|
206
|
+
display_name?: string;
|
|
207
|
+
blocked_path?: string;
|
|
208
|
+
decision_reason?: string;
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface CLIKeepAliveMessage {
|
|
213
|
+
type: "keep_alive";
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface CLIAuthStatusMessage {
|
|
217
|
+
type: "auth_status";
|
|
218
|
+
isAuthenticating: boolean;
|
|
219
|
+
output: string[];
|
|
220
|
+
error?: string;
|
|
221
|
+
uuid: string;
|
|
222
|
+
session_id: string;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface CLIControlResponseMessage {
|
|
226
|
+
type: "control_response";
|
|
227
|
+
response: {
|
|
228
|
+
subtype: "success" | "error";
|
|
229
|
+
request_id: string;
|
|
230
|
+
response?: Record<string, unknown>;
|
|
231
|
+
error?: string;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** CLI echoes user messages back (including subagent tool_result blocks). */
|
|
236
|
+
export interface CLIUserEchoMessage {
|
|
237
|
+
type: "user";
|
|
238
|
+
message: { role: string; content: unknown };
|
|
239
|
+
uuid?: string;
|
|
240
|
+
session_id?: string;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Rate-limit status from Claude API (allowed/throttled). */
|
|
244
|
+
export interface CLIRateLimitEventMessage {
|
|
245
|
+
type: "rate_limit_event";
|
|
246
|
+
rate_limit_info: Record<string, unknown>;
|
|
247
|
+
uuid?: string;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** CLI cancels a pending control_request (e.g. permission revoked). */
|
|
251
|
+
export interface CLIControlCancelRequestMessage {
|
|
252
|
+
type: "control_cancel_request";
|
|
253
|
+
request_id: string;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Simplified assistant text (streamlined output mode). @internal */
|
|
257
|
+
export interface CLIStreamlinedTextMessage {
|
|
258
|
+
type: "streamlined_text";
|
|
259
|
+
text: string;
|
|
260
|
+
session_id: string;
|
|
261
|
+
uuid: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Simplified tool use summary (e.g. "Read 2 files, wrote 1 file"). @internal */
|
|
265
|
+
export interface CLIStreamlinedToolUseSummaryMessage {
|
|
266
|
+
type: "streamlined_tool_use_summary";
|
|
267
|
+
tool_summary: string;
|
|
268
|
+
session_id: string;
|
|
269
|
+
uuid: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Predicted next user prompts (enabled via promptSuggestions in initialize). */
|
|
273
|
+
export interface CLIPromptSuggestionMessage {
|
|
274
|
+
type: "prompt_suggestion";
|
|
275
|
+
suggestions: string[];
|
|
276
|
+
session_id: string;
|
|
277
|
+
uuid: string;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export type CLIMessage =
|
|
281
|
+
| CLISystemMessage
|
|
282
|
+
| CLIAssistantMessage
|
|
283
|
+
| CLIResultMessage
|
|
284
|
+
| CLIStreamEventMessage
|
|
285
|
+
| CLIToolProgressMessage
|
|
286
|
+
| CLIToolUseSummaryMessage
|
|
287
|
+
| CLIControlRequestMessage
|
|
288
|
+
| CLIControlResponseMessage
|
|
289
|
+
| CLIKeepAliveMessage
|
|
290
|
+
| CLIAuthStatusMessage
|
|
291
|
+
| CLIUserEchoMessage
|
|
292
|
+
| CLIRateLimitEventMessage
|
|
293
|
+
| CLIControlCancelRequestMessage
|
|
294
|
+
| CLIStreamlinedTextMessage
|
|
295
|
+
| CLIStreamlinedToolUseSummaryMessage
|
|
296
|
+
| CLIPromptSuggestionMessage;
|
|
297
|
+
|
|
298
|
+
// ─── Content Block Types ──────────────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
export type ContentBlock =
|
|
301
|
+
| { type: "text"; text: string }
|
|
302
|
+
| { type: "tool_use"; id: string; name: string; input: Record<string, unknown> }
|
|
303
|
+
| { type: "tool_result"; tool_use_id: string; content: string | ContentBlock[]; is_error?: boolean }
|
|
304
|
+
| { type: "thinking"; thinking: string; budget_tokens?: number };
|
|
305
|
+
|
|
306
|
+
// ─── Browser Message Types (browser <-> bridge) ──────────────────────────────
|
|
307
|
+
|
|
308
|
+
/** Messages the browser sends to the bridge */
|
|
309
|
+
export type BrowserOutgoingMessage =
|
|
310
|
+
| { type: "user_message"; content: string; session_id?: string; images?: { media_type: string; data: string }[]; client_msg_id?: string }
|
|
311
|
+
| { type: "permission_response"; request_id: string; behavior: "allow" | "deny"; updated_input?: Record<string, unknown>; updated_permissions?: PermissionUpdate[]; message?: string; client_msg_id?: string }
|
|
312
|
+
| { type: "session_subscribe"; last_seq: number }
|
|
313
|
+
| { type: "session_ack"; last_seq: number }
|
|
314
|
+
| { type: "interrupt"; client_msg_id?: string }
|
|
315
|
+
| { type: "set_model"; model: string; client_msg_id?: string }
|
|
316
|
+
| { type: "set_permission_mode"; mode: string; client_msg_id?: string }
|
|
317
|
+
| { type: "mcp_get_status"; client_msg_id?: string }
|
|
318
|
+
| { type: "mcp_toggle"; serverName: string; enabled: boolean; client_msg_id?: string }
|
|
319
|
+
| { type: "mcp_reconnect"; serverName: string; client_msg_id?: string }
|
|
320
|
+
| { type: "mcp_set_servers"; servers: Record<string, McpServerConfig>; client_msg_id?: string }
|
|
321
|
+
| { type: "set_ai_validation"; aiValidationEnabled?: boolean | null; aiValidationAutoApprove?: boolean | null; aiValidationAutoDeny?: boolean | null; client_msg_id?: string }
|
|
322
|
+
| { type: "end_session"; reason?: string; client_msg_id?: string }
|
|
323
|
+
| { type: "stop_task"; task_id: string; client_msg_id?: string }
|
|
324
|
+
| { type: "update_environment_variables"; variables: Record<string, string>; client_msg_id?: string };
|
|
325
|
+
|
|
326
|
+
/** Messages the bridge sends to the browser */
|
|
327
|
+
export type BrowserIncomingMessageBase =
|
|
328
|
+
| { type: "session_init"; session: SessionState }
|
|
329
|
+
| { type: "session_update"; session: Partial<SessionState> }
|
|
330
|
+
| { type: "assistant"; message: CLIAssistantMessage["message"]; parent_tool_use_id: string | null; timestamp?: number }
|
|
331
|
+
| { type: "stream_event"; event: unknown; parent_tool_use_id: string | null }
|
|
332
|
+
| {
|
|
333
|
+
type: "system_event";
|
|
334
|
+
event:
|
|
335
|
+
| Pick<CLICompactBoundaryMessage, "subtype" | "compact_metadata" | "uuid" | "session_id">
|
|
336
|
+
| Pick<CLITaskNotificationMessage, "subtype" | "task_id" | "status" | "output_file" | "summary" | "uuid" | "session_id">
|
|
337
|
+
| Pick<CLIFilesPersistedMessage, "subtype" | "files" | "failed" | "processed_at" | "uuid" | "session_id">
|
|
338
|
+
| Pick<CLIHookStartedMessage, "subtype" | "hook_id" | "hook_name" | "hook_event" | "uuid" | "session_id">
|
|
339
|
+
| Pick<CLIHookProgressMessage, "subtype" | "hook_id" | "hook_name" | "hook_event" | "stdout" | "stderr" | "output" | "uuid" | "session_id">
|
|
340
|
+
| Pick<CLIHookResponseMessage, "subtype" | "hook_id" | "hook_name" | "hook_event" | "output" | "stdout" | "stderr" | "exit_code" | "outcome" | "uuid" | "session_id">;
|
|
341
|
+
timestamp?: number;
|
|
342
|
+
}
|
|
343
|
+
| { type: "result"; data: CLIResultMessage }
|
|
344
|
+
| { type: "permission_request"; request: PermissionRequest }
|
|
345
|
+
| { type: "permission_cancelled"; request_id: string }
|
|
346
|
+
| { type: "permission_auto_resolved"; request: PermissionRequest; behavior: "allow" | "deny"; reason: string }
|
|
347
|
+
| { type: "tool_progress"; tool_use_id: string; tool_name: string; elapsed_time_seconds: number }
|
|
348
|
+
| { type: "tool_use_summary"; summary: string; tool_use_ids: string[] }
|
|
349
|
+
| { type: "status_change"; status: "compacting" | "idle" | "running" | null }
|
|
350
|
+
| { type: "auth_status"; isAuthenticating: boolean; output: string[]; error?: string }
|
|
351
|
+
| { type: "error"; message: string }
|
|
352
|
+
| { type: "cli_disconnected" }
|
|
353
|
+
| { type: "cli_connected" }
|
|
354
|
+
| { type: "user_message"; content: string; timestamp: number; id?: string }
|
|
355
|
+
| { type: "message_history"; messages: BrowserIncomingMessage[] }
|
|
356
|
+
| { type: "event_replay"; events: BufferedBrowserEvent[] }
|
|
357
|
+
| { type: "session_name_update"; name: string }
|
|
358
|
+
| { type: "pr_status_update"; pr: import("./github-pr.js").GitHubPRInfo | null; available: boolean }
|
|
359
|
+
| { type: "mcp_status"; servers: McpServerDetail[] }
|
|
360
|
+
| { type: "session_phase"; phase: SessionPhase; previousPhase: SessionPhase }
|
|
361
|
+
| { type: "prompt_suggestion"; suggestions: string[] }
|
|
362
|
+
| { type: "streamlined_text"; text: string }
|
|
363
|
+
| { type: "streamlined_tool_use_summary"; tool_summary: string };
|
|
364
|
+
|
|
365
|
+
export type BrowserIncomingMessage = BrowserIncomingMessageBase & { seq?: number };
|
|
366
|
+
|
|
367
|
+
export type ReplayableBrowserIncomingMessage = Exclude<BrowserIncomingMessageBase, { type: "event_replay" }>;
|
|
368
|
+
|
|
369
|
+
export interface BufferedBrowserEvent {
|
|
370
|
+
seq: number;
|
|
371
|
+
message: ReplayableBrowserIncomingMessage;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ─── Session State ────────────────────────────────────────────────────────────
|
|
375
|
+
|
|
376
|
+
export type BackendType = "claude" | "codex" | "ollama" | "openrouter" | "gemini";
|
|
377
|
+
|
|
378
|
+
export interface SessionState {
|
|
379
|
+
session_id: string;
|
|
380
|
+
backend_type?: BackendType;
|
|
381
|
+
model: string;
|
|
382
|
+
cwd: string;
|
|
383
|
+
tools: string[];
|
|
384
|
+
permissionMode: string;
|
|
385
|
+
claude_code_version: string;
|
|
386
|
+
mcp_servers: { name: string; status: string }[];
|
|
387
|
+
agents: string[];
|
|
388
|
+
slash_commands: string[];
|
|
389
|
+
skills: string[];
|
|
390
|
+
total_cost_usd: number;
|
|
391
|
+
num_turns: number;
|
|
392
|
+
context_used_percent: number;
|
|
393
|
+
is_compacting: boolean;
|
|
394
|
+
git_branch: string;
|
|
395
|
+
is_worktree: boolean;
|
|
396
|
+
is_containerized: boolean;
|
|
397
|
+
repo_root: string;
|
|
398
|
+
git_ahead: number;
|
|
399
|
+
git_behind: number;
|
|
400
|
+
total_lines_added: number;
|
|
401
|
+
total_lines_removed: number;
|
|
402
|
+
// Codex-specific token details (forwarded from thread/tokenUsage/updated)
|
|
403
|
+
codex_token_details?: {
|
|
404
|
+
inputTokens: number;
|
|
405
|
+
outputTokens: number;
|
|
406
|
+
cachedInputTokens: number;
|
|
407
|
+
reasoningOutputTokens: number;
|
|
408
|
+
modelContextWindow: number;
|
|
409
|
+
};
|
|
410
|
+
// Codex-specific rate limits (forwarded from account/rateLimits/updated)
|
|
411
|
+
codex_rate_limits?: {
|
|
412
|
+
primary: { usedPercent: number; windowDurationMins: number; resetsAt: number } | null;
|
|
413
|
+
secondary: { usedPercent: number; windowDurationMins: number; resetsAt: number } | null;
|
|
414
|
+
};
|
|
415
|
+
/** If this session was spawned by a cron job */
|
|
416
|
+
cronJobId?: string;
|
|
417
|
+
/** Human-readable name of the cron job that spawned this session */
|
|
418
|
+
cronJobName?: string;
|
|
419
|
+
/** If this session was spawned by an agent */
|
|
420
|
+
agentId?: string;
|
|
421
|
+
/** Human-readable name of the agent that spawned this session */
|
|
422
|
+
agentName?: string;
|
|
423
|
+
/** Per-session AI validation override. null/undefined = use global default */
|
|
424
|
+
aiValidationEnabled?: boolean | null;
|
|
425
|
+
/** Per-session auto-approve override. null/undefined = use global default */
|
|
426
|
+
aiValidationAutoApprove?: boolean | null;
|
|
427
|
+
/** Per-session auto-deny override. null/undefined = use global default */
|
|
428
|
+
aiValidationAutoDeny?: boolean | null;
|
|
429
|
+
/** Federation: remote node ID if this session originates from a peer */
|
|
430
|
+
nodeId?: string;
|
|
431
|
+
/** Federation: remote node display name */
|
|
432
|
+
nodeName?: string;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ─── MCP Types ───────────────────────────────────────────────────────────────
|
|
436
|
+
|
|
437
|
+
export interface McpServerConfig {
|
|
438
|
+
type: "stdio" | "sse" | "http" | "sdk";
|
|
439
|
+
command?: string;
|
|
440
|
+
args?: string[];
|
|
441
|
+
env?: Record<string, string>;
|
|
442
|
+
url?: string;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export interface McpServerDetail {
|
|
446
|
+
name: string;
|
|
447
|
+
status: "connected" | "failed" | "disabled" | "connecting";
|
|
448
|
+
serverInfo?: unknown;
|
|
449
|
+
error?: string;
|
|
450
|
+
config: { type: string; url?: string; command?: string; args?: string[] };
|
|
451
|
+
scope: string;
|
|
452
|
+
tools?: { name: string; annotations?: { readOnly?: boolean; destructive?: boolean; openWorld?: boolean } }[];
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ─── Permission Request ──────────────────────────────────────────────────────
|
|
456
|
+
|
|
457
|
+
// ─── Permission Rule Types ───────────────────────────────────────────────────
|
|
458
|
+
|
|
459
|
+
export type PermissionDestination = "userSettings" | "projectSettings" | "localSettings" | "session" | "cliArg";
|
|
460
|
+
|
|
461
|
+
export type PermissionUpdate =
|
|
462
|
+
| { type: "addRules"; rules: { toolName: string; ruleContent?: string }[]; behavior: "allow" | "deny" | "ask"; destination: PermissionDestination }
|
|
463
|
+
| { type: "replaceRules"; rules: { toolName: string; ruleContent?: string }[]; behavior: "allow" | "deny" | "ask"; destination: PermissionDestination }
|
|
464
|
+
| { type: "removeRules"; rules: { toolName: string; ruleContent?: string }[]; behavior: "allow" | "deny" | "ask"; destination: PermissionDestination }
|
|
465
|
+
| { type: "setMode"; mode: string; destination: PermissionDestination }
|
|
466
|
+
| { type: "addDirectories"; directories: string[]; destination: PermissionDestination }
|
|
467
|
+
| { type: "removeDirectories"; directories: string[]; destination: PermissionDestination };
|
|
468
|
+
|
|
469
|
+
export interface AiValidationInfo {
|
|
470
|
+
verdict: "safe" | "dangerous" | "uncertain";
|
|
471
|
+
reason: string;
|
|
472
|
+
ruleBasedOnly: boolean;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export interface PermissionRequest {
|
|
476
|
+
request_id: string;
|
|
477
|
+
tool_name: string;
|
|
478
|
+
input: Record<string, unknown>;
|
|
479
|
+
permission_suggestions?: PermissionUpdate[];
|
|
480
|
+
description?: string;
|
|
481
|
+
tool_use_id: string;
|
|
482
|
+
agent_id?: string;
|
|
483
|
+
title?: string;
|
|
484
|
+
display_name?: string;
|
|
485
|
+
blocked_path?: string;
|
|
486
|
+
decision_reason?: string;
|
|
487
|
+
timestamp: number;
|
|
488
|
+
ai_validation?: AiValidationInfo;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// ─── Session Creation Progress (SSE streaming) ──────────────────────────────
|
|
492
|
+
|
|
493
|
+
export type CreationStepId =
|
|
494
|
+
| "resolving_env"
|
|
495
|
+
| "fetching_git"
|
|
496
|
+
| "checkout_branch"
|
|
497
|
+
| "pulling_git"
|
|
498
|
+
| "creating_worktree"
|
|
499
|
+
| "pulling_image"
|
|
500
|
+
| "building_image"
|
|
501
|
+
| "creating_container"
|
|
502
|
+
| "copying_workspace"
|
|
503
|
+
| "running_init_script"
|
|
504
|
+
| "launching_cli";
|
|
505
|
+
|
|
506
|
+
export interface CreationProgressEvent {
|
|
507
|
+
step: CreationStepId;
|
|
508
|
+
label: string;
|
|
509
|
+
status: "in_progress" | "done" | "error";
|
|
510
|
+
detail?: string;
|
|
511
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdirSync,
|
|
3
|
+
readFileSync,
|
|
4
|
+
writeFileSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
} from "node:fs";
|
|
7
|
+
import { join, dirname } from "node:path";
|
|
8
|
+
import { HEYHANK_HOME } from "./paths.js";
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-6";
|
|
11
|
+
|
|
12
|
+
export type UpdateChannel = "stable" | "prerelease";
|
|
13
|
+
|
|
14
|
+
export interface HeyHankSettings {
|
|
15
|
+
anthropicApiKey: string;
|
|
16
|
+
anthropicModel: string;
|
|
17
|
+
/** OAuth token obtained via `claude setup-token` — injected as CLAUDE_CODE_OAUTH_TOKEN */
|
|
18
|
+
claudeCodeOAuthToken: string;
|
|
19
|
+
/** OpenAI API key for Codex — injected as OPENAI_API_KEY */
|
|
20
|
+
openaiApiKey: string;
|
|
21
|
+
/** Whether the onboarding wizard has been completed */
|
|
22
|
+
onboardingCompleted: boolean;
|
|
23
|
+
/** Gemini API key for voice chat */
|
|
24
|
+
geminiApiKey: string;
|
|
25
|
+
/** Gemini Live voice name (e.g. Kore, Puck, Charon, Fenrir, Aoede, Leda, Orus, Zephyr) */
|
|
26
|
+
geminiVoice: string;
|
|
27
|
+
/** Custom name for the voice assistant (e.g. "Jarvis", "Friday") */
|
|
28
|
+
assistantName: string;
|
|
29
|
+
/** User's display name so the assistant knows who it's talking to */
|
|
30
|
+
userName: string;
|
|
31
|
+
editorTabEnabled: boolean;
|
|
32
|
+
/** Provider ID for internal AI features (auto-renaming, AI validation). Empty = auto-detect. */
|
|
33
|
+
internalAiProvider: string;
|
|
34
|
+
aiValidationEnabled: boolean;
|
|
35
|
+
aiValidationAutoApprove: boolean;
|
|
36
|
+
aiValidationAutoDeny: boolean;
|
|
37
|
+
publicUrl: string;
|
|
38
|
+
updateChannel: UpdateChannel;
|
|
39
|
+
dockerAutoUpdate: boolean;
|
|
40
|
+
updatedAt: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const DEFAULT_PATH = join(HEYHANK_HOME, "settings.json");
|
|
44
|
+
|
|
45
|
+
let loaded = false;
|
|
46
|
+
let filePath = DEFAULT_PATH;
|
|
47
|
+
let settings: HeyHankSettings = {
|
|
48
|
+
anthropicApiKey: "",
|
|
49
|
+
anthropicModel: DEFAULT_ANTHROPIC_MODEL,
|
|
50
|
+
claudeCodeOAuthToken: "",
|
|
51
|
+
openaiApiKey: "",
|
|
52
|
+
onboardingCompleted: false,
|
|
53
|
+
geminiApiKey: "",
|
|
54
|
+
geminiVoice: "Kore",
|
|
55
|
+
assistantName: "",
|
|
56
|
+
userName: "",
|
|
57
|
+
editorTabEnabled: false,
|
|
58
|
+
internalAiProvider: "",
|
|
59
|
+
aiValidationEnabled: false,
|
|
60
|
+
aiValidationAutoApprove: true,
|
|
61
|
+
aiValidationAutoDeny: false,
|
|
62
|
+
publicUrl: "",
|
|
63
|
+
updateChannel: "stable",
|
|
64
|
+
dockerAutoUpdate: false,
|
|
65
|
+
updatedAt: 0,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
function normalize(raw: Partial<HeyHankSettings> | null | undefined): HeyHankSettings {
|
|
69
|
+
return {
|
|
70
|
+
anthropicApiKey: typeof raw?.anthropicApiKey === "string" ? raw.anthropicApiKey : "",
|
|
71
|
+
anthropicModel:
|
|
72
|
+
typeof raw?.anthropicModel === "string" && raw.anthropicModel.trim()
|
|
73
|
+
? raw.anthropicModel === "claude-sonnet-4.6" ? DEFAULT_ANTHROPIC_MODEL : raw.anthropicModel
|
|
74
|
+
: DEFAULT_ANTHROPIC_MODEL,
|
|
75
|
+
claudeCodeOAuthToken: typeof raw?.claudeCodeOAuthToken === "string" ? raw.claudeCodeOAuthToken : "",
|
|
76
|
+
openaiApiKey: typeof raw?.openaiApiKey === "string" ? raw.openaiApiKey : "",
|
|
77
|
+
onboardingCompleted: typeof raw?.onboardingCompleted === "boolean" ? raw.onboardingCompleted : false,
|
|
78
|
+
geminiApiKey: typeof raw?.geminiApiKey === "string" ? raw.geminiApiKey : "",
|
|
79
|
+
geminiVoice: typeof raw?.geminiVoice === "string" && raw.geminiVoice.trim() ? raw.geminiVoice : "Kore",
|
|
80
|
+
assistantName: typeof raw?.assistantName === "string" ? raw.assistantName.trim() : "",
|
|
81
|
+
userName: typeof raw?.userName === "string" ? raw.userName.trim() : "",
|
|
82
|
+
editorTabEnabled: typeof raw?.editorTabEnabled === "boolean" ? raw.editorTabEnabled : false,
|
|
83
|
+
internalAiProvider: typeof raw?.internalAiProvider === "string" ? raw.internalAiProvider.trim() : "",
|
|
84
|
+
aiValidationEnabled: typeof raw?.aiValidationEnabled === "boolean" ? raw.aiValidationEnabled : false,
|
|
85
|
+
aiValidationAutoApprove: typeof raw?.aiValidationAutoApprove === "boolean" ? raw.aiValidationAutoApprove : true,
|
|
86
|
+
aiValidationAutoDeny: typeof raw?.aiValidationAutoDeny === "boolean" ? raw.aiValidationAutoDeny : false,
|
|
87
|
+
publicUrl: typeof raw?.publicUrl === "string" ? raw.publicUrl.trim().replace(/\/+$/, "") : "",
|
|
88
|
+
updateChannel: raw?.updateChannel === "prerelease" ? "prerelease" : "stable",
|
|
89
|
+
dockerAutoUpdate: typeof raw?.dockerAutoUpdate === "boolean" ? raw.dockerAutoUpdate : false,
|
|
90
|
+
updatedAt: typeof raw?.updatedAt === "number" ? raw.updatedAt : 0,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function ensureLoaded(): void {
|
|
95
|
+
if (loaded) return;
|
|
96
|
+
try {
|
|
97
|
+
if (existsSync(filePath)) {
|
|
98
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
99
|
+
settings = normalize(JSON.parse(raw) as Partial<HeyHankSettings>);
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
settings = normalize(null);
|
|
103
|
+
}
|
|
104
|
+
loaded = true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function persist(): void {
|
|
108
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
109
|
+
writeFileSync(filePath, JSON.stringify(settings, null, 2), { encoding: "utf-8", mode: 0o600 });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function getSettings(): HeyHankSettings {
|
|
113
|
+
ensureLoaded();
|
|
114
|
+
return { ...settings };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function updateSettings(
|
|
118
|
+
patch: Partial<Pick<HeyHankSettings, "anthropicApiKey" | "anthropicModel" | "claudeCodeOAuthToken" | "openaiApiKey" | "onboardingCompleted" | "geminiApiKey" | "geminiVoice" | "assistantName" | "userName" | "editorTabEnabled" | "internalAiProvider" | "aiValidationEnabled" | "aiValidationAutoApprove" | "aiValidationAutoDeny" | "publicUrl" | "updateChannel" | "dockerAutoUpdate">>,
|
|
119
|
+
): HeyHankSettings {
|
|
120
|
+
ensureLoaded();
|
|
121
|
+
settings = normalize({
|
|
122
|
+
anthropicApiKey: patch.anthropicApiKey ?? settings.anthropicApiKey,
|
|
123
|
+
anthropicModel: patch.anthropicModel ?? settings.anthropicModel,
|
|
124
|
+
claudeCodeOAuthToken: patch.claudeCodeOAuthToken ?? settings.claudeCodeOAuthToken,
|
|
125
|
+
openaiApiKey: patch.openaiApiKey ?? settings.openaiApiKey,
|
|
126
|
+
onboardingCompleted: patch.onboardingCompleted ?? settings.onboardingCompleted,
|
|
127
|
+
geminiApiKey: patch.geminiApiKey ?? settings.geminiApiKey,
|
|
128
|
+
geminiVoice: patch.geminiVoice ?? settings.geminiVoice,
|
|
129
|
+
assistantName: patch.assistantName ?? settings.assistantName,
|
|
130
|
+
userName: patch.userName ?? settings.userName,
|
|
131
|
+
editorTabEnabled: patch.editorTabEnabled ?? settings.editorTabEnabled,
|
|
132
|
+
internalAiProvider: patch.internalAiProvider ?? settings.internalAiProvider,
|
|
133
|
+
aiValidationEnabled: patch.aiValidationEnabled ?? settings.aiValidationEnabled,
|
|
134
|
+
aiValidationAutoApprove: patch.aiValidationAutoApprove ?? settings.aiValidationAutoApprove,
|
|
135
|
+
aiValidationAutoDeny: patch.aiValidationAutoDeny ?? settings.aiValidationAutoDeny,
|
|
136
|
+
publicUrl: patch.publicUrl ?? settings.publicUrl,
|
|
137
|
+
updateChannel: patch.updateChannel ?? settings.updateChannel,
|
|
138
|
+
dockerAutoUpdate: patch.dockerAutoUpdate ?? settings.dockerAutoUpdate,
|
|
139
|
+
updatedAt: Date.now(),
|
|
140
|
+
});
|
|
141
|
+
persist();
|
|
142
|
+
return { ...settings };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function _resetForTest(customPath?: string): void {
|
|
146
|
+
loaded = false;
|
|
147
|
+
filePath = customPath || DEFAULT_PATH;
|
|
148
|
+
settings = normalize(null);
|
|
149
|
+
}
|