aegis-bridge 2.6.0 → 2.6.2

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 (40) hide show
  1. package/dashboard/dist/assets/index-9Hkkvm_I.css +32 -0
  2. package/dashboard/dist/assets/index-Bfabq3q-.js +262 -0
  3. package/dashboard/dist/index.html +2 -2
  4. package/dist/config.d.ts +2 -0
  5. package/dist/config.js +3 -0
  6. package/dist/continuation-pointer.d.ts +11 -0
  7. package/dist/continuation-pointer.js +64 -0
  8. package/dist/dashboard/assets/index-9Hkkvm_I.css +32 -0
  9. package/dist/dashboard/assets/index-Bfabq3q-.js +262 -0
  10. package/dist/dashboard/index.html +2 -2
  11. package/dist/diagnostics.d.ts +27 -0
  12. package/dist/diagnostics.js +95 -0
  13. package/dist/events.d.ts +14 -0
  14. package/dist/events.js +43 -14
  15. package/dist/fault-injection.d.ts +29 -0
  16. package/dist/fault-injection.js +115 -0
  17. package/dist/handshake.d.ts +21 -1
  18. package/dist/handshake.js +37 -3
  19. package/dist/hook-settings.d.ts +3 -2
  20. package/dist/hook-settings.js +6 -4
  21. package/dist/hook.js +10 -1
  22. package/dist/hooks.js +5 -0
  23. package/dist/logger.d.ts +35 -0
  24. package/dist/logger.js +65 -0
  25. package/dist/monitor.js +72 -11
  26. package/dist/permission-routes.d.ts +7 -0
  27. package/dist/permission-routes.js +28 -0
  28. package/dist/server.js +119 -44
  29. package/dist/session.d.ts +1 -0
  30. package/dist/session.js +41 -23
  31. package/dist/tmux.js +2 -2
  32. package/dist/utils/circular-buffer.d.ts +11 -0
  33. package/dist/utils/circular-buffer.js +37 -0
  34. package/dist/validation.d.ts +34 -0
  35. package/dist/validation.js +45 -3
  36. package/package.json +3 -2
  37. package/dashboard/dist/assets/index-B7DYf7vF.css +0 -32
  38. package/dashboard/dist/assets/index-DIyuyrlO.js +0 -262
  39. package/dist/dashboard/assets/index-B7DYf7vF.css +0 -32
  40. package/dist/dashboard/assets/index-DIyuyrlO.js +0 -262
package/dist/session.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * Manages the lifecycle of CC sessions running in tmux windows.
5
5
  * Tracks: session ID, window ID, byte offset for JSONL reading, status.
6
6
  */
7
+ import { randomBytes } from 'node:crypto';
7
8
  import { readFile, writeFile, rename, mkdir, stat } from 'node:fs/promises';
8
9
  import { existsSync, unlinkSync, readdirSync } from 'node:fs';
9
10
  import { join, dirname } from 'node:path';
@@ -13,9 +14,11 @@ import { findSessionFileWithFanout } from './worktree-lookup.js';
13
14
  import { detectUIState, extractInteractiveContent, parseStatusLine } from './terminal-parser.js';
14
15
  import { computeStallThreshold } from './config.js';
15
16
  import { neutralizeBypassPermissions, restoreSettings, cleanOrphanedBackup } from './permission-guard.js';
16
- import { persistedStateSchema, sessionMapSchema } from './validation.js';
17
+ import { persistedStateSchema } from './validation.js';
18
+ import { loadContinuationPointers } from './continuation-pointer.js';
17
19
  import { writeHookSettingsFile, cleanupHookSettingsFile } from './hook-settings.js';
18
20
  import { Mutex } from 'async-mutex';
21
+ import { maybeInjectFault } from './fault-injection.js';
19
22
  /** Convert parsed JSON arrays to Sets for activeSubagents (#668). */
