codepiper 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/.env.example +28 -0
  2. package/CHANGELOG.md +10 -0
  3. package/LEGAL_NOTICE.md +39 -0
  4. package/LICENSE +21 -0
  5. package/README.md +524 -0
  6. package/package.json +90 -0
  7. package/packages/cli/package.json +13 -0
  8. package/packages/cli/src/commands/analytics.ts +157 -0
  9. package/packages/cli/src/commands/attach.ts +299 -0
  10. package/packages/cli/src/commands/audit.ts +50 -0
  11. package/packages/cli/src/commands/auth.ts +261 -0
  12. package/packages/cli/src/commands/daemon.ts +162 -0
  13. package/packages/cli/src/commands/doctor.ts +303 -0
  14. package/packages/cli/src/commands/env-set.ts +162 -0
  15. package/packages/cli/src/commands/hook-forward.ts +268 -0
  16. package/packages/cli/src/commands/keys.ts +77 -0
  17. package/packages/cli/src/commands/kill.ts +19 -0
  18. package/packages/cli/src/commands/logs.ts +419 -0
  19. package/packages/cli/src/commands/model.ts +172 -0
  20. package/packages/cli/src/commands/policy-set.ts +185 -0
  21. package/packages/cli/src/commands/policy.ts +227 -0
  22. package/packages/cli/src/commands/providers.ts +114 -0
  23. package/packages/cli/src/commands/resize.ts +34 -0
  24. package/packages/cli/src/commands/send.ts +184 -0
  25. package/packages/cli/src/commands/sessions.ts +202 -0
  26. package/packages/cli/src/commands/slash.ts +92 -0
  27. package/packages/cli/src/commands/start.ts +243 -0
  28. package/packages/cli/src/commands/stop.ts +19 -0
  29. package/packages/cli/src/commands/tail.ts +137 -0
  30. package/packages/cli/src/commands/workflow.ts +786 -0
  31. package/packages/cli/src/commands/workspace.ts +127 -0
  32. package/packages/cli/src/lib/api.ts +78 -0
  33. package/packages/cli/src/lib/args.ts +72 -0
  34. package/packages/cli/src/lib/format.ts +93 -0
  35. package/packages/cli/src/main.ts +563 -0
  36. package/packages/core/package.json +7 -0
  37. package/packages/core/src/config.ts +30 -0
  38. package/packages/core/src/errors.ts +38 -0
  39. package/packages/core/src/eventBus.ts +56 -0
  40. package/packages/core/src/eventBusAdapter.ts +143 -0
  41. package/packages/core/src/index.ts +10 -0
  42. package/packages/core/src/sqliteEventBus.ts +336 -0
  43. package/packages/core/src/types.ts +63 -0
  44. package/packages/daemon/package.json +11 -0
  45. package/packages/daemon/src/api/analyticsRoutes.ts +343 -0
  46. package/packages/daemon/src/api/authRoutes.ts +344 -0
  47. package/packages/daemon/src/api/bodyLimit.ts +133 -0
  48. package/packages/daemon/src/api/envSetRoutes.ts +170 -0
  49. package/packages/daemon/src/api/gitRoutes.ts +409 -0
  50. package/packages/daemon/src/api/hooks.ts +588 -0
  51. package/packages/daemon/src/api/inputPolicy.ts +249 -0
  52. package/packages/daemon/src/api/notificationRoutes.ts +532 -0
  53. package/packages/daemon/src/api/policyRoutes.ts +234 -0
  54. package/packages/daemon/src/api/policySetRoutes.ts +445 -0
  55. package/packages/daemon/src/api/routeUtils.ts +28 -0
  56. package/packages/daemon/src/api/routes.ts +1004 -0
  57. package/packages/daemon/src/api/server.ts +1388 -0
  58. package/packages/daemon/src/api/settingsRoutes.ts +367 -0
  59. package/packages/daemon/src/api/sqliteErrors.ts +47 -0
  60. package/packages/daemon/src/api/stt.ts +143 -0
  61. package/packages/daemon/src/api/terminalRoutes.ts +200 -0
  62. package/packages/daemon/src/api/validation.ts +287 -0
  63. package/packages/daemon/src/api/validationRoutes.ts +174 -0
  64. package/packages/daemon/src/api/workflowRoutes.ts +567 -0
  65. package/packages/daemon/src/api/workspaceRoutes.ts +151 -0
  66. package/packages/daemon/src/api/ws.ts +1588 -0
  67. package/packages/daemon/src/auth/apiRateLimiter.ts +73 -0
  68. package/packages/daemon/src/auth/authMiddleware.ts +305 -0
  69. package/packages/daemon/src/auth/authService.ts +496 -0
  70. package/packages/daemon/src/auth/rateLimiter.ts +137 -0
  71. package/packages/daemon/src/config/pricing.ts +79 -0
  72. package/packages/daemon/src/crypto/encryption.ts +196 -0
  73. package/packages/daemon/src/db/db.ts +2745 -0
  74. package/packages/daemon/src/db/index.ts +16 -0
  75. package/packages/daemon/src/db/migrations.ts +182 -0
  76. package/packages/daemon/src/db/policyDb.ts +349 -0
  77. package/packages/daemon/src/db/schema.sql +408 -0
  78. package/packages/daemon/src/db/workflowDb.ts +464 -0
  79. package/packages/daemon/src/git/gitUtils.ts +544 -0
  80. package/packages/daemon/src/index.ts +6 -0
  81. package/packages/daemon/src/main.ts +525 -0
  82. package/packages/daemon/src/notifications/pushNotifier.ts +369 -0
  83. package/packages/daemon/src/providers/codexAppServerScaffold.ts +49 -0
  84. package/packages/daemon/src/providers/registry.ts +111 -0
  85. package/packages/daemon/src/providers/types.ts +82 -0
  86. package/packages/daemon/src/sessions/auditLogger.ts +103 -0
  87. package/packages/daemon/src/sessions/policyEngine.ts +165 -0
  88. package/packages/daemon/src/sessions/policyMatcher.ts +114 -0
  89. package/packages/daemon/src/sessions/policyTypes.ts +94 -0
  90. package/packages/daemon/src/sessions/ptyProcess.ts +141 -0
  91. package/packages/daemon/src/sessions/sessionManager.ts +1770 -0
  92. package/packages/daemon/src/sessions/tmuxSession.ts +1073 -0
  93. package/packages/daemon/src/sessions/transcriptManager.ts +110 -0
  94. package/packages/daemon/src/sessions/transcriptParser.ts +149 -0
  95. package/packages/daemon/src/sessions/transcriptTailer.ts +214 -0
  96. package/packages/daemon/src/tracking/tokenTracker.ts +168 -0
  97. package/packages/daemon/src/workflows/contextManager.ts +83 -0
  98. package/packages/daemon/src/workflows/index.ts +31 -0
  99. package/packages/daemon/src/workflows/resultExtractor.ts +118 -0
  100. package/packages/daemon/src/workflows/waitConditionPoller.ts +131 -0
  101. package/packages/daemon/src/workflows/workflowParser.ts +217 -0
  102. package/packages/daemon/src/workflows/workflowRunner.ts +969 -0
  103. package/packages/daemon/src/workflows/workflowTypes.ts +188 -0
  104. package/packages/daemon/src/workflows/workflowValidator.ts +533 -0
  105. package/packages/providers/claude-code/package.json +11 -0
  106. package/packages/providers/claude-code/src/index.ts +7 -0
  107. package/packages/providers/claude-code/src/overlaySettings.ts +198 -0
  108. package/packages/providers/claude-code/src/provider.ts +311 -0
  109. package/packages/web/dist/android-chrome-192x192.png +0 -0
  110. package/packages/web/dist/android-chrome-512x512.png +0 -0
  111. package/packages/web/dist/apple-touch-icon.png +0 -0
  112. package/packages/web/dist/assets/AnalyticsPage-BIopKWRf.js +17 -0
  113. package/packages/web/dist/assets/PoliciesPage-CjdLN3dl.js +11 -0
  114. package/packages/web/dist/assets/SessionDetailPage-BtSA0V0M.js +179 -0
  115. package/packages/web/dist/assets/SettingsPage-Dbbz4Ca5.js +37 -0
  116. package/packages/web/dist/assets/WorkflowsPage-Dv6f3GgU.js +1 -0
  117. package/packages/web/dist/assets/chart-vendor-DlOHLaCG.js +49 -0
  118. package/packages/web/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  119. package/packages/web/dist/assets/css.worker-BvV5MPou.js +93 -0
  120. package/packages/web/dist/assets/editor.worker-CKy7Pnvo.js +26 -0
  121. package/packages/web/dist/assets/html.worker-BLJhxQJQ.js +470 -0
  122. package/packages/web/dist/assets/index-BbdhRfr2.css +1 -0
  123. package/packages/web/dist/assets/index-hgphORiw.js +204 -0
  124. package/packages/web/dist/assets/json.worker-usMZ-FED.js +58 -0
  125. package/packages/web/dist/assets/monaco-core-B_19GPAS.css +1 -0
  126. package/packages/web/dist/assets/monaco-core-DQ5Mk8AK.js +1234 -0
  127. package/packages/web/dist/assets/monaco-react-DfZNWvtW.js +11 -0
  128. package/packages/web/dist/assets/monacoSetup-DvBj52bT.js +1 -0
  129. package/packages/web/dist/assets/pencil-Dbczxz59.js +11 -0
  130. package/packages/web/dist/assets/react-vendor-B5MgMUHH.js +136 -0
  131. package/packages/web/dist/assets/refresh-cw-B0MGsYPL.js +6 -0
  132. package/packages/web/dist/assets/tabs-C8LsWiR5.js +1 -0
  133. package/packages/web/dist/assets/terminal-vendor-Cs8KPbV3.js +9 -0
  134. package/packages/web/dist/assets/terminal-vendor-LcAfv9l9.css +32 -0
  135. package/packages/web/dist/assets/trash-2-Btlg0d4l.js +6 -0
  136. package/packages/web/dist/assets/ts.worker-DGHjMaqB.js +67731 -0
  137. package/packages/web/dist/favicon.ico +0 -0
  138. package/packages/web/dist/icon.svg +1 -0
  139. package/packages/web/dist/index.html +29 -0
  140. package/packages/web/dist/manifest.json +29 -0
  141. package/packages/web/dist/og-image.png +0 -0
  142. package/packages/web/dist/originals/android-chrome-192x192.png +0 -0
  143. package/packages/web/dist/originals/android-chrome-512x512.png +0 -0
  144. package/packages/web/dist/originals/apple-touch-icon.png +0 -0
  145. package/packages/web/dist/originals/favicon.ico +0 -0
  146. package/packages/web/dist/piper.svg +1 -0
  147. package/packages/web/dist/sounds/codepiper-soft-chime.wav +0 -0
  148. package/packages/web/dist/sw.js +257 -0
  149. package/scripts/postinstall-link-workspaces.mjs +58 -0
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@codepiper/provider-claude-code",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "dependencies": {
8
+ "@codepiper/core": "workspace:*",
9
+ "@codepiper/daemon": "workspace:*"
10
+ }
11
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Claude Code provider package exports
3
+ */
4
+
5
+ export type { ClaudeCodeSettings, OverlaySettingsOptions } from "./overlaySettings";
6
+ export { generateOverlaySettings } from "./overlaySettings";
7
+ export { ClaudeCodeProvider } from "./provider";
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Overlay settings generator for Claude Code sessions
3
+ *
4
+ * Generates a per-session settings file that configures hooks and statusline
5
+ * to forward events to the codepiper daemon.
6
+ */
7
+
8
+ import * as fs from "node:fs";
9
+ import { mkdir } from "node:fs/promises";
10
+ import { tmpdir } from "node:os";
11
+ import { dirname, join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ export interface OverlaySettingsOptions {
15
+ sessionId: string;
16
+ socketPath: string;
17
+ secret: string;
18
+ outputDir?: string;
19
+ enableStatusline?: boolean;
20
+ }
21
+
22
+ export interface ClaudeCodeSettings {
23
+ hooks?: {
24
+ SessionStart?: HookEntry[];
25
+ Notification?: HookEntry[];
26
+ PermissionRequest?: HookEntry[];
27
+ Stop?: HookEntry[];
28
+ };
29
+ statusline?: {
30
+ command: string;
31
+ stdin?: string;
32
+ };
33
+ }
34
+
35
+ interface HookEntry {
36
+ matcher?: Record<string, unknown>;
37
+ hooks: Hook[];
38
+ }
39
+
40
+ interface Hook {
41
+ type: "command";
42
+ command: string;
43
+ stdin?: string;
44
+ stdout?: string;
45
+ }
46
+
47
+ function shellQuote(value: string): string {
48
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
49
+ }
50
+
51
+ function writeExecutableScript(filePath: string, content: string): void {
52
+ const fd = fs.openSync(filePath, "w", 0o700);
53
+ try {
54
+ fs.writeSync(fd, content);
55
+ } finally {
56
+ fs.closeSync(fd);
57
+ }
58
+ try {
59
+ fs.chmodSync(filePath, 0o700);
60
+ } catch {
61
+ // best-effort on non-POSIX filesystems
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Generate overlay settings file for a Claude Code session
67
+ *
68
+ * The overlay configures:
69
+ * - Hooks to forward events to codepiper daemon
70
+ * - Environment variables for socket path, session ID, and auth secret
71
+ * - Optional statusline for session state tracking
72
+ *
73
+ * @param options Configuration for overlay generation
74
+ * @returns Path to generated settings file
75
+ */
76
+ export async function generateOverlaySettings(options: OverlaySettingsOptions): Promise<string> {
77
+ const {
78
+ sessionId,
79
+ socketPath,
80
+ secret,
81
+ outputDir = join(tmpdir(), "codepiper", "settings"),
82
+ enableStatusline = false,
83
+ } = options;
84
+
85
+ // Ensure output directory exists with restrictive permissions (contains session secret)
86
+ await mkdir(outputDir, { recursive: true, mode: 0o700 });
87
+ try {
88
+ fs.chmodSync(outputDir, 0o700);
89
+ } catch {
90
+ // best-effort on non-POSIX filesystems
91
+ }
92
+
93
+ // Generate settings file path
94
+ const settingsPath = join(outputDir, `${sessionId}.json`);
95
+
96
+ // Build hook command using full path to CLI (not relying on PATH)
97
+ // Get project root: packages/providers/claude-code/src/overlaySettings.ts -> ../../../..
98
+ const currentFile = fileURLToPath(import.meta.url);
99
+ const currentDir = dirname(currentFile);
100
+ const projectRoot = join(currentDir, "..", "..", "..", "..");
101
+ const cliPath = join(projectRoot, "packages", "cli", "src", "main.ts");
102
+ const hookScriptPath = join(outputDir, `${sessionId}.hook-forward.sh`);
103
+ const hookCommand = `sh ${shellQuote(hookScriptPath)}`;
104
+ const hookScript = [
105
+ "#!/usr/bin/env sh",
106
+ "set -eu",
107
+ `export CODEPIPER_UNIX_SOCK=${shellQuote(socketPath)}`,
108
+ `export CODEPIPER_SESSION=${shellQuote(sessionId)}`,
109
+ `export CODEPIPER_SECRET=${shellQuote(secret)}`,
110
+ `exec bun run ${shellQuote(cliPath)} hook-forward`,
111
+ "",
112
+ ].join("\n");
113
+ writeExecutableScript(hookScriptPath, hookScript);
114
+
115
+ // Build settings object using new hooks format (array with matchers)
116
+ const settings: ClaudeCodeSettings = {
117
+ hooks: {
118
+ SessionStart: [
119
+ {
120
+ hooks: [
121
+ {
122
+ type: "command",
123
+ command: hookCommand,
124
+ stdin: "event",
125
+ },
126
+ ],
127
+ },
128
+ ],
129
+ Notification: [
130
+ {
131
+ hooks: [
132
+ {
133
+ type: "command",
134
+ command: hookCommand,
135
+ stdin: "event",
136
+ },
137
+ ],
138
+ },
139
+ ],
140
+ PermissionRequest: [
141
+ {
142
+ hooks: [
143
+ {
144
+ type: "command",
145
+ command: hookCommand,
146
+ stdin: "event",
147
+ stdout: "context", // Claude Code reads decision from stdout
148
+ },
149
+ ],
150
+ },
151
+ ],
152
+ Stop: [
153
+ {
154
+ hooks: [
155
+ {
156
+ type: "command",
157
+ command: hookCommand,
158
+ stdin: "event",
159
+ },
160
+ ],
161
+ },
162
+ ],
163
+ },
164
+ };
165
+
166
+ // Add statusline if enabled
167
+ if (enableStatusline) {
168
+ const statuslineScriptPath = join(outputDir, `${sessionId}.statusline-forward.sh`);
169
+ const statuslineCommand = `sh ${shellQuote(statuslineScriptPath)}`;
170
+ const statuslineScript = [
171
+ "#!/usr/bin/env sh",
172
+ "set -eu",
173
+ `export CODEPIPER_UNIX_SOCK=${shellQuote(socketPath)}`,
174
+ `export CODEPIPER_SESSION=${shellQuote(sessionId)}`,
175
+ `export CODEPIPER_SECRET=${shellQuote(secret)}`,
176
+ "exec codepiper statusline-forward",
177
+ "",
178
+ ].join("\n");
179
+ writeExecutableScript(statuslineScriptPath, statuslineScript);
180
+
181
+ settings.statusline = {
182
+ command: statuslineCommand,
183
+ stdin: "event",
184
+ };
185
+ }
186
+
187
+ // Write settings file with restrictive permissions (contains session secret)
188
+ const fd = fs.openSync(settingsPath, "w", 0o600);
189
+ fs.writeSync(fd, JSON.stringify(settings, null, 2));
190
+ fs.closeSync(fd);
191
+ try {
192
+ fs.chmodSync(settingsPath, 0o600);
193
+ } catch {
194
+ // best-effort on non-POSIX filesystems
195
+ }
196
+
197
+ return settingsPath;
198
+ }
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Claude Code provider implementation
3
+ *
4
+ * Spawns and manages Claude Code sessions via PTY, configuring hooks
5
+ * for event forwarding to the CodePiper daemon.
6
+ */
7
+
8
+ import { randomBytes } from "node:crypto";
9
+ import type {
10
+ Provider,
11
+ ProviderEvent,
12
+ ProviderId,
13
+ SessionHandle,
14
+ StartSessionOptions,
15
+ } from "@codepiper/core";
16
+ import { PTYProcess } from "@codepiper/daemon";
17
+ import { generateOverlaySettings } from "./overlaySettings";
18
+
19
+ interface ActiveSession {
20
+ handle: SessionHandle;
21
+ pty: PTYProcess;
22
+ settingsPath: string;
23
+ currentModel?: string;
24
+ }
25
+
26
+ /**
27
+ * Key name to escape sequence mapping
28
+ */
29
+ const KEY_SEQUENCES: Record<string, string> = {
30
+ // Basic keys
31
+ enter: "\r",
32
+ return: "\r",
33
+ tab: "\t",
34
+ "shift+tab": "\x1b[Z",
35
+ escape: "\x1b",
36
+ esc: "\x1b",
37
+ space: " ",
38
+ backspace: "\x7f",
39
+ delete: "\x1b[3~",
40
+
41
+ // Control keys
42
+ "ctrl+a": "\x01",
43
+ "ctrl+b": "\x02",
44
+ "ctrl+c": "\x03",
45
+ "ctrl+d": "\x04",
46
+ "ctrl+e": "\x05",
47
+ "ctrl+f": "\x06",
48
+ "ctrl+g": "\x07",
49
+ "ctrl+h": "\x08",
50
+ "ctrl+i": "\x09",
51
+ "ctrl+j": "\x0a",
52
+ "ctrl+k": "\x0b",
53
+ "ctrl+l": "\x0c",
54
+ "ctrl+m": "\x0d",
55
+ "ctrl+n": "\x0e",
56
+ "ctrl+o": "\x0f",
57
+ "ctrl+p": "\x10",
58
+ "ctrl+q": "\x11",
59
+ "ctrl+r": "\x12",
60
+ "ctrl+s": "\x13",
61
+ "ctrl+t": "\x14",
62
+ "ctrl+u": "\x15",
63
+ "ctrl+v": "\x16",
64
+ "ctrl+w": "\x17",
65
+ "ctrl+x": "\x18",
66
+ "ctrl+y": "\x19",
67
+ "ctrl+z": "\x1a",
68
+
69
+ // Arrow keys
70
+ up: "\x1b[A",
71
+ down: "\x1b[B",
72
+ right: "\x1b[C",
73
+ left: "\x1b[D",
74
+
75
+ // Function keys
76
+ f1: "\x1bOP",
77
+ f2: "\x1bOQ",
78
+ f3: "\x1bOR",
79
+ f4: "\x1bOS",
80
+ f5: "\x1b[15~",
81
+ f6: "\x1b[17~",
82
+ f7: "\x1b[18~",
83
+ f8: "\x1b[19~",
84
+ f9: "\x1b[20~",
85
+ f10: "\x1b[21~",
86
+ f11: "\x1b[23~",
87
+ f12: "\x1b[24~",
88
+
89
+ // Special
90
+ home: "\x1b[H",
91
+ end: "\x1b[F",
92
+ pageup: "\x1b[5~",
93
+ pagedown: "\x1b[6~",
94
+ insert: "\x1b[2~",
95
+ };
96
+
97
+ export class ClaudeCodeProvider implements Provider {
98
+ public readonly id: ProviderId = "claude-code";
99
+
100
+ private sessions: Map<string, ActiveSession> = new Map();
101
+ private eventCallbacks: Array<(evt: ProviderEvent) => void> = [];
102
+ private socketPath: string;
103
+ private enableStatusline: boolean;
104
+
105
+ constructor(options?: {
106
+ socketPath?: string;
107
+ enableStatusline?: boolean;
108
+ }) {
109
+ this.socketPath = options?.socketPath || "/tmp/codepiper.sock";
110
+ this.enableStatusline = options?.enableStatusline ?? false;
111
+ }
112
+
113
+ async startSession(opts: StartSessionOptions): Promise<SessionHandle> {
114
+ const { id: sessionId, cwd, env, args = [], model, billingMode } = opts;
115
+
116
+ // Conditionally scrub ANTHROPIC_API_KEY based on billing mode
117
+ const cleanEnv = { ...env };
118
+ if ((billingMode ?? "subscription") === "subscription") {
119
+ delete cleanEnv.ANTHROPIC_API_KEY;
120
+ }
121
+
122
+ // Generate per-session secret for hook authentication
123
+ const secret = randomBytes(32).toString("hex");
124
+
125
+ // Generate overlay settings file
126
+ const settingsPath = await generateOverlaySettings({
127
+ sessionId,
128
+ socketPath: this.socketPath,
129
+ secret,
130
+ enableStatusline: this.enableStatusline,
131
+ });
132
+
133
+ // Build command arguments
134
+ const command = ["claude", "--session-id", sessionId, "--settings", settingsPath];
135
+
136
+ // Add model flag if specified
137
+ if (model) {
138
+ command.push("--model", model);
139
+ }
140
+
141
+ command.push(...args);
142
+
143
+ // Create session handle
144
+ const handle: SessionHandle = {
145
+ id: sessionId,
146
+ provider: this.id,
147
+ cwd,
148
+ status: "STARTING",
149
+ createdAt: new Date(),
150
+ updatedAt: new Date(),
151
+ metadata: {
152
+ settingsPath,
153
+ secret,
154
+ },
155
+ };
156
+
157
+ // Spawn PTY process
158
+ const pty = new PTYProcess({
159
+ command,
160
+ cwd,
161
+ env: cleanEnv,
162
+ cols: 120,
163
+ rows: 30,
164
+ onData: (data) => {
165
+ this.emitEvent({
166
+ sessionId,
167
+ type: "pty_output",
168
+ timestamp: new Date(),
169
+ payload: { data },
170
+ });
171
+ },
172
+ onExit: (exitCode, signal) => {
173
+ this.emitEvent({
174
+ sessionId,
175
+ type: "pty_exit",
176
+ timestamp: new Date(),
177
+ payload: { exitCode, signal },
178
+ });
179
+
180
+ // Update handle and clean up
181
+ const session = this.sessions.get(sessionId);
182
+ if (session) {
183
+ session.handle.status = exitCode === 0 ? "STOPPED" : "CRASHED";
184
+ session.handle.updatedAt = new Date();
185
+ this.sessions.delete(sessionId);
186
+ }
187
+ },
188
+ });
189
+
190
+ // Update handle with PID
191
+ handle.pid = pty.pid;
192
+
193
+ // Store session
194
+ const activeSession: ActiveSession = {
195
+ handle,
196
+ pty,
197
+ settingsPath,
198
+ };
199
+ if (model !== undefined) {
200
+ activeSession.currentModel = model;
201
+ }
202
+ this.sessions.set(sessionId, activeSession);
203
+
204
+ return handle;
205
+ }
206
+
207
+ async sendText(sessionId: string, text: string): Promise<void> {
208
+ const session = this.sessions.get(sessionId);
209
+ if (!session) {
210
+ throw new Error(`Session not found: ${sessionId}`);
211
+ }
212
+
213
+ session.pty.write(text);
214
+ session.handle.updatedAt = new Date();
215
+ }
216
+
217
+ async sendKeys(sessionId: string, keys: string[]): Promise<void> {
218
+ const session = this.sessions.get(sessionId);
219
+ if (!session) {
220
+ throw new Error(`Session not found: ${sessionId}`);
221
+ }
222
+
223
+ for (const key of keys) {
224
+ const sequence = KEY_SEQUENCES[key.toLowerCase()];
225
+ if (sequence) {
226
+ session.pty.write(sequence);
227
+ } else {
228
+ // If not a special key, write it directly
229
+ session.pty.write(key);
230
+ }
231
+ }
232
+
233
+ session.handle.updatedAt = new Date();
234
+ }
235
+
236
+ async stopSession(sessionId: string): Promise<void> {
237
+ const session = this.sessions.get(sessionId);
238
+ if (!session) {
239
+ throw new Error(`Session not found: ${sessionId}`);
240
+ }
241
+
242
+ await session.pty.kill();
243
+ session.handle.status = "STOPPED";
244
+ session.handle.updatedAt = new Date();
245
+
246
+ this.sessions.delete(sessionId);
247
+ }
248
+
249
+ /**
250
+ * Switch the model for an active session
251
+ *
252
+ * Uses the /model slash command to change the model in Claude Code
253
+ *
254
+ * @param sessionId - Session ID
255
+ * @param model - Model name (e.g., "sonnet", "opus", "haiku", "opusplan")
256
+ */
257
+ async switchModel(sessionId: string, model: string): Promise<void> {
258
+ const session = this.sessions.get(sessionId);
259
+ if (!session) {
260
+ throw new Error(`Session not found: ${sessionId}`);
261
+ }
262
+
263
+ // Send /model slash command
264
+ const command = `/model ${model}\r`;
265
+ session.pty.write(command);
266
+
267
+ // Update current model
268
+ session.currentModel = model;
269
+ session.handle.updatedAt = new Date();
270
+
271
+ // Emit model switch event
272
+ this.emitEvent({
273
+ sessionId,
274
+ type: "model_switch",
275
+ timestamp: new Date(),
276
+ payload: { model },
277
+ });
278
+ }
279
+
280
+ /**
281
+ * Get the current model for a session
282
+ */
283
+ getCurrentModel(sessionId: string): string | undefined {
284
+ const session = this.sessions.get(sessionId);
285
+ return session?.currentModel;
286
+ }
287
+
288
+ onEvent(cb: (evt: ProviderEvent) => void): void {
289
+ this.eventCallbacks.push(cb);
290
+ }
291
+
292
+ /**
293
+ * Get list of active session IDs (useful for testing)
294
+ */
295
+ getSessions(): string[] {
296
+ return Array.from(this.sessions.keys());
297
+ }
298
+
299
+ /**
300
+ * Emit event to all registered callbacks
301
+ */
302
+ private emitEvent(evt: ProviderEvent): void {
303
+ for (const cb of this.eventCallbacks) {
304
+ try {
305
+ cb(evt);
306
+ } catch (error) {
307
+ console.error("Error in event callback:", error);
308
+ }
309
+ }
310
+ }
311
+ }
@@ -0,0 +1,17 @@
1
+ import{r as i,j as e}from"./react-vendor-B5MgMUHH.js";import{c as A,a as f,f as h,u as U,S as ne,b as le,d as ce,e as de,g as N,B as xe,M as me,A as he,Z as pe}from"./index-hgphORiw.js";import{R as g,A as B,C as E,X as w,Y as S,T as v,a as u,B as K,b as F,P as ue,d as fe,e as je}from"./chart-vendor-DlOHLaCG.js";import"./monaco-core-DQ5Mk8AK.js";/**
2
+ * @license lucide-react v0.564.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const ye=[["line",{x1:"12",x2:"12",y1:"2",y2:"22",key:"7eqyqh"}],["path",{d:"M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6",key:"1b0p4s"}]],ge=A("dollar-sign",ye);/**
7
+ * @license lucide-react v0.564.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const ve=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],be=A("download",ve);/**
12
+ * @license lucide-react v0.564.0 - ISC
13
+ *
14
+ * This source code is licensed under the ISC license.
15
+ * See the LICENSE file in the root directory of this source tree.
16
+ */const ke=[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]],Ne=A("server",ke),l=i.forwardRef(({className:t,...a},s)=>e.jsx("div",{ref:s,className:f("rounded-lg border bg-card text-card-foreground shadow-sm",t),...a}));l.displayName="Card";const c=i.forwardRef(({className:t,...a},s)=>e.jsx("div",{ref:s,className:f("flex flex-col space-y-1.5 p-6",t),...a}));c.displayName="CardHeader";const d=i.forwardRef(({className:t,...a},s)=>e.jsx("h3",{ref:s,className:f("text-2xl font-semibold leading-none tracking-tight",t),...a}));d.displayName="CardTitle";const Ce=i.forwardRef(({className:t,...a},s)=>e.jsx("p",{ref:s,className:f("text-sm text-muted-foreground",t),...a}));Ce.displayName="CardDescription";const x=i.forwardRef(({className:t,...a},s)=>e.jsx("div",{ref:s,className:f("p-6 pt-0",t),...a}));x.displayName="CardContent";const we=i.forwardRef(({className:t,...a},s)=>e.jsx("div",{ref:s,className:f("flex items-center p-6 pt-0",t),...a}));we.displayName="CardFooter";function Se({data:t}){return t.length===0?e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Activity Timeline"})}),e.jsx(x,{children:e.jsx("div",{className:"flex h-[280px] items-center justify-center text-sm text-muted-foreground/50",children:"No activity data yet"})})]}):e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Activity Timeline"})}),e.jsx(x,{children:e.jsx(g,{width:"100%",height:280,children:e.jsxs(B,{data:t,children:[e.jsxs("defs",{children:[e.jsxs("linearGradient",{id:"userGrad",x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"5%",stopColor:"#06b6d4",stopOpacity:.3}),e.jsx("stop",{offset:"95%",stopColor:"#06b6d4",stopOpacity:0})]}),e.jsxs("linearGradient",{id:"assistantGrad",x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"5%",stopColor:"#8b5cf6",stopOpacity:.3}),e.jsx("stop",{offset:"95%",stopColor:"#8b5cf6",stopOpacity:0})]})]}),e.jsx(E,{strokeDasharray:"3 3",stroke:"hsl(var(--border))",strokeOpacity:.3}),e.jsx(w,{dataKey:"date",stroke:"hsl(var(--muted-foreground))",fontSize:11,tickLine:!1,axisLine:!1,tickFormatter:a=>{const s=new Date(a);return`${s.getMonth()+1}/${s.getDate()}`}}),e.jsx(S,{stroke:"hsl(var(--muted-foreground))",fontSize:11,tickLine:!1,axisLine:!1,allowDecimals:!1}),e.jsx(v,{contentStyle:{backgroundColor:"hsl(var(--card))",border:"1px solid hsl(var(--border))",borderRadius:"8px",fontSize:"12px",color:"hsl(var(--foreground))"},labelFormatter:a=>`Date: ${a}`}),e.jsx(u,{type:"monotone",dataKey:"user_messages",name:"User messages",stroke:"#06b6d4",strokeWidth:2,fill:"url(#userGrad)"}),e.jsx(u,{type:"monotone",dataKey:"assistant_messages",name:"Assistant messages",stroke:"#8b5cf6",strokeWidth:2,fill:"url(#assistantGrad)"})]})})})]})}function Re({data:t}){return t.length===0?e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Sessions by Provider"})}),e.jsx(x,{children:e.jsx("div",{className:"flex h-[280px] items-center justify-center text-sm text-muted-foreground/50",children:"No session data yet"})})]}):e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Sessions by Provider"})}),e.jsx(x,{children:e.jsx(g,{width:"100%",height:280,children:e.jsxs(K,{data:t,children:[e.jsx(w,{dataKey:"provider",stroke:"hsl(var(--muted-foreground))",fontSize:11,tickLine:!1,axisLine:!1}),e.jsx(S,{stroke:"hsl(var(--muted-foreground))",fontSize:11,tickLine:!1,axisLine:!1,allowDecimals:!1}),e.jsx(v,{contentStyle:{backgroundColor:"hsl(var(--card))",border:"1px solid hsl(var(--border))",borderRadius:"8px",fontSize:"12px",color:"hsl(var(--foreground))"},formatter:a=>[`${a??0} sessions`,"Count"]}),e.jsx(F,{dataKey:"count",fill:"#8b5cf6",radius:[4,4,0,0],maxBarSize:48})]})})})]})}function y({title:t,value:a,subtitle:s,icon:r}){return e.jsxs("div",{className:"rounded-xl border border-border bg-card backdrop-blur-sm p-5",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("span",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:t}),r]}),e.jsx("div",{className:"text-2xl font-bold tracking-tight",children:a}),s&&e.jsx("p",{className:"text-xs text-muted-foreground/60 mt-1",children:s})]})}const C=["#06b6d4","#8b5cf6","#10b981","#f59e0b","#ef4444","#ec4899"];function Te(t){return t.replace("claude-","").replace("-20250929","").replace("-20251001","").replace("-20260214","")}function Le({data:t}){if(t.length===0)return e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Tokens by Model"})}),e.jsx(x,{children:e.jsx("div",{className:"flex h-[280px] items-center justify-center text-sm text-muted-foreground/50",children:"No model data yet"})})]});const a=t.map(r=>({...r,usedTokens:(r.prompt_tokens??0)+(r.completion_tokens??0)})),s=a.reduce((r,p)=>r+p.usedTokens,0);return e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Tokens by Model"})}),e.jsx(x,{children:e.jsxs("div",{className:"flex items-center gap-6",children:[e.jsx(g,{width:180,height:180,children:e.jsxs(ue,{children:[e.jsx(fe,{data:a,dataKey:"usedTokens",nameKey:"model",cx:"50%",cy:"50%",innerRadius:50,outerRadius:80,strokeWidth:2,stroke:"hsl(var(--card))",children:a.map((r,p)=>e.jsx(je,{fill:C[p%C.length]},r.model))}),e.jsx(v,{contentStyle:{backgroundColor:"hsl(var(--card))",border:"1px solid hsl(var(--border))",borderRadius:"8px",fontSize:"12px",color:"hsl(var(--foreground))"},formatter:r=>h(r??0)})]})}),e.jsx("div",{className:"flex-1 space-y-2.5",children:a.map((r,p)=>{const b=r.usedTokens,R=s>0?(b/s*100).toFixed(1):"0";return e.jsxs("div",{className:"flex items-center gap-2.5",children:[e.jsx("div",{className:"h-2.5 w-2.5 rounded-full shrink-0",style:{backgroundColor:C[p%C.length]}}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"text-xs font-mono truncate",children:Te(r.model)}),e.jsxs("div",{className:"text-[10px] text-muted-foreground/60",children:[h(b)," tokens (",R,"%)",(r.cache_read??0)>0&&e.jsxs("span",{children:[" · ",h(r.cache_read)," cached"]}),e.jsxs("span",{children:[" · ",r.requests," req"]}),r.costEstimate!=null&&r.costEstimate>0&&e.jsxs("span",{children:[" · ~$",r.costEstimate.toFixed(2)]})]})]})]},r.model)})})]})})]})}const n={prompt:"#06b6d4",completion:"#8b5cf6",cacheCreation:"#f59e0b",cacheRead:"#10b981"};function De({data:t}){return t.length===0?e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Token Usage Over Time"})}),e.jsx(x,{children:e.jsx("div",{className:"flex h-[280px] items-center justify-center text-sm text-muted-foreground/50",children:"No token data yet"})})]}):e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Token Usage Over Time"})}),e.jsxs(x,{children:[e.jsx(g,{width:"100%",height:280,children:e.jsxs(B,{data:t,children:[e.jsxs("defs",{children:[e.jsxs("linearGradient",{id:"promptGrad",x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"5%",stopColor:n.prompt,stopOpacity:.3}),e.jsx("stop",{offset:"95%",stopColor:n.prompt,stopOpacity:0})]}),e.jsxs("linearGradient",{id:"completionGrad",x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"5%",stopColor:n.completion,stopOpacity:.3}),e.jsx("stop",{offset:"95%",stopColor:n.completion,stopOpacity:0})]}),e.jsxs("linearGradient",{id:"cacheCreateGrad",x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"5%",stopColor:n.cacheCreation,stopOpacity:.3}),e.jsx("stop",{offset:"95%",stopColor:n.cacheCreation,stopOpacity:0})]}),e.jsxs("linearGradient",{id:"cacheReadGrad",x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"5%",stopColor:n.cacheRead,stopOpacity:.3}),e.jsx("stop",{offset:"95%",stopColor:n.cacheRead,stopOpacity:0})]})]}),e.jsx(E,{strokeDasharray:"3 3",stroke:"hsl(var(--border))",strokeOpacity:.3}),e.jsx(w,{dataKey:"date",stroke:"hsl(var(--muted-foreground))",fontSize:11,tickLine:!1,axisLine:!1,tickFormatter:a=>{const s=new Date(a);return`${s.getMonth()+1}/${s.getDate()}`}}),e.jsx(S,{stroke:"hsl(var(--muted-foreground))",fontSize:11,tickLine:!1,axisLine:!1,allowDecimals:!1,tickFormatter:a=>h(a)}),e.jsx(v,{contentStyle:{backgroundColor:"hsl(var(--card))",border:"1px solid hsl(var(--border))",borderRadius:"8px",fontSize:"12px",color:"hsl(var(--foreground))"},formatter:a=>h(a??0),labelFormatter:a=>`Date: ${a}`}),e.jsx(u,{type:"monotone",dataKey:"prompt",stackId:"1",stroke:n.prompt,strokeWidth:2,fill:"url(#promptGrad)",name:"Prompt"}),e.jsx(u,{type:"monotone",dataKey:"completion",stackId:"1",stroke:n.completion,strokeWidth:2,fill:"url(#completionGrad)",name:"Completion"}),e.jsx(u,{type:"monotone",dataKey:"cacheCreation",stackId:"1",stroke:n.cacheCreation,strokeWidth:2,fill:"url(#cacheCreateGrad)",name:"Cache Creation"}),e.jsx(u,{type:"monotone",dataKey:"cacheRead",stackId:"1",stroke:n.cacheRead,strokeWidth:2,fill:"url(#cacheReadGrad)",name:"Cache Read"})]})}),e.jsx("div",{className:"mt-3 flex justify-center gap-5 text-xs",children:Object.entries(n).map(([a,s])=>e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("div",{className:"h-2 w-2 rounded-full",style:{backgroundColor:s}}),e.jsx("span",{className:"text-muted-foreground capitalize",children:a.replace(/([A-Z])/g," $1").trim()})]},a))})]})]})}function Ae({data:t}){return t.length===0?e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Tool Usage"})}),e.jsx(x,{children:e.jsx("div",{className:"flex h-[280px] items-center justify-center text-sm text-muted-foreground/50",children:"No tool usage data yet"})})]}):e.jsxs(l,{children:[e.jsx(c,{children:e.jsx(d,{className:"text-base",children:"Tool Usage"})}),e.jsx(x,{children:e.jsx(g,{width:"100%",height:280,children:e.jsxs(K,{data:t,layout:"vertical",margin:{left:60},children:[e.jsx(w,{type:"number",stroke:"hsl(var(--muted-foreground))",fontSize:11,tickLine:!1,axisLine:!1,allowDecimals:!1}),e.jsx(S,{type:"category",dataKey:"tool",stroke:"hsl(var(--muted-foreground))",fontSize:10,tickLine:!1,axisLine:!1,width:55}),e.jsx(v,{contentStyle:{backgroundColor:"hsl(var(--card))",border:"1px solid hsl(var(--border))",borderRadius:"8px",fontSize:"12px",color:"hsl(var(--foreground))"},formatter:a=>[`${a??0} calls`,"Usage"]}),e.jsx(F,{dataKey:"count",fill:"#06b6d4",radius:[0,4,4,0],maxBarSize:24})]})})})]})}function Me(){const[t,a]=i.useState("7d"),[s,r]=i.useState(null),[p,b]=i.useState([]),[R,_]=i.useState([]),[$,q]=i.useState([]),[H,I]=i.useState([]),[O,W]=i.useState([]),[V,Z]=i.useState([]),[X,P]=i.useState(!0),G=i.useCallback(async()=>{P(!0);try{const[o,T,L,D,k,j,m]=await Promise.all([fetch(`/api/analytics/overview?range=${t}`),fetch(`/api/analytics/activity-timeline?range=${t}`),fetch(`/api/analytics/tokens-by-model?range=${t}`),fetch(`/api/analytics/token-usage?range=${t}`),fetch(`/api/analytics/tool-usage?range=${t}`),fetch(`/api/analytics/sessions-by-provider?range=${t}`),fetch("/api/providers")]);if(!o.ok)throw new Error("Failed to load overview");const[se,te,ae,re,oe,ie,z]=await Promise.all([o.json(),T.json(),L.json(),D.json(),k.json(),j.json(),m.ok?m.json():Promise.resolve({providers:[]})]);r(se),b(te),_(ae),q(re),I(oe),W(ie),Z(Array.isArray(z.providers)?z.providers:[])}catch(o){console.error("Error loading analytics:",o),U.error("Failed to load analytics data")}finally{P(!1)}},[t]);i.useEffect(()=>{G()},[G]);const Y=()=>{const o=["Date","Prompt Tokens","Completion Tokens","Cache Read","Cache Creation"],T=$.map(m=>[m.date,m.prompt,m.completion,m.cacheRead,m.cacheCreation]),L=[o,...T].map(m=>m.join(",")).join(`
17
+ `),D=new Blob([L],{type:"text/csv"}),k=URL.createObjectURL(D),j=document.createElement("a");j.href=k,j.download=`analytics-${t}.csv`,j.click(),URL.revokeObjectURL(k),U.success("CSV exported successfully")};if(X||!s)return e.jsxs("div",{className:"p-4 md:p-8 max-w-7xl mx-auto",children:[e.jsx("div",{className:"mb-6 flex items-center justify-between",children:e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl md:text-3xl font-bold tracking-tight",children:"Analytics"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Token usage, activity patterns, and session metrics"})]})}),e.jsx("div",{className:"flex h-64 items-center justify-center",children:e.jsxs("div",{className:"flex items-center gap-3 text-muted-foreground",children:[e.jsx("div",{className:"w-4 h-4 border-2 border-cyan-500/30 border-t-cyan-500 rounded-full animate-spin"}),e.jsx("span",{className:"text-sm",children:"Loading analytics..."})]})})]});const J=new Map(V.map(o=>[o.id,o])),M=O.filter(o=>Number(o.count)>0).map(o=>J.get(String(o.provider))).filter(o=>!!o&&o.capabilities.metricsChannel!=="transcript"),Q=M.length>0,ee=Array.from(new Set(M.map(o=>o.label)));return e.jsxs("div",{className:"p-4 md:p-8 max-w-7xl mx-auto",children:[e.jsxs("div",{className:"mb-6 flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl md:text-3xl font-bold tracking-tight",children:"Analytics"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Token usage, activity patterns, and session metrics"})]}),e.jsxs("div",{className:"flex gap-3",children:[e.jsxs(ne,{value:t,onValueChange:a,children:[e.jsx(le,{className:"w-36 border-border bg-muted/30",children:e.jsx(ce,{})}),e.jsxs(de,{children:[e.jsx(N,{value:"today",children:"Today"}),e.jsx(N,{value:"7d",children:"Last 7 Days"}),e.jsx(N,{value:"30d",children:"Last 30 Days"}),e.jsx(N,{value:"all",children:"All Time"})]})]}),e.jsxs(xe,{variant:"outline",onClick:Y,className:"border-border hover:bg-accent/60",children:[e.jsx(be,{className:"h-3.5 w-3.5 mr-1.5"}),"Export"]})]})]}),e.jsxs("div",{className:"mb-6 grid grid-cols-2 gap-3 md:gap-4 md:grid-cols-3 lg:grid-cols-5",children:[e.jsx(y,{title:"Total Sessions",value:s.sessionsCount,subtitle:`${s.activeSessions} active`,icon:e.jsx(Ne,{className:"h-4 w-4 text-cyan-400"})}),e.jsx(y,{title:"Total Messages",value:h(s.totalMessages),subtitle:"User + assistant turns",icon:e.jsx(me,{className:"h-4 w-4 text-emerald-400"})}),e.jsx(y,{title:"Tokens Used",value:h(s.inputTokens!=null?s.inputTokens+s.outputTokens:s.totalTokens),subtitle:(s.cacheReadTokens??0)>0?`${h(s.cacheReadTokens)} cached`:void 0,icon:e.jsx(he,{className:"h-4 w-4 text-violet-400"})}),e.jsx(y,{title:"Cache Hit Rate",value:`${s.cacheHitRate}%`,subtitle:s.cacheHitRate>=50?"Excellent":s.cacheHitRate>=20?"Good":"Low",icon:e.jsx(pe,{className:"h-4 w-4 text-amber-400"})}),e.jsx(y,{title:"Est. API Cost",value:`$${s.costEstimate.toFixed(2)}`,subtitle:"Equivalent if billed per-token",icon:e.jsx(ge,{className:"h-4 w-4 text-emerald-400"})})]}),s.costEstimate>0&&e.jsx("div",{className:"mb-6 rounded-lg border border-border/60 bg-muted/20 px-4 py-3",children:e.jsxs("p",{className:"text-xs text-muted-foreground/70",children:[e.jsx("span",{className:"font-medium text-muted-foreground",children:"Cost estimate disclaimer:"})," ","This is an approximate equivalent API cost based on published Anthropic pricing. Actual costs may differ due to long-context pricing (>200K tokens), internal requests not captured in transcripts (e.g. context summarization), and pricing changes. Max plan subscribers are not charged per-token."]})}),Q&&e.jsx("div",{className:"mb-6 rounded-lg border border-amber-500/40 bg-amber-500/5 px-4 py-3",children:e.jsxs("p",{className:"text-xs text-muted-foreground/80",children:[e.jsx("span",{className:"font-medium text-amber-300",children:"Provider metrics note:"})," Token and tool analytics are transcript-driven. Active sessions from"," ",ee.join(", ")," may not fully appear in token charts."]})}),e.jsxs("div",{className:"grid gap-4 lg:grid-cols-2",children:[e.jsx("div",{className:"lg:col-span-2",children:e.jsx(Se,{data:p})}),e.jsx(Le,{data:R}),e.jsx(Ae,{data:H}),e.jsx("div",{className:"lg:col-span-2",children:e.jsx(De,{data:$})}),e.jsx(Re,{data:O})]})]})}export{Me as AnalyticsPage};
@@ -0,0 +1,11 @@
1
+ import{r as c,j as e}from"./react-vendor-B5MgMUHH.js";import{c as z,S as V,b as O,d as U,e as q,g as y,h as v,i as B,j as f,u,B as p,P as D,I as j,D as S,k as $,l as F,m as I,n as E,o as R,p as T,C as X,q as J,X as K,r as L}from"./index-hgphORiw.js";import{T as _}from"./trash-2-Btlg0d4l.js";import{P as Q,L as k}from"./pencil-Dbczxz59.js";import{T as Y,a as Z,b as C,c as P}from"./tabs-C8LsWiR5.js";import"./monaco-core-DQ5Mk8AK.js";import"./chart-vendor-DlOHLaCG.js";/**
2
+ * @license lucide-react v0.564.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const ee=[["path",{d:"M12 3v18",key:"108xh3"}],["path",{d:"m19 8 3 8a5 5 0 0 1-6 0zV7",key:"zcdpyk"}],["path",{d:"M3 7h1a17 17 0 0 0 8-2 17 17 0 0 0 8 2h1",key:"1yorad"}],["path",{d:"m5 8 3 8a5 5 0 0 1-6 0zV7",key:"eua70x"}],["path",{d:"M7 21h10",key:"1b0cd5"}]],se=z("scale",ee);/**
7
+ * @license lucide-react v0.564.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const te=[["path",{d:"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z",key:"r04s7s"}]],M=z("star",te);function re(){const[s,h]=c.useState([]),[n,i]=c.useState(!0),[m,a]=c.useState("all");c.useEffect(()=>{(async()=>{i(!0);try{const x=await f.getPolicyDecisions({decision:m==="all"?void 0:m,limit:100});h(x.decisions)}catch{u.error("Failed to load audit log")}finally{i(!1)}})()},[m]);const r=t=>{switch(t){case"allow":return"success";case"deny":return"destructive";case"ask":return"warning";default:return"secondary"}};return e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("p",{className:"text-sm text-muted-foreground",children:"Recent permission decisions across all sessions."}),e.jsxs(V,{value:m,onValueChange:t=>a(t),children:[e.jsx(O,{className:"w-32 h-8 text-xs border-border bg-muted/30",children:e.jsx(U,{})}),e.jsxs(q,{children:[e.jsx(y,{value:"all",children:"All"}),e.jsx(y,{value:"allow",children:"Allow"}),e.jsx(y,{value:"deny",children:"Deny"}),e.jsx(y,{value:"ask",children:"Ask"})]})]})]}),n?e.jsxs("div",{className:"flex items-center gap-2 text-muted-foreground py-8 justify-center",children:[e.jsx("div",{className:"w-3 h-3 border-2 border-cyan-500/30 border-t-cyan-500 rounded-full animate-spin"}),e.jsx("span",{className:"text-xs",children:"Loading audit log..."})]}):s.length===0?e.jsx("div",{className:"text-center py-12 text-muted-foreground/50 text-sm",children:"No policy decisions recorded yet."}):e.jsxs("div",{className:"rounded-xl border border-border overflow-hidden bg-card/80 backdrop-blur-sm",children:[e.jsxs("div",{className:"grid grid-cols-[80px_100px_1fr_80px_1fr_100px] gap-3 px-4 py-2.5 border-b border-border text-[11px] font-medium text-muted-foreground uppercase tracking-wider",children:[e.jsx("div",{children:"Decision"}),e.jsx("div",{children:"Session"}),e.jsx("div",{children:"Tool"}),e.jsx("div",{children:"Policy"}),e.jsx("div",{children:"Reason"}),e.jsx("div",{children:"Time"})]}),s.map(t=>e.jsxs("div",{className:"grid grid-cols-[80px_100px_1fr_80px_1fr_100px] gap-3 px-4 py-2.5 border-b border-border/60 last:border-0 text-sm items-center",children:[e.jsx("div",{children:e.jsx(v,{variant:r(t.decision),className:"text-[10px]",children:t.decision})}),e.jsx("div",{className:"font-mono text-xs text-muted-foreground truncate",children:t.sessionId.slice(0,8)}),e.jsx("div",{className:"font-mono text-xs truncate",children:t.toolName}),e.jsx("div",{className:"font-mono text-[10px] text-muted-foreground/50 truncate",children:t.policyId?t.policyId.slice(0,8):"-"}),e.jsx("div",{className:"text-xs text-muted-foreground truncate",children:t.reason||"-"}),e.jsx("div",{className:"text-[11px] text-muted-foreground",children:B(t.timestamp)})]},t.id))]})]})}function G({rules:s,onChange:h}){const n=()=>{h([...s,{id:crypto.randomUUID(),action:"allow",tool:"",reason:""}])},i=(a,r)=>{const t=[...s];t[a]={...t[a],...r},h(t)},m=a=>{h(s.filter((r,t)=>t!==a))};return e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("p",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Rules"}),e.jsxs(p,{type:"button",variant:"ghost",size:"sm",onClick:n,className:"h-7 text-xs",children:[e.jsx(D,{className:"h-3 w-3 mr-1"}),"Add Rule"]})]}),s.length===0&&e.jsx("p",{className:"text-xs text-muted-foreground/50 text-center py-4 border border-dashed border-border rounded-lg",children:"No rules yet. Add a rule to define permission behavior."}),s.map((a,r)=>e.jsxs("div",{className:"grid grid-cols-[100px_1fr_1fr_32px] gap-2 items-start p-3 rounded-lg border border-border bg-muted/20",children:[e.jsxs(V,{value:a.action,onValueChange:t=>i(r,{action:t}),children:[e.jsx(O,{className:"h-8 text-xs border-border bg-muted/30",children:e.jsx(U,{})}),e.jsxs(q,{children:[e.jsx(y,{value:"allow",children:"Allow"}),e.jsx(y,{value:"deny",children:"Deny"}),e.jsx(y,{value:"ask",children:"Ask"})]})]}),e.jsx(j,{placeholder:"Tool pattern (e.g. Bash, Write)",value:typeof a.tool=="string"?a.tool:(a.tool||[]).join(", "),onChange:t=>i(r,{tool:t.target.value}),className:"h-8 text-xs border-border bg-muted/30 font-mono placeholder:text-muted-foreground/30"}),e.jsx(j,{placeholder:"Reason (optional)",value:a.reason||"",onChange:t=>i(r,{reason:t.target.value}),className:"h-8 text-xs border-border bg-muted/30 placeholder:text-muted-foreground/30"}),e.jsx(p,{type:"button",variant:"ghost",size:"sm",className:"h-8 w-8 p-0 text-muted-foreground hover:text-red-400",onClick:()=>m(r),children:e.jsx(_,{className:"h-3.5 w-3.5"})})]},a.id))]})}function ae({onCreated:s}){const[h,n]=c.useState(!1),[i,m]=c.useState(!1),[a,r]=c.useState({name:"",description:"",priority:0,enabled:!0,rules:[]}),t=async()=>{if(!a.name.trim()){u.error("Policy name is required");return}if(a.rules.length===0){u.error("At least one rule is required");return}try{m(!0),await f.createPolicy({id:crypto.randomUUID(),name:a.name,description:a.description||void 0,priority:a.priority,enabled:a.enabled,rules:a.rules}),u.success("Policy created"),n(!1),r({name:"",description:"",priority:0,enabled:!0,rules:[]}),s()}catch(x){u.error(x instanceof Error?x.message:"Failed to create policy")}finally{m(!1)}};return e.jsxs(S,{open:h,onOpenChange:n,children:[e.jsx($,{asChild:!0,children:e.jsxs(p,{className:"bg-cyan-600 hover:bg-cyan-700 text-white border-0",children:[e.jsx(D,{className:"h-4 w-4 mr-1.5"}),"New Policy"]})}),e.jsxs(F,{className:"border-border bg-popover max-w-2xl max-h-[85vh] overflow-y-auto mx-4 sm:mx-auto",children:[e.jsxs(I,{children:[e.jsx(E,{children:"Create Policy"}),e.jsx(R,{className:"text-muted-foreground/60",children:"Define permission rules for tool access control."})]}),e.jsxs("div",{className:"grid gap-4 py-4",children:[e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"grid gap-2",children:[e.jsx("label",{htmlFor:"create-policy-name",className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Name *"}),e.jsx(j,{id:"create-policy-name",placeholder:"My Policy",value:a.name,onChange:x=>r(l=>({...l,name:x.target.value})),className:"border-border bg-muted/30 text-sm placeholder:text-muted-foreground/30"})]}),e.jsxs("div",{className:"grid gap-2",children:[e.jsx("label",{htmlFor:"create-policy-priority",className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Priority"}),e.jsx(j,{id:"create-policy-priority",type:"number",value:a.priority,onChange:x=>r(l=>({...l,priority:parseInt(x.target.value,10)||0})),className:"border-border bg-muted/30 text-sm font-mono"})]})]}),e.jsxs("div",{className:"grid gap-2",children:[e.jsx("label",{htmlFor:"create-policy-desc",className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Description"}),e.jsx(j,{id:"create-policy-desc",placeholder:"Optional description",value:a.description,onChange:x=>r(l=>({...l,description:x.target.value})),className:"border-border bg-muted/30 text-sm placeholder:text-muted-foreground/30"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"checkbox",id:"enabled",checked:a.enabled,onChange:x=>r(l=>({...l,enabled:x.target.checked})),className:"rounded border-border"}),e.jsx("label",{htmlFor:"enabled",className:"text-sm text-muted-foreground",children:"Enabled"})]}),e.jsx(G,{rules:a.rules,onChange:x=>r(l=>({...l,rules:x}))})]}),e.jsxs(T,{children:[e.jsx(p,{variant:"outline",onClick:()=>n(!1),disabled:i,className:"border-border",children:"Cancel"}),e.jsx(p,{onClick:t,disabled:i,className:"bg-cyan-600 hover:bg-cyan-700 text-white border-0",children:i?"Creating...":"Create Policy"})]})]})]})}function de({policies:s,onCreated:h}){const[n,i]=c.useState(!1),[m,a]=c.useState(!1),[r,t]=c.useState({name:"",description:"",isDefault:!1,selectedPolicyIds:[]}),x=d=>{t(b=>({...b,selectedPolicyIds:b.selectedPolicyIds.includes(d)?b.selectedPolicyIds.filter(w=>w!==d):[...b.selectedPolicyIds,d]}))},l=async()=>{if(!r.name.trim()){u.error("Set name is required");return}try{a(!0),await f.createPolicySet({id:crypto.randomUUID(),name:r.name,description:r.description||void 0,isDefault:r.isDefault,policyIds:r.selectedPolicyIds.length>0?r.selectedPolicyIds:void 0}),u.success("Policy set created"),i(!1),t({name:"",description:"",isDefault:!1,selectedPolicyIds:[]}),h()}catch(d){u.error(d instanceof Error?d.message:"Failed to create policy set")}finally{a(!1)}};return e.jsxs(S,{open:n,onOpenChange:i,children:[e.jsx($,{asChild:!0,children:e.jsxs(p,{variant:"outline",className:"border-border",children:[e.jsx(D,{className:"h-4 w-4 mr-1.5"}),"New Set"]})}),e.jsxs(F,{className:"border-border bg-popover max-w-lg max-h-[85vh] overflow-y-auto",children:[e.jsxs(I,{children:[e.jsx(E,{children:"Create Policy Set"}),e.jsx(R,{className:"text-muted-foreground/60",children:"Group policies together for easy application to sessions."})]}),e.jsxs("div",{className:"grid gap-4 py-4",children:[e.jsxs("div",{className:"grid gap-2",children:[e.jsx("label",{htmlFor:"create-set-name",className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Name *"}),e.jsx(j,{id:"create-set-name",placeholder:"Production Rules",value:r.name,onChange:d=>t(b=>({...b,name:d.target.value})),className:"border-border bg-muted/30 text-sm placeholder:text-muted-foreground/30"})]}),e.jsxs("div",{className:"grid gap-2",children:[e.jsx("label",{htmlFor:"create-set-desc",className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Description"}),e.jsx(j,{id:"create-set-desc",placeholder:"Optional description",value:r.description,onChange:d=>t(b=>({...b,description:d.target.value})),className:"border-border bg-muted/30 text-sm placeholder:text-muted-foreground/30"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"checkbox",id:"set-default",checked:r.isDefault,onChange:d=>t(b=>({...b,isDefault:d.target.checked})),className:"rounded border-border"}),e.jsx("label",{htmlFor:"set-default",className:"text-sm text-muted-foreground",children:"Set as default (auto-apply to new sessions)"})]}),s.length>0&&e.jsxs("div",{className:"grid gap-2",children:[e.jsx("p",{className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Include Policies"}),e.jsx("div",{className:"space-y-1.5 max-h-48 overflow-y-auto rounded-lg border border-border p-2",children:s.map(d=>e.jsxs("label",{className:"flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-accent/30 cursor-pointer transition-colors",children:[e.jsx("input",{type:"checkbox",checked:r.selectedPolicyIds.includes(d.id),onChange:()=>x(d.id),className:"rounded border-border"}),e.jsx("span",{className:"text-sm",children:d.name}),e.jsxs("span",{className:"text-[10px] font-mono text-muted-foreground/50 ml-auto",children:["P:",d.priority]})]},d.id))})]})]}),e.jsxs(T,{children:[e.jsx(p,{variant:"outline",onClick:()=>i(!1),disabled:m,className:"border-border",children:"Cancel"}),e.jsx(p,{onClick:l,disabled:m,className:"bg-cyan-600 hover:bg-cyan-700 text-white border-0",children:m?"Creating...":"Create Set"})]})]})]})}function ie({policy:s,open:h,onOpenChange:n,onSaved:i}){const[m,a]=c.useState(!1),[r,t]=c.useState({name:s.name,description:s.description||"",priority:s.priority,enabled:s.enabled,rules:[...s.rules]}),x=async()=>{if(!r.name.trim()){u.error("Policy name is required");return}try{a(!0),await f.updatePolicy(s.id,{name:r.name,description:r.description||void 0,priority:r.priority,enabled:r.enabled,rules:r.rules}),u.success("Policy updated"),n(!1),i()}catch(l){u.error(l instanceof Error?l.message:"Failed to update policy")}finally{a(!1)}};return e.jsx(S,{open:h,onOpenChange:n,children:e.jsxs(F,{className:"border-border bg-popover max-w-2xl max-h-[85vh] overflow-y-auto",children:[e.jsxs(I,{children:[e.jsx(E,{children:"Edit Policy"}),e.jsx(R,{className:"text-muted-foreground/60",children:"Modify the permission rules for this policy."})]}),e.jsxs("div",{className:"grid gap-4 py-4",children:[e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{className:"grid gap-2",children:[e.jsx("label",{htmlFor:"edit-policy-name",className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Name *"}),e.jsx(j,{id:"edit-policy-name",value:r.name,onChange:l=>t(d=>({...d,name:l.target.value})),className:"border-border bg-muted/30 text-sm"})]}),e.jsxs("div",{className:"grid gap-2",children:[e.jsx("label",{htmlFor:"edit-policy-priority",className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Priority"}),e.jsx(j,{id:"edit-policy-priority",type:"number",value:r.priority,onChange:l=>t(d=>({...d,priority:parseInt(l.target.value,10)||0})),className:"border-border bg-muted/30 text-sm font-mono"})]})]}),e.jsxs("div",{className:"grid gap-2",children:[e.jsx("label",{htmlFor:"edit-policy-desc",className:"text-xs font-medium text-muted-foreground uppercase tracking-wider",children:"Description"}),e.jsx(j,{id:"edit-policy-desc",value:r.description,onChange:l=>t(d=>({...d,description:l.target.value})),className:"border-border bg-muted/30 text-sm"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{type:"checkbox",id:"edit-enabled",checked:r.enabled,onChange:l=>t(d=>({...d,enabled:l.target.checked})),className:"rounded border-border"}),e.jsx("label",{htmlFor:"edit-enabled",className:"text-sm text-muted-foreground",children:"Enabled"})]}),e.jsx(G,{rules:r.rules,onChange:l=>t(d=>({...d,rules:l}))})]}),e.jsxs(T,{children:[e.jsx(p,{variant:"outline",onClick:()=>n(!1),disabled:m,className:"border-border",children:"Cancel"}),e.jsx(p,{onClick:x,disabled:m,className:"bg-cyan-600 hover:bg-cyan-700 text-white border-0",children:m?"Saving...":"Save Changes"})]})]})})}function oe({policy:s,onUpdated:h}){const[n,i]=c.useState(!1),[m,a]=c.useState(!1),r=async()=>{if(confirm(`Delete policy "${s.name}"?`))try{a(!0),await f.deletePolicy(s.id),u.success("Policy deleted"),h()}catch(t){u.error(t instanceof Error?t.message:"Failed to delete policy")}finally{a(!1)}};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"rounded-xl border border-border bg-card/80 backdrop-blur-sm p-5 hover:bg-accent/30 transition-colors",children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("h3",{className:"text-sm font-semibold",children:s.name}),e.jsx(v,{variant:s.enabled?"success":"secondary",children:s.enabled?"Active":"Disabled"}),e.jsxs("span",{className:"text-[10px] font-mono text-muted-foreground/50 bg-muted/50 px-1.5 py-0.5 rounded border border-border",children:["Priority: ",s.priority]}),s.sessionId&&e.jsxs("span",{className:"text-[10px] font-mono text-cyan-400/60 bg-cyan-500/5 px-1.5 py-0.5 rounded border border-cyan-500/10",children:["Session: ",s.sessionId.slice(0,8)]}),!s.sessionId&&e.jsx("span",{className:"text-[10px] font-mono text-amber-400/60 bg-amber-500/5 px-1.5 py-0.5 rounded border border-amber-500/10",children:"Global"})]}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(p,{variant:"ghost",size:"sm",className:"h-7 w-7 p-0 text-muted-foreground hover:text-foreground",onClick:()=>i(!0),children:e.jsx(Q,{className:"h-3.5 w-3.5"})}),e.jsx(p,{variant:"ghost",size:"sm",className:"h-7 w-7 p-0 text-muted-foreground hover:text-red-400",onClick:r,disabled:m,children:e.jsx(_,{className:"h-3.5 w-3.5"})})]})]}),s.description&&e.jsx("p",{className:"text-sm text-muted-foreground mb-3",children:s.description}),e.jsxs("div",{className:"space-y-1.5 mb-3",children:[e.jsxs("p",{className:"text-[11px] font-medium text-muted-foreground uppercase tracking-wider",children:["Rules (",s.rules.length,")"]}),s.rules.map(t=>e.jsxs("div",{className:"flex items-center gap-2 text-sm",children:[e.jsx(v,{variant:t.action==="allow"?"success":t.action==="deny"?"destructive":"warning",children:t.action}),t.tool&&e.jsx("code",{className:"bg-muted/50 px-1.5 py-0.5 rounded text-[11px] font-mono border border-border",children:Array.isArray(t.tool)?t.tool.join(", "):t.tool}),t.reason&&e.jsxs("span",{className:"text-muted-foreground/60 text-xs",children:["- ",t.reason]})]},t.id))]}),e.jsxs("p",{className:"text-[11px] text-muted-foreground/50",children:["Updated ",B(s.updatedAt)]})]}),n&&e.jsx(ie,{policy:s,open:n,onOpenChange:i,onSaved:h})]})}function ne({policySet:s,allPolicies:h,onUpdated:n}){const[i,m]=c.useState(!1),[a,r]=c.useState([]),[t,x]=c.useState(!1),[l,d]=c.useState(!1);c.useEffect(()=>{if(!i)return;(async()=>{x(!0);try{const g=await f.getPolicySet(s.id);r(g.policySet.policies)}catch{u.error("Failed to load policy set members")}finally{x(!1)}})()},[i,s.id]);const b=async()=>{if(confirm(`Delete policy set "${s.name}"?`))try{d(!0),await f.deletePolicySet(s.id),u.success("Policy set deleted"),n()}catch(o){u.error(o instanceof Error?o.message:"Failed to delete")}finally{d(!1)}},w=async()=>{try{await f.updatePolicySet(s.id,{isDefault:!s.isDefault}),u.success(s.isDefault?"Removed as default":"Set as default"),n()}catch(o){u.error(o instanceof Error?o.message:"Failed to update")}},H=async o=>{try{await f.removePolicyFromSet(s.id,o),r(g=>g.filter(N=>N.id!==o)),u.success("Policy removed from set"),n()}catch(g){u.error(g instanceof Error?g.message:"Failed to remove")}},W=async o=>{try{await f.addPolicyToSet(s.id,o);const g=h.find(N=>N.id===o);g&&r(N=>[...N,g]),u.success("Policy added to set"),n()}catch(g){u.error(g instanceof Error?g.message:"Failed to add")}},A=h.filter(o=>!a.some(g=>g.id===o.id));return e.jsxs("div",{className:"rounded-xl border border-border bg-card/80 backdrop-blur-sm overflow-hidden",children:[e.jsxs("button",{type:"button",className:"flex items-center justify-between w-full p-5 cursor-pointer hover:bg-accent/30 transition-colors text-left",onClick:()=>m(!i),children:[e.jsxs("div",{className:"flex items-center gap-3",children:[i?e.jsx(X,{className:"h-4 w-4 text-muted-foreground"}):e.jsx(J,{className:"h-4 w-4 text-muted-foreground"}),e.jsx(k,{className:"h-4 w-4 text-cyan-400"}),e.jsx("h3",{className:"text-sm font-semibold",children:s.name}),s.isDefault&&e.jsxs(v,{variant:"warning",className:"text-[10px]",children:[e.jsx(M,{className:"h-2.5 w-2.5 mr-1"}),"Default"]}),e.jsxs("span",{className:"text-xs text-muted-foreground",children:[s.policyCount," ",s.policyCount===1?"policy":"policies"]}),e.jsxs("span",{className:"text-xs text-muted-foreground/50",children:[s.sessionCount," ",s.sessionCount===1?"session":"sessions"]})]}),e.jsxs("div",{className:"flex items-center gap-1",onClick:o=>o.stopPropagation(),children:[e.jsxs(p,{variant:"ghost",size:"sm",className:"h-7 px-2 text-xs text-muted-foreground hover:text-amber-400",onClick:w,children:[e.jsx(M,{className:`h-3 w-3 mr-1 ${s.isDefault?"fill-amber-400":""}`}),s.isDefault?"Unset Default":"Set Default"]}),e.jsx(p,{variant:"ghost",size:"sm",className:"h-7 w-7 p-0 text-muted-foreground hover:text-red-400",onClick:b,disabled:l,children:e.jsx(_,{className:"h-3.5 w-3.5"})})]})]}),s.description&&!i&&e.jsx("div",{className:"px-5 pb-3 -mt-2",children:e.jsx("p",{className:"text-xs text-muted-foreground/60 pl-11",children:s.description})}),i&&e.jsxs("div",{className:"border-t border-border p-5 pt-4 space-y-3",children:[s.description&&e.jsx("p",{className:"text-sm text-muted-foreground mb-3",children:s.description}),t?e.jsxs("div",{className:"flex items-center gap-2 text-muted-foreground py-4 justify-center",children:[e.jsx("div",{className:"w-3 h-3 border-2 border-cyan-500/30 border-t-cyan-500 rounded-full animate-spin"}),e.jsx("span",{className:"text-xs",children:"Loading..."})]}):e.jsxs(e.Fragment,{children:[e.jsx("p",{className:"text-[11px] font-medium text-muted-foreground uppercase tracking-wider",children:"Member Policies"}),a.length===0?e.jsx("p",{className:"text-xs text-muted-foreground/50 text-center py-3 border border-dashed border-border rounded-lg",children:"No policies in this set yet."}):e.jsx("div",{className:"space-y-1.5",children:a.map(o=>e.jsxs("div",{className:"flex items-center justify-between py-2 px-3 rounded-lg bg-muted/20 border border-border/50",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(v,{variant:o.enabled?"success":"secondary",className:"text-[10px]",children:o.enabled?"Active":"Off"}),e.jsx("span",{className:"text-sm",children:o.name}),e.jsxs("span",{className:"text-[10px] font-mono text-muted-foreground/50",children:["P:",o.priority]})]}),e.jsx(p,{variant:"ghost",size:"sm",className:"h-6 w-6 p-0 text-muted-foreground hover:text-red-400",onClick:()=>H(o.id),children:e.jsx(K,{className:"h-3 w-3"})})]},o.id))}),A.length>0&&e.jsxs("div",{className:"pt-2",children:[e.jsx("p",{className:"text-[11px] font-medium text-muted-foreground uppercase tracking-wider mb-2",children:"Add Policy"}),e.jsx("div",{className:"flex flex-wrap gap-1.5",children:A.map(o=>e.jsxs("button",{type:"button",onClick:()=>W(o.id),className:"text-xs px-2.5 py-1 rounded-md border border-border bg-muted/30 text-muted-foreground hover:text-foreground hover:bg-accent/50 hover:border-cyan-500/30 transition-colors",children:["+ ",o.name]},o.id))})]})]})]})]})}function ge(){const[s,h]=c.useState([]),[n,i]=c.useState([]),[m,a]=c.useState(!0),r=c.useCallback(async()=>{try{const[t,x]=await Promise.all([f.getPolicies(),f.getPolicySets()]);h(t.policies),i(x.policySets)}catch{u.error("Failed to load policy data")}finally{a(!1)}},[]);return c.useEffect(()=>{r()},[r]),m?e.jsx("div",{className:"p-4 md:p-8 max-w-7xl mx-auto",children:e.jsx("div",{className:"flex justify-center items-center h-64",children:e.jsxs("div",{className:"flex items-center gap-3 text-muted-foreground",children:[e.jsx("div",{className:"w-4 h-4 border-2 border-cyan-500/30 border-t-cyan-500 rounded-full animate-spin"}),e.jsx("span",{className:"text-sm",children:"Loading policies..."})]})})}):e.jsxs("div",{className:"p-4 md:p-8 max-w-7xl mx-auto",children:[e.jsxs("div",{className:"mb-6",children:[e.jsx("h1",{className:"text-2xl md:text-3xl font-bold tracking-tight",children:"Policies"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Permission rules, policy sets, and audit trail"})]}),e.jsxs(Y,{defaultValue:"policies",children:[e.jsxs(Z,{className:"bg-muted/50 border border-border mb-6",children:[e.jsxs(C,{value:"policies",className:"data-[state=active]:bg-background data-[state=active]:text-foreground text-sm gap-1.5",children:[e.jsx(L,{className:"h-3.5 w-3.5"}),"Policies"]}),e.jsxs(C,{value:"sets",className:"data-[state=active]:bg-background data-[state=active]:text-foreground text-sm gap-1.5",children:[e.jsx(k,{className:"h-3.5 w-3.5"}),"Policy Sets"]}),e.jsxs(C,{value:"audit",className:"data-[state=active]:bg-background data-[state=active]:text-foreground text-sm gap-1.5",children:[e.jsx(se,{className:"h-3.5 w-3.5"}),"Audit Log"]})]}),e.jsxs(P,{value:"policies",children:[e.jsxs("div",{className:"flex justify-between items-center mb-4",children:[e.jsxs("p",{className:"text-sm text-muted-foreground",children:[s.length," ",s.length===1?"policy":"policies"," configured"]}),e.jsx(ae,{onCreated:r})]}),s.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center h-64 rounded-xl border border-border bg-card/80",children:[e.jsx(L,{className:"h-10 w-10 text-muted-foreground/20 mb-3"}),e.jsx("p",{className:"text-sm text-muted-foreground mb-1",children:"No policies configured"}),e.jsx("p",{className:"text-xs text-muted-foreground/60",children:"Create your first policy to control tool permissions."})]}):e.jsx("div",{className:"space-y-3",children:s.map(t=>e.jsx(oe,{policy:t,onUpdated:r},t.id))})]}),e.jsxs(P,{value:"sets",children:[e.jsxs("div",{className:"flex justify-between items-center mb-4",children:[e.jsxs("p",{className:"text-sm text-muted-foreground",children:[n.length," ",n.length===1?"set":"sets"," configured"]}),e.jsx(de,{policies:s,onCreated:r})]}),n.length===0?e.jsxs("div",{className:"flex flex-col items-center justify-center h-64 rounded-xl border border-border bg-card/80",children:[e.jsx(k,{className:"h-10 w-10 text-muted-foreground/20 mb-3"}),e.jsx("p",{className:"text-sm text-muted-foreground mb-1",children:"No policy sets"}),e.jsx("p",{className:"text-xs text-muted-foreground/60",children:"Group policies into sets for easy application to sessions."})]}):e.jsx("div",{className:"space-y-3",children:n.map(t=>e.jsx(ne,{policySet:t,allPolicies:s,onUpdated:r},t.id))})]}),e.jsx(P,{value:"audit",children:e.jsx(re,{})})]})]})}export{ge as PoliciesPage};