@agentstep/agent-sdk 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 (105) hide show
  1. package/package.json +45 -0
  2. package/src/auth/middleware.ts +38 -0
  3. package/src/backends/claude/args.ts +88 -0
  4. package/src/backends/claude/index.ts +193 -0
  5. package/src/backends/claude/permission-hook.ts +152 -0
  6. package/src/backends/claude/tool-bridge.ts +211 -0
  7. package/src/backends/claude/translator.ts +209 -0
  8. package/src/backends/claude/wrapper-script.ts +45 -0
  9. package/src/backends/codex/args.ts +69 -0
  10. package/src/backends/codex/auth.ts +35 -0
  11. package/src/backends/codex/index.ts +57 -0
  12. package/src/backends/codex/setup.ts +37 -0
  13. package/src/backends/codex/translator.ts +223 -0
  14. package/src/backends/codex/wrapper-script.ts +26 -0
  15. package/src/backends/factory/args.ts +45 -0
  16. package/src/backends/factory/auth.ts +30 -0
  17. package/src/backends/factory/index.ts +56 -0
  18. package/src/backends/factory/setup.ts +34 -0
  19. package/src/backends/factory/translator.ts +139 -0
  20. package/src/backends/factory/wrapper-script.ts +33 -0
  21. package/src/backends/gemini/args.ts +44 -0
  22. package/src/backends/gemini/auth.ts +30 -0
  23. package/src/backends/gemini/index.ts +53 -0
  24. package/src/backends/gemini/setup.ts +34 -0
  25. package/src/backends/gemini/translator.ts +139 -0
  26. package/src/backends/gemini/wrapper-script.ts +26 -0
  27. package/src/backends/opencode/args.ts +53 -0
  28. package/src/backends/opencode/auth.ts +53 -0
  29. package/src/backends/opencode/index.ts +70 -0
  30. package/src/backends/opencode/mcp.ts +67 -0
  31. package/src/backends/opencode/setup.ts +54 -0
  32. package/src/backends/opencode/translator.ts +168 -0
  33. package/src/backends/opencode/wrapper-script.ts +46 -0
  34. package/src/backends/registry.ts +38 -0
  35. package/src/backends/shared/ndjson.ts +29 -0
  36. package/src/backends/shared/translator-types.ts +69 -0
  37. package/src/backends/shared/wrap-prompt.ts +17 -0
  38. package/src/backends/types.ts +85 -0
  39. package/src/config/index.ts +95 -0
  40. package/src/db/agents.ts +185 -0
  41. package/src/db/api_keys.ts +78 -0
  42. package/src/db/batch.ts +142 -0
  43. package/src/db/client.ts +81 -0
  44. package/src/db/environments.ts +127 -0
  45. package/src/db/events.ts +208 -0
  46. package/src/db/memory.ts +143 -0
  47. package/src/db/migrations.ts +295 -0
  48. package/src/db/proxy.ts +37 -0
  49. package/src/db/sessions.ts +295 -0
  50. package/src/db/vaults.ts +110 -0
  51. package/src/errors.ts +53 -0
  52. package/src/handlers/agents.ts +194 -0
  53. package/src/handlers/batch.ts +41 -0
  54. package/src/handlers/docs.ts +87 -0
  55. package/src/handlers/environments.ts +154 -0
  56. package/src/handlers/events.ts +234 -0
  57. package/src/handlers/index.ts +12 -0
  58. package/src/handlers/memory.ts +141 -0
  59. package/src/handlers/openapi.ts +14 -0
  60. package/src/handlers/sessions.ts +223 -0
  61. package/src/handlers/stream.ts +76 -0
  62. package/src/handlers/threads.ts +26 -0
  63. package/src/handlers/ui/app.js +984 -0
  64. package/src/handlers/ui/index.html +112 -0
  65. package/src/handlers/ui/style.css +164 -0
  66. package/src/handlers/ui.ts +1281 -0
  67. package/src/handlers/vaults.ts +99 -0
  68. package/src/http.ts +35 -0
  69. package/src/index.ts +104 -0
  70. package/src/init.ts +227 -0
  71. package/src/openapi/registry.ts +8 -0
  72. package/src/openapi/schemas.ts +625 -0
  73. package/src/openapi/spec.ts +691 -0
  74. package/src/providers/apple.ts +220 -0
  75. package/src/providers/daytona.ts +217 -0
  76. package/src/providers/docker.ts +264 -0
  77. package/src/providers/e2b.ts +203 -0
  78. package/src/providers/fly.ts +276 -0
  79. package/src/providers/modal.ts +222 -0
  80. package/src/providers/podman.ts +206 -0
  81. package/src/providers/registry.ts +28 -0
  82. package/src/providers/shared.ts +11 -0
  83. package/src/providers/sprites.ts +55 -0
  84. package/src/providers/types.ts +73 -0
  85. package/src/providers/vercel.ts +208 -0
  86. package/src/proxy/forward.ts +111 -0
  87. package/src/queue/index.ts +111 -0
  88. package/src/sessions/actor.ts +53 -0
  89. package/src/sessions/bus.ts +155 -0
  90. package/src/sessions/driver.ts +818 -0
  91. package/src/sessions/grader.ts +120 -0
  92. package/src/sessions/interrupt.ts +14 -0
  93. package/src/sessions/sweeper.ts +136 -0
  94. package/src/sessions/threads.ts +126 -0
  95. package/src/sessions/tools.ts +50 -0
  96. package/src/shutdown.ts +78 -0
  97. package/src/sprite/client.ts +294 -0
  98. package/src/sprite/exec.ts +161 -0
  99. package/src/sprite/lifecycle.ts +339 -0
  100. package/src/sprite/pool.ts +65 -0
  101. package/src/sprite/setup.ts +159 -0
  102. package/src/state.ts +61 -0
  103. package/src/types.ts +339 -0
  104. package/src/util/clock.ts +7 -0
  105. package/src/util/ids.ts +11 -0