20
23
  function hydrateSessions(raw) {
21
24
  const sessions = {};
@@ -441,12 +444,36 @@ export class SessionManager {
441
444
  // Merge defaultSessionEnv (from config) with per-session env (per-session wins)
442
445
  // Security: validate env var names to prevent injection attacks
443
446
  const ENV_NAME_RE = /^[A-Z_][A-Z0-9_]*$/;
447
+ // Issue #630: Expanded blocklist with additional dangerous env vars
444
448
  const DANGEROUS_ENV_VARS = new Set([
449
+ // Dynamic loader / library injection
445
450
  'PATH', 'LD_PRELOAD', 'LD_LIBRARY_PATH', 'NODE_OPTIONS',
446
451
  'DYLD_INSERT_LIBRARIES', 'IFS', 'SHELL', 'ENV', 'BASH_ENV',
452
+ // Language-specific library paths
447
453
  'PYTHONPATH', 'PERL5LIB', 'RUBYLIB', 'CLASSPATH',
448
454
  'NODE_PATH', 'PYTHONHOME', 'PYTHONSTARTUP',
455
+ // Issue #630: Shell command injection vectors
456
+ 'PROMPT_COMMAND', 'GIT_SSH_COMMAND', 'EDITOR', 'VISUAL',
457
+ 'SUDO_ASKPASS', 'GIT_EXEC_PATH', 'NODE_ENV',
458
+ // Issue #630: Token/credential leakage
459
+ 'GITHUB_TOKEN', 'NPM_TOKEN', 'GITLAB_TOKEN',
460
+ 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN',
461
+ 'AZURE_CLIENT_SECRET', 'GOOGLE_APPLICATION_CREDENTIALS',
462
+ 'DOCKER_TOKEN', 'HEROKU_API_KEY',
449
463
  ]);
464
+ // Issue #630: Dangerous env var prefixes (prefix match)
465
+ const DANGEROUS_ENV_PREFIXES = [
466
+ 'npm_config_', // npm configuration override
467
+ 'BASH_FUNC_', // bash function export
468
+ 'SSH_', // SSH keys/agent config (SSH_AUTH_SOCK, SSH_PRIVATE_KEY, etc.)
469
+ 'GITHUB_', // GitHub tokens/keys (except GITHUB_PATH which is benign)
470
+ 'GITLAB_', // GitLab tokens
471
+ 'AWS_', // AWS credentials
472
+ 'AZURE_', // Azure credentials
473
+ 'TF_', // Terraform tokens
474
+ 'CI_', // CI tokens (CI_JOB_TOKEN, CI_BUILD_TOKEN, etc.)
475
+ 'DOCKER_', // Docker registry tokens
476
+ ];
450
477
  const mergedEnv = {};
451
478
  const allEnv = { ...this.config.defaultSessionEnv, ...opts.env };
452
479
  for (const [key, value] of Object.entries(allEnv)) {
@@ -456,6 +483,10 @@ export class SessionManager {
456
483
  if (DANGEROUS_ENV_VARS.has(key)) {
457
484
  throw new Error(`Forbidden env var: "${key}" — cannot override dangerous environment variables`);
458
485
  }
486
+ // Issue #630: Check dangerous prefixes
487
+ if (DANGEROUS_ENV_PREFIXES.some(prefix => key.startsWith(prefix))) {
488
+ throw new Error(`Forbidden env var: "${key}" — cannot override dangerous environment variable prefix "${DANGEROUS_ENV_PREFIXES.find(p => key.startsWith(p))}"`);
489
+ }
459
490
  mergedEnv[key] = value;
460
491
  }
461
492
  const hasEnv = Object.keys(mergedEnv).length > 0;
@@ -471,12 +502,14 @@ export class SessionManager {
471
502
  if (effectivePermissionMode !== 'bypassPermissions') {
472
503
  settingsPatched = await neutralizeBypassPermissions(opts.workDir, effectivePermissionMode);
473
504
  }
505
+ // Issue #629: Generate per-session HMAC secret for hook URL authentication.
506
+ const hookSecret = randomBytes(32).toString('hex');
474
507
  // Issue #169 Phase 2: Generate HTTP hook settings for this session.
475
508
  // Writes a temp file with hooks pointing to Aegis's hook receiver.
476
509
  let hookSettingsFile;
477
510
  try {
478
511
  const baseUrl = `http://${this.config.host}:${this.config.port}`;
479
- hookSettingsFile = await writeHookSettingsFile(baseUrl, id, opts.workDir);
512
+ hookSettingsFile = await writeHookSettingsFile(baseUrl, id, hookSecret, opts.workDir);
480
513
  }
481
514
  catch (e) {
482
515
  console.error(`Hook settings: failed to generate settings file: ${e.message}`);
@@ -509,6 +542,7 @@ export class SessionManager {
509
542
  permissionMode: effectivePermissionMode,
510
543
  settingsPatched,
511
544
  hookSettingsFile,
545
+ hookSecret,
512
546
  };
513
547
  this.state.sessions[id] = session;
514
548
  await this.save();
@@ -721,6 +755,7 @@ export class SessionManager {
721
755
  * Issue #840/#880: Atomically acquires the session under a mutex to prevent TOCTOU race. */
722
756
  async findIdleSessionByWorkDir(workDir) {
723
757
  return this.sessionAcquireMutex.runExclusive(async () => {
758
+ await maybeInjectFault('session.findIdleSessionByWorkDir.start');
724
759
  const candidates = Object.values(this.state.sessions).filter((s) => s.workDir === workDir && s.status === 'idle');
725
760
  if (candidates.length === 0)
726
761
  return null;
@@ -728,6 +763,7 @@ export class SessionManager {
728
763
  candidates.sort((a, b) => b.lastActivity - a.lastActivity);
729
764
  // Issue #636: verify tmux window exists before returning
730
765
  for (const candidate of candidates) {
766
+ await maybeInjectFault('session.findIdleSessionByWorkDir.windowExists');
731
767
  if (await this.tmux.windowExists(candidate.windowId)) {
732
768
  // Issue #840: Mark session as acquired immediately to prevent
733
769
  // concurrent callers from grabbing the same session
@@ -1303,13 +1339,7 @@ export class SessionManager {
1303
1339
  if (!existsSync(this.sessionMapFile))
1304
1340
  return;
1305
1341
  try {
1306
- const raw = await readFile(this.sessionMapFile, 'utf-8');
1307
- const parsed = sessionMapSchema.safeParse(JSON.parse(raw));
1308
- if (!parsed.success) {
1309
- console.warn('session_map.json failed validation in cleanSessionMapForWindow');
1310
- return;
1311
- }
1312
- const mapData = parsed.data;
1342
+ const mapData = await loadContinuationPointers(this.sessionMapFile, this.config.continuationPointerTtlMs);
1313
1343
  let changed = false;
1314
1344
  for (const [key, info] of Object.entries(mapData)) {
1315
1345
  // Clean by window_name (original behavior)
@@ -1341,13 +1371,7 @@ export class SessionManager {
1341
1371
  if (!existsSync(this.sessionMapFile))
1342
1372
  return;
1343
1373
  try {
1344
- const raw = await readFile(this.sessionMapFile, 'utf-8');
1345
- const parsed = sessionMapSchema.safeParse(JSON.parse(raw));
1346
- if (!parsed.success) {
1347
- console.warn('session_map.json failed validation in purgeStaleSessionMapEntries');
1348
- return;
1349
- }
1350
- const mapData = parsed.data;
1374
+ const mapData = await loadContinuationPointers(this.sessionMapFile, this.config.continuationPointerTtlMs);
1351
1375
  let changed = false;
1352
1376
  const activeNamesLower = new Set([...activeWindowNames].map(n => n.toLowerCase()));
1353
1377
  for (const [key, info] of Object.entries(mapData)) {
@@ -1480,13 +1504,7 @@ export class SessionManager {
1480
1504
  if (!existsSync(this.sessionMapFile))
1481
1505
  return;
1482
1506
  try {
1483
- const mapRaw = await readFile(this.sessionMapFile, 'utf-8');
1484
- const mapParsed = sessionMapSchema.safeParse(JSON.parse(mapRaw));
1485
- if (!mapParsed.success) {
1486
- console.warn('session_map.json failed validation in syncSessionMap');
1487
- return;
1488
- }
1489
- const mapData = mapParsed.data;
1507
+ const mapData = await loadContinuationPointers(this.sessionMapFile, this.config.continuationPointerTtlMs);
1490
1508
  for (const session of Object.values(this.state.sessions)) {
1491
1509
  if (session.claudeSessionId)
1492
1510
  continue;
package/dist/tmux.js CHANGED
@@ -15,8 +15,8 @@ import { randomBytes } from 'node:crypto';
15
15
  function shellEscape(s) {
16
16
  return `'${s.replace(/'/g, "'\\''")}'`;
17
17
  }
18
- /** Validate that an env var key contains only safe characters. */
19
- const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
18
+ /** Validate that an env var key contains only safe characters (Issue #630: uppercase only, aligned with session.ts). */
19
+ const ENV_KEY_RE = /^[A-Z_][A-Z0-9_]*$/;
20
20
  const execFileAsync = promisify(execFile);
21
21
  /** Default timeout for tmux commands (ms). Prevents hung commands from blocking the server. */
22
22
  const TMUX_DEFAULT_TIMEOUT_MS = 10_000;
@@ -0,0 +1,11 @@
1
+ export declare class CircularBuffer<T> {
2
+ private readonly capacity;
3
+ private readonly items;
4
+ private head;
5
+ private count;
6
+ constructor(capacity: number);
7
+ push(item: T): void;
8
+ toArray(): T[];
9
+ clear(): void;
10
+ size(): number;
11
+ }
@@ -0,0 +1,37 @@
1
+ export class CircularBuffer {
2
+ capacity;
3
+ items;
4
+ head = 0;
5
+ count = 0;
6
+ constructor(capacity) {
7
+ this.capacity = capacity;
8
+ if (!Number.isInteger(capacity) || capacity <= 0) {
9
+ throw new Error(`CircularBuffer capacity must be a positive integer, got: ${capacity}`);
10
+ }
11
+ this.items = new Array(capacity);
12
+ }
13
+ push(item) {
14
+ this.items[this.head] = item;
15
+ this.head = (this.head + 1) % this.capacity;
16
+ if (this.count < this.capacity) {
17
+ this.count++;
18
+ }
19
+ }
20
+ toArray() {
21
+ if (this.count === 0) {
22
+ return [];
23
+ }
24
+ if (this.count < this.capacity) {
25
+ return this.items.slice(0, this.count);
26
+ }
27
+ return [...this.items.slice(this.head), ...this.items.slice(0, this.head)];
28
+ }
29
+ clear() {
30
+ this.items.fill(undefined);
31
+ this.head = 0;
32
+ this.count = 0;
33
+ }
34
+ size() {
35
+ return this.count;
36
+ }
37
+ }
@@ -111,6 +111,12 @@ export declare const pipelineSchema: z.ZodObject<{
111
111
  autoApprove: z.ZodOptional<z.ZodBoolean>;
112
112
  }, z.core.$strip>>;
113
113
  }, z.core.$strict>;
114
+ /** POST /v1/handshake */
115
+ export declare const handshakeRequestSchema: z.ZodObject<{
116
+ protocolVersion: z.ZodString;
117
+ clientCapabilities: z.ZodOptional<z.ZodArray<z.ZodString>>;
118
+ clientVersion: z.ZodOptional<z.ZodString>;
119
+ }, z.core.$strict>;
114
120
  /** Clamp a numeric value to [min, max]. Returns default if input is NaN. */
115
121
  export declare function clamp(value: number, min: number, max: number, fallback: number): number;
116
122
  /** Parse an env string to integer with NaN/isFinite guard. Returns fallback on failure. */
@@ -165,6 +171,21 @@ export declare const persistedStateSchema: z.ZodRecord<z.ZodString, z.ZodObject<
165
171
  lastDeadAt: z.ZodOptional<z.ZodNumber>;
166
172
  ccPid: z.ZodOptional<z.ZodNumber>;
167
173
  }, z.core.$strip>>;
174
+ /** Schema for a single continuation pointer entry in session_map.json (Issue #900). */
175
+ export declare const sessionMapEntrySchema: z.ZodObject<{
176
+ session_id: z.ZodString;
177
+ cwd: z.ZodString;
178
+ window_name: z.ZodString;
179
+ transcript_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
180
+ permission_mode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
181
+ agent_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
182
+ source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
183
+ agent_type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
184
+ model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
185
+ written_at: z.ZodNumber;
186
+ schema_version: z.ZodOptional<z.ZodNumber>;
187
+ expires_at: z.ZodOptional<z.ZodNumber>;
188
+ }, z.core.$strip>;
168
189
  /** Schema for session_map.json entries. */
169
190
  export declare const sessionMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
170
191
  session_id: z.ZodString;
@@ -177,6 +198,8 @@ export declare const sessionMapSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
177
198
  agent_type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
178
199
  model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
179
200
  written_at: z.ZodNumber;
201
+ schema_version: z.ZodOptional<z.ZodNumber>;
202
+ expires_at: z.ZodOptional<z.ZodNumber>;
180
203
  }, z.core.$strip>>;
181
204
  /** Schema for stop_signals.json entries. */
182
205
  export declare const stopSignalsSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -247,6 +270,17 @@ export declare const ccSettingsSchema: z.ZodObject<{
247
270
  }, z.core.$loose>;
248
271
  /** Helper: extract error message from unknown catch value. */
249
272
  export declare function getErrorMessage(e: unknown): string;
273
+ /** Minimum supported Claude Code version. */
274
+ export declare const MIN_CC_VERSION = "2.1.80";
275
+ /** Parse a semver string into [major, minor, patch], or null if invalid. */
276
+ export declare function parseSemver(v: string): [number, number, number] | null;
277
+ /**
278
+ * Compare two semver strings.
279
+ * Returns -1 if a < b, 0 if equal or either is unparseable (fails open), 1 if a > b.
280
+ */
281
+ export declare function compareSemver(a: string, b: string): number;
282
+ /** Extract version number from `claude --version` output. */
283
+ export declare function extractCCVersion(output: string): string | null;
250
284
  /** Validate workDir to prevent path traversal attacks (Issue #435).
251
285
  * 1. Reject raw strings containing ".." before any normalization.
252
286
  * 2. Resolve to absolute path and resolve symlinks via fs.realpath().
@@ -100,6 +100,12 @@ export const pipelineSchema = z.object({
100
100
  workDir: z.string().min(1),
101
101
  stages: z.array(pipelineStageSchema).min(1),
102
102
  }).strict();
103
+ /** POST /v1/handshake */
104
+ export const handshakeRequestSchema = z.object({
105
+ protocolVersion: z.string().min(1),
106
+ clientCapabilities: z.array(z.string().min(1)).optional(),
107
+ clientVersion: z.string().min(1).optional(),
108
+ }).strict();
103
109
  /** Clamp a numeric value to [min, max]. Returns default if input is NaN. */
104
110
  export function clamp(value, min, max, fallback) {
105
111
  if (Number.isNaN(value))
@@ -153,8 +159,8 @@ export const persistedStateSchema = z.record(z.string(), z.object({
153
159
  lastDeadAt: z.number().optional(),
154
160
  ccPid: z.number().optional(),
155
161
  }));
156
- /** Schema for session_map.json entries. */
157
- export const sessionMapSchema = z.record(z.string(), z.object({
162
+ /** Schema for a single continuation pointer entry in session_map.json (Issue #900). */
163
+ export const sessionMapEntrySchema = z.object({
158
164
  session_id: z.string(),
159
165
  cwd: z.string(),
160
166
  window_name: z.string(),
@@ -165,7 +171,11 @@ export const sessionMapSchema = z.record(z.string(), z.object({
165
171
  agent_type: z.string().nullable().optional(),
166
172
  model: z.string().nullable().optional(),
167
173
  written_at: z.number(),
168
- }));
174
+ schema_version: z.number().int().positive().optional(),
175
+ expires_at: z.number().optional(),
176
+ });
177
+ /** Schema for session_map.json entries. */
178
+ export const sessionMapSchema = z.record(z.string(), sessionMapEntrySchema);
169
179
  /** Schema for stop_signals.json entries. */
170
180
  export const stopSignalsSchema = z.record(z.string(), z.object({
171
181
  event: z.string().optional(),
@@ -235,6 +245,38 @@ export function getErrorMessage(e) {
235
245
  return e;
236
246
  return String(e);
237
247
  }
248
+ // ── CC version validation (Issue #564) ─────────────────────────────────
249
+ /** Minimum supported Claude Code version. */
250
+ export const MIN_CC_VERSION = '2.1.80';
251
+ /** Parse a semver string into [major, minor, patch], or null if invalid. */
252
+ export function parseSemver(v) {
253
+ const match = v.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
254
+ if (!match)
255
+ return null;
256
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
257
+ }
258
+ /**
259
+ * Compare two semver strings.
260
+ * Returns -1 if a < b, 0 if equal or either is unparseable (fails open), 1 if a > b.
261
+ */
262
+ export function compareSemver(a, b) {
263
+ const pa = parseSemver(a);
264
+ const pb = parseSemver(b);
265
+ if (!pa || !pb)
266
+ return 0;
267
+ for (let i = 0; i < 3; i++) {
268
+ if (pa[i] < pb[i])
269
+ return -1;
270
+ if (pa[i] > pb[i])
271
+ return 1;
272
+ }
273
+ return 0;
274
+ }
275
+ /** Extract version number from `claude --version` output. */
276
+ export function extractCCVersion(output) {
277
+ const match = output.match(/(\d+\.\d+\.\d+)/);
278
+ return match ? match[1] : null;
279
+ }
238
280
  /** Default safe base directories used when allowedWorkDirs is not configured.
239
281
  * Prevents sessions from running in system-critical directories. */
240
282
  function getDefaultSafeDirs() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aegis-bridge",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "type": "module",
5
5
  "description": "Orchestrate Claude Code sessions via API. Create, brief, monitor, refine, ship.",
6
6
  "main": "dist/server.js",
@@ -30,7 +30,8 @@
30
30
  "start": "node dist/cli.js",
31
31
  "dev": "tsc && node dist/cli.js",
32
32
  "prepublishOnly": "npm run build:dashboard && npm run build",
33
- "test": "vitest run"
33
+ "test": "vitest run",
34
+ "test:fault-harness": "vitest run src/__tests__/fault-injection-harness-901.test.ts"
34
35
  },
35
36
  "keywords": [
36
37
  "claude",
@@ -1,32 +0,0 @@
1
- /**
2
- * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
- * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
- * https://github.com/chjj/term.js
5
- * @license MIT
6
- *
7
- * Permission is hereby granted, free of charge, to any person obtaining a copy
8
- * of this software and associated documentation files (the "Software"), to deal
9
- * in the Software without restriction, including without limitation the rights
10
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
- * copies of the Software, and to permit persons to whom the Software is
12
- * furnished to do so, subject to the following conditions:
13
- *
14
- * The above copyright notice and this permission notice shall be included in
15
- * all copies or substantial portions of the Software.
16
- *
17
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
- * THE SOFTWARE.
24
- *
25
- * Originally forked from (with the author's permission):
26
- * Fabrice Bellard's javascript vt100 for jslinux:
27
- * http://bellard.org/jslinux/
28
- * Copyright (c) 2011 Fabrice Bellard
29
- * The original design remains. The terminal itself
30
- * has been extended to include xterm CSI codes, among
31
- * other features.
32
- */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", "Consolas", monospace;--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-amber-500:oklch(76.9% .188 70.08);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-900:oklch(42.1% .095 57.708);--color-yellow-950:oklch(28.6% .066 53.813);--color-green-200:oklch(92.5% .084 155.995);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-900:oklch(39.3% .095 152.535);--color-green-950:oklch(26.6% .065 152.934);--color-emerald-400:oklch(76.5% .177 163.223);--color-cyan-200:oklch(91.7% .08 205.041);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-950:oklch(30.2% .056 229.695);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-2xl:42rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0, 0, .2, 1) infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-void:#0a0a0f;--color-void-light:#12121a;--color-void-lighter:#1a1a2e;--color-cyan:#00e5ff;--color-cyan-dim:#00e5ff40;--color-warning:#fa0;--color-error:#f36}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.right-0{right:calc(var(--spacing) * 0)}.right-4{right:calc(var(--spacing) * 4)}.bottom-0{bottom:calc(var(--spacing) * 0)}.bottom-4{bottom:calc(var(--spacing) * 4)}.left-0{left:calc(var(--spacing) * 0)}.z-10{z-index:10}.z-50{z-index:50}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-4{margin-left:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-0\.5{height:calc(var(--spacing) * .5)}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-10{height:calc(var(--spacing) * 10)}.h-48{height:calc(var(--spacing) * 48)}.h-\[calc\(100vh-380px\)\]{height:calc(100vh - 380px)}.h-\[calc\(100vh-420px\)\]{height:calc(100vh - 420px)}.h-full{height:100%}.h-screen{height:100vh}.max-h-32{max-height:calc(var(--spacing) * 32)}.max-h-64{max-height:calc(var(--spacing) * 64)}.max-h-\[90vh\]{max-height:90vh}.max-h-\[300px\]{max-height:300px}.max-h-\[360px\]{max-height:360px}.min-h-\[44px\]{min-height:44px}.min-h-\[50vh\]{min-height:50vh}.min-h-\[88px\]{min-height:88px}.min-h-\[250px\]{min-height:250px}.min-h-\[300px\]{min-height:300px}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-6{width:calc(var(--spacing) * 6)}.w-10{width:calc(var(--spacing) * 10)}.w-16{width:calc(var(--spacing) * 16)}.w-56{width:calc(var(--spacing) * 56)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-\[80\%\]{max-width:80%}.max-w-\[160px\]{max-width:160px}.max-w-\[200px\]{max-width:200px}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[44px\]{min-width:44px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-ping{animation:var(--animate-ping)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-\[1fr_120px_1fr_44px\]{grid-template-columns:1fr 120px 1fr 44px}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-\[\#1a1a2e\]\/50>:not(:last-child)){border-color:#1a1a2e80}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-br-sm{border-bottom-right-radius:var(--radius-sm)}.rounded-bl-sm{border-bottom-left-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-\[\#00e5ff\]\/30{border-color:#00e5ff4d}.border-\[\#00e5ff\]\/40{border-color:#00e5ff66}.border-\[\#00ff88\]\/30{border-color:#00ff884d}.border-\[\#1a1a2e\]{border-color:#1a1a2e}.border-\[\#1a1a2e\]\/50{border-color:#1a1a2e80}.border-\[\#ff3366\]\/20{border-color:#f363}.border-\[\#ff3366\]\/30{border-color:#ff33664d}.border-\[\#ff3366\]\/40{border-color:#f366}.border-\[\#ff336620\]{border-color:#ff336620}.border-\[\#ffaa00\]\/40{border-color:#fa06}.border-cyan-500\/50{border-color:#00b7d780}@supports (color:color-mix(in lab,red,red)){.border-cyan-500\/50{border-color:color-mix(in oklab,var(--color-cyan-500) 50%,transparent)}}.border-cyan\/30{border-color:#00e5ff4d}@supports (color:color-mix(in lab,red,red)){.border-cyan\/30{border-color:color-mix(in oklab,var(--color-cyan) 30%,transparent)}}.border-emerald-400\/20{border-color:#00d29433}@supports (color:color-mix(in lab,red,red)){.border-emerald-400\/20{border-color:color-mix(in oklab,var(--color-emerald-400) 20%,transparent)}}.border-emerald-400\/30{border-color:#00d2944d}@supports (color:color-mix(in lab,red,red)){.border-emerald-400\/30{border-color:color-mix(in oklab,var(--color-emerald-400) 30%,transparent)}}.border-gray-500\/30{border-color:#6a72824d}@supports (color:color-mix(in lab,red,red)){.border-gray-500\/30{border-color:color-mix(in oklab,var(--color-gray-500) 30%,transparent)}}.border-green-500\/50{border-color:#00c75880}@supports (color:color-mix(in lab,red,red)){.border-green-500\/50{border-color:color-mix(in oklab,var(--color-green-500) 50%,transparent)}}.border-red-400\/30{border-color:#ff65684d}@supports (color:color-mix(in lab,red,red)){.border-red-400\/30{border-color:color-mix(in oklab,var(--color-red-400) 30%,transparent)}}.border-red-500\/50{border-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.border-red-500\/50{border-color:color-mix(in oklab,var(--color-red-500) 50%,transparent)}}.border-void-lighter{border-color:var(--color-void-lighter)}.border-void-lighter\/50{border-color:#1a1a2e80}@supports (color:color-mix(in lab,red,red)){.border-void-lighter\/50{border-color:color-mix(in oklab,var(--color-void-lighter) 50%,transparent)}}.border-yellow-500\/50{border-color:#edb20080}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/50{border-color:color-mix(in oklab,var(--color-yellow-500) 50%,transparent)}}.bg-\[\#0a0a0f\]{background-color:#0a0a0f}.bg-\[\#0d0d12\]{background-color:#0d0d12}.bg-\[\#00e5ff\]{background-color:#00e5ff}.bg-\[\#00e5ff\]\/10{background-color:#00e5ff1a}.bg-\[\#1a1a00\]\/60{background-color:#1a1a0099}.bg-\[\#1a1a2e\]{background-color:#1a1a2e}.bg-\[\#1a1a3e\]{background-color:#1a1a3e}.bg-\[\#003322\]{background-color:#032}.bg-\[\#003322\]\/50{background-color:#00332280}.bg-\[\#111118\]{background-color:#111118}.bg-\[\#331111\]{background-color:#311}.bg-\[\#ff3366\]\/10{background-color:#ff33661a}.bg-\[\#ff336610\]{background-color:#ff336610}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-current{background-color:currentColor}.bg-cyan-950\/80{background-color:#053345cc}@supports (color:color-mix(in lab,red,red)){.bg-cyan-950\/80{background-color:color-mix(in oklab,var(--color-cyan-950) 80%,transparent)}}.bg-cyan\/10{background-color:#00e5ff1a}@supports (color:color-mix(in lab,red,red)){.bg-cyan\/10{background-color:color-mix(in oklab,var(--color-cyan) 10%,transparent)}}.bg-emerald-400\/10{background-color:#00d2941a}@supports (color:color-mix(in lab,red,red)){.bg-emerald-400\/10{background-color:color-mix(in oklab,var(--color-emerald-400) 10%,transparent)}}.bg-gray-500\/10{background-color:#6a72821a}@supports (color:color-mix(in lab,red,red)){.bg-gray-500\/10{background-color:color-mix(in oklab,var(--color-gray-500) 10%,transparent)}}.bg-green-900\/30{background-color:#0d542b4d}@supports (color:color-mix(in lab,red,red)){.bg-green-900\/30{background-color:color-mix(in oklab,var(--color-green-900) 30%,transparent)}}.bg-green-950\/80{background-color:#032e15cc}@supports (color:color-mix(in lab,red,red)){.bg-green-950\/80{background-color:color-mix(in oklab,var(--color-green-950) 80%,transparent)}}.bg-red-400\/10{background-color:#ff65681a}@supports (color:color-mix(in lab,red,red)){.bg-red-400\/10{background-color:color-mix(in oklab,var(--color-red-400) 10%,transparent)}}.bg-red-900\/30{background-color:#82181a4d}@supports (color:color-mix(in lab,red,red)){.bg-red-900\/30{background-color:color-mix(in oklab,var(--color-red-900) 30%,transparent)}}.bg-red-950\/80{background-color:#460809cc}@supports (color:color-mix(in lab,red,red)){.bg-red-950\/80{background-color:color-mix(in oklab,var(--color-red-950) 80%,transparent)}}.bg-void{background-color:var(--color-void)}.bg-void-light{background-color:var(--color-void-light)}.bg-void-lighter{background-color:var(--color-void-lighter)}.bg-yellow-900\/30{background-color:#733e0a4d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-900\/30{background-color:color-mix(in oklab,var(--color-yellow-900) 30%,transparent)}}.bg-yellow-950\/80{background-color:#432004cc}@supports (color:color-mix(in lab,red,red)){.bg-yellow-950\/80{background-color:color-mix(in oklab,var(--color-yellow-950) 80%,transparent)}}.p-0\.5{padding:calc(var(--spacing) * .5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.p-12{padding:calc(var(--spacing) * 12)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-8{padding-block:calc(var(--spacing) * 8)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pb-0{padding-bottom:calc(var(--spacing) * 0)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-\[\#00e5ff\]{color:#00e5ff}.text-\[\#00ff88\]{color:#0f8}.text-\[\#333\]{color:#333}.text-\[\#444\]{color:#444}.text-\[\#555\]{color:#555}.text-\[\#666\]{color:#666}.text-\[\#888\]{color:#888}.text-\[\#e0e0e0\]{color:#e0e0e0}.text-\[\#ff3366\]{color:#f36}.text-\[\#ffaa00\]{color:#fa0}.text-amber-500{color:var(--color-amber-500)}.text-cyan{color:var(--color-cyan)}.text-cyan-200{color:var(--color-cyan-200)}.text-emerald-400{color:var(--color-emerald-400)}.text-gray-100{color:var(--color-gray-100)}.text-gray-200{color:var(--color-gray-200)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-green-200{color:var(--color-green-200)}.text-green-400{color:var(--color-green-400)}.text-red-200{color:var(--color-red-200)}.text-red-400{color:var(--color-red-400)}.text-yellow-200{color:var(--color-yellow-200)}.text-yellow-400{color:var(--color-yellow-400)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-gray-600::placeholder{color:var(--color-gray-600)}.opacity-40{opacity:.4}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}@media(hover:hover){.hover\:border-l-2:hover{border-left-style:var(--tw-border-style);border-left-width:2px}.hover\:border-\[\#00e5ff\]\/30:hover{border-color:#00e5ff4d}.hover\:border-l-cyan:hover{border-left-color:var(--color-cyan)}.hover\:bg-\[\#00e5ff\]\/20:hover{background-color:#00e5ff33}.hover\:bg-\[\#1a1a2e\]\/30:hover{background-color:#1a1a2e4d}.hover\:bg-\[\#2a2a3e\]:hover{background-color:#2a2a3e}.hover\:bg-\[\#004433\]:hover{background-color:#043}.hover\:bg-\[\#442222\]:hover{background-color:#422}.hover\:bg-green-900\/50:hover{background-color:#0d542b80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-green-900\/50:hover{background-color:color-mix(in oklab,var(--color-green-900) 50%,transparent)}}.hover\:bg-red-900\/50:hover{background-color:#82181a80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-900\/50:hover{background-color:color-mix(in oklab,var(--color-red-900) 50%,transparent)}}.hover\:bg-void-lighter:hover{background-color:var(--color-void-lighter)}.hover\:bg-yellow-900\/50:hover{background-color:#733e0a80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-900\/50:hover{background-color:color-mix(in oklab,var(--color-yellow-900) 50%,transparent)}}.hover\:text-\[\#00e5ff\]:hover{color:#00e5ff}.hover\:text-\[\#777\]:hover{color:#777}.hover\:text-\[\#888\]:hover{color:#888}.hover\:text-\[\#e0e0e0\]:hover{color:#e0e0e0}.hover\:text-\[\#ff3366\]:hover{color:#f36}.hover\:text-cyan:hover{color:var(--color-cyan)}.hover\:text-gray-200:hover{color:var(--color-gray-200)}.hover\:text-gray-300:hover{color:var(--color-gray-300)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}}.focus\:border-\[\#00e5ff\]:focus{border-color:#00e5ff}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:bg-\[\#1a1a2e\]\/50:active{background-color:#1a1a2e80}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:flex{display:flex}.sm\:inline{display:inline}.sm\:inline-block{display:inline-block}.sm\:h-\[calc\(100vh-420px\)\]{height:calc(100vh - 420px)}.sm\:h-\[calc\(100vh-460px\)\]{height:calc(100vh - 460px)}.sm\:min-h-\[300px\]{min-height:300px}.sm\:min-h-\[400px\]{min-height:400px}.sm\:max-w-xs{max-width:var(--container-xs)}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-2{gap:calc(var(--spacing) * 2)}.sm\:gap-3{gap:calc(var(--spacing) * 3)}.sm\:gap-4{gap:calc(var(--spacing) * 4)}:where(.sm\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.sm\:p-4{padding:calc(var(--spacing) * 4)}.sm\:p-5{padding:calc(var(--spacing) * 5)}.sm\:px-4{padding-inline:calc(var(--spacing) * 4)}.sm\:px-5{padding-inline:calc(var(--spacing) * 5)}.sm\:py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.sm\:py-4{padding-block:calc(var(--spacing) * 4)}.sm\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}@media(min-width:48rem){.md\:block{display:block}.md\:hidden{display:none}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:64rem){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}}body{font-family:var(--font-sans);background-color:var(--color-void);color:#e0e0e8}*{scrollbar-width:thin;scrollbar-color:var(--color-void-lighter) transparent}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--color-void-lighter);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--color-cyan-dim)}.status-dot{border-radius:50%;flex-shrink:0;width:8px;height:8px;display:inline-block}.status-dot--idle{background-color:var(--color-cyan);box-shadow:0 0 6px var(--color-cyan-dim)}.status-dot--working{background-color:var(--color-warning);animation:1.5s ease-in-out infinite pulse;box-shadow:0 0 6px #ffaa0040}.status-dot--permission_prompt,.status-dot--bash_approval{background-color:var(--color-error);animation:1s ease-in-out infinite pulse;box-shadow:0 0 6px #ff336640}.status-dot--plan_mode,.status-dot--ask_question{background-color:#a78bfa;box-shadow:0 0 6px #a78bfa40}.status-dot--settings{background-color:var(--color-warning)}.status-dot--unknown{background-color:#666}@keyframes pulse{50%{opacity:.5}}.font-code{font-family:var(--font-mono)}.glow-cyan{box-shadow:0 0 12px var(--color-cyan-dim)}.glow-text-cyan{text-shadow:0 0 8px var(--color-cyan-dim)}@keyframes slide-in{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}.animate-slide-in{animation:.2s ease-out slide-in}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}