@hellcoder/companion 0.111.0 → 0.111.1

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 (28) hide show
  1. package/dist/assets/{AgentsPage-DO5MiZyp.js → AgentsPage-Dc23pQBM.js} +1 -1
  2. package/dist/assets/{CronManager-BSNkRBGn.js → CronManager-CKvnO3df.js} +1 -1
  3. package/dist/assets/{DashboardPage-DqwmmyJ6.js → DashboardPage-_gU5oXmV.js} +1 -1
  4. package/dist/assets/{IntegrationsPage-CxavpMnn.js → IntegrationsPage-zBdnuEDO.js} +1 -1
  5. package/dist/assets/{LinearOAuthSettingsPage-Bs_FXqPA.js → LinearOAuthSettingsPage-v4LYNLyW.js} +1 -1
  6. package/dist/assets/{LinearSettingsPage-zerN3EmK.js → LinearSettingsPage-C-Gm_aeW.js} +1 -1
  7. package/dist/assets/{Playground-DNlOjOnC.js → Playground-BheM7iin.js} +1 -1
  8. package/dist/assets/{PromptsPage-D20LKwla.js → PromptsPage-Bt2idDR8.js} +1 -1
  9. package/dist/assets/{RunsPage-ho-uEj6h.js → RunsPage-ib31kftd.js} +1 -1
  10. package/dist/assets/{SandboxManager-BY2NPfsU.js → SandboxManager-Ch0-1iE8.js} +1 -1
  11. package/dist/assets/SettingsPage-fqy7b9b8.js +1 -0
  12. package/dist/assets/{TailscalePage-pv47ZX2f.js → TailscalePage-Cf9_ENdO.js} +1 -1
  13. package/dist/assets/{index-C7qt99R_.js → index-Baw3PrqK.js} +52 -52
  14. package/dist/assets/{sw-register-DZ08pZIJ.js → sw-register-D-9XveNW.js} +1 -1
  15. package/dist/index.html +1 -1
  16. package/dist/sw.js +1 -1
  17. package/package.json +1 -1
  18. package/server/auto-namer.test.ts +4 -0
  19. package/server/claude-adapter.test.ts +111 -6
  20. package/server/claude-adapter.ts +106 -0
  21. package/server/linear-connections.test.ts +4 -0
  22. package/server/routes/settings-routes.ts +10 -0
  23. package/server/routes.test.ts +27 -0
  24. package/server/session-orchestrator.test.ts +3 -0
  25. package/server/settings-manager.test.ts +3 -0
  26. package/server/settings-manager.ts +14 -1
  27. package/server/ws-bridge-codex.test.ts +6 -0
  28. package/dist/assets/SettingsPage-CvYCqkm1.js +0 -1
@@ -1159,6 +1159,7 @@ describe("GET /api/sessions/:id/archive-info", () => {
1159
1159
  dockerAutoUpdate: false,
1160
1160
  proactiveKeepaliveEnabled: true,
1161
1161
  wedgeKillEnabled: true,
1162
+ silenceProbeEnabled: true,
1162
1163
  updatedAt: 0,
1163
1164
  });
1164
1165
  const res = await app.request("/api/sessions/s1/archive-info", { method: "GET" });
@@ -1535,6 +1536,7 @@ describe("GET /api/settings", () => {
1535
1536
  dockerAutoUpdate: false,
1536
1537
  proactiveKeepaliveEnabled: true,
1537
1538
  wedgeKillEnabled: true,
1539
+ silenceProbeEnabled: true,
1538
1540
  updatedAt: 123,
1539
1541
  });
1540
1542
 
@@ -1569,6 +1571,7 @@ describe("GET /api/settings", () => {
1569
1571
  dockerAutoUpdate: false,
1570
1572
  proactiveKeepaliveEnabled: true,
1571
1573
  wedgeKillEnabled: true,
1574
+ silenceProbeEnabled: true,
1572
1575
  });
1573
1576
  });
1574
1577
 
@@ -1603,6 +1606,7 @@ describe("GET /api/settings", () => {
1603
1606
  dockerAutoUpdate: false,
1604
1607
  proactiveKeepaliveEnabled: true,
1605
1608
  wedgeKillEnabled: true,
1609
+ silenceProbeEnabled: true,
1606
1610
  updatedAt: 123,
1607
1611
  });
1608
1612
 
@@ -1637,6 +1641,7 @@ describe("GET /api/settings", () => {
1637
1641
  dockerAutoUpdate: false,
1638
1642
  proactiveKeepaliveEnabled: true,
1639
1643
  wedgeKillEnabled: true,
1644
+ silenceProbeEnabled: true,
1640
1645
  });
1641
1646
  });
1642
1647
 
@@ -1672,6 +1677,7 @@ describe("GET /api/settings", () => {
1672
1677
  dockerAutoUpdate: false,
1673
1678
  proactiveKeepaliveEnabled: true,
1674
1679
  wedgeKillEnabled: true,
1680
+ silenceProbeEnabled: true,
1675
1681
  updatedAt: 100,
1676
1682
  });
1677
1683
 
@@ -1715,6 +1721,7 @@ describe("PUT /api/settings", () => {
1715
1721
  dockerAutoUpdate: false,
1716
1722
  proactiveKeepaliveEnabled: true,
1717
1723
  wedgeKillEnabled: true,
1724
+ silenceProbeEnabled: true,
1718
1725
  updatedAt: 456,
1719
1726
  });
1720
1727
 
@@ -1771,6 +1778,7 @@ describe("PUT /api/settings", () => {
1771
1778
  dockerAutoUpdate: false,
1772
1779
  proactiveKeepaliveEnabled: true,
1773
1780
  wedgeKillEnabled: true,
1781
+ silenceProbeEnabled: true,
1774
1782
  });
1775
1783
  });
1776
1784
 
@@ -1805,6 +1813,7 @@ describe("PUT /api/settings", () => {
1805
1813
  dockerAutoUpdate: false,
1806
1814
  proactiveKeepaliveEnabled: true,
1807
1815
  wedgeKillEnabled: true,
1816
+ silenceProbeEnabled: true,
1808
1817
  updatedAt: 789,
1809
1818
  });
1810
1819
 
@@ -1856,6 +1865,7 @@ describe("PUT /api/settings", () => {
1856
1865
  dockerAutoUpdate: false,
1857
1866
  proactiveKeepaliveEnabled: true,
1858
1867
  wedgeKillEnabled: true,
1868
+ silenceProbeEnabled: true,
1859
1869
  updatedAt: 999,
1860
1870
  });
1861
1871
 
@@ -2007,6 +2017,7 @@ describe("PUT /api/settings", () => {
2007
2017
  dockerAutoUpdate: false,
2008
2018
  proactiveKeepaliveEnabled: true,
2009
2019
  wedgeKillEnabled: true,
2020
+ silenceProbeEnabled: true,
2010
2021
  updatedAt: 500,
2011
2022
  });
2012
2023
 
@@ -2264,6 +2275,7 @@ describe("GET /api/linear/issues", () => {
2264
2275
  dockerAutoUpdate: false,
2265
2276
  proactiveKeepaliveEnabled: true,
2266
2277
  wedgeKillEnabled: true,
2278
+ silenceProbeEnabled: true,
2267
2279
  updatedAt: 0,
2268
2280
  });
2269
2281
  vi.mocked(resolveApiKey).mockReturnValue(null);
@@ -2305,6 +2317,7 @@ describe("GET /api/linear/issues", () => {
2305
2317
  dockerAutoUpdate: false,
2306
2318
  proactiveKeepaliveEnabled: true,
2307
2319
  wedgeKillEnabled: true,
2320
+ silenceProbeEnabled: true,
2308
2321
  updatedAt: 0,
2309
2322
  });
2310
2323
 
@@ -2399,6 +2412,7 @@ describe("GET /api/linear/issues", () => {
2399
2412
  dockerAutoUpdate: false,
2400
2413
  proactiveKeepaliveEnabled: true,
2401
2414
  wedgeKillEnabled: true,
2415
+ silenceProbeEnabled: true,
2402
2416
  updatedAt: 0,
2403
2417
  });
2404
2418
 
@@ -2500,6 +2514,7 @@ describe("GET /api/linear/issues", () => {
2500
2514
  dockerAutoUpdate: false,
2501
2515
  proactiveKeepaliveEnabled: true,
2502
2516
  wedgeKillEnabled: true,
2517
+ silenceProbeEnabled: true,
2503
2518
  updatedAt: 0,
2504
2519
  });
2505
2520
 
@@ -2566,6 +2581,7 @@ describe("GET /api/linear/connection", () => {
2566
2581
  dockerAutoUpdate: false,
2567
2582
  proactiveKeepaliveEnabled: true,
2568
2583
  wedgeKillEnabled: true,
2584
+ silenceProbeEnabled: true,
2569
2585
  updatedAt: 0,
2570
2586
  });
2571
2587
  vi.mocked(resolveApiKey).mockReturnValue(null);
@@ -2607,6 +2623,7 @@ describe("GET /api/linear/connection", () => {
2607
2623
  dockerAutoUpdate: false,
2608
2624
  proactiveKeepaliveEnabled: true,
2609
2625
  wedgeKillEnabled: true,
2626
+ silenceProbeEnabled: true,
2610
2627
  updatedAt: 0,
2611
2628
  });
2612
2629
 
@@ -2670,6 +2687,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
2670
2687
  dockerAutoUpdate: false,
2671
2688
  proactiveKeepaliveEnabled: true,
2672
2689
  wedgeKillEnabled: true,
2690
+ silenceProbeEnabled: true,
2673
2691
  updatedAt: 0,
2674
2692
  });
2675
2693
 
@@ -2715,6 +2733,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
2715
2733
  dockerAutoUpdate: false,
2716
2734
  proactiveKeepaliveEnabled: true,
2717
2735
  wedgeKillEnabled: true,
2736
+ silenceProbeEnabled: true,
2718
2737
  updatedAt: 0,
2719
2738
  });
2720
2739
 
@@ -2759,6 +2778,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
2759
2778
  dockerAutoUpdate: false,
2760
2779
  proactiveKeepaliveEnabled: true,
2761
2780
  wedgeKillEnabled: true,
2781
+ silenceProbeEnabled: true,
2762
2782
  updatedAt: 0,
2763
2783
  });
2764
2784
  vi.mocked(resolveApiKey).mockReturnValue(null);
@@ -2805,6 +2825,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
2805
2825
  dockerAutoUpdate: false,
2806
2826
  proactiveKeepaliveEnabled: true,
2807
2827
  wedgeKillEnabled: true,
2828
+ silenceProbeEnabled: true,
2808
2829
  updatedAt: 0,
2809
2830
  });
2810
2831
 
@@ -2885,6 +2906,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
2885
2906
  dockerAutoUpdate: false,
2886
2907
  proactiveKeepaliveEnabled: true,
2887
2908
  wedgeKillEnabled: true,
2909
+ silenceProbeEnabled: true,
2888
2910
  updatedAt: 0,
2889
2911
  });
2890
2912
 
@@ -2944,6 +2966,7 @@ describe("GET /api/linear/projects", () => {
2944
2966
  dockerAutoUpdate: false,
2945
2967
  proactiveKeepaliveEnabled: true,
2946
2968
  wedgeKillEnabled: true,
2969
+ silenceProbeEnabled: true,
2947
2970
  updatedAt: 0,
2948
2971
  });
2949
2972
  vi.mocked(resolveApiKey).mockReturnValue(null);
@@ -2985,6 +3008,7 @@ describe("GET /api/linear/projects", () => {
2985
3008
  dockerAutoUpdate: false,
2986
3009
  proactiveKeepaliveEnabled: true,
2987
3010
  wedgeKillEnabled: true,
3011
+ silenceProbeEnabled: true,
2988
3012
  updatedAt: 0,
2989
3013
  });
2990
3014
 
@@ -3056,6 +3080,7 @@ describe("GET /api/linear/project-issues", () => {
3056
3080
  dockerAutoUpdate: false,
3057
3081
  proactiveKeepaliveEnabled: true,
3058
3082
  wedgeKillEnabled: true,
3083
+ silenceProbeEnabled: true,
3059
3084
  updatedAt: 0,
3060
3085
  });
3061
3086
  vi.mocked(resolveApiKey).mockReturnValue(null);
@@ -3097,6 +3122,7 @@ describe("GET /api/linear/project-issues", () => {
3097
3122
  dockerAutoUpdate: false,
3098
3123
  proactiveKeepaliveEnabled: true,
3099
3124
  wedgeKillEnabled: true,
3125
+ silenceProbeEnabled: true,
3100
3126
  updatedAt: 0,
3101
3127
  });
3102
3128
 
@@ -3183,6 +3209,7 @@ describe("GET /api/linear/project-issues", () => {
3183
3209
  dockerAutoUpdate: false,
3184
3210
  proactiveKeepaliveEnabled: true,
3185
3211
  wedgeKillEnabled: true,
3212
+ silenceProbeEnabled: true,
3186
3213
  updatedAt: 0,
3187
3214
  });
3188
3215
 
@@ -51,6 +51,7 @@ vi.mock("./settings-manager.js", () => ({
51
51
  onboardingCompleted: false,
52
52
  proactiveKeepaliveEnabled: true,
53
53
  wedgeKillEnabled: true,
54
+ silenceProbeEnabled: true,
54
55
  })),
55
56
  }));
56
57
 
@@ -249,6 +250,7 @@ describe("SessionOrchestrator", () => {
249
250
  onboardingCompleted: false,
250
251
  proactiveKeepaliveEnabled: true,
251
252
  wedgeKillEnabled: true,
253
+ silenceProbeEnabled: true,
252
254
  } as any);