@@ -0,0 +1,112 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta http-equiv="cache-control" content="no-store, no-cache, must-revalidate" />
7
+ <meta http-equiv="pragma" content="no-cache" />
8
+ <meta http-equiv="expires" content="0" />
9
+ <title>AgentStep Gateway</title>
10
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><defs><linearGradient id='g' x1='0' y1='0' x2='1' y2='1'><stop offset='0%25' stop-color='%23eafb6e'/><stop offset='100%25' stop-color='%23b8fc5e'/></linearGradient></defs><circle cx='50' cy='50' r='40' fill='url(%23g)'/></svg>" />
11
+ <script src="https://cdn.jsdelivr.net/npm/marked@15/marked.min.js"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
13
+ <link rel="preconnect" href="https://rsms.me/" />
14
+ <link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
15
+ <style>__STYLE__</style>
16
+ </head>
17
+ <body>
18
+ <div id="app">
19
+ <!-- Header -->
20
+ <div class="header">
21
+ <h1>AgentStep Gateway</h1>
22
+ <div class="key-input">
23
+ <label style="font-size:11px;color:var(--dim);margin-right:4px">API Key</label>
24
+ <input type="password" id="apiKeyInput" placeholder="ck_..." />
25
+ <button class="btn-icon" onclick="toggleKeyVisibility()" id="keyEyeBtn" title="Show/hide key"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></svg></button>
26
+ </div>
27
+ </div>
28
+
29
+ <!-- Tabs -->
30
+ <div class="tabs">
31
+ <button class="tab active" data-tab="chat">Chat</button>
32
+ <button class="tab" data-tab="config">Config</button>
33
+ <button class="tab" data-tab="events">Events</button>
34
+ </div>
35
+
36
+ <!-- Content -->
37
+ <div class="content">
38
+ <!-- Chat Panel -->
39
+ <div class="panel active" id="panel-chat">
40
+ <div class="chat-layout">
41
+ <div class="chat-sidebar">
42
+ <div class="chat-sidebar-header">
43
+ <span>Sessions</span>
44
+ <button class="btn-icon" onclick="loadSessions()" title="Refresh"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/></svg></button>
45
+ <button class="btn-icon" onclick="showNewSessionModal()" title="New"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg></button>
46
+ </div>
47
+ <div class="session-list" id="sessionList"></div>
48
+ </div>
49
+ <div class="chat-main">
50
+ <div class="messages" id="messages">
51
+ <div class="empty-state" id="chatEmpty">
52
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="#a3e635" opacity="0.3"/><circle cx="50" cy="50" r="20" fill="#a3e635" opacity="0.6"/></svg>
53
+ <p style="font-size:16px;font-weight:600;margin-bottom:4px">AgentStep Gateway</p>
54
+ <p style="font-size:12px">Select a session to continue, or click + to start a new one</p>
55
+ </div>
56
+ </div>
57
+ <div class="chat-input-area" id="chatInputArea" style="display:none">
58
+ <div class="chat-input-wrap">
59
+ <textarea id="chatInput" placeholder="Message..." rows="1" onkeydown="handleChatKey(event)"></textarea>
60
+ <button class="btn btn-primary" onclick="sendMessage()">Send</button>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+
67
+ <!-- Config Panel -->
68
+ <div class="panel" id="panel-config">
69
+ <div class="config-layout">
70
+ <div class="config-col">
71
+ <h2>Agents <button class="btn btn-sm btn-secondary" onclick="showCreateAgentModal()">+ New</button></h2>
72
+ <div class="card" id="agentsList"><p style="color:var(--dim);font-size:12px">Loading...</p></div>
73
+ </div>
74
+ <div class="config-col">
75
+ <h2>Environments <button class="btn btn-sm btn-secondary" onclick="showCreateEnvModal()">+ New</button></h2>
76
+ <div class="card" id="envsList"><p style="color:var(--dim);font-size:12px">Loading...</p></div>
77
+ </div>
78
+ <div class="config-col">
79
+ <h2>Secrets <button class="btn btn-sm btn-secondary" onclick="showCreateVaultModal()">+ New</button></h2>
80
+ <div class="card" id="vaultsList"><p style="color:var(--dim);font-size:12px">Loading...</p></div>
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ <!-- Events Panel -->
86
+ <div class="panel" id="panel-events">
87
+ <div class="events-layout">
88
+ <div class="events-toolbar">
89
+ <select class="form-select" id="eventsSessionSelect" onchange="loadEvents()" style="width:280px">
90
+ <option value="">Select session...</option>
91
+ </select>
92
+ <button class="btn btn-sm btn-secondary" onclick="copyEvents()">Copy JSON</button>
93
+ <span id="eventsStats" style="margin-left:auto;font-size:11px;color:var(--dim)"></span>
94
+ </div>
95
+ <div class="events-list" id="eventsList">
96
+ <div class="empty-state">
97
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
98
+ <p>Select a session to inspect events</p>
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+
106
+ <!-- Modals container -->
107
+ <div id="modals"></div>
108
+
109
+ __INJECT__
110
+ <script>__SCRIPT__</script>
111
+ </body>
112
+ </html>
@@ -0,0 +1,164 @@
1
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
2
+
3
+ :root {
4
+ --bg: #0a0a0a; --surface: #171717; --surface2: #1e1e1e;
5
+ --border: rgba(255,255,255,0.08); --border-strong: rgba(255,255,255,0.14);
6
+ --heading: #fafafa; --body: #d4d4d4; --muted: #a3a3a3; --dim: #636363;
7
+ --accent: rgb(163, 230, 53); --accent-hover: rgb(190, 242, 100);
8
+ --error: #f87171; --success: #4ade80;
9
+ --font: 'Inter', -apple-system, sans-serif;
10
+ --mono: 'Geist Mono', 'SF Mono', ui-monospace, monospace;
11
+ }
12
+
13
+ html, body { height: 100%; background: var(--bg); color: var(--body); font-family: var(--font); font-size: 14px; -webkit-font-smoothing: antialiased; }
14
+ a { color: var(--accent); text-decoration: none; }
15
+ input, select, textarea, button { font-family: inherit; font-size: inherit; }
16
+
17
+ /* Layout */
18
+ #app { display: flex; flex-direction: column; height: 100vh; }
19
+ .header { display: flex; align-items: center; height: 48px; border-bottom: 1px solid var(--border); padding: 0 16px; gap: 12px; flex-shrink: 0; }
20
+ .header h1 { font-size: 16px; font-weight: 600; color: var(--heading); }
21
+ .header .key-input { margin-left: auto; display: flex; align-items: center; gap: 8px; }
22
+ .header input[type="text"], .header input[type="password"] { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 4px 10px; color: var(--body); font-size: 12px; width: 200px; }
23
+
24
+ /* Tabs */
25
+ .tabs { display: flex; gap: 2px; padding: 0 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
26
+ .tab { padding: 8px 16px; font-size: 13px; font-weight: 500; color: var(--muted); cursor: pointer; border: none; background: none; border-bottom: 2px solid transparent; transition: all 0.15s; }
27
+ .tab:hover { color: var(--heading); }
28
+ .tab.active { color: var(--accent); border-bottom-color: var(--accent); }
29
+
30
+ /* Content */
31
+ .content { flex: 1; overflow: hidden; display: flex; }
32
+ .panel { display: none; flex: 1; overflow: hidden; }
33
+ .panel.active { display: flex; }
34
+
35
+ /* Chat */
36
+ .chat-layout { display: flex; flex: 1; overflow: hidden; }
37
+ .chat-sidebar { width: 220px; border-right: 1px solid var(--border); display: flex; flex-direction: column; flex-shrink: 0; }
38
+ .chat-sidebar-header { display: flex; align-items: center; height: 40px; padding: 0 12px; border-bottom: 1px solid var(--border); gap: 8px; }
39
+ .chat-sidebar-header span { flex: 1; font-size: 12px; font-weight: 500; color: var(--muted); }
40
+ .session-list { flex: 1; overflow-y: auto; padding: 4px; }
41
+ .session-item { display: flex; flex-direction: column; gap: 2px; padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: 12px; }
42
+ .session-item:hover { background: var(--surface); }
43
+ .session-item.active { background: var(--surface2); }
44
+ .session-item .title { color: var(--body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
45
+ .session-item .meta { font-size: 10px; color: var(--dim); display: flex; justify-content: space-between; }
46
+
47
+ .chat-main { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
48
+ .messages { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; }
49
+ .message { max-width: 720px; width: 100%; margin: 0 auto; }
50
+ .message.user { }
51
+ .message.assistant { }
52
+ .message-content { padding: 10px 14px; border-radius: 12px; font-size: 13px; line-height: 1.6; word-break: break-word; }
53
+ .message.user .message-content { white-space: pre-wrap; }
54
+ .message.user .message-content { background: var(--surface2); color: var(--body); }
55
+ .message.assistant .message-content { color: var(--heading); }
56
+ .message.tool .message-content { background: var(--surface); color: var(--muted); font-family: var(--mono); font-size: 11px; border: 1px solid var(--border); }
57
+ .message.error .message-content { background: #dc2626; color: #fff; border-radius: 8px; }
58
+ .message.error .message-role { color: #dc2626; }
59
+ .message-role { font-size: 10px; font-weight: 500; color: var(--dim); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.05em; }
60
+ .typing { display: flex; align-items: center; gap: 8px; padding: 10px 14px; color: var(--muted); font-size: 13px; max-width: 720px; margin: 0 auto; }
61
+ .typing-dots { display: flex; gap: 3px; }
62
+ .typing-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--muted); animation: bounce 1.2s infinite; }
63
+ .typing-dot:nth-child(2) { animation-delay: 0.15s; }
64
+ .typing-dot:nth-child(3) { animation-delay: 0.3s; }
65
+ @keyframes bounce { 0%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-6px); } }
66
+
67
+ .chat-input-area { padding: 12px 16px; border-top: 1px solid var(--border); }
68
+ .chat-input-wrap { max-width: 720px; margin: 0 auto; display: flex; gap: 8px; }
69
+ .chat-input-wrap textarea { flex: 1; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 10px 14px; color: var(--body); resize: none; min-height: 42px; max-height: 150px; }
70
+ .chat-input-wrap textarea:focus { outline: none; border-color: var(--accent); }
71
+ .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; flex: 1; gap: 12px; color: var(--dim); }
72
+ .empty-state svg { opacity: 0.3; }
73
+
74
+ /* Config */
75
+ .config-layout { flex: 1; display: flex; gap: 1px; background: var(--border); overflow: hidden; }
76
+ .config-col { flex: 1; display: flex; flex-direction: column; gap: 12px; overflow-y: auto; padding: 16px; background: var(--bg); }
77
+ .config-col h2 { font-size: 14px; font-weight: 600; color: var(--heading); display: flex; align-items: center; justify-content: space-between; }
78
+ .card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 12px; }
79
+ .card-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border); }
80
+ .card-item:last-child { border-bottom: none; }
81
+ .card-item .name { font-size: 13px; color: var(--heading); }
82
+ .card-item .detail { font-size: 11px; color: var(--dim); font-family: var(--mono); }
83
+
84
+ /* Events */
85
+ .events-layout { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
86
+ .events-toolbar { display: flex; align-items: center; gap: 8px; padding: 8px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
87
+ .events-list { flex: 1; overflow-y: auto; }
88
+ .event-row { display: flex; align-items: flex-start; gap: 8px; padding: 6px 16px; border-bottom: 1px solid var(--border); cursor: pointer; font-size: 12px; }
89
+ .event-row:hover { background: var(--surface); }
90
+ .event-row .seq { color: var(--dim); font-family: var(--mono); font-size: 10px; width: 28px; text-align: right; flex-shrink: 0; padding-top: 2px; }
91
+ .event-row .delta { color: var(--dim); font-family: var(--mono); font-size: 10px; flex-shrink: 0; padding-top: 2px; }
92
+ .event-row .tokens { color: #a78bfa; font-family: var(--mono); font-size: 10px; flex-shrink: 0; padding-top: 2px; }
93
+ .event-row .preview { flex: 1; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
94
+ .event-detail { padding: 4px 16px 8px 52px; }
95
+ .event-detail pre { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 8px; font-family: var(--mono); font-size: 10px; color: var(--body); overflow-x: auto; max-height: 200px; overflow-y: auto; white-space: pre-wrap; }
96
+
97
+ /* Badges */
98
+ .badge { display: inline-block; padding: 1px 6px; border-radius: 4px; font-size: 10px; font-weight: 500; font-family: var(--mono); }
99
+ .badge-user { background: #1e3a5f; color: #60a5fa; }
100
+ .badge-agent { background: #064e3b; color: #34d399; }
101
+ .badge-status { background: #422006; color: #fbbf24; }
102
+ .badge-error { background: #450a0a; color: #f87171; }
103
+ .badge-span { background: #2e1065; color: #a78bfa; }
104
+ .badge-idle { background: var(--surface2); color: var(--muted); }
105
+ .badge-running { background: #064e3b; color: #34d399; }
106
+
107
+ /* Markdown in messages */
108
+ .message-content strong { color: var(--heading); font-weight: 600; }
109
+ .message-content em { font-style: italic; }
110
+ .message-content code { background: var(--surface); padding: 1px 5px; border-radius: 4px; font-family: var(--mono); font-size: 0.9em; }
111
+ .message-content pre { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; margin: 8px 0; overflow-x: auto; font-family: var(--mono); font-size: 12px; line-height: 1.5; }
112
+ .message-content pre code { background: none; padding: 0; }
113
+ .message-content a { color: var(--accent); text-decoration: underline; }
114
+ /* Reset ALL margins inside message content — browser defaults cause spacing issues */
115
+ .message-content p, .message-content ul, .message-content ol,
116
+ .message-content li, .message-content h1, .message-content h2,
117
+ .message-content h3, .message-content h4, .message-content blockquote {
118
+ margin: 0 !important; padding: 0 !important;
119
+ }
120
+ .message-content ul, .message-content ol { padding-left: 1.5em !important; margin: 2px 0 !important; }
121
+ .message-content li { line-height: 1.5; }
122
+ .message-content p + p { margin-top: 8px !important; }
123
+ .message-content p + ul, .message-content p + ol { margin-top: 2px !important; }
124
+ .message-content ul + p, .message-content ol + p { margin-top: 8px !important; }
125
+ .message-content h1, .message-content h2, .message-content h3 { color: var(--heading); margin: 8px 0 2px !important; }
126
+ .message-content h1 { font-size: 1.3em; } .message-content h2 { font-size: 1.15em; } .message-content h3 { font-size: 1.05em; }
127
+ .badge-running { background: #064e3b; color: #34d399; }
128
+
129
+ /* Buttons */
130
+ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 10px 20px; border-radius: 10px; font-size: 13px; font-weight: 600; border: none; cursor: pointer; transition: all 0.15s; }
131
+ .btn-primary { background: linear-gradient(135deg, #e2f751, #a5fb3c); color: #0a0a0a; }
132
+ .btn-primary:hover { background: linear-gradient(135deg, #eafb6e, #b8fc5e); box-shadow: 0 0 16px 2px rgba(165, 251, 60, 0.25); }
133
+ .btn-secondary { background: var(--surface); color: var(--body); border: 1px solid var(--border); }
134
+ .btn-secondary:hover { background: var(--surface2); }
135
+ .btn-danger { background: transparent; color: var(--error); border: 1px solid var(--error); }
136
+ .btn-danger:hover { background: rgba(248,113,113,0.1); }
137
+ .btn-sm { padding: 4px 8px; font-size: 11px; }
138
+ .btn-icon { padding: 4px; border-radius: 6px; background: none; border: none; color: var(--muted); cursor: pointer; }
139
+ .btn-icon:hover { color: var(--heading); background: var(--surface); }
140
+
141
+ /* Forms */
142
+ .form-group { display: flex; flex-direction: column; gap: 4px; }
143
+ .form-label { font-size: 11px; font-weight: 500; color: var(--muted); }
144
+ .form-input, .form-select, .form-textarea { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 6px 10px; color: var(--body); font-size: 13px; }
145
+ .form-input:focus, .form-select:focus, .form-textarea:focus { outline: none; border-color: var(--accent); }
146
+ .form-select { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a3a3a3' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 8px center; padding-right: 28px; }
147
+
148
+ /* Modal */
149
+ .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; z-index: 100; }
150
+ .modal { background: var(--bg); border: 1px solid var(--border-strong); border-radius: 12px; padding: 20px; width: 380px; max-width: 90vw; display: flex; flex-direction: column; gap: 16px; }
151
+ .modal h2 { font-size: 16px; font-weight: 600; color: var(--heading); }
152
+ .modal p { font-size: 12px; color: var(--muted); }
153
+ .modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
154
+
155
+ /* Toast */
156
+ .toast { position: fixed; bottom: 16px; right: 16px; padding: 10px 16px; border-radius: 8px; font-size: 12px; z-index: 200; animation: fadeIn 0.2s; }
157
+ .toast-error { background: #450a0a; color: var(--error); border: 1px solid var(--error); }
158
+ .toast-success { background: #052e16; color: var(--success); border: 1px solid var(--success); }
159
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
160
+
161
+ /* Scrollbar */
162
+ ::-webkit-scrollbar { width: 6px; }
163
+ ::-webkit-scrollbar-track { background: transparent; }
164
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }