@hellcoder/companion 0.113.4 → 0.113.5-preview.20260730100656.ecb60c9
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/dist/assets/{AgentsPage-B2NtxJig.js → AgentsPage-Ct9Hvk1J.js} +1 -1
- package/dist/assets/{CronManager-CES2-E0_.js → CronManager-B4XnCjHZ.js} +1 -1
- package/dist/assets/{DashboardPage-aFf4uQfU.js → DashboardPage-CfgavOrA.js} +1 -1
- package/dist/assets/{IntegrationsPage-C_tZ2P0K.js → IntegrationsPage-CmRcLZ2Y.js} +1 -1
- package/dist/assets/{LinearOAuthSettingsPage-DUQik5kh.js → LinearOAuthSettingsPage-D5fdWchh.js} +1 -1
- package/dist/assets/{LinearSettingsPage-DFh8jfhh.js → LinearSettingsPage-BHrdSu6B.js} +1 -1
- package/dist/assets/{Playground-C4t0-Zia.js → Playground-BDjcdHjZ.js} +1 -1
- package/dist/assets/{PromptsPage-Ma4gA0R7.js → PromptsPage-C4XeLDqH.js} +1 -1
- package/dist/assets/{RunsPage-jk2yW392.js → RunsPage-9_OGNofF.js} +1 -1
- package/dist/assets/{SandboxManager-BA6-RlkR.js → SandboxManager-C0UI27DS.js} +1 -1
- package/dist/assets/SettingsPage-1ggRq3DY.js +1 -0
- package/dist/assets/{TailscalePage-CZ6V3kc7.js → TailscalePage-9EDSWy6L.js} +1 -1
- package/dist/assets/{index-B1Oejky6.js → index-DRO5Cupx.js} +3 -3
- package/dist/assets/{sw-register-B9GK06Mo.js → sw-register-UeoQRzHq.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/sw.js +1 -1
- package/package.json +2 -1
- package/server/claude-adapter.ts +9 -7
- package/server/claude-sdk-adapter.test.ts +298 -0
- package/server/claude-sdk-adapter.ts +394 -0
- package/server/cli-launcher.ts +110 -0
- package/server/routes/settings-routes.ts +10 -0
- package/server/routes.test.ts +27 -0
- package/server/settings-manager.test.ts +21 -0
- package/server/settings-manager.ts +16 -1
- package/dist/assets/SettingsPage-BehGv-KV.js +0 -1
package/server/routes.test.ts
CHANGED
|
@@ -1161,6 +1161,7 @@ describe("GET /api/sessions/:id/archive-info", () => {
|
|
|
1161
1161
|
keepaliveDetachedSessions: false,
|
|
1162
1162
|
wedgeKillEnabled: true,
|
|
1163
1163
|
silenceProbeEnabled: true,
|
|
1164
|
+
claudeTransport: "stdio",
|
|
1164
1165
|
updatedAt: 0,
|
|
1165
1166
|
});
|
|
1166
1167
|
const res = await app.request("/api/sessions/s1/archive-info", { method: "GET" });
|
|
@@ -1539,6 +1540,7 @@ describe("GET /api/settings", () => {
|
|
|
1539
1540
|
keepaliveDetachedSessions: false,
|
|
1540
1541
|
wedgeKillEnabled: true,
|
|
1541
1542
|
silenceProbeEnabled: true,
|
|
1543
|
+
claudeTransport: "stdio",
|
|
1542
1544
|
updatedAt: 123,
|
|
1543
1545
|
});
|
|
1544
1546
|
|
|
@@ -1575,6 +1577,7 @@ describe("GET /api/settings", () => {
|
|
|
1575
1577
|
keepaliveDetachedSessions: false,
|
|
1576
1578
|
wedgeKillEnabled: true,
|
|
1577
1579
|
silenceProbeEnabled: true,
|
|
1580
|
+
claudeTransport: "stdio",
|
|
1578
1581
|
});
|
|
1579
1582
|
});
|
|
1580
1583
|
|
|
@@ -1611,6 +1614,7 @@ describe("GET /api/settings", () => {
|
|
|
1611
1614
|
keepaliveDetachedSessions: false,
|
|
1612
1615
|
wedgeKillEnabled: true,
|
|
1613
1616
|
silenceProbeEnabled: true,
|
|
1617
|
+
claudeTransport: "stdio",
|
|
1614
1618
|
updatedAt: 123,
|
|
1615
1619
|
});
|
|
1616
1620
|
|
|
@@ -1647,6 +1651,7 @@ describe("GET /api/settings", () => {
|
|
|
1647
1651
|
keepaliveDetachedSessions: false,
|
|
1648
1652
|
wedgeKillEnabled: true,
|
|
1649
1653
|
silenceProbeEnabled: true,
|
|
1654
|
+
claudeTransport: "stdio",
|
|
1650
1655
|
});
|
|
1651
1656
|
});
|
|
1652
1657
|
|
|
@@ -1684,6 +1689,7 @@ describe("GET /api/settings", () => {
|
|
|
1684
1689
|
keepaliveDetachedSessions: false,
|
|
1685
1690
|
wedgeKillEnabled: true,
|
|
1686
1691
|
silenceProbeEnabled: true,
|
|
1692
|
+
claudeTransport: "stdio",
|
|
1687
1693
|
updatedAt: 100,
|
|
1688
1694
|
});
|
|
1689
1695
|
|
|
@@ -1729,6 +1735,7 @@ describe("PUT /api/settings", () => {
|
|
|
1729
1735
|
keepaliveDetachedSessions: false,
|
|
1730
1736
|
wedgeKillEnabled: true,
|
|
1731
1737
|
silenceProbeEnabled: true,
|
|
1738
|
+
claudeTransport: "stdio",
|
|
1732
1739
|
updatedAt: 456,
|
|
1733
1740
|
});
|
|
1734
1741
|
|
|
@@ -1787,6 +1794,7 @@ describe("PUT /api/settings", () => {
|
|
|
1787
1794
|
keepaliveDetachedSessions: false,
|
|
1788
1795
|
wedgeKillEnabled: true,
|
|
1789
1796
|
silenceProbeEnabled: true,
|
|
1797
|
+
claudeTransport: "stdio",
|
|
1790
1798
|
});
|
|
1791
1799
|
});
|
|
1792
1800
|
|
|
@@ -1823,6 +1831,7 @@ describe("PUT /api/settings", () => {
|
|
|
1823
1831
|
keepaliveDetachedSessions: false,
|
|
1824
1832
|
wedgeKillEnabled: true,
|
|
1825
1833
|
silenceProbeEnabled: true,
|
|
1834
|
+
claudeTransport: "stdio",
|
|
1826
1835
|
updatedAt: 789,
|
|
1827
1836
|
});
|
|
1828
1837
|
|
|
@@ -1876,6 +1885,7 @@ describe("PUT /api/settings", () => {
|
|
|
1876
1885
|
keepaliveDetachedSessions: false,
|
|
1877
1886
|
wedgeKillEnabled: true,
|
|
1878
1887
|
silenceProbeEnabled: true,
|
|
1888
|
+
claudeTransport: "stdio",
|
|
1879
1889
|
updatedAt: 999,
|
|
1880
1890
|
});
|
|
1881
1891
|
|
|
@@ -2029,6 +2039,7 @@ describe("PUT /api/settings", () => {
|
|
|
2029
2039
|
keepaliveDetachedSessions: false,
|
|
2030
2040
|
wedgeKillEnabled: true,
|
|
2031
2041
|
silenceProbeEnabled: true,
|
|
2042
|
+
claudeTransport: "stdio",
|
|
2032
2043
|
updatedAt: 500,
|
|
2033
2044
|
});
|
|
2034
2045
|
|
|
@@ -2288,6 +2299,7 @@ describe("GET /api/linear/issues", () => {
|
|
|
2288
2299
|
keepaliveDetachedSessions: false,
|
|
2289
2300
|
wedgeKillEnabled: true,
|
|
2290
2301
|
silenceProbeEnabled: true,
|
|
2302
|
+
claudeTransport: "stdio",
|
|
2291
2303
|
updatedAt: 0,
|
|
2292
2304
|
});
|
|
2293
2305
|
vi.mocked(resolveApiKey).mockReturnValue(null);
|
|
@@ -2331,6 +2343,7 @@ describe("GET /api/linear/issues", () => {
|
|
|
2331
2343
|
keepaliveDetachedSessions: false,
|
|
2332
2344
|
wedgeKillEnabled: true,
|
|
2333
2345
|
silenceProbeEnabled: true,
|
|
2346
|
+
claudeTransport: "stdio",
|
|
2334
2347
|
updatedAt: 0,
|
|
2335
2348
|
});
|
|
2336
2349
|
|
|
@@ -2427,6 +2440,7 @@ describe("GET /api/linear/issues", () => {
|
|
|
2427
2440
|
keepaliveDetachedSessions: false,
|
|
2428
2441
|
wedgeKillEnabled: true,
|
|
2429
2442
|
silenceProbeEnabled: true,
|
|
2443
|
+
claudeTransport: "stdio",
|
|
2430
2444
|
updatedAt: 0,
|
|
2431
2445
|
});
|
|
2432
2446
|
|
|
@@ -2530,6 +2544,7 @@ describe("GET /api/linear/issues", () => {
|
|
|
2530
2544
|
keepaliveDetachedSessions: false,
|
|
2531
2545
|
wedgeKillEnabled: true,
|
|
2532
2546
|
silenceProbeEnabled: true,
|
|
2547
|
+
claudeTransport: "stdio",
|
|
2533
2548
|
updatedAt: 0,
|
|
2534
2549
|
});
|
|
2535
2550
|
|
|
@@ -2598,6 +2613,7 @@ describe("GET /api/linear/connection", () => {
|
|
|
2598
2613
|
keepaliveDetachedSessions: false,
|
|
2599
2614
|
wedgeKillEnabled: true,
|
|
2600
2615
|
silenceProbeEnabled: true,
|
|
2616
|
+
claudeTransport: "stdio",
|
|
2601
2617
|
updatedAt: 0,
|
|
2602
2618
|
});
|
|
2603
2619
|
vi.mocked(resolveApiKey).mockReturnValue(null);
|
|
@@ -2641,6 +2657,7 @@ describe("GET /api/linear/connection", () => {
|
|
|
2641
2657
|
keepaliveDetachedSessions: false,
|
|
2642
2658
|
wedgeKillEnabled: true,
|
|
2643
2659
|
silenceProbeEnabled: true,
|
|
2660
|
+
claudeTransport: "stdio",
|
|
2644
2661
|
updatedAt: 0,
|
|
2645
2662
|
});
|
|
2646
2663
|
|
|
@@ -2706,6 +2723,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
|
|
|
2706
2723
|
keepaliveDetachedSessions: false,
|
|
2707
2724
|
wedgeKillEnabled: true,
|
|
2708
2725
|
silenceProbeEnabled: true,
|
|
2726
|
+
claudeTransport: "stdio",
|
|
2709
2727
|
updatedAt: 0,
|
|
2710
2728
|
});
|
|
2711
2729
|
|
|
@@ -2753,6 +2771,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
|
|
|
2753
2771
|
keepaliveDetachedSessions: false,
|
|
2754
2772
|
wedgeKillEnabled: true,
|
|
2755
2773
|
silenceProbeEnabled: true,
|
|
2774
|
+
claudeTransport: "stdio",
|
|
2756
2775
|
updatedAt: 0,
|
|
2757
2776
|
});
|
|
2758
2777
|
|
|
@@ -2799,6 +2818,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
|
|
|
2799
2818
|
keepaliveDetachedSessions: false,
|
|
2800
2819
|
wedgeKillEnabled: true,
|
|
2801
2820
|
silenceProbeEnabled: true,
|
|
2821
|
+
claudeTransport: "stdio",
|
|
2802
2822
|
updatedAt: 0,
|
|
2803
2823
|
});
|
|
2804
2824
|
vi.mocked(resolveApiKey).mockReturnValue(null);
|
|
@@ -2847,6 +2867,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
|
|
|
2847
2867
|
keepaliveDetachedSessions: false,
|
|
2848
2868
|
wedgeKillEnabled: true,
|
|
2849
2869
|
silenceProbeEnabled: true,
|
|
2870
|
+
claudeTransport: "stdio",
|
|
2850
2871
|
updatedAt: 0,
|
|
2851
2872
|
});
|
|
2852
2873
|
|
|
@@ -2929,6 +2950,7 @@ describe("POST /api/linear/issues/:id/transition", () => {
|
|
|
2929
2950
|
keepaliveDetachedSessions: false,
|
|
2930
2951
|
wedgeKillEnabled: true,
|
|
2931
2952
|
silenceProbeEnabled: true,
|
|
2953
|
+
claudeTransport: "stdio",
|
|
2932
2954
|
updatedAt: 0,
|
|
2933
2955
|
});
|
|
2934
2956
|
|
|
@@ -2990,6 +3012,7 @@ describe("GET /api/linear/projects", () => {
|
|
|
2990
3012
|
keepaliveDetachedSessions: false,
|
|
2991
3013
|
wedgeKillEnabled: true,
|
|
2992
3014
|
silenceProbeEnabled: true,
|
|
3015
|
+
claudeTransport: "stdio",
|
|
2993
3016
|
updatedAt: 0,
|
|
2994
3017
|
});
|
|
2995
3018
|
vi.mocked(resolveApiKey).mockReturnValue(null);
|
|
@@ -3033,6 +3056,7 @@ describe("GET /api/linear/projects", () => {
|
|
|
3033
3056
|
keepaliveDetachedSessions: false,
|
|
3034
3057
|
wedgeKillEnabled: true,
|
|
3035
3058
|
silenceProbeEnabled: true,
|
|
3059
|
+
claudeTransport: "stdio",
|
|
3036
3060
|
updatedAt: 0,
|
|
3037
3061
|
});
|
|
3038
3062
|
|
|
@@ -3106,6 +3130,7 @@ describe("GET /api/linear/project-issues", () => {
|
|
|
3106
3130
|
keepaliveDetachedSessions: false,
|
|
3107
3131
|
wedgeKillEnabled: true,
|
|
3108
3132
|
silenceProbeEnabled: true,
|
|
3133
|
+
claudeTransport: "stdio",
|
|
3109
3134
|
updatedAt: 0,
|
|
3110
3135
|
});
|
|
3111
3136
|
vi.mocked(resolveApiKey).mockReturnValue(null);
|
|
@@ -3149,6 +3174,7 @@ describe("GET /api/linear/project-issues", () => {
|
|
|
3149
3174
|
keepaliveDetachedSessions: false,
|
|
3150
3175
|
wedgeKillEnabled: true,
|
|
3151
3176
|
silenceProbeEnabled: true,
|
|
3177
|
+
claudeTransport: "stdio",
|
|
3152
3178
|
updatedAt: 0,
|
|
3153
3179
|
});
|
|
3154
3180
|
|
|
@@ -3237,6 +3263,7 @@ describe("GET /api/linear/project-issues", () => {
|
|
|
3237
3263
|
keepaliveDetachedSessions: false,
|
|
3238
3264
|
wedgeKillEnabled: true,
|
|
3239
3265
|
silenceProbeEnabled: true,
|
|
3266
|
+
claudeTransport: "stdio",
|
|
3240
3267
|
updatedAt: 0,
|
|
3241
3268
|
});
|
|
3242
3269
|
|
|
@@ -56,6 +56,7 @@ describe("settings-manager", () => {
|
|
|
56
56
|
keepaliveDetachedSessions: false,
|
|
57
57
|
wedgeKillEnabled: true,
|
|
58
58
|
silenceProbeEnabled: true,
|
|
59
|
+
claudeTransport: "stdio",
|
|
59
60
|
cliBridgeMode: "loopback",
|
|
60
61
|
claudeBridgeMode: "none",
|
|
61
62
|
claudeBridgeIngressUrl: "",
|
|
@@ -123,6 +124,7 @@ describe("settings-manager", () => {
|
|
|
123
124
|
keepaliveDetachedSessions: false,
|
|
124
125
|
wedgeKillEnabled: true,
|
|
125
126
|
silenceProbeEnabled: true,
|
|
127
|
+
claudeTransport: "stdio",
|
|
126
128
|
cliBridgeMode: "loopback",
|
|
127
129
|
claudeBridgeMode: "none",
|
|
128
130
|
claudeBridgeIngressUrl: "",
|
|
@@ -216,6 +218,7 @@ describe("settings-manager", () => {
|
|
|
216
218
|
keepaliveDetachedSessions: false,
|
|
217
219
|
wedgeKillEnabled: true,
|
|
218
220
|
silenceProbeEnabled: true,
|
|
221
|
+
claudeTransport: "stdio",
|
|
219
222
|
cliBridgeMode: "loopback",
|
|
220
223
|
claudeBridgeMode: "none",
|
|
221
224
|
claudeBridgeIngressUrl: "",
|
|
@@ -310,4 +313,22 @@ describe("settings-manager", () => {
|
|
|
310
313
|
const updated = updateSettings({ anthropicModel: "claude-haiku-3" });
|
|
311
314
|
expect(updated.publicUrl).toBe("https://example.com");
|
|
312
315
|
});
|
|
316
|
+
|
|
317
|
+
// claudeTransport selects which layer drives Claude sessions (stdio bridge
|
|
318
|
+
// vs Agent SDK). It must default to "stdio", persist a valid "sdk" value,
|
|
319
|
+
// and normalize anything invalid back to "stdio" so a corrupt settings file
|
|
320
|
+
// can never strand sessions on a nonexistent transport.
|
|
321
|
+
it("defaults claudeTransport to stdio and persists sdk", () => {
|
|
322
|
+
expect(getSettings().claudeTransport).toBe("stdio");
|
|
323
|
+
const updated = updateSettings({ claudeTransport: "sdk" });
|
|
324
|
+
expect(updated.claudeTransport).toBe("sdk");
|
|
325
|
+
// Survives an unrelated update.
|
|
326
|
+
expect(updateSettings({ anthropicModel: "claude-haiku-3" }).claudeTransport).toBe("sdk");
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("normalizes an invalid claudeTransport value from disk to stdio", () => {
|
|
330
|
+
writeFileSync(settingsPath, JSON.stringify({ claudeTransport: "warp-drive" }));
|
|
331
|
+
_resetForTest(settingsPath);
|
|
332
|
+
expect(getSettings().claudeTransport).toBe("stdio");
|
|
333
|
+
});
|
|
313
334
|
});
|
|
@@ -124,6 +124,18 @@ export interface CompanionSettings {
|
|
|
124
124
|
* with no error surfaced. Disable to leave silent sessions alone.
|
|
125
125
|
*/
|
|
126
126
|
silenceProbeEnabled: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Which transport drives Claude Code sessions.
|
|
129
|
+
*
|
|
130
|
+
* "stdio" (default): companion spawns `claude --print --input-format
|
|
131
|
+
* stream-json` and bridges its pipes itself (ClaudeAdapter.attachStdio).
|
|
132
|
+
* "sdk": sessions run through the official @anthropic-ai/claude-agent-sdk
|
|
133
|
+
* (SdkClaudeAdapter) — same binary, same subscription auth, but Anthropic's
|
|
134
|
+
* maintained protocol layer instead of ours. Applied at spawn/relaunch time,
|
|
135
|
+
* so flipping this and hitting Reconnect migrates a live session between
|
|
136
|
+
* transports via --resume. Claude sessions only; Codex is unaffected.
|
|
137
|
+
*/
|
|
138
|
+
claudeTransport?: "stdio" | "sdk";
|
|
127
139
|
/** See CliBridgeMode. Defaults to "loopback". Optional in fixtures; normalize() applies the default. */
|
|
128
140
|
cliBridgeMode?: CliBridgeMode;
|
|
129
141
|
/** See ClaudeBridgeMode. Defaults to "none". Persists across companion restarts. */
|
|
@@ -171,6 +183,7 @@ let settings: CompanionSettings = {
|
|
|
171
183
|
keepaliveDetachedSessions: false,
|
|
172
184
|
wedgeKillEnabled: true,
|
|
173
185
|
silenceProbeEnabled: true,
|
|
186
|
+
claudeTransport: "stdio",
|
|
174
187
|
cliBridgeMode: "loopback",
|
|
175
188
|
claudeBridgeMode: "none",
|
|
176
189
|
claudeBridgeIngressUrl: "",
|
|
@@ -225,6 +238,7 @@ function normalize(raw: Partial<CompanionSettings> | null | undefined): Companio
|
|
|
225
238
|
keepaliveDetachedSessions: typeof raw?.keepaliveDetachedSessions === "boolean" ? raw.keepaliveDetachedSessions : false,
|
|
226
239
|
wedgeKillEnabled: typeof raw?.wedgeKillEnabled === "boolean" ? raw.wedgeKillEnabled : true,
|
|
227
240
|
silenceProbeEnabled: typeof raw?.silenceProbeEnabled === "boolean" ? raw.silenceProbeEnabled : true,
|
|
241
|
+
claudeTransport: raw?.claudeTransport === "sdk" ? "sdk" : "stdio",
|
|
228
242
|
cliBridgeMode: raw?.cliBridgeMode === "jsonHandoff" ? "jsonHandoff" : "loopback",
|
|
229
243
|
claudeBridgeMode: raw?.claudeBridgeMode === "patched" ? "patched" : "none",
|
|
230
244
|
claudeBridgeIngressUrl: typeof raw?.claudeBridgeIngressUrl === "string" ? raw.claudeBridgeIngressUrl : "",
|
|
@@ -258,7 +272,7 @@ export function getSettings(): CompanionSettings {
|
|
|
258
272
|
}
|
|
259
273
|
|
|
260
274
|
export function updateSettings(
|
|
261
|
-
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" | "keepaliveDetachedSessions" | "wedgeKillEnabled" | "silenceProbeEnabled" | "cliBridgeMode" | "claudeBridgeMode" | "claudeBridgeIngressUrl" | "claudeCompatBannerDismissedVersion">>,
|
|
275
|
+
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" | "keepaliveDetachedSessions" | "wedgeKillEnabled" | "silenceProbeEnabled" | "claudeTransport" | "cliBridgeMode" | "claudeBridgeMode" | "claudeBridgeIngressUrl" | "claudeCompatBannerDismissedVersion">>,
|
|
262
276
|
): CompanionSettings {
|
|
263
277
|
ensureLoaded();
|
|
264
278
|
settings = normalize({
|
|
@@ -293,6 +307,7 @@ export function updateSettings(
|
|
|
293
307
|
keepaliveDetachedSessions: patch.keepaliveDetachedSessions ?? settings.keepaliveDetachedSessions,
|
|
294
308
|
wedgeKillEnabled: patch.wedgeKillEnabled ?? settings.wedgeKillEnabled,
|
|
295
309
|
silenceProbeEnabled: patch.silenceProbeEnabled ?? settings.silenceProbeEnabled,
|
|
310
|
+
claudeTransport: patch.claudeTransport ?? settings.claudeTransport,
|
|
296
311
|
cliBridgeMode: patch.cliBridgeMode ?? settings.cliBridgeMode,
|
|
297
312
|
claudeBridgeMode: patch.claudeBridgeMode ?? settings.claudeBridgeMode,
|
|
298
313
|
claudeBridgeIngressUrl: patch.claudeBridgeIngressUrl ?? settings.claudeBridgeIngressUrl,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as s,u as o,g as gt,a as n,j as e,n as vt,b as yt,s as jt}from"./index-B1Oejky6.js";const ue=[{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 wt({embedded:xe=!1}){const[p,pe]=s.useState(""),[me,he]=s.useState("claude-sonnet-4-6"),[d,be]=s.useState(!1),[k,Ze]=s.useState(!0),[f,S]=s.useState(!1),[fe,g]=s.useState(""),[ge,v]=s.useState(!1),et=o(t=>t.darkMode),tt=o(t=>t.toggleDarkMode),ve=o(t=>t.diffBase),st=o(t=>t.setDiffBase),at=o(t=>t.notificationSound),nt=o(t=>t.toggleNotificationSound),ye=o(t=>t.notificationDesktop),je=o(t=>t.setNotificationDesktop),r=o(t=>t.updateInfo),O=o(t=>t.setUpdateInfo),ct=o(t=>t.setUpdateOverlayActive),ot=typeof Notification<"u",[x,y]=s.useState("stable"),[C,V]=s.useState(!1),[A,D]=s.useState(!0),[E,L]=s.useState(!0),[U,K]=s.useState(!0),[Ne,M]=s.useState("loopback"),[$,we]=s.useState(!1),[F,ke]=s.useState(!1),[Se,j]=s.useState(""),[Ce,T]=s.useState(""),[Ae,rt]=s.useState(gt()),[I,H]=s.useState(!1),[B,Q]=s.useState(!0),[_,W]=s.useState(!1),[G,q]=s.useState(!1),[Ee,Y]=s.useState("claude-haiku-4-5"),[Ue,z]=s.useState(3),[Te,J]=s.useState(30),[P,X]=s.useState(""),[Ie,Pe]=s.useState("general"),[it,Re]=s.useState(!1),[Z,Oe]=s.useState(!1),[N,m]=s.useState(null),[h,Ve]=s.useState(""),[ee,De]=s.useState(!1),[b,Le]=s.useState(""),[te,Ke]=s.useState(!1),[se,Me]=s.useState(!1),[lt,ae]=s.useState(!1),[$e,Fe]=s.useState(""),[dt,He]=s.useState(!1),[ut,Be]=s.useState(!1),[w,Qe]=s.useState(null),[ne,_e]=s.useState(!1),[u,We]=s.useState(null),[R,xt]=s.useState(0),[ce,Ge]=s.useState(!1),[oe,qe]=s.useState(!1),[pt,Ye]=s.useState(!1),ze=s.useRef(null),re=s.useRef({});s.useEffect(()=>{const t=ze.current;if(!t)return;const a=new IntersectionObserver(c=>{var Xe;let i=null;for(const de of c)de.isIntersecting&&(!i||de.boundingClientRect.top<i.boundingClientRect.top)&&(i=de);(Xe=i==null?void 0:i.target)!=null&&Xe.id&&Pe(i.target.id)},{root:t,rootMargin:"-10% 0px -70% 0px",threshold:0});for(const c of ue){const i=re.current[c.id];i&&a.observe(i)}return()=>a.disconnect()},[k]);const Je=s.useCallback(t=>{Pe(t);const a=re.current[t];a&&a.scrollIntoView({behavior:"smooth",block:"start"})},[]);s.useEffect(()=>{n.getSettings().then(t=>{be(t.anthropicApiKeyConfigured),De(t.claudeCodeOAuthTokenConfigured),Ke(t.openaiApiKeyConfigured),he(t.anthropicModel||"claude-sonnet-4-6"),typeof t.aiValidationEnabled=="boolean"&&H(t.aiValidationEnabled),typeof t.aiValidationAutoApprove=="boolean"&&Q(t.aiValidationAutoApprove),typeof t.aiValidationAutoDeny=="boolean"&&W(t.aiValidationAutoDeny),typeof t.dashboardEnabled=="boolean"&&q(t.dashboardEnabled),typeof t.dashboardModel=="string"&&t.dashboardModel&&Y(t.dashboardModel),typeof t.dashboardRunHour=="number"&&z(t.dashboardRunHour),typeof t.dashboardMaxSessionsPerRun=="number"&&J(t.dashboardMaxSessionsPerRun),(t.updateChannel==="stable"||t.updateChannel==="prerelease")&&y(t.updateChannel),typeof t.dockerAutoUpdate=="boolean"&&V(t.dockerAutoUpdate),typeof t.proactiveKeepaliveEnabled=="boolean"&&D(t.proactiveKeepaliveEnabled),typeof t.wedgeKillEnabled=="boolean"&&L(t.wedgeKillEnabled),typeof t.silenceProbeEnabled=="boolean"&&K(t.silenceProbeEnabled),(t.cliBridgeMode==="loopback"||t.cliBridgeMode==="jsonHandoff")&&M(t.cliBridgeMode),typeof t.publicUrl=="string"&&(X(t.publicUrl),o.getState().setPublicUrl(t.publicUrl))}).catch(t=>g(t instanceof Error?t.message:String(t))).finally(()=>Ze(!1)),n.getAuthToken().then(t=>Qe(t.token)).catch(()=>{})},[]);async function mt(t){t.preventDefault(),S(!0),g(""),v(!1);try{const a=p.trim(),c={anthropicModel:me.trim()||"claude-sonnet-4-6"};a&&(c.anthropicApiKey=a);const i=await n.updateSettings(c);be(i.anthropicApiKeyConfigured),pe(""),v(!0),setTimeout(()=>v(!1),1800)}catch(a){g(a instanceof Error?a.message:String(a))}finally{S(!1)}}async function ie(t){const a=t==="aiValidationEnabled"?I:t==="aiValidationAutoApprove"?B:_,c=!a;t==="aiValidationEnabled"?H(c):t==="aiValidationAutoApprove"?Q(c):W(c);try{await n.updateSettings({[t]:c})}catch{t==="aiValidationEnabled"?H(a):t==="aiValidationAutoApprove"?Q(a):W(a)}}async function ht(){const t=G;q(!t);try{await n.updateSettings({dashboardEnabled:!t})}catch{q(t)}}async function le(t,a){try{await n.updateSettings(t)}catch{a()}}async function bt(){we(!0),j(""),T("");try{const t=await n.forceCheckForUpdate();O(t),t.updateAvailable&&t.latestVersion?j(`Update v${t.latestVersion} is available.`):j("You are up to date.")}catch(t){T(t instanceof Error?t.message:String(t))}finally{we(!1)}}async function ft(){ke(!0),j(""),T("");try{localStorage.setItem("companion_docker_prompt_pending","1");const t=await n.triggerUpdate();j(t.message),ct(!0)}catch(t){localStorage.removeItem("companion_docker_prompt_pending"),T(t instanceof Error?t.message:String(t)),ke(!1)}}const l=s.useCallback(t=>a=>{re.current[t]=a},[]);return e.jsxs("div",{className:`${xe?"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."})]}),!xe&&e.jsx("button",{onClick:()=>{const t=o.getState().currentSessionId;t?vt(t):yt()},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:ue.map(t=>e.jsx("button",{type:"button",onClick:()=>Je(t.id),className:`shrink-0 px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors cursor-pointer ${Ie===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:ue.map(t=>e.jsx("button",{type:"button",onClick:()=>Je(t.id),className:`text-left px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors cursor-pointer ${Ie===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:ze,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:tt,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:et?"Dark":"Light"})]}),e.jsxs("button",{type:"button",onClick:()=>st(ve==="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:ve==="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:Ne,onChange:async t=>{const a=t.target.value==="jsonHandoff"?"jsonHandoff":"loopback",c=Ne;M(a);try{await n.updateSettings({cliBridgeMode:a})}catch{M(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;D(t);try{await n.updateSettings({proactiveKeepaliveEnabled: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:"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;L(t);try{await n.updateSettings({wedgeKillEnabled:t})}catch{L(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("div",{className:"pt-3 border-t border-cc-border",children:[e.jsxs("button",{type:"button",role:"switch","aria-checked":U,"aria-label":"Probe silent CLI processes",onClick:async()=>{const t=!U,a=U;K(t);try{await n.updateSettings({silenceProbeEnabled:t})}catch{K(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:"Probe silent CLI processes"}),e.jsx("span",{className:"text-xs text-cc-muted",children:U?"On":"Off"})]}),e.jsx("p",{className:"mt-1 text-xs text-cc-muted px-1",children:"When on (default), a CLI that stops responding while its output stream stays open is sent a liveness probe after ~2 minutes of silence. If it does not answer within a further minute the session is relaunched and the in-flight turn replayed. Without this such a session hangs indefinitely showing “Still working…” with no error, because nothing ever closes the stream for companion to notice."})]})]})]}),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:P,onChange:t=>X(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:P?`Using: ${P}`:`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:P.trim()});X(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...":ge?"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:w?ne?w:"••••••••••••••••":e.jsx("span",{className:"text-cc-muted",children:"Loading..."})}),e.jsx("button",{type:"button",onClick:()=>_e(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:ne?"Hide token":"Show token",children:ne?"Hide":"Show"}),e.jsx("button",{type:"button",onClick:()=>{w&&navigator.clipboard.writeText(w).then(()=>{Ye(!0),setTimeout(()=>Ye(!1),1500)})},disabled:!w,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:pt?"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:()=>xt(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()=>{Ge(!0);try{const t=await n.getAuthQr();We(t.qrCodes)}catch{}finally{Ge(!1)}},disabled:ce,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${ce?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-hover hover:bg-cc-active text-cc-fg cursor-pointer"}`,children:ce?"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.")){qe(!0);try{const t=await n.regenerateAuthToken();Qe(t.token),_e(!0),We(null)}catch{}finally{qe(!1)}}},disabled:oe,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${oe?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-error/10 hover:bg-cc-error/20 text-cc-error cursor-pointer"}`,children:oe?"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:nt,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:at?"On":"Off"})]}),ot&&e.jsxs("button",{type:"button",onClick:async()=>{if(ye)je(!1);else{if(Notification.permission!=="granted"&&await Notification.requestPermission()!=="granted")return;je(!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:ye?"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:ee&&!dt&&!h?"••••••••••••••••":h,onChange:t=>Ve(t.target.value),onFocus:()=>He(!0),onBlur:()=>He(!1),placeholder:ee?"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:ee?"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:te&&!ut&&!b?"••••••••••••••••":b,onChange:t=>Le(t.target.value),onFocus:()=>Be(!0),onBlur:()=>Be(!1),placeholder:te?"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:te?"OpenAI key configured":"OpenAI key not configured"})]}),$e&&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:$e}),lt&&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:se||!h.trim()&&!b.trim(),onClick:async()=>{Me(!0),Fe(""),ae(!1);try{const t={};h.trim()&&(t.claudeCodeOAuthToken=h.trim()),b.trim()&&(t.openaiApiKey=b.trim());const a=await n.updateSettings(t);De(a.claudeCodeOAuthTokenConfigured),Ke(a.openaiApiKeyConfigured),Ve(""),Le(""),ae(!0),setTimeout(()=>ae(!1),1800)}catch(t){Fe(t instanceof Error?t.message:String(t))}finally{Me(!1)}},className:`px-4 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${se||!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:se?"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:mt,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&&!it&&!p?"••••••••••••••••":p,onChange:t=>{pe(t.target.value),m(null)},onFocus:()=>Re(!0),onBlur:()=>Re(!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:me,onChange:t=>he(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"})]}),fe&&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:fe}),ge&&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:k?"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:Z||!p.trim(),onClick:async()=>{Oe(!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{Oe(!1)}},className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${Z||!p.trim()?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-hover hover:bg-cc-active text-cc-fg cursor-pointer"}`,children:Z?"Verifying...":"Verify"}),e.jsx("button",{type:"submit",disabled:f||k,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${f||k?"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:()=>ie("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 ${I&&d?"text-cc-success":"text-cc-muted"}`,children:I&&d?"On":"Off"})]}),!d&&e.jsx("p",{className:"text-[11px] text-cc-warning",children:"Configure an Anthropic API key above to enable AI validation."}),I&&d&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{type:"button",onClick:()=>ie("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 ${B?"text-cc-success":"text-cc-muted"}`,children:B?"On":"Off"})]}),e.jsxs("button",{type:"button",onClick:()=>ie("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 ${_?"text-cc-success":"text-cc-muted"}`,children:_?"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:ht,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 ${G?"text-cc-success":"text-cc-muted"}`,children:G?"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:Ee,onChange:t=>{const a=Ee;Y(t.target.value),le({dashboardModel:t.target.value},()=>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:[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:Ue,onChange:t=>{const a=Ue,c=Number(t.target.value);z(c),le({dashboardRunHour:c},()=>z(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:Te,onChange:t=>{const a=Te,c=Number(t.target.value);J(c),le({dashboardMaxSessionsPerRun:c},()=>J(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();O(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();O(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"}`})})]}),Ce&&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:Ce}),Se&&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:Se}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",onClick:bt,disabled:$,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${$?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-cc-hover hover:bg-cc-active text-cc-fg cursor-pointer"}`,children:$?"Checking...":"Check for updates"}),r!=null&&r.isServiceMode?e.jsx("button",{type:"button",onClick:ft,disabled:F||r.updateInProgress||!r.updateAvailable,className:`px-3 py-2 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${F||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:F||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=!Ae;jt(t),rt(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:Ae?"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{wt as SettingsPage};
|