253
255
  vi.mocked(containerManager.createContainer).mockReturnValue({
254
256
  containerId: "cid-1",
@@ -1834,6 +1836,7 @@ describe("SessionOrchestrator", () => {
1834
1836
  ...settingsManager.getSettings(),
1835
1837
  proactiveKeepaliveEnabled: false,
1836
1838
  wedgeKillEnabled: true,
1839
+ silenceProbeEnabled: true,
1837
1840
  });
1838
1841
  deps.launcher.getSession.mockReturnValue({
1839
1842
  archived: false,
@@ -54,6 +54,7 @@ describe("settings-manager", () => {
54
54
  dockerAutoUpdate: false,
55
55
  proactiveKeepaliveEnabled: true,
56
56
  wedgeKillEnabled: true,
57
+ silenceProbeEnabled: true,
57
58
  cliBridgeMode: "loopback",
58
59
  claudeBridgeMode: "none",
59
60
  claudeBridgeIngressUrl: "",
@@ -119,6 +120,7 @@ describe("settings-manager", () => {
119
120
  dockerAutoUpdate: false,
120
121
  proactiveKeepaliveEnabled: true,
121
122
  wedgeKillEnabled: true,
123
+ silenceProbeEnabled: true,
122
124
  cliBridgeMode: "loopback",
123
125
  claudeBridgeMode: "none",
124
126
  claudeBridgeIngressUrl: "",
@@ -210,6 +212,7 @@ describe("settings-manager", () => {
210
212
  dockerAutoUpdate: false,
211
213
  proactiveKeepaliveEnabled: true,
212
214
  wedgeKillEnabled: true,
215
+ silenceProbeEnabled: true,
213
216
  cliBridgeMode: "loopback",
214
217
  claudeBridgeMode: "none",
215
218
  claudeBridgeIngressUrl: "",
@@ -104,6 +104,16 @@ export interface CompanionSettings {
104
104
  * to exit on its own instead of being SIGTERMed.
105
105
  */
106
106
  wedgeKillEnabled: boolean;
107
+ /**
108
+ * When true (default), a CLI that goes silent while its stdout stays OPEN is
109
+ * probed with a control request and, if it does not answer, treated as
110
+ * disconnected so the session relaunches and replays its in-flight turn.
111
+ *
112
+ * This covers the case no stdout-EOF logic can see: the stream never closes,
113
+ * the process stays healthy, and the session would otherwise hang forever
114
+ * with no error surfaced. Disable to leave silent sessions alone.
115
+ */
116
+ silenceProbeEnabled: boolean;
107
117
  /** See CliBridgeMode. Defaults to "loopback". Optional in fixtures; normalize() applies the default. */
108
118
  cliBridgeMode?: CliBridgeMode;
109
119
  /** See ClaudeBridgeMode. Defaults to "none". Persists across companion restarts. */
@@ -149,6 +159,7 @@ let settings: CompanionSettings = {
149
159
  dockerAutoUpdate: false,
150
160
  proactiveKeepaliveEnabled: true,
151
161
  wedgeKillEnabled: true,
162
+ silenceProbeEnabled: true,
152
163
  cliBridgeMode: "loopback",
153
164
  claudeBridgeMode: "none",
154
165
  claudeBridgeIngressUrl: "",
@@ -201,6 +212,7 @@ function normalize(raw: Partial<CompanionSettings> | null | undefined): Companio
201
212
  dockerAutoUpdate: typeof raw?.dockerAutoUpdate === "boolean" ? raw.dockerAutoUpdate : false,
202
213
  proactiveKeepaliveEnabled: typeof raw?.proactiveKeepaliveEnabled === "boolean" ? raw.proactiveKeepaliveEnabled : true,
203
214
  wedgeKillEnabled: typeof raw?.wedgeKillEnabled === "boolean" ? raw.wedgeKillEnabled : true,
215
+ silenceProbeEnabled: typeof raw?.silenceProbeEnabled === "boolean" ? raw.silenceProbeEnabled : true,
204
216
  cliBridgeMode: raw?.cliBridgeMode === "jsonHandoff" ? "jsonHandoff" : "loopback",
205
217
  claudeBridgeMode: raw?.claudeBridgeMode === "patched" ? "patched" : "none",
206
218
  claudeBridgeIngressUrl: typeof raw?.claudeBridgeIngressUrl === "string" ? raw.claudeBridgeIngressUrl : "",
@@ -234,7 +246,7 @@ export function getSettings(): CompanionSettings {
234
246
  }
235
247
 
236
248
  export function updateSettings(
237
- patch: Partial<Pick<CompanionSettings, "anthropicApiKey" | "anthropicModel" | "claudeCodeOAuthToken" | "openaiApiKey" | "onboardingCompleted" | "linearApiKey" | "linearAutoTransition" | "linearAutoTransitionStateId" | "linearAutoTransitionStateName" | "linearArchiveTransition" | "linearArchiveTransitionStateId" | "linearArchiveTransitionStateName" | "linearOAuthClientId" | "linearOAuthClientSecret" | "linearOAuthWebhookSecret" | "linearOAuthAccessToken" | "linearOAuthRefreshToken" | "aiValidationEnabled" | "aiValidationAutoApprove" | "aiValidationAutoDeny" | "dashboardEnabled" | "dashboardModel" | "dashboardRunHour" | "dashboardMaxSessionsPerRun" | "publicUrl" | "updateChannel" | "dockerAutoUpdate" | "proactiveKeepaliveEnabled" | "wedgeKillEnabled" | "cliBridgeMode" | "claudeBridgeMode" | "claudeBridgeIngressUrl" | "claudeCompatBannerDismissedVersion">>,
249
+ patch: Partial<Pick<CompanionSettings, "anthropicApiKey" | "anthropicModel" | "claudeCodeOAuthToken" | "openaiApiKey" | "onboardingCompleted" | "linearApiKey" | "linearAutoTransition" | "linearAutoTransitionStateId" | "linearAutoTransitionStateName" | "linearArchiveTransition" | "linearArchiveTransitionStateId" | "linearArchiveTransitionStateName" | "linearOAuthClientId" | "linearOAuthClientSecret" | "linearOAuthWebhookSecret" | "linearOAuthAccessToken" | "linearOAuthRefreshToken" | "aiValidationEnabled" | "aiValidationAutoApprove" | "aiValidationAutoDeny" | "dashboardEnabled" | "dashboardModel" | "dashboardRunHour" | "dashboardMaxSessionsPerRun" | "publicUrl" | "updateChannel" | "dockerAutoUpdate" | "proactiveKeepaliveEnabled" | "wedgeKillEnabled" | "silenceProbeEnabled" | "cliBridgeMode" | "claudeBridgeMode" | "claudeBridgeIngressUrl" | "claudeCompatBannerDismissedVersion">>,
238
250
  ): CompanionSettings {
239
251
  ensureLoaded();
240
252
  settings = normalize({
@@ -267,6 +279,7 @@ export function updateSettings(
267
279
  dockerAutoUpdate: patch.dockerAutoUpdate ?? settings.dockerAutoUpdate,
268
280
  proactiveKeepaliveEnabled: patch.proactiveKeepaliveEnabled ?? settings.proactiveKeepaliveEnabled,
269
281
  wedgeKillEnabled: patch.wedgeKillEnabled ?? settings.wedgeKillEnabled,
282
+ silenceProbeEnabled: patch.silenceProbeEnabled ?? settings.silenceProbeEnabled,
270
283
  cliBridgeMode: patch.cliBridgeMode ?? settings.cliBridgeMode,
271
284
  claudeBridgeMode: patch.claudeBridgeMode ?? settings.claudeBridgeMode,
272
285
  claudeBridgeIngressUrl: patch.claudeBridgeIngressUrl ?? settings.claudeBridgeIngressUrl,
@@ -141,6 +141,7 @@ describe("attachCodexAdapterHandlers", () => {
141
141
  dockerAutoUpdate: false,
142
142
  proactiveKeepaliveEnabled: true,
143
143
  wedgeKillEnabled: true,
144
+ silenceProbeEnabled: true,
144
145
  updatedAt: 0,
145
146
  });
146
147
  });
@@ -1133,6 +1134,7 @@ describe("attachCodexAdapterHandlers", () => {
1133
1134
  dockerAutoUpdate: false,
1134
1135
  proactiveKeepaliveEnabled: true,
1135
1136
  wedgeKillEnabled: true,
1137
+ silenceProbeEnabled: true,
1136
1138
  updatedAt: 0,
1137
1139
  });
1138
1140
  }
@@ -1315,6 +1317,7 @@ describe("attachCodexAdapterHandlers", () => {
1315
1317
  dockerAutoUpdate: false,
1316
1318
  proactiveKeepaliveEnabled: true,
1317
1319
  wedgeKillEnabled: true,
1320
+ silenceProbeEnabled: true,
1318
1321
  updatedAt: 0,
1319
1322
  });
1320
1323
 
@@ -1367,6 +1370,7 @@ describe("attachCodexAdapterHandlers", () => {
1367
1370
  dockerAutoUpdate: false,
1368
1371
  proactiveKeepaliveEnabled: true,
1369
1372
  wedgeKillEnabled: true,
1373
+ silenceProbeEnabled: true,
1370
1374
  updatedAt: 0,
1371
1375
  });
1372
1376
 
@@ -1484,6 +1488,7 @@ describe("attachCodexAdapterHandlers", () => {
1484
1488
  dockerAutoUpdate: false,
1485
1489
  proactiveKeepaliveEnabled: true,
1486
1490
  wedgeKillEnabled: true,
1491
+ silenceProbeEnabled: true,
1487
1492
  updatedAt: 0,
1488
1493
  });
1489
1494
 
@@ -1620,6 +1625,7 @@ describe("attachCodexAdapterHandlers", () => {
1620
1625
  dockerAutoUpdate: false,
1621
1626
  proactiveKeepaliveEnabled: true,
1622
1627
  wedgeKillEnabled: true,
1628
+ silenceProbeEnabled: true,
1623
1629
  updatedAt: 0,
1624
1630
  });
1625
1631
 
@@ -1 +0,0 @@
1
- import{r as s,u as o,g as bt,a as n,j as e,n as ft,b as gt,s as vt}from"./index-C7qt99R_.js";const le=[{id:"general",label:"General"},{id:"webhooks",label:"Webhooks"},{id:"authentication",label:"Authentication"},{id:"notifications",label:"Notifications"},{id:"providers",label:"Providers"},{id:"anthropic",label:"Anthropic"},{id:"ai-validation",label:"AI Validation"},{id:"dashboard",label:"Dashboard"},{id:"updates",label:"Updates"},{id:"telemetry",label:"Telemetry"},{id:"environments",label:"Environments"}];function jt({embedded:de=!1}){const[p,ue]=s.useState(""),[xe,pe]=s.useState("claude-sonnet-4-6"),[d,me]=s.useState(!1),[w,Je]=s.useState(!0),[f,S]=s.useState(!1),[he,g]=s.useState(""),[be,v]=s.useState(!1),Xe=o(t=>t.darkMode),Ze=o(t=>t.toggleDarkMode),fe=o(t=>t.diffBase),et=o(t=>t.setDiffBase),tt=o(t=>t.notificationSound),st=o(t=>t.toggleNotificationSound),ge=o(t=>t.notificationDesktop),ve=o(t=>t.setNotificationDesktop),r=o(t=>t.updateInfo),P=o(t=>t.setUpdateInfo),at=o(t=>t.setUpdateOverlayActive),nt=typeof Notification<"u",[x,y]=s.useState("stable"),[C,V]=s.useState(!1),[A,O]=s.useState(!0),[E,D]=s.useState(!0),[ye,K]=s.useState("loopback"),[M,je]=s.useState(!1),[L,Ne]=s.useState(!1),[ke,j]=s.useState(""),[we,U]=s.useState(""),[Se,ct]=s.useState(bt()),[T,$]=s.useState(!1),[F,H]=s.useState(!0),[B,Q]=s.useState(!1),[_,G]=s.useState(!1),[Ce,W]=s.useState("claude-haiku-4-5"),[Ae,q]=s.useState(3),[Ee,Y]=s.useState(30),[I,z]=s.useState(""),[Ue,Te]=s.useState("general"),[ot,Ie]=s.useState(!1),[J,Re]=s.useState(!1),[N,m]=s.useState(null),[h,Pe]=s.useState(""),[X,Ve]=s.useState(!1),[b,Oe]=s.useState(""),[Z,De]=s.useState(!1),[ee,Ke]=s.useState(!1),[rt,te]=s.useState(!1),[Me,Le]=s.useState(""),[it,$e]=s.useState(!1),[lt,Fe]=s.useState(!1),[k,He]=s.useState(null),[se,Be]=s.useState(!1),[u,Qe]=s.useState(null),[R,dt]=s.useState(0),[ae,_e]=s.useState(!1),[ne,Ge]=s.useState(!1),[ut,We]=s.useState(!1),qe=s.useRef(null),ce=s.useRef({});s.useEffect(()=>{const t=qe.current;if(!t)return;const a=new IntersectionObserver(c=>{var ze;let i=null;for(const ie of c)ie.isIntersecting&&(!i||ie.boundingClientRect.top<i.boundingClientRect.top)&&(i=ie);(ze=i==null?void 0:i.target)!=null&&ze.id&&Te(i.target.id)},{root:t,rootMargin:"-10% 0px -70% 0px",threshold:0});for(const c of le){const i=ce.current[c.id];i&&a.observe(i)}return()=>a.disconnect()},[w]);const Ye=s.useCallback(t=>{Te(t);const a=ce.current[t];a&&a.scrollIntoView({behavior:"smooth",block:"start"})},[]);s.useEffect(()=>{n.getSettings().then(t=>{me(t.anthropicApiKeyConfigured),Ve(t.claudeCodeOAuthTokenConfigured),De(t.openaiApiKeyConfigured),pe(t.anthropicModel||"claude-sonnet-4-6"),typeof t.aiValidationEnabled=="boolean"&&$(t.aiValidationEnabled),typeof t.aiValidationAutoApprove=="boolean"&&H(t.aiValidationAutoApprove),typeof t.aiValidationAutoDeny=="boolean"&&Q(t.aiValidationAutoDeny),typeof t.dashboardEnabled=="boolean"&&G(t.dashboardEnabled),typeof t.dashboardModel=="string"&&t.dashboardModel&&W(t.dashboardModel),typeof t.dashboardRunHour=="number"&&q(t.dashboardRunHour),typeof t.dashboardMaxSessionsPerRun=="number"&&Y(t.dashboardMaxSessionsPerRun),(t.updateChannel==="stable"||t.updateChannel==="prerelease")&&y(t.updateChannel),typeof t.dockerAutoUpdate=="boolean"&&V(t.dockerAutoUpdate),typeof t.proactiveKeepaliveEnabled=="boolean"&&O(t.proactiveKeepaliveEnabled),typeof t.wedgeKillEnabled=="boolean"&&D(t.wedgeKillEnabled),(t.cliBridgeMode==="loopback"||t.cliBridgeMode==="jsonHandoff")&&K(t.cliBridgeMode),typeof t.publicUrl=="string"&&(z(t.publicUrl),o.getState().setPublicUrl(t.publicUrl))}).catch(t=>g(t instanceof Error?t.message:String(t))).finally(()=>Je(!1)),n.getAuthToken().then(t=>He(t.token)).catch(()=>{})},[]);async function xt(t){t.preventDefault(),S(!0),g(""),v(!1);try{const a=p.trim(),c={anthropicModel:xe.trim()||"claude-sonnet-4-6"};a&&(c.anthropicApiKey=a);const i=await n.updateSettings(c);me(i.anthropicApiKeyConfigured),ue(""),v(!0),setTimeout(()=>v(!1),1800)}catch(a){g(a instanceof Error?a.message:String(a))}finally{S(!1)}}async function oe(t){const a=t==="aiValidationEnabled"?T:t==="aiValidationAutoApprove"?F:B,c=!a;t==="aiValidationEnabled"?$(c):t==="aiValidationAutoApprove"?H(c):Q(c);try{await n.updateSettings({[t]:c})}catch{t==="aiValidationEnabled"?$(a):t==="aiValidationAutoApprove"?H(a):Q(a)}}async function pt(){const t=_;G(!t);try{await n.updateSettings({dashboardEnabled:!t})}catch{G(t)}}async function re(t,a){try{await n.updateSettings(t)}catch{a()}}async function mt(){je(!0),j(""),U("");try{const t=await n.forceCheckForUpdate();P(t),t.updateAvailable&&t.latestVersion?j(`Update v${t.latestVersion} is available.`):j("You are up to date.")}catch(t){U(t instanceof Error?t.message:String(t))}finally{je(!1)}}async function ht(){Ne(!0),j(""),U("");try{localStorage.setItem("companion_docker_prompt_pending","1");const t=await n.triggerUpdate();j(t.message),at(!0)}catch(t){localStorage.removeItem("companion_docker_prompt_pending"),U(t instanceof Error?t.message:String(t)),Ne(!1)}}const l=s.useCallback(t=>a=>{ce.current[t]=a},[]);return e.jsxs("div",{className:`${de?"h-full":"h-[100dvh]"} bg-cc-bg text-cc-fg font-sans-ui antialiased flex flex-col`,children:[e.jsx("div",{className:"shrink-0 max-w-5xl w-full mx-auto px-4 sm:px-8 pt-6 sm:pt-10",children:e.jsxs("div",{className:"flex items-start justify-between gap-3 mb-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-xl font-semibold text-cc-fg",children:"Settings"}),e.jsx("p",{className:"mt-1 text-sm text-cc-muted",children:"Configure API access, notifications, appearance, and workspace defaults."})]}),!de&&e.jsx("button",{onClick:()=>{const t=o.getState().currentSessionId;t?ft(t):gt()},className:"px-3 py-2.5 min-h-[44px] rounded-lg text-sm text-cc-muted hover:text-cc-fg hover:bg-cc-hover transition-colors cursor-pointer",children:"Back"})]})}),e.jsx("div",{className:"sm:hidden shrink-0 border-b border-cc-border",children:e.jsx("nav",{className:"flex gap-1 px-4 py-2 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden","aria-label":"Settings categories",children:le.map(t=>e.jsx("button",{type:"button",onClick:()=>Ye(t.id),className:`shrink-0 px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors cursor-pointer ${Ue===t.id?"text-cc-primary bg-cc-primary/8":"text-cc-muted hover:text-cc-fg hover:bg-cc-hover"}`,children:t.label},t.id))})}),e.jsxs("div",{className:"flex-1 min-h-0 flex max-w-5xl w-full mx-auto",children:[e.jsx("nav",{className:"hidden sm:flex flex-col gap-0.5 w-44 shrink-0 pt-2 pr-6 pl-8 sticky top-0 self-start","aria-label":"Settings categories",children:le.map(t=>e.jsx("button",{type:"button",onClick:()=>Ye(t.id),className:`text-left px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors cursor-pointer ${Ue===t.id?"text-cc-primary bg-cc-primary/8":"text-cc-muted hover:text-cc-fg hover:bg-cc-hover"}`,children:t.label},t.id))}),e.jsx("div",{ref:qe,className:"flex-1 min-w-0 overflow-y-auto px-4 sm:px-8 sm:pl-0 pb-safe",children:e.jsxs("div",{className:"space-y-10 py-4 sm:py-2",children:[e.jsxs("section",{id:"general",ref:l("general"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"General"}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("button",{type:"button",onClick:Ze,className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg text-sm bg-cc-hover text-cc-fg hover:bg-cc-active transition-colors cursor-pointer",children:[e.jsx("span",{children:"Theme"}),e.jsx("span",{className:"text-xs text-cc-muted",children:Xe?"Dark":"Light"})]}),e.jsxs("button",{type:"button",onClick:()=>et(fe==="last-commit"?"default-branch":"last-commit"),className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg text-sm bg-cc-hover text-cc-fg hover:bg-cc-active transition-colors cursor-pointer",children:[e.jsx("span",{children:"Diff compare against"}),e.jsx("span",{className:"text-xs text-cc-muted",children:fe==="last-commit"?"Last commit (HEAD)":"Default branch"})]}),e.jsx("p",{className:"text-xs text-cc-muted px-1",children:"Last commit shows only uncommitted changes. Default branch shows all changes since diverging from main."}),e.jsx("div",{className:"pt-3 border-t border-cc-border",children:e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("span",{className:"block text-sm font-medium",children:"Claude Code bridge mode"}),e.jsx("p",{className:"mt-0.5 text-xs text-cc-muted",children:'How the Companion hands the bridge URL to the spawned Claude Code CLI. Loopback is the default fix for Claude Code v1.2.1+ which rejects the literal "localhost". JSON handoff is the more robust just-every/code-style approach: writes a temp descriptor with a one-shot token and passes its path via CLAUDE_BRIDGE_CONFIG.'})]}),e.jsxs("select",{"aria-label":"CLI bridge mode",value:ye,onChange:async t=>{const a=t.target.value==="jsonHandoff"?"jsonHandoff":"loopback",c=ye;K(a);try{await n.updateSettings({cliBridgeMode:a})}catch{K(c)}},className:"ml-3 px-2 py-1.5 text-xs bg-cc-bg rounded-lg border border-cc-border text-cc-fg focus:outline-none focus:ring-1 focus:ring-cc-primary",children:[e.jsx("option",{value:"loopback",children:"Loopback (default)"}),e.jsx("option",{value:"jsonHandoff",children:"JSON handoff (experimental)"})]})]})}),e.jsxs("div",{className:"pt-3 border-t border-cc-border",children:[e.jsxs("button",{type:"button",role:"switch","aria-checked":A,"aria-label":"Proactive keepalive relaunch",onClick:async()=>{const t=!A,a=A;O(t);try{await n.updateSettings({proactiveKeepaliveEnabled:t})}catch{O(a)}},className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg text-sm bg-cc-hover text-cc-fg hover:bg-cc-active transition-colors cursor-pointer",children:[e.jsx("span",{children:"Proactive keepalive relaunch"}),e.jsx("span",{className:"text-xs text-cc-muted",children:A?"On":"Off"})]}),e.jsx("p",{className:"mt-1 text-xs text-cc-muted px-1",children:"When on (default), a CLI process that exits unexpectedly with no browser attached is automatically relaunched to keep long-running sessions (agents, cron) alive. Turn off to experiment with letting dead sessions stay dead."})]}),e.jsxs("div",{className:"pt-3 border-t border-cc-border",children:[e.jsxs("button",{type:"button",role:"switch","aria-checked":E,"aria-label":"Kill wedged CLI processes",onClick:async()=>{const t=!E,a=E;D(t);try{await n.updateSettings({wedgeKillEnabled:t})}catch{D(a)}},className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg text-sm bg-cc-hover text-cc-fg hover:bg-cc-active transition-colors cursor-pointer",children:[e.jsx("span",{children:"Kill wedged CLI processes"}),e.jsx("span",{className:"text-xs text-cc-muted",children:E?"On":"Off"})]}),e.jsx("p",{className:"mt-1 text-xs text-cc-muted px-1",children:"When on (default), a CLI whose output stream closes but which does not exit is treated as wedged and killed so the session can recover. This was built for older CLI behaviour, and every process sampled since has turned out to be healthy — usually one supervising a background command. Turn off to test whether the wedge still happens at all: sessions still relaunch, but processes are left to exit on their own instead of being terminated."})]})]})]}),e.jsxs("section",{id:"webhooks",ref:l("webhooks"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Webhooks"}),e.jsxs("div",{className:"space-y-4",children:[e.jsx("p",{className:"text-xs text-cc-muted",children:"The public URL is used for webhook URLs that external services (Linear, GitHub) send events to. Set this to the externally-reachable address of your Companion instance."}),e.jsxs("p",{className:"text-xs text-cc-muted",children:["Tip:"," ",e.jsx("a",{href:"#/integrations/tailscale",className:"text-cc-primary hover:underline",children:"Use the Tailscale integration"})," ","to get an HTTPS URL automatically."]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-medium text-cc-fg mb-1.5",htmlFor:"public-url",children:"Public URL"}),e.jsx("input",{id:"public-url",type:"url","aria-label":"Public URL",value:I,onChange:t=>z(t.target.value),placeholder:"https://your-domain.example.com",className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg border border-cc-border text-cc-fg placeholder:text-cc-muted focus:outline-none focus:ring-1 focus:ring-cc-primary font-mono-code"}),e.jsx("p",{className:"mt-1.5 text-[10px] text-cc-muted",children:I?`Using: ${I}`:`Fallback: ${typeof window<"u"?window.location.origin:"http://localhost:3456"}`})]}),e.jsx("button",{type:"button",onClick:async()=>{S(!0),g("");try{const t=await n.updateSettings({publicUrl:I.trim()});z(t.publicUrl),o.getState().setPublicUrl(t.publicUrl),v(!0),setTimeout(()=>v(!1),1800)}catch(t){g(t instanceof Error?t.message:String(t))}finally{S(!1)}},disabled:f,className:"px-4 py-2 min-h-[44px] rounded-lg text-sm font-medium bg-cc-primary text-white hover:opacity-90 transition-opacity disabled:opacity-50 cursor-pointer",children:f?"Saving...":be?"Saved!":"Save Public URL"})]})]}),e.jsxs("section",{id:"authentication",ref:l("authentication"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Authentication"}),e.jsxs("div",{className:"space-y-4",children:[e.jsx("p",{className:"text-xs text-cc-muted",children:"Use the auth token or QR code to connect additional devices (e.g. mobile over Tailscale)."}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1.5",children:"Auth Token"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"flex-1 px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg font-mono-code select-all break-all flex items-center",children:k?se?k:"••••••••••••••••":e.jsx("span",{className:"text-cc-muted",children:"Loading..."})}),e.jsx("button",{type:"button",onClick:()=>Be(t=>!t),className:"px-3 py-2.5 min-h-[44px] rounded-lg text-sm bg-cc-hover hover:bg-cc-active text-cc-fg transition-colors cursor-pointer",title:se?"Hide token":"Show token",children:se?"Hide":"Show"}),e.jsx("button",{type:"button",onClick:()=>{k&&navigator.clipboard.writeText(k).then(()=>{We(!0),setTimeout(()=>We(!1),1500)})},disabled:!k,className:"px-3 py-2.5 min-h-[44px] rounded-lg text-sm bg-cc-hover hover:bg-cc-active text-cc-fg transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed",title:"Copy token to clipboard",children:ut?"Copied":"Copy"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1.5",children:"Mobile Login QR"}),u&&u.length>0?e.jsxs("div",{className:"space-y-3",children:[u.length>1&&e.jsx("div",{className:"flex gap-1",children:u.map((t,a)=>e.jsx("button",{type:"button",onClick:()=>dt(a),className:`px-3 py-1.5 rounded-md text-xs font-medium transition-colors cursor-pointer ${a===R?"bg-cc-primary text-white":"bg-cc-hover text-cc-muted hover:text-cc-fg"}`,children:t.label},t.label))}),e.jsx("div",{className:"inline-block rounded-lg bg-white p-2",children:e.jsx("img",{src:u[R].qrDataUrl,alt:`QR code for ${u[R].label} login`,className:"w-48 h-48"})}),e.jsx("div",{className:"px-3 py-2 rounded-lg bg-cc-bg text-sm font-mono-code text-cc-fg break-all select-all",children:u[R].url}),e.jsx("p",{className:"text-xs text-cc-muted",children:"Scan with your phone's camera app — it will open the URL and auto-authenticate."})]}):u&&u.length===0?e.jsx("p",{className:"text-xs text-cc-muted",children:"No remote addresses detected (LAN or Tailscale). Connect to a network to generate a QR code."}):e.jsx("button",{type:"button",onClick:async()=>{_e(!0);try{const t=await n.getAuthQr();Qe(t.qrCodes)}catch{}finally{_e(!1)}},disabled:ae,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${ae?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-hover hover:bg-cc-active text-cc-fg cursor-pointer"}`,children:ae?"Generating...":"Show QR Code"})]}),e.jsxs("div",{className:"pt-2",children:[e.jsx("button",{type:"button",onClick:async()=>{if(confirm("Regenerate auth token? All existing sessions on other devices will be signed out.")){Ge(!0);try{const t=await n.regenerateAuthToken();He(t.token),Be(!0),Qe(null)}catch{}finally{Ge(!1)}}},disabled:ne,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${ne?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-error/10 hover:bg-cc-error/20 text-cc-error cursor-pointer"}`,children:ne?"Regenerating...":"Regenerate Token"}),e.jsx("p",{className:"mt-1.5 text-xs text-cc-muted",children:"Creates a new token. All other signed-in devices will need to re-authenticate."})]})]})]}),e.jsxs("section",{id:"notifications",ref:l("notifications"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Notifications"}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("button",{type:"button",onClick:st,className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg text-sm bg-cc-hover text-cc-fg hover:bg-cc-active transition-colors cursor-pointer",children:[e.jsx("span",{children:"Sound"}),e.jsx("span",{className:"text-xs text-cc-muted",children:tt?"On":"Off"})]}),nt&&e.jsxs("button",{type:"button",onClick:async()=>{if(ge)ve(!1);else{if(Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted")return;ve(!0)}},className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg text-sm bg-cc-hover text-cc-fg hover:bg-cc-active transition-colors cursor-pointer",children:[e.jsx("span",{children:"Desktop Alerts"}),e.jsx("span",{className:"text-xs text-cc-muted",children:ge?"On":"Off"})]})]})]}),e.jsxs("section",{id:"providers",ref:l("providers"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Providers"}),e.jsxs("div",{className:"space-y-6",children:[e.jsx("p",{className:"text-xs text-cc-muted",children:"Configure authentication tokens for Claude Code and Codex. These are injected into sessions automatically."}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"block text-sm font-medium",htmlFor:"claude-code-token",children:"Claude Code OAuth Token"}),e.jsxs("p",{className:"text-xs text-cc-muted",children:["Run ",e.jsx("code",{className:"font-mono-code bg-cc-code-bg px-1 py-0.5 rounded text-cc-code-fg",children:"claude setup-token"})," in your terminal, then paste the token here."]}),e.jsx("input",{id:"claude-code-token",type:"password",value:X&&!it&&!h?"••••••••••••••••":h,onChange:t=>Pe(t.target.value),onFocus:()=>$e(!0),onBlur:()=>$e(!1),placeholder:X?"Enter a new token to replace":"Paste token from claude setup-token",className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg placeholder:text-cc-muted focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow"}),e.jsx("p",{className:"text-xs text-cc-muted",children:X?"Claude Code token configured":"Claude Code token not configured"})]}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("label",{className:"block text-sm font-medium",htmlFor:"openai-api-key",children:"OpenAI API Key (Codex)"}),e.jsxs("p",{className:"text-xs text-cc-muted",children:["Used to authenticate Codex sessions. You can also use ",e.jsx("code",{className:"font-mono-code bg-cc-code-bg px-1 py-0.5 rounded text-cc-code-fg",children:"codex --login"})," for device-based auth."]}),e.jsx("input",{id:"openai-api-key",type:"password",value:Z&&!lt&&!b?"••••••••••••••••":b,onChange:t=>Oe(t.target.value),onFocus:()=>Fe(!0),onBlur:()=>Fe(!1),placeholder:Z?"Enter a new key to replace":"sk-...",className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg placeholder:text-cc-muted focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow"}),e.jsx("p",{className:"text-xs text-cc-muted",children:Z?"OpenAI key configured":"OpenAI key not configured"})]}),Me&&e.jsx("div",{className:"px-3 py-2 rounded-lg bg-cc-error/10 border border-cc-error/20 text-xs text-cc-error",children:Me}),rt&&e.jsx("div",{className:"px-3 py-2 rounded-lg bg-cc-success/10 border border-cc-success/20 text-xs text-cc-success",children:"Provider settings saved."}),e.jsx("button",{type:"button",disabled:ee||!h.trim()&&!b.trim(),onClick:async()=>{Ke(!0),Le(""),te(!1);try{const t={};h.trim()&&(t.claudeCodeOAuthToken=h.trim()),b.trim()&&(t.openaiApiKey=b.trim());const a=await n.updateSettings(t);Ve(a.claudeCodeOAuthTokenConfigured),De(a.openaiApiKeyConfigured),Pe(""),Oe(""),te(!0),setTimeout(()=>te(!1),1800)}catch(t){Le(t instanceof Error?t.message:String(t))}finally{Ke(!1)}},className:`px-4 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${ee||!h.trim()&&!b.trim()?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-primary hover:bg-cc-primary-hover text-white cursor-pointer"}`,children:ee?"Saving...":"Save Provider Settings"})]})]}),e.jsxs("section",{id:"anthropic",ref:l("anthropic"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Anthropic"}),e.jsxs("form",{onSubmit:xt,className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1.5",htmlFor:"anthropic-key",children:"Anthropic API Key"}),e.jsx("input",{id:"anthropic-key",type:"password",value:d&&!ot&&!p?"••••••••••••••••":p,onChange:t=>{ue(t.target.value),m(null)},onFocus:()=>Ie(!0),onBlur:()=>Ie(!1),placeholder:d?"Enter a new key to replace":"sk-ant-api03-...",className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg placeholder:text-cc-muted focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow"}),e.jsx("p",{className:"mt-1.5 text-xs text-cc-muted",children:"Auto-renaming is disabled until this key is configured."})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1.5",htmlFor:"anthropic-model",children:"Anthropic Model"}),e.jsx("input",{id:"anthropic-model",type:"text",value:xe,onChange:t=>pe(t.target.value),placeholder:"claude-sonnet-4-6",className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg placeholder:text-cc-muted focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow"})]}),he&&e.jsx("div",{className:"px-3 py-2 rounded-lg bg-cc-error/10 border border-cc-error/20 text-xs text-cc-error",children:he}),be&&e.jsx("div",{className:"px-3 py-2 rounded-lg bg-cc-success/10 border border-cc-success/20 text-xs text-cc-success",children:"Settings saved."}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-xs text-cc-muted",children:w?"Loading...":d?"Anthropic key configured":"Anthropic key not configured"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{type:"button",disabled:J||!p.trim(),onClick:async()=>{Re(!0),m(null);try{const t=await n.verifyAnthropicKey(p.trim());m(t),setTimeout(()=>m(null),5e3)}catch(t){m({valid:!1,error:t instanceof Error?t.message:String(t)}),setTimeout(()=>m(null),5e3)}finally{Re(!1)}},className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${J||!p.trim()?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-hover hover:bg-cc-active text-cc-fg cursor-pointer"}`,children:J?"Verifying...":"Verify"}),e.jsx("button",{type:"submit",disabled:f||w,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${f||w?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-primary hover:bg-cc-primary-hover text-white cursor-pointer"}`,children:f?"Saving...":"Save"})]})]}),N&&e.jsx("div",{className:`px-3 py-2 rounded-lg text-xs ${N.valid?"bg-cc-success/10 border border-cc-success/20 text-cc-success":"bg-cc-error/10 border border-cc-error/20 text-cc-error"}`,children:N.valid?"API key is valid.":`Invalid API key${N.error?`: ${N.error}`:"."}`})]})]}),e.jsxs("section",{id:"ai-validation",ref:l("ai-validation"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"AI Validation"}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{className:"text-xs text-cc-muted leading-relaxed",children:"When enabled, an AI model evaluates tool calls before they execute. Safe operations are auto-approved, dangerous ones are blocked, and uncertain cases are shown to you with a recommendation. Requires an Anthropic API key. These settings serve as defaults for new sessions. Each session can override AI validation independently via the shield icon in the session header."}),e.jsxs("button",{type:"button",onClick:()=>oe("aiValidationEnabled"),disabled:!d,className:`w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg transition-colors ${d?"bg-cc-hover hover:bg-cc-active text-cc-fg cursor-pointer":"bg-cc-hover text-cc-muted cursor-not-allowed opacity-60"}`,children:[e.jsx("span",{className:"text-sm",children:"AI Validation Mode"}),e.jsx("span",{className:`text-xs font-medium ${T&&d?"text-cc-success":"text-cc-muted"}`,children:T&&d?"On":"Off"})]}),!d&&e.jsx("p",{className:"text-[11px] text-cc-warning",children:"Configure an Anthropic API key above to enable AI validation."}),T&&d&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{type:"button",onClick:()=>oe("aiValidationAutoApprove"),className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg bg-cc-hover hover:bg-cc-active text-cc-fg transition-colors cursor-pointer",children:[e.jsxs("div",{children:[e.jsx("span",{className:"text-sm",children:"Auto-approve safe tools"}),e.jsx("p",{className:"text-[11px] text-cc-muted mt-0.5",children:"Automatically allow read-only tools and benign commands"})]}),e.jsx("span",{className:`text-xs font-medium ${F?"text-cc-success":"text-cc-muted"}`,children:F?"On":"Off"})]}),e.jsxs("button",{type:"button",onClick:()=>oe("aiValidationAutoDeny"),className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg bg-cc-hover hover:bg-cc-active text-cc-fg transition-colors cursor-pointer",children:[e.jsxs("div",{children:[e.jsx("span",{className:"text-sm",children:"Auto-deny dangerous tools"}),e.jsx("p",{className:"text-[11px] text-cc-muted mt-0.5",children:"Automatically block destructive commands like rm -rf"})]}),e.jsx("span",{className:`text-xs font-medium ${B?"text-cc-success":"text-cc-muted"}`,children:B?"On":"Off"})]})]})]})]}),e.jsxs("section",{id:"dashboard",ref:l("dashboard"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Project Dashboard"}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("p",{className:"text-xs text-cc-muted leading-relaxed",children:["A nightly job summarizes every session with recent activity so the"," ",e.jsx("a",{href:"#/dashboard",className:"underline hover:no-underline",children:"dashboard"})," ",'can show where each project stands. It runs through the Claude Code CLI with the same login your normal sessions use — no separate API key needed. This is opt-in because it spends tokens every night. You can always refresh manually with "Update now" on the dashboard, even when the nightly run is off.']}),e.jsxs("button",{type:"button",onClick:pt,className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg bg-cc-hover hover:bg-cc-active text-cc-fg transition-colors cursor-pointer",children:[e.jsx("span",{className:"text-sm",children:"Nightly dashboard updates"}),e.jsx("span",{className:`text-xs font-medium ${_?"text-cc-success":"text-cc-muted"}`,children:_?"On":"Off"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1.5",htmlFor:"dashboard-model",children:"Summarization model"}),e.jsxs("select",{id:"dashboard-model",value:Ce,onChange:t=>{const a=Ce;W(t.target.value),re({dashboardModel:t.target.value},()=>W(a))},className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow",children:[e.jsx("option",{value:"claude-haiku-4-5",children:"Claude Haiku 4.5 — cheapest, recommended"}),e.jsx("option",{value:"claude-sonnet-4-6",children:"Claude Sonnet 4.6"}),e.jsx("option",{value:"claude-sonnet-5",children:"Claude Sonnet 5"})]}),e.jsx("p",{className:"mt-1.5 text-xs text-cc-muted",children:"Summarize-and-classify doesn't need an expensive model — Haiku keeps the nightly cost low."})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1.5",htmlFor:"dashboard-run-hour",children:"Run at"}),e.jsx("select",{id:"dashboard-run-hour",value:Ae,onChange:t=>{const a=Ae,c=Number(t.target.value);q(c),re({dashboardRunHour:c},()=>q(a))},className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow",children:Array.from({length:24},(t,a)=>e.jsxs("option",{value:a,children:[String(a).padStart(2,"0"),":00"]},a))})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1.5",htmlFor:"dashboard-max-sessions",children:"Max sessions per run"}),e.jsx("select",{id:"dashboard-max-sessions",value:Ee,onChange:t=>{const a=Ee,c=Number(t.target.value);Y(c),re({dashboardMaxSessionsPerRun:c},()=>Y(a))},className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow",children:[10,20,30,50,100,200].map(t=>e.jsx("option",{value:t,children:t},t))})]})]})]})]}),e.jsxs("section",{id:"updates",ref:l("updates"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Updates"}),e.jsxs("div",{className:"space-y-3",children:[r?e.jsxs("p",{className:"text-xs text-cc-muted",children:["Current version: v",r.currentVersion,r.latestVersion?` • Latest: v${r.latestVersion}`:"",r.channel==="prerelease"?" (prerelease)":""]}):e.jsx("p",{className:"text-xs text-cc-muted",children:"Version information not loaded yet."}),e.jsxs("div",{children:[e.jsx("span",{id:"update-channel-label",className:"block text-sm font-medium mb-1.5",children:"Update Channel"}),e.jsxs("div",{className:"flex gap-1",role:"radiogroup","aria-labelledby":"update-channel-label",children:[e.jsx("button",{type:"button",role:"radio","aria-checked":x==="stable",onClick:async()=>{if(x!=="stable"){y("stable");try{await n.updateSettings({updateChannel:"stable"})}catch{y("prerelease");return}try{const t=await n.forceCheckForUpdate();P(t)}catch{}}},className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors cursor-pointer ${x==="stable"?"bg-cc-primary text-white":"bg-cc-hover text-cc-muted hover:text-cc-fg hover:bg-cc-active"}`,children:"Stable"}),e.jsx("button",{type:"button",role:"radio","aria-checked":x==="prerelease",onClick:async()=>{if(x!=="prerelease"){y("prerelease");try{await n.updateSettings({updateChannel:"prerelease"})}catch{y("stable");return}try{const t=await n.forceCheckForUpdate();P(t)}catch{}}},className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors cursor-pointer ${x==="prerelease"?"bg-cc-primary text-white":"bg-cc-hover text-cc-muted hover:text-cc-fg hover:bg-cc-active"}`,children:"Prerelease"})]}),e.jsx("p",{className:"mt-1.5 text-xs text-cc-muted",children:x==="prerelease"?"Tracking prerelease channel. You will receive preview builds from the latest main branch.":"Tracking stable channel. You will only receive versioned releases."})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("span",{className:"block text-sm font-medium",children:"Auto-update Docker image"}),e.jsx("p",{className:"mt-0.5 text-xs text-cc-muted",children:"Automatically re-pull the sandbox Docker image when updating The Companion"})]}),e.jsx("button",{type:"button",role:"switch","aria-checked":C,onClick:async()=>{const t=!C;V(t);try{await n.updateSettings({dockerAutoUpdate:t})}catch{V(!t)}},className:`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${C?"bg-cc-primary":"bg-cc-hover"}`,children:e.jsx("span",{className:`pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform transition-transform ${C?"translate-x-5":"translate-x-0"}`})})]}),we&&e.jsx("div",{className:"px-3 py-2 rounded-lg bg-cc-error/10 border border-cc-error/20 text-xs text-cc-error",children:we}),ke&&e.jsx("div",{className:"px-3 py-2 rounded-lg bg-cc-success/10 border border-cc-success/20 text-xs text-cc-success",children:ke}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",onClick:mt,disabled:M,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${M?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-hover hover:bg-cc-active text-cc-fg cursor-pointer"}`,children:M?"Checking...":"Check for updates"}),r!=null&&r.isServiceMode?e.jsx("button",{type:"button",onClick:ht,disabled:L||r.updateInProgress||!r.updateAvailable,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${L||r.updateInProgress||!r.updateAvailable?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-primary hover:bg-cc-primary-hover text-white cursor-pointer"}`,children:L||r.updateInProgress?"Updating...":"Update & Restart"}):e.jsxs("p",{className:"text-xs text-cc-muted self-center",children:["Install service mode with ",e.jsx("code",{className:"font-mono-code bg-cc-code-bg px-1 py-0.5 rounded text-cc-code-fg",children:"the-companion install"})," to enable one-click updates."]})]})]})]}),e.jsxs("section",{id:"telemetry",ref:l("telemetry"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Telemetry"}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{className:"text-xs text-cc-muted",children:"Anonymous product analytics and crash reports via PostHog to improve reliability."}),e.jsxs("button",{type:"button",onClick:()=>{const t=!Se;vt(t),ct(t)},className:"w-full flex items-center justify-between px-3 py-3 min-h-[44px] rounded-lg text-sm bg-cc-hover text-cc-fg hover:bg-cc-active transition-colors cursor-pointer",children:[e.jsx("span",{children:"Usage analytics and errors"}),e.jsx("span",{className:"text-xs text-cc-muted",children:Se?"On":"Off"})]}),e.jsx("p",{className:"text-xs text-cc-muted",children:"Browser Do Not Track is respected automatically."})]})]}),e.jsxs("section",{id:"environments",ref:l("environments"),children:[e.jsx("h2",{className:"text-sm font-semibold text-cc-fg mb-4",children:"Environments"}),e.jsxs("div",{className:"space-y-3",children:[e.jsx("p",{className:"text-xs text-cc-muted",children:"Manage reusable environment profiles used when creating sessions."}),e.jsx("button",{type:"button",onClick:()=>{window.location.hash="#/environments"},className:"px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium bg-cc-primary hover:bg-cc-primary-hover text-white transition-colors cursor-pointer",children:"Open Environments Page"})]})]})]})})]})]})}export{jt as SettingsPage};