brainctl 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -136
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +9 -1
- package/dist/commands/profile.d.ts +8 -1
- package/dist/commands/profile.js +31 -1
- package/dist/mcp/server.js +185 -0
- package/dist/services/agent-config-service.d.ts +20 -0
- package/dist/services/agent-config-service.js +189 -0
- package/dist/services/profile-export-service.d.ts +15 -0
- package/dist/services/profile-export-service.js +63 -0
- package/dist/services/profile-import-service.d.ts +11 -0
- package/dist/services/profile-import-service.js +81 -0
- package/dist/services/profile-service.d.ts +10 -0
- package/dist/services/profile-service.js +29 -2
- package/dist/services/sync/agent-reader.d.ts +25 -0
- package/dist/services/sync/agent-reader.js +221 -0
- package/dist/ui/routes.js +165 -1
- package/dist/web/assets/{index-CRJ6cM0Q.css → index-364NYWPA.css} +1 -1
- package/dist/web/assets/index-BmfE7rus.js +16 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -1
- package/dist/web/assets/index-Cr8gt3VF.js +0 -9
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export function createClaudeReader() {
|
|
5
|
+
return {
|
|
6
|
+
async read(options) {
|
|
7
|
+
const configPath = path.join(homedir(), '.claude.json');
|
|
8
|
+
try {
|
|
9
|
+
const source = await readFile(configPath, 'utf8');
|
|
10
|
+
const data = JSON.parse(source);
|
|
11
|
+
const projects = (data.projects ?? {});
|
|
12
|
+
const projectConfig = projects[options.cwd] ?? {};
|
|
13
|
+
const rawServers = (projectConfig.mcpServers ?? {});
|
|
14
|
+
const mcpServers = {};
|
|
15
|
+
for (const [name, entry] of Object.entries(rawServers)) {
|
|
16
|
+
mcpServers[name] = {
|
|
17
|
+
command: String(entry.command ?? ''),
|
|
18
|
+
args: Array.isArray(entry.args) ? entry.args.map(String) : undefined,
|
|
19
|
+
env: parseEnvObject(entry.env),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const skills = await readClaudePlugins();
|
|
23
|
+
return { agent: 'claude', configPath, exists: true, mcpServers, skills };
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
const skills = await readClaudePlugins();
|
|
27
|
+
return { agent: 'claude', configPath, exists: false, mcpServers: {}, skills };
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function createCodexReader() {
|
|
33
|
+
return {
|
|
34
|
+
async read() {
|
|
35
|
+
const configPath = path.join(homedir(), '.codex', 'config.toml');
|
|
36
|
+
try {
|
|
37
|
+
const source = await readFile(configPath, 'utf8');
|
|
38
|
+
const mcpServers = parseCodexToml(source);
|
|
39
|
+
const skills = await readCodexSkills();
|
|
40
|
+
return { agent: 'codex', configPath, exists: true, mcpServers, skills };
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
const skills = await readCodexSkills();
|
|
44
|
+
return { agent: 'codex', configPath, exists: false, mcpServers: {}, skills };
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function createGeminiReader() {
|
|
50
|
+
return {
|
|
51
|
+
async read() {
|
|
52
|
+
const configPath = path.join(homedir(), '.gemini', 'settings.json');
|
|
53
|
+
try {
|
|
54
|
+
const source = await readFile(configPath, 'utf8');
|
|
55
|
+
const data = JSON.parse(source);
|
|
56
|
+
const rawServers = (data.mcpServers ?? {});
|
|
57
|
+
const mcpServers = {};
|
|
58
|
+
for (const [name, entry] of Object.entries(rawServers)) {
|
|
59
|
+
mcpServers[name] = {
|
|
60
|
+
command: String(entry.command ?? ''),
|
|
61
|
+
args: Array.isArray(entry.args) ? entry.args.map(String) : undefined,
|
|
62
|
+
env: parseEnvObject(entry.env),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const skills = await readGeminiSkills();
|
|
66
|
+
return { agent: 'gemini', configPath, exists: true, mcpServers, skills };
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
const skills = await readGeminiSkills();
|
|
70
|
+
return { agent: 'gemini', configPath, exists: false, mcpServers: {}, skills };
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function parseEnvObject(value) {
|
|
76
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
77
|
+
return undefined;
|
|
78
|
+
const result = {};
|
|
79
|
+
for (const [k, v] of Object.entries(value)) {
|
|
80
|
+
result[k] = String(v);
|
|
81
|
+
}
|
|
82
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
83
|
+
}
|
|
84
|
+
function parseCodexToml(source) {
|
|
85
|
+
const servers = {};
|
|
86
|
+
const lines = source.split('\n');
|
|
87
|
+
let currentServer = null;
|
|
88
|
+
let inEnv = false;
|
|
89
|
+
let currentEntry = { command: '' };
|
|
90
|
+
let currentEnv = {};
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
const trimmed = line.trim();
|
|
93
|
+
// Match [mcp_servers.name.env]
|
|
94
|
+
const envMatch = trimmed.match(/^\[mcp_servers\.([^.]+)\.env\]$/);
|
|
95
|
+
if (envMatch) {
|
|
96
|
+
inEnv = true;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// Match [mcp_servers.name]
|
|
100
|
+
const serverMatch = trimmed.match(/^\[mcp_servers\.([^\].]+)\]$/);
|
|
101
|
+
if (serverMatch) {
|
|
102
|
+
// Save previous server
|
|
103
|
+
if (currentServer) {
|
|
104
|
+
if (Object.keys(currentEnv).length > 0)
|
|
105
|
+
currentEntry.env = currentEnv;
|
|
106
|
+
servers[currentServer] = currentEntry;
|
|
107
|
+
}
|
|
108
|
+
currentServer = serverMatch[1];
|
|
109
|
+
currentEntry = { command: '' };
|
|
110
|
+
currentEnv = {};
|
|
111
|
+
inEnv = false;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
// New non-mcp section — flush current server
|
|
115
|
+
if (/^\[/.test(trimmed) && !/^\[mcp_servers/.test(trimmed)) {
|
|
116
|
+
if (currentServer) {
|
|
117
|
+
if (Object.keys(currentEnv).length > 0)
|
|
118
|
+
currentEntry.env = currentEnv;
|
|
119
|
+
servers[currentServer] = currentEntry;
|
|
120
|
+
currentServer = null;
|
|
121
|
+
currentEntry = { command: '' };
|
|
122
|
+
currentEnv = {};
|
|
123
|
+
}
|
|
124
|
+
inEnv = false;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
// Key-value pair
|
|
128
|
+
const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
129
|
+
if (!kvMatch || !currentServer)
|
|
130
|
+
continue;
|
|
131
|
+
const [, key, rawValue] = kvMatch;
|
|
132
|
+
if (inEnv) {
|
|
133
|
+
currentEnv[key] = parseTomlValue(rawValue);
|
|
134
|
+
}
|
|
135
|
+
else if (key === 'command') {
|
|
136
|
+
currentEntry.command = parseTomlValue(rawValue);
|
|
137
|
+
}
|
|
138
|
+
else if (key === 'args') {
|
|
139
|
+
currentEntry.args = parseTomlArray(rawValue);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Flush last server
|
|
143
|
+
if (currentServer) {
|
|
144
|
+
if (Object.keys(currentEnv).length > 0)
|
|
145
|
+
currentEntry.env = currentEnv;
|
|
146
|
+
servers[currentServer] = currentEntry;
|
|
147
|
+
}
|
|
148
|
+
return servers;
|
|
149
|
+
}
|
|
150
|
+
function parseTomlValue(raw) {
|
|
151
|
+
const trimmed = raw.trim();
|
|
152
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
153
|
+
return trimmed.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
154
|
+
}
|
|
155
|
+
return trimmed;
|
|
156
|
+
}
|
|
157
|
+
function parseTomlArray(raw) {
|
|
158
|
+
const trimmed = raw.trim();
|
|
159
|
+
if (!trimmed.startsWith('[') || !trimmed.endsWith(']'))
|
|
160
|
+
return [];
|
|
161
|
+
const inner = trimmed.slice(1, -1);
|
|
162
|
+
const result = [];
|
|
163
|
+
const parts = inner.split(',');
|
|
164
|
+
for (const part of parts) {
|
|
165
|
+
const val = parseTomlValue(part.trim());
|
|
166
|
+
if (val.length > 0)
|
|
167
|
+
result.push(val);
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
/* ---- Skill readers ---- */
|
|
172
|
+
async function readClaudePlugins() {
|
|
173
|
+
try {
|
|
174
|
+
const pluginsPath = path.join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
175
|
+
const source = await readFile(pluginsPath, 'utf8');
|
|
176
|
+
const data = JSON.parse(source);
|
|
177
|
+
if (!data.plugins)
|
|
178
|
+
return [];
|
|
179
|
+
return Object.keys(data.plugins).map((key) => {
|
|
180
|
+
const [name, source] = key.split('@');
|
|
181
|
+
return { name, source };
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function readCodexSkills() {
|
|
189
|
+
try {
|
|
190
|
+
const skillsDir = path.join(homedir(), '.codex', 'skills');
|
|
191
|
+
return await readSkillDirs(skillsDir);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function readGeminiSkills() {
|
|
198
|
+
try {
|
|
199
|
+
const skillsDir = path.join(homedir(), '.gemini', 'skills');
|
|
200
|
+
return await readSkillDirs(skillsDir);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** Shared: read skill directories (Codex and Gemini use the same SKILL.md convention) */
|
|
207
|
+
async function readSkillDirs(skillsDir) {
|
|
208
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
209
|
+
const skills = [];
|
|
210
|
+
for (const entry of entries) {
|
|
211
|
+
if (entry.name.startsWith('.'))
|
|
212
|
+
continue;
|
|
213
|
+
if (entry.isDirectory()) {
|
|
214
|
+
skills.push({ name: entry.name, source: 'local' });
|
|
215
|
+
}
|
|
216
|
+
else if (entry.isSymbolicLink()) {
|
|
217
|
+
skills.push({ name: entry.name, source: 'linked' });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return skills;
|
|
221
|
+
}
|
package/dist/ui/routes.js
CHANGED
|
@@ -2,10 +2,14 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { loadConfig } from '../config.js';
|
|
4
4
|
import { parseConfigPayload } from '../config.js';
|
|
5
|
+
import { ProfileError, ProfileNotFoundError } from '../errors.js';
|
|
5
6
|
import { loadMemory } from '../context/memory.js';
|
|
7
|
+
import { createAgentConfigService } from '../services/agent-config-service.js';
|
|
6
8
|
import { createConfigWriteService } from '../services/config-write-service.js';
|
|
9
|
+
import { createProfileService } from '../services/profile-service.js';
|
|
7
10
|
import { createRunService } from '../services/run-service.js';
|
|
8
11
|
import { createStatusService } from '../services/status-service.js';
|
|
12
|
+
import { createSyncService } from '../services/sync-service.js';
|
|
9
13
|
import { startSseStream, writeSseEvent } from './streaming.js';
|
|
10
14
|
import path from 'node:path';
|
|
11
15
|
import { fileURLToPath } from 'node:url';
|
|
@@ -14,6 +18,9 @@ export function createUiRouteHandler(dependencies) {
|
|
|
14
18
|
const statusService = dependencies.statusService ?? createStatusService();
|
|
15
19
|
const runService = dependencies.runService ?? createRunService();
|
|
16
20
|
const configWriteService = createConfigWriteService();
|
|
21
|
+
const profileService = createProfileService();
|
|
22
|
+
const syncService = createSyncService({ profileService });
|
|
23
|
+
const agentConfigService = createAgentConfigService();
|
|
17
24
|
return async (request, response) => {
|
|
18
25
|
const url = new URL(request.url ?? '/', 'http://localhost');
|
|
19
26
|
switch (url.pathname) {
|
|
@@ -96,7 +103,152 @@ export function createUiRouteHandler(dependencies) {
|
|
|
96
103
|
const overview = await statusService.execute({ cwd: dependencies.cwd });
|
|
97
104
|
return sendJson(response, 200, overview.agents);
|
|
98
105
|
}
|
|
99
|
-
|
|
106
|
+
case '/api/agents/live': {
|
|
107
|
+
if (request.method !== 'GET') {
|
|
108
|
+
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
109
|
+
}
|
|
110
|
+
const configs = await agentConfigService.readAll({ cwd: dependencies.cwd });
|
|
111
|
+
return sendJson(response, 200, configs);
|
|
112
|
+
}
|
|
113
|
+
case '/api/profiles': {
|
|
114
|
+
if (request.method === 'GET') {
|
|
115
|
+
const result = await profileService.list({ cwd: dependencies.cwd });
|
|
116
|
+
return sendJson(response, 200, result);
|
|
117
|
+
}
|
|
118
|
+
if (request.method === 'POST') {
|
|
119
|
+
const body = await readJsonBody(request);
|
|
120
|
+
if (!body.ok) {
|
|
121
|
+
return sendJson(response, 400, { error: 'Invalid JSON body' });
|
|
122
|
+
}
|
|
123
|
+
const data = body.value;
|
|
124
|
+
try {
|
|
125
|
+
const result = await profileService.create({
|
|
126
|
+
cwd: dependencies.cwd,
|
|
127
|
+
name: String(data.name ?? ''),
|
|
128
|
+
description: typeof data.description === 'string' ? data.description : undefined,
|
|
129
|
+
});
|
|
130
|
+
return sendJson(response, 201, result);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return sendProfileError(response, error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
137
|
+
}
|
|
138
|
+
case '/api/sync': {
|
|
139
|
+
if (request.method !== 'POST') {
|
|
140
|
+
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const result = await syncService.execute({ cwd: dependencies.cwd });
|
|
144
|
+
return sendJson(response, 200, result);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return sendJson(response, 500, {
|
|
148
|
+
error: error instanceof Error ? error.message : 'Sync failed',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
default: {
|
|
153
|
+
// Agent MCP routes: /api/agents/:name/mcps(/:key)
|
|
154
|
+
const agentMcpMatch = url.pathname.match(/^\/api\/agents\/(claude|codex|gemini)\/mcps(?:\/(.+))?$/);
|
|
155
|
+
if (agentMcpMatch) {
|
|
156
|
+
const agentName = agentMcpMatch[1];
|
|
157
|
+
const mcpKey = agentMcpMatch[2] ? decodeURIComponent(agentMcpMatch[2]) : null;
|
|
158
|
+
if (request.method === 'POST' && !mcpKey) {
|
|
159
|
+
const body = await readJsonBody(request);
|
|
160
|
+
if (!body.ok) {
|
|
161
|
+
return sendJson(response, 400, { error: 'Invalid JSON body' });
|
|
162
|
+
}
|
|
163
|
+
const data = body.value;
|
|
164
|
+
if (!data.key || !data.entry?.command) {
|
|
165
|
+
return sendJson(response, 400, { error: 'Missing key or entry.command' });
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
await agentConfigService.addMcp({
|
|
169
|
+
cwd: dependencies.cwd,
|
|
170
|
+
agent: agentName,
|
|
171
|
+
key: data.key,
|
|
172
|
+
entry: data.entry,
|
|
173
|
+
});
|
|
174
|
+
return sendJson(response, 200, { ok: true });
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return sendJson(response, 500, {
|
|
178
|
+
error: error instanceof Error ? error.message : 'Failed to add MCP',
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (request.method === 'DELETE' && mcpKey) {
|
|
183
|
+
try {
|
|
184
|
+
await agentConfigService.removeMcp({
|
|
185
|
+
cwd: dependencies.cwd,
|
|
186
|
+
agent: agentName,
|
|
187
|
+
key: mcpKey,
|
|
188
|
+
});
|
|
189
|
+
return sendJson(response, 200, { ok: true });
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
return sendJson(response, 500, {
|
|
193
|
+
error: error instanceof Error ? error.message : 'Failed to remove MCP',
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
198
|
+
}
|
|
199
|
+
const profileMatch = url.pathname.match(/^\/api\/profiles\/([^/]+)(\/activate)?$/);
|
|
200
|
+
if (profileMatch) {
|
|
201
|
+
const name = decodeURIComponent(profileMatch[1]);
|
|
202
|
+
const isActivate = profileMatch[2] === '/activate';
|
|
203
|
+
if (isActivate) {
|
|
204
|
+
if (request.method !== 'POST') {
|
|
205
|
+
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const result = await profileService.use({ cwd: dependencies.cwd, name });
|
|
209
|
+
return sendJson(response, 200, result);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
return sendProfileError(response, error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (request.method === 'GET') {
|
|
216
|
+
try {
|
|
217
|
+
const profile = await profileService.get({ cwd: dependencies.cwd, name });
|
|
218
|
+
return sendJson(response, 200, profile);
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
return sendProfileError(response, error);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (request.method === 'PUT') {
|
|
225
|
+
const body = await readJsonBody(request);
|
|
226
|
+
if (!body.ok) {
|
|
227
|
+
return sendJson(response, 400, { error: 'Invalid JSON body' });
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
await profileService.update({
|
|
231
|
+
cwd: dependencies.cwd,
|
|
232
|
+
name,
|
|
233
|
+
config: body.value,
|
|
234
|
+
});
|
|
235
|
+
return sendJson(response, 200, { ok: true });
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
return sendProfileError(response, error);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (request.method === 'DELETE') {
|
|
242
|
+
try {
|
|
243
|
+
await profileService.delete({ cwd: dependencies.cwd, name });
|
|
244
|
+
return sendJson(response, 200, { ok: true });
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
return sendProfileError(response, error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
251
|
+
}
|
|
100
252
|
if (url.pathname === '/api' || url.pathname.startsWith('/api/')) {
|
|
101
253
|
return sendJson(response, 404, { error: 'Not found' });
|
|
102
254
|
}
|
|
@@ -104,6 +256,7 @@ export function createUiRouteHandler(dependencies) {
|
|
|
104
256
|
return sendJson(response, 405, { error: 'Method not allowed' });
|
|
105
257
|
}
|
|
106
258
|
return serveUiResponse(url.pathname, response);
|
|
259
|
+
}
|
|
107
260
|
}
|
|
108
261
|
};
|
|
109
262
|
}
|
|
@@ -150,6 +303,17 @@ function parseAgentName(value) {
|
|
|
150
303
|
}
|
|
151
304
|
return null;
|
|
152
305
|
}
|
|
306
|
+
function sendProfileError(response, error) {
|
|
307
|
+
if (error instanceof ProfileNotFoundError) {
|
|
308
|
+
return sendJson(response, 404, { error: error.message });
|
|
309
|
+
}
|
|
310
|
+
if (error instanceof ProfileError) {
|
|
311
|
+
return sendJson(response, 400, { error: error.message });
|
|
312
|
+
}
|
|
313
|
+
return sendJson(response, 500, {
|
|
314
|
+
error: error instanceof Error ? error.message : 'Internal server error',
|
|
315
|
+
});
|
|
316
|
+
}
|
|
153
317
|
function sendJson(response, statusCode, body) {
|
|
154
318
|
response.statusCode = statusCode;
|
|
155
319
|
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{color-scheme:light;--bg: #f4f5f2;--bg-accent: rgba(255, 255, 255, .7);--panel: rgba(255, 255, 255, .82);--panel-strong: rgba(255, 255, 255, .94);--border: rgba(17, 24, 39, .1);--border-strong: rgba(17, 24, 39, .16);--text: #101114;--muted: rgba(16, 17, 20, .64);--shadow: 0 26px 70px rgba(17, 24, 39, .08);--shadow-soft: 0 12px 28px rgba(17, 24, 39, .06);--radius-xl: 28px;--radius-lg: 22px;--radius-md: 16px;--radius-sm: 12px;font-family:-apple-system,BlinkMacSystemFont,SF Pro Text,SF Pro Display,Segoe UI,sans-serif;background:var(--bg);color:var(--text)}*{box-sizing:border-box}html,body,#root{min-height:100%;margin:0}body{min-height:100vh;background:radial-gradient(circle at top left,rgba(255,255,255,.8),transparent 26%),radial-gradient(circle at top right,rgba(17,24,39,.05),transparent 28%),linear-gradient(180deg,#f7f8f6,#eef1eb);color:var(--text)}button,textarea{font:inherit}button{cursor:pointer}button:disabled{cursor:default}#root{min-height:100vh}.app-shell{min-height:100vh;padding:24px}.shell{width:min(1440px,100%);margin:0 auto;display:grid;gap:18px}.panel{border:1px solid var(--border);border-radius:var(--radius-xl);background:var(--panel);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.topbar{padding:22px 24px;display:flex;align-items:center;justify-content:space-between;gap:20px}.brand-block{display:flex;align-items:center;gap:14px;min-width:0}.brand-mark{width:44px;height:44px;border-radius:14px;display:grid;place-items:center;background:#101114;color:#fff;box-shadow:inset 0 1px #ffffff14}.eyebrow{margin:0 0 6px;text-transform:uppercase;letter-spacing:.14em;font-size:.72rem;font-weight:700;color:var(--muted)}.brand-block h1,.workspace-header h2,.panel-heading h2,.section-header h3,.empty-state h2{margin:0;font-size:clamp(1.4rem,2.5vw,2.3rem);line-height:1.05;letter-spacing:-.04em}.workspace-header h2,.panel-heading h2,.section-header h3{font-size:clamp(1.05rem,1.7vw,1.35rem)}.status-strip{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:10px}.status-chip,.muted-pill,.skill-chip,.agent-command{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--border);border-radius:999px;background:#ffffffa8;color:#101114bd;padding:8px 12px;font-size:.84rem;line-height:1}.muted-pill{background:#ffffff7a;color:var(--muted)}.shell-grid{display:grid;grid-template-columns:320px minmax(0,1fr);gap:18px;align-items:start}.nav-card,.workspace-card{padding:20px}.nav-card{position:sticky;top:24px}.panel-heading,.workspace-header,.section-header,.metric-card-header,.agent-row,.agent-row-meta{display:flex;align-items:center;justify-content:space-between;gap:16px}.panel-heading{margin-bottom:18px}.section-nav{display:grid;gap:10px}.section-button{border:1px solid var(--border);border-radius:var(--radius-lg);background:#ffffffa8;color:var(--text);padding:16px;display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:12px;align-items:center;text-align:left;transition:transform .14s ease,border-color .14s ease,background-color .14s ease,box-shadow .14s ease}.section-button:hover:not(:disabled){transform:translateY(-1px);border-color:var(--border-strong);box-shadow:var(--shadow-soft)}.section-button:disabled{opacity:.62}.section-button.is-active{background:#101114;color:#fff;border-color:#101114;box-shadow:0 14px 30px #1011142e}.section-button.is-disabled{opacity:.7}.section-button-icon{width:36px;height:36px;border-radius:12px;display:grid;place-items:center;background:#1011140f}.section-button.is-active .section-button-icon{background:#ffffff1a}.section-button-label{display:grid;gap:4px;min-width:0}.section-button-label span:first-child{font-weight:650}.section-button-subtitle{color:var(--muted);font-size:.82rem}.section-button.is-active .section-button-subtitle{color:#ffffffc2}.section-button-chevron{color:var(--muted)}.section-button.is-active .section-button-chevron{color:#fffc}.workspace-card{display:grid;gap:20px;min-width:0}.workspace-header{padding-bottom:18px;border-bottom:1px solid var(--border);align-items:flex-start}.workspace-header-meta{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:10px}.muted-copy{margin:8px 0 0;color:var(--muted);line-height:1.5;word-break:break-word}.view-stack{display:grid;gap:18px}.metrics-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.metric-card{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--panel-strong);padding:18px;min-width:0;box-shadow:var(--shadow-soft)}.metric-card.is-wide{grid-column:1 / -1}.metric-card-header{margin-bottom:14px;justify-content:flex-start}.metric-card-icon{width:32px;height:32px;border-radius:11px;display:grid;place-items:center;background:#1011140f}.metric-card-label{font-size:.86rem;color:var(--muted)}.metric-card-value{display:block;font-size:clamp(1rem,1.8vw,1.2rem);line-height:1.35;letter-spacing:-.03em;word-break:break-word}.metric-card-note{margin:10px 0 0;color:var(--muted);line-height:1.45}.panel-inner{border:1px solid var(--border);border-radius:var(--radius-lg);background:#ffffffb8;padding:18px}.chip-row{display:flex;flex-wrap:wrap;gap:10px;margin-top:12px}.skill-chip{padding-inline:14px}.agent-list,.memory-file-list{display:grid;gap:12px}.agent-row{justify-content:space-between;border:1px solid var(--border);border-radius:var(--radius-md);background:#fffc;padding:14px 16px}.agent-row-meta{justify-content:flex-start;min-width:0}.agent-row-meta strong{display:block;text-transform:capitalize}.agent-row-meta p{margin:3px 0 0;color:var(--muted)}.agent-status-dot{width:10px;height:10px;border-radius:999px;background:#1011143d;flex:0 0 auto}.agent-status-dot.is-online{background:#101114}.agent-status-dot.is-offline{background:#10111447}.agent-command{flex:0 0 auto}.memory-layout{display:grid;grid-template-columns:320px minmax(0,1fr);gap:16px;min-width:0}.memory-list,.memory-preview{min-width:0}.memory-file-button{border:1px solid var(--border);border-radius:var(--radius-md);background:#ffffffd1;padding:14px 16px;text-align:left;display:grid;gap:6px;transition:transform .14s ease,border-color .14s ease,background-color .14s ease}.memory-file-button:hover{transform:translateY(-1px);border-color:var(--border-strong)}.memory-file-button.is-selected{background:#101114;color:#fff;border-color:#101114}.memory-file-button strong,.memory-file-button span{min-width:0;overflow:hidden;text-overflow:ellipsis}.memory-file-button span{color:var(--muted);font-size:.8rem}.memory-file-button.is-selected span{color:#ffffffbd}.memory-preview{display:grid;gap:14px}.memory-editor{width:100%;min-height:420px;resize:vertical;border:1px solid var(--border);border-radius:var(--radius-lg);background:#fffffff0;color:var(--text);padding:18px;line-height:1.55;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Monaco,Consolas,Liberation Mono,monospace;box-shadow:var(--shadow-soft)}.memory-editor:focus{outline:2px solid rgba(16,17,20,.18);outline-offset:2px}.run-view,.config-view{gap:16px}.config-panel{display:grid;gap:18px}.config-toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px}.config-list{display:grid;gap:14px}.config-card,.config-empty-state{border:1px solid var(--border);border-radius:var(--radius-lg);background:#ffffffd6;padding:18px;box-shadow:var(--shadow-soft)}.config-card{display:grid;gap:16px}.config-card-header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.config-card-header h4,.config-empty-state strong{margin:0;font-size:1rem;line-height:1.25;letter-spacing:-.02em}.config-empty-state p{margin:8px 0 0;color:var(--muted);line-height:1.5}.config-field-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.secondary-button,.danger-button{border-radius:999px;padding:11px 15px;display:inline-flex;align-items:center;justify-content:center;gap:9px;font-weight:650;transition:transform .14s ease,border-color .14s ease,background-color .14s ease,opacity .14s ease}.secondary-button{border:1px solid var(--border);background:#ffffffe0;color:var(--text)}.danger-button{border:1px solid rgba(16,17,20,.12);background:#1011140a;color:var(--text)}.secondary-button:hover:not(:disabled),.danger-button:hover:not(:disabled){transform:translateY(-1px);border-color:var(--border-strong)}.secondary-button:disabled,.danger-button:disabled{opacity:.5}.editor-textarea{min-height:180px;resize:vertical}.editor-code,.editor-textarea{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Monaco,Consolas,Liberation Mono,monospace;line-height:1.55}.save-feedback{border:1px solid var(--border);border-radius:var(--radius-md);background:#ffffffe0;padding:13px 15px;display:inline-flex;align-items:center;gap:10px;font-size:.92rem}.save-feedback.saved{border-color:#10111424}.save-feedback.error{border-color:#1011142e}.run-panel{display:grid;gap:18px}.run-form{display:grid;gap:16px}.field{display:grid;gap:8px}.field-label{font-size:.86rem;font-weight:650;letter-spacing:-.01em}.field-control{width:100%;border:1px solid var(--border);border-radius:var(--radius-md);background:#fffffff0;color:var(--text);padding:13px 14px;box-shadow:var(--shadow-soft);min-width:0}.field-control:focus{outline:2px solid rgba(16,17,20,.18);outline-offset:2px}.field-control:disabled{opacity:.72}.field-help{margin:0;color:var(--muted);font-size:.82rem;line-height:1.45}.run-agent-grid,.run-detail-grid,.run-summary-grid{display:grid;gap:14px}.run-agent-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.run-detail-grid{grid-template-columns:minmax(0,1.35fr) minmax(320px,.95fr);align-items:start}.run-summary-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.run-actions{display:flex;flex-wrap:wrap;align-items:center;gap:14px}.run-button{border:1px solid #101114;border-radius:999px;background:#101114;color:#fff;padding:13px 18px;display:inline-flex;align-items:center;gap:10px;font-weight:650;box-shadow:0 14px 30px #1011142e;transition:transform .14s ease,opacity .14s ease}.run-button:hover:not(:disabled){transform:translateY(-1px)}.run-button:disabled{opacity:.55}.run-button-spinner,.run-state-spinner{animation:spin .9s linear infinite}.run-state{white-space:nowrap}.run-state.running{border-color:#1011142e;background:#10111414}.run-state.success{border-color:#10111424;background:#1011140f}.run-state.error{border-color:#1011142e;background:#10111414}.run-output-panel,.run-summary-panel{min-width:0}.run-output{width:100%;min-height:380px;resize:vertical;border:1px solid var(--border);border-radius:var(--radius-lg);background:#fffffff0;color:var(--text);padding:18px;line-height:1.55;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Monaco,Consolas,Liberation Mono,monospace;box-shadow:var(--shadow-soft)}.run-output:focus{outline:2px solid rgba(16,17,20,.18);outline-offset:2px}.run-summary-tile{border:1px solid var(--border);border-radius:var(--radius-md);background:#ffffffe0;padding:14px 15px;display:grid;gap:8px}.run-summary-tile-icon{width:32px;height:32px;border-radius:11px;display:grid;place-items:center;background:#1011140f}.run-summary-tile-label{color:var(--muted);font-size:.82rem}.run-summary-tile-value{font-size:1rem;line-height:1.4;word-break:break-word}.run-empty-state{border:1px solid var(--border);border-radius:var(--radius-lg);background:#fffc;padding:18px;min-height:220px;display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:start}.run-empty-state.is-error{background:#ffffffe6}.run-empty-state strong{display:block;margin-bottom:6px}.run-empty-state p{margin:0;color:var(--muted);line-height:1.55}.empty-state{border:1px solid var(--border);border-radius:var(--radius-xl);background:#ffffffc2;padding:30px;min-height:260px;display:grid;align-content:start;gap:12px;box-shadow:var(--shadow-soft)}.empty-state p{margin:0;color:var(--muted);line-height:1.6;max-width:60ch}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:1080px){.shell-grid,.memory-layout,.run-detail-grid,.metrics-grid,.config-field-grid,.run-agent-grid,.run-summary-grid{grid-template-columns:1fr}.nav-card{position:static}}@media(max-width:720px){.app-shell{padding:16px}.topbar{padding:18px;align-items:flex-start;flex-direction:column}.nav-card,.workspace-card,.panel-inner{padding:16px}.run-actions{align-items:stretch}.config-toolbar,.config-card-header{flex-direction:column;align-items:stretch}.run-button{width:100%;justify-content:center}.secondary-button,.danger-button{width:100%}}.empty-state-icon{width:42px;height:42px;border-radius:14px;display:grid;place-items:center;background:#10111412}@media(max-width:1100px){.shell-grid,.memory-layout,.metrics-grid{grid-template-columns:1fr}.nav-card{position:static}}@media(max-width:720px){.app-shell{padding:14px}.topbar,.workspace-header{flex-direction:column;align-items:flex-start}.status-strip,.workspace-header-meta{justify-content:flex-start}.panel,.nav-card,.workspace-card{padding:16px;border-radius:22px}.memory-editor{min-height:300px}}
|
|
1
|
+
:root{color-scheme:light;--bg: #f4f5f2;--bg-accent: rgba(255, 255, 255, .7);--panel: rgba(255, 255, 255, .82);--panel-strong: rgba(255, 255, 255, .94);--border: rgba(17, 24, 39, .1);--border-strong: rgba(17, 24, 39, .16);--text: #101114;--muted: rgba(16, 17, 20, .64);--shadow: 0 26px 70px rgba(17, 24, 39, .08);--shadow-soft: 0 12px 28px rgba(17, 24, 39, .06);--radius-xl: 28px;--radius-lg: 22px;--radius-md: 16px;--radius-sm: 12px;font-family:-apple-system,BlinkMacSystemFont,SF Pro Text,SF Pro Display,Segoe UI,sans-serif;background:var(--bg);color:var(--text)}*{box-sizing:border-box}html,body,#root{min-height:100%;margin:0}body{min-height:100vh;background:radial-gradient(circle at top left,rgba(255,255,255,.8),transparent 26%),radial-gradient(circle at top right,rgba(17,24,39,.05),transparent 28%),linear-gradient(180deg,#f7f8f6,#eef1eb);color:var(--text)}button,textarea{font:inherit}button{cursor:pointer}button:disabled{cursor:default}#root{min-height:100vh}.app-shell{min-height:100vh;padding:24px}.shell{width:min(1440px,100%);margin:0 auto;display:grid;gap:18px}.panel{border:1px solid var(--border);border-radius:var(--radius-xl);background:var(--panel);box-shadow:var(--shadow);-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px)}.topbar{padding:22px 24px;display:flex;align-items:center;justify-content:space-between;gap:20px}.brand-block{display:flex;align-items:center;gap:14px;min-width:0}.brand-mark{width:44px;height:44px;border-radius:14px;display:grid;place-items:center;background:#101114;color:#fff;box-shadow:inset 0 1px #ffffff14}.eyebrow{margin:0 0 6px;text-transform:uppercase;letter-spacing:.14em;font-size:.72rem;font-weight:700;color:var(--muted)}.brand-block h1,.workspace-header h2,.panel-heading h2,.section-header h3,.empty-state h2{margin:0;font-size:clamp(1.4rem,2.5vw,2.3rem);line-height:1.05;letter-spacing:-.04em}.workspace-header h2,.panel-heading h2,.section-header h3{font-size:clamp(1.05rem,1.7vw,1.35rem)}.status-strip{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:10px}.status-chip,.muted-pill,.skill-chip,.agent-command{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--border);border-radius:999px;background:#ffffffa8;color:#101114bd;padding:8px 12px;font-size:.84rem;line-height:1}.muted-pill{background:#ffffff7a;color:var(--muted)}.shell-grid{display:grid;grid-template-columns:320px minmax(0,1fr);gap:18px;align-items:start}.nav-card,.workspace-card{padding:20px}.nav-card{position:sticky;top:24px}.panel-heading,.workspace-header,.section-header,.metric-card-header,.agent-row,.agent-row-meta{display:flex;align-items:center;justify-content:space-between;gap:16px}.panel-heading{margin-bottom:18px}.section-nav{display:grid;gap:10px}.section-button{border:1px solid var(--border);border-radius:var(--radius-lg);background:#ffffffa8;color:var(--text);padding:16px;display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:12px;align-items:center;text-align:left;transition:transform .14s ease,border-color .14s ease,background-color .14s ease,box-shadow .14s ease}.section-button:hover:not(:disabled){transform:translateY(-1px);border-color:var(--border-strong);box-shadow:var(--shadow-soft)}.section-button:disabled{opacity:.62}.section-button.is-active{background:#101114;color:#fff;border-color:#101114;box-shadow:0 14px 30px #1011142e}.section-button.is-disabled{opacity:.7}.section-button-icon{width:36px;height:36px;border-radius:12px;display:grid;place-items:center;background:#1011140f}.section-button.is-active .section-button-icon{background:#ffffff1a}.section-button-label{display:grid;gap:4px;min-width:0}.section-button-label span:first-child{font-weight:650}.section-button-subtitle{color:var(--muted);font-size:.82rem}.section-button.is-active .section-button-subtitle{color:#ffffffc2}.section-button-chevron{color:var(--muted)}.section-button.is-active .section-button-chevron{color:#fffc}.workspace-card{display:grid;gap:20px;min-width:0}.workspace-header{padding-bottom:18px;border-bottom:1px solid var(--border);align-items:flex-start}.workspace-header-meta{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:10px}.muted-copy{margin:8px 0 0;color:var(--muted);line-height:1.5;word-break:break-word}.view-stack{display:grid;gap:18px}.metrics-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.metric-card{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--panel-strong);padding:18px;min-width:0;box-shadow:var(--shadow-soft)}.metric-card.is-wide{grid-column:1 / -1}.metric-card-header{margin-bottom:14px;justify-content:flex-start}.metric-card-icon{width:32px;height:32px;border-radius:11px;display:grid;place-items:center;background:#1011140f}.metric-card-label{font-size:.86rem;color:var(--muted)}.metric-card-value{display:block;font-size:clamp(1rem,1.8vw,1.2rem);line-height:1.35;letter-spacing:-.03em;word-break:break-word}.metric-card-note{margin:10px 0 0;color:var(--muted);line-height:1.45}.panel-inner{border:1px solid var(--border);border-radius:var(--radius-lg);background:#ffffffb8;padding:18px}.chip-row{display:flex;flex-wrap:wrap;gap:10px;margin-top:12px}.skill-chip{padding-inline:14px}.agent-list,.memory-file-list{display:grid;gap:12px}.agent-row{justify-content:space-between;border:1px solid var(--border);border-radius:var(--radius-md);background:#fffc;padding:14px 16px}.agent-row-meta{justify-content:flex-start;min-width:0}.agent-row-meta strong{display:block;text-transform:capitalize}.agent-row-meta p{margin:3px 0 0;color:var(--muted)}.agent-status-dot{width:10px;height:10px;border-radius:999px;background:#1011143d;flex:0 0 auto}.agent-status-dot.is-online{background:#101114}.agent-status-dot.is-offline{background:#10111447}.agent-command{flex:0 0 auto}.memory-layout{display:grid;grid-template-columns:320px minmax(0,1fr);gap:16px;min-width:0}.memory-list,.memory-preview{min-width:0}.memory-file-button{border:1px solid var(--border);border-radius:var(--radius-md);background:#ffffffd1;padding:14px 16px;text-align:left;display:grid;gap:6px;transition:transform .14s ease,border-color .14s ease,background-color .14s ease}.memory-file-button:hover{transform:translateY(-1px);border-color:var(--border-strong)}.memory-file-button.is-selected{background:#101114;color:#fff;border-color:#101114}.memory-file-button strong,.memory-file-button span{min-width:0;overflow:hidden;text-overflow:ellipsis}.memory-file-button span{color:var(--muted);font-size:.8rem}.memory-file-button.is-selected span{color:#ffffffbd}.memory-preview{display:grid;gap:14px}.memory-editor{width:100%;min-height:420px;resize:vertical;border:1px solid var(--border);border-radius:var(--radius-lg);background:#fffffff0;color:var(--text);padding:18px;line-height:1.55;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Monaco,Consolas,Liberation Mono,monospace;box-shadow:var(--shadow-soft)}.memory-editor:focus{outline:2px solid rgba(16,17,20,.18);outline-offset:2px}.run-view,.config-view{gap:16px}.config-panel{display:grid;gap:18px}.config-toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px}.config-list{display:grid;gap:14px}.config-card,.config-empty-state{border:1px solid var(--border);border-radius:var(--radius-lg);background:#ffffffd6;padding:18px;box-shadow:var(--shadow-soft)}.config-card{display:grid;gap:16px}.config-card-header{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.config-card-header h4,.config-empty-state strong{margin:0;font-size:1rem;line-height:1.25;letter-spacing:-.02em}.config-empty-state p{margin:8px 0 0;color:var(--muted);line-height:1.5}.config-field-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.secondary-button,.danger-button{border-radius:999px;padding:11px 15px;display:inline-flex;align-items:center;justify-content:center;gap:9px;font-weight:650;transition:transform .14s ease,border-color .14s ease,background-color .14s ease,opacity .14s ease}.secondary-button{border:1px solid var(--border);background:#ffffffe0;color:var(--text)}.danger-button{border:1px solid rgba(16,17,20,.12);background:#1011140a;color:var(--text)}.secondary-button:hover:not(:disabled),.danger-button:hover:not(:disabled){transform:translateY(-1px);border-color:var(--border-strong)}.secondary-button:disabled,.danger-button:disabled{opacity:.5}.editor-textarea{min-height:180px;resize:vertical}.editor-code,.editor-textarea{font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Monaco,Consolas,Liberation Mono,monospace;line-height:1.55}.save-feedback{border:1px solid var(--border);border-radius:var(--radius-md);background:#ffffffe0;padding:13px 15px;display:inline-flex;align-items:center;gap:10px;font-size:.92rem}.save-feedback.saved{border-color:#10111424}.save-feedback.error{border-color:#1011142e}.run-panel{display:grid;gap:18px}.run-form{display:grid;gap:16px}.field{display:grid;gap:8px}.field-label{font-size:.86rem;font-weight:650;letter-spacing:-.01em}.field-control{width:100%;border:1px solid var(--border);border-radius:var(--radius-md);background:#fffffff0;color:var(--text);padding:13px 14px;box-shadow:var(--shadow-soft);min-width:0}.field-control:focus{outline:2px solid rgba(16,17,20,.18);outline-offset:2px}.field-control:disabled{opacity:.72}.field-help{margin:0;color:var(--muted);font-size:.82rem;line-height:1.45}.run-agent-grid,.run-detail-grid,.run-summary-grid{display:grid;gap:14px}.run-agent-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.run-detail-grid{grid-template-columns:minmax(0,1.35fr) minmax(320px,.95fr);align-items:start}.run-summary-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.run-actions{display:flex;flex-wrap:wrap;align-items:center;gap:14px}.run-button{border:1px solid #101114;border-radius:999px;background:#101114;color:#fff;padding:13px 18px;display:inline-flex;align-items:center;gap:10px;font-weight:650;box-shadow:0 14px 30px #1011142e;transition:transform .14s ease,opacity .14s ease}.run-button:hover:not(:disabled){transform:translateY(-1px)}.run-button:disabled{opacity:.55}.run-button-spinner,.run-state-spinner{animation:spin .9s linear infinite}.run-state{white-space:nowrap}.run-state.running{border-color:#1011142e;background:#10111414}.run-state.success{border-color:#10111424;background:#1011140f}.run-state.error{border-color:#1011142e;background:#10111414}.run-output-panel,.run-summary-panel{min-width:0}.run-output{width:100%;min-height:380px;resize:vertical;border:1px solid var(--border);border-radius:var(--radius-lg);background:#fffffff0;color:var(--text);padding:18px;line-height:1.55;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Monaco,Consolas,Liberation Mono,monospace;box-shadow:var(--shadow-soft)}.run-output:focus{outline:2px solid rgba(16,17,20,.18);outline-offset:2px}.run-summary-tile{border:1px solid var(--border);border-radius:var(--radius-md);background:#ffffffe0;padding:14px 15px;display:grid;gap:8px}.run-summary-tile-icon{width:32px;height:32px;border-radius:11px;display:grid;place-items:center;background:#1011140f}.run-summary-tile-label{color:var(--muted);font-size:.82rem}.run-summary-tile-value{font-size:1rem;line-height:1.4;word-break:break-word}.run-empty-state{border:1px solid var(--border);border-radius:var(--radius-lg);background:#fffc;padding:18px;min-height:220px;display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:start}.run-empty-state.is-error{background:#ffffffe6}.run-empty-state strong{display:block;margin-bottom:6px}.run-empty-state p{margin:0;color:var(--muted);line-height:1.55}.empty-state{border:1px solid var(--border);border-radius:var(--radius-xl);background:#ffffffc2;padding:30px;min-height:260px;display:grid;align-content:start;gap:12px;box-shadow:var(--shadow-soft)}.empty-state p{margin:0;color:var(--muted);line-height:1.6;max-width:60ch}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:1080px){.shell-grid,.memory-layout,.run-detail-grid,.metrics-grid,.config-field-grid,.run-agent-grid,.run-summary-grid{grid-template-columns:1fr}.nav-card{position:static}}@media(max-width:720px){.app-shell{padding:16px}.topbar{padding:18px;align-items:flex-start;flex-direction:column}.nav-card,.workspace-card,.panel-inner{padding:16px}.run-actions{align-items:stretch}.config-toolbar,.config-card-header{flex-direction:column;align-items:stretch}.run-button{width:100%;justify-content:center}.secondary-button,.danger-button{width:100%}}.empty-state-icon{width:42px;height:42px;border-radius:14px;display:grid;place-items:center;background:#10111412}.profile-columns{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;min-width:0}.profile-column{display:grid;gap:16px;align-content:start}.profile-column-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.profile-column-title{display:grid;gap:6px}.profile-active-badge{background:var(--text);color:#fff;border-color:var(--text)}.profile-drop-zone{display:grid;gap:10px;min-height:60px;border:2px dashed transparent;border-radius:var(--radius-lg);padding:12px;transition:border-color .14s ease,background-color .14s ease}.profile-drop-zone.is-over{border-color:var(--border-strong);background:#10111408}.profile-card{border:1px solid var(--border);border-radius:var(--radius-md);background:#ffffffd6;padding:14px 16px;cursor:grab;touch-action:none;transition:transform .14s ease,border-color .14s ease,box-shadow .14s ease}.profile-card:hover{transform:translateY(-1px);border-color:var(--border-strong);box-shadow:var(--shadow-soft)}.profile-card.is-dragging{opacity:.4}.profile-card-overlay{border:1px solid var(--border-strong);border-radius:var(--radius-md);background:#fffffff5;padding:14px 16px;box-shadow:0 20px 50px #11182724;cursor:grabbing}.profile-card-row{display:flex;align-items:center;gap:10px}.profile-card-grip{color:var(--muted);flex-shrink:0}.profile-card-content{display:grid;gap:2px;min-width:0}.profile-card-content strong{font-size:.88rem;font-weight:650}.profile-card-content .muted-copy{font-size:.76rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.agent-config-path{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.7rem;word-break:break-all}.agent-skills-section{margin-top:4px}.agent-skills-section .eyebrow{display:flex;align-items:center;gap:5px;margin-bottom:8px}.agent-skills-list{display:grid;gap:6px}.agent-skill-item{display:grid;gap:2px;padding:8px 12px;border-radius:8px;background:#fff9;border:1px solid var(--border)}.agent-skill-item strong{font-size:.84rem;font-weight:600}.agent-skill-item .muted-copy{font-size:.74rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.profile-card-grip-area{cursor:grab;display:grid;place-items:center;flex-shrink:0;touch-action:none}.profile-card-remove{background:none;border:none;cursor:pointer;color:var(--muted);padding:4px;border-radius:6px;display:grid;place-items:center;flex-shrink:0;transition:color .14s ease,background .14s ease}.profile-card-remove:hover{color:#b91c1c;background:#b91c1c14}.profile-card.is-pending-add{border-color:#16a34a66;background:#16a34a0f}.profile-card.is-pending-remove{border-color:#b91c1c4d;background:#b91c1c0d;opacity:.6;text-decoration:line-through}.pending-badge{font-size:.7rem;font-weight:700;padding:2px 7px;border-radius:6px;flex-shrink:0;line-height:1}.pending-badge.is-add{color:#16a34a;background:#16a34a1a}.pending-badge.is-remove{color:#b91c1c;background:#b91c1c1a}.pending-changes-bar{display:grid;gap:12px;border:1px solid rgba(59,130,246,.3);background:#3b82f60a}.pending-changes-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.pending-changes-actions{display:flex;gap:10px}.pending-changes-list{display:grid;gap:6px}.pending-change-item{display:flex;align-items:center;gap:8px;font-size:.82rem;padding:6px 10px;border-radius:8px;background:#fff9;border:1px solid var(--border)}.pending-change-item.is-add{border-color:#16a34a33}.pending-change-item.is-remove{border-color:#b91c1c33}.pending-change-icon{display:grid;place-items:center;flex-shrink:0}.pending-change-item.is-add .pending-change-icon{color:#16a34a}.pending-change-item.is-remove .pending-change-icon{color:#b91c1c}.pending-change-text{flex:1;min-width:0}.pending-change-text strong{font-weight:650}.pending-change-undo{background:none;border:none;cursor:pointer;color:var(--muted);padding:3px;border-radius:4px;display:grid;place-items:center;flex-shrink:0;transition:color .14s ease}.pending-change-undo:hover{color:var(--text)}.profile-loading{display:flex;align-items:center;gap:10px;color:var(--muted);padding:20px 0}@media(max-width:1100px){.shell-grid,.memory-layout,.metrics-grid,.profile-columns{grid-template-columns:1fr}.nav-card{position:static}}@media(max-width:720px){.app-shell{padding:14px}.topbar,.workspace-header{flex-direction:column;align-items:flex-start}.status-strip,.workspace-header-meta{justify-content:flex-start}.panel,.nav-card,.workspace-card{padding:16px;border-radius:22px}.memory-editor{min-height:300px}}
|