singleton-pipeline 0.4.0-beta.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.
- package/LICENSE +21 -0
- package/README.md +200 -0
- package/docs/reference.md +1038 -0
- package/package.json +75 -0
- package/packages/cli/package.json +18 -0
- package/packages/cli/src/commands/new.js +440 -0
- package/packages/cli/src/commands/repl.js +551 -0
- package/packages/cli/src/executor.js +2646 -0
- package/packages/cli/src/index.js +107 -0
- package/packages/cli/src/parser.js +78 -0
- package/packages/cli/src/runners/_shared.js +83 -0
- package/packages/cli/src/runners/claude.js +119 -0
- package/packages/cli/src/runners/codex-instructions.js +75 -0
- package/packages/cli/src/runners/codex.js +162 -0
- package/packages/cli/src/runners/copilot.js +208 -0
- package/packages/cli/src/runners/index.js +20 -0
- package/packages/cli/src/runners/opencode.js +265 -0
- package/packages/cli/src/scanner.js +47 -0
- package/packages/cli/src/security/policy.js +126 -0
- package/packages/cli/src/shell.js +542 -0
- package/packages/cli/src/theme.js +46 -0
- package/packages/cli/src/timeline.js +146 -0
- package/packages/server/package.json +11 -0
- package/packages/server/src/index.js +43 -0
- package/packages/server/src/routes/agents.js +32 -0
- package/packages/server/src/routes/files.js +42 -0
- package/packages/server/src/routes/pipelines.js +74 -0
- package/packages/web/dist/assets/index-CCFWfCA2.css +1 -0
- package/packages/web/dist/assets/index-CnKytBly.js +55 -0
- package/packages/web/dist/index.html +13 -0
- package/packages/web/package.json +23 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import blessed from 'blessed';
|
|
2
|
+
import { C } from './shell.js';
|
|
3
|
+
|
|
4
|
+
const FRAMES = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
5
|
+
|
|
6
|
+
// widgets: { screen, logPanel, statusBox } — provided by shell when running inside the TUI.
|
|
7
|
+
// If omitted, a standalone blessed screen is created.
|
|
8
|
+
export function createTimeline(stepNames, widgets = null) {
|
|
9
|
+
const N = stepNames.length;
|
|
10
|
+
const statuses = stepNames.map(() => 'pending');
|
|
11
|
+
const meta = stepNames.map(() => '');
|
|
12
|
+
let runningIdx = -1;
|
|
13
|
+
let spinnerInterval = null;
|
|
14
|
+
let spinnerFrame = 0;
|
|
15
|
+
|
|
16
|
+
let screen, logPanel, statusBox, ownScreen = false;
|
|
17
|
+
|
|
18
|
+
if (widgets) {
|
|
19
|
+
({ screen, logPanel, statusBox } = widgets);
|
|
20
|
+
} else {
|
|
21
|
+
ownScreen = true;
|
|
22
|
+
screen = blessed.screen({ smartCSR: true, title: 'Singleton' });
|
|
23
|
+
|
|
24
|
+
logPanel = blessed.log({
|
|
25
|
+
top: 0, left: 0,
|
|
26
|
+
width: '100%', height: '100%-6',
|
|
27
|
+
tags: true,
|
|
28
|
+
scrollable: true,
|
|
29
|
+
alwaysScroll: true,
|
|
30
|
+
padding: { left: 2, top: 1, right: 2 }
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const separator = blessed.line({
|
|
34
|
+
orientation: 'horizontal',
|
|
35
|
+
bottom: 5, left: 0,
|
|
36
|
+
width: '100%',
|
|
37
|
+
style: { fg: C.line }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
statusBox = blessed.box({
|
|
41
|
+
bottom: 1, left: 0,
|
|
42
|
+
width: '100%', height: 4,
|
|
43
|
+
tags: true,
|
|
44
|
+
padding: { left: 2, right: 2 }
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
screen.append(logPanel);
|
|
48
|
+
screen.append(separator);
|
|
49
|
+
screen.append(statusBox);
|
|
50
|
+
|
|
51
|
+
screen.key(['C-c'], () => { screen.destroy(); process.exit(0); });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function dot(status, frame = 0) {
|
|
55
|
+
if (status === 'done') return `{${C.mint}-fg}●{/}`;
|
|
56
|
+
if (status === 'running') return `{#FFFFFF-fg}${FRAMES[frame % FRAMES.length]}{/}`;
|
|
57
|
+
if (status === 'paused') return `{${C.peach}-fg}●{/}`;
|
|
58
|
+
if (status === 'error') return `{${C.salmon}-fg}●{/}`;
|
|
59
|
+
return `{${C.ghost}-fg}○{/}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function shimmerName(text, frame = 0) {
|
|
63
|
+
const peak = frame % (text.length + 6);
|
|
64
|
+
return text.split('').map((ch, i) => {
|
|
65
|
+
const dist = Math.abs(i - peak);
|
|
66
|
+
let color = C.violet;
|
|
67
|
+
if (dist === 0) color = '#FFFFFF';
|
|
68
|
+
else if (dist === 1) color = '#EDD9FF';
|
|
69
|
+
else if (dist === 2) color = '#D4B0FE';
|
|
70
|
+
return ch === ' ' ? ch : `{${color}-fg}{bold}${ch}{/}`;
|
|
71
|
+
}).join('');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function compactDots(frame = 0) {
|
|
75
|
+
return stepNames.map((_name, i) => dot(statuses[i], frame)).join(` {${C.ghost}-fg}─{/} `);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function renderTimeline(frame = 0) {
|
|
79
|
+
const currentMeta = runningIdx >= 0 && meta[runningIdx]
|
|
80
|
+
? ` {${C.ghost}-fg}${meta[runningIdx]}{/}`
|
|
81
|
+
: '';
|
|
82
|
+
const isPaused = runningIdx >= 0 && statuses[runningIdx] === 'paused';
|
|
83
|
+
const activityLabel = isPaused
|
|
84
|
+
? `{${C.peach}-fg}{bold}Paused{/}`
|
|
85
|
+
: `{bold}Running{/}`;
|
|
86
|
+
const activityIcon = isPaused
|
|
87
|
+
? `{${C.peach}-fg}●{/}`
|
|
88
|
+
: `{#FFFFFF-fg}${FRAMES[frame % FRAMES.length]}{/}`;
|
|
89
|
+
const runningLabel = runningIdx >= 0
|
|
90
|
+
? `${activityLabel} ${activityIcon} ${shimmerName(stepNames[runningIdx], frame)} {${C.ghost}-fg}step ${runningIdx + 1}/${N}{/}${currentMeta}`
|
|
91
|
+
: `{bold}Running:{/} {${C.dimV}-fg}idle{/}`;
|
|
92
|
+
const statusLines = [
|
|
93
|
+
'',
|
|
94
|
+
runningLabel,
|
|
95
|
+
'',
|
|
96
|
+
compactDots(frame)
|
|
97
|
+
];
|
|
98
|
+
statusBox.setContent(statusLines.join('\n'));
|
|
99
|
+
screen.render();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
renderTimeline();
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
log(text) { logPanel.log(`{${C.blue}-fg}${text}{/}`); screen.render(); },
|
|
106
|
+
logMuted(text) { logPanel.log(`{${C.dimV}-fg}${text}{/}`); screen.render(); },
|
|
107
|
+
|
|
108
|
+
setRunning(i, info = '') {
|
|
109
|
+
if (spinnerInterval) { clearInterval(spinnerInterval); spinnerInterval = null; }
|
|
110
|
+
runningIdx = i;
|
|
111
|
+
statuses[i] = 'running';
|
|
112
|
+
meta[i] = info;
|
|
113
|
+
renderTimeline();
|
|
114
|
+
spinnerInterval = setInterval(() => { spinnerFrame++; renderTimeline(spinnerFrame); }, 80);
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
setPaused(i, info = '') {
|
|
118
|
+
if (spinnerInterval) { clearInterval(spinnerInterval); spinnerInterval = null; }
|
|
119
|
+
runningIdx = i;
|
|
120
|
+
statuses[i] = 'paused';
|
|
121
|
+
meta[i] = info;
|
|
122
|
+
renderTimeline();
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
setDone(i, info = '') {
|
|
126
|
+
if (spinnerInterval) { clearInterval(spinnerInterval); spinnerInterval = null; }
|
|
127
|
+
statuses[i] = 'done';
|
|
128
|
+
meta[i] = info;
|
|
129
|
+
if (runningIdx === i) runningIdx = -1;
|
|
130
|
+
renderTimeline();
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
setError(i, info = '') {
|
|
134
|
+
if (spinnerInterval) { clearInterval(spinnerInterval); spinnerInterval = null; }
|
|
135
|
+
statuses[i] = 'error';
|
|
136
|
+
meta[i] = info;
|
|
137
|
+
if (runningIdx === i) runningIdx = -1;
|
|
138
|
+
renderTimeline();
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
end() {
|
|
142
|
+
if (spinnerInterval) { clearInterval(spinnerInterval); spinnerInterval = null; }
|
|
143
|
+
if (ownScreen) screen.destroy();
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const WEB_DIST = path.resolve(__dirname, '../../web/dist');
|
|
9
|
+
import { agentsRouter } from './routes/agents.js';
|
|
10
|
+
import { pipelinesRouter } from './routes/pipelines.js';
|
|
11
|
+
import { filesRouter } from './routes/files.js';
|
|
12
|
+
|
|
13
|
+
export async function startServer({ port = 4317, root = process.cwd(), logger = console.log } = {}) {
|
|
14
|
+
const app = express();
|
|
15
|
+
app.use(cors());
|
|
16
|
+
app.use(express.json({ limit: '2mb' }));
|
|
17
|
+
|
|
18
|
+
const ctx = {
|
|
19
|
+
root,
|
|
20
|
+
pipelinesDir: path.join(root, '.singleton', 'pipelines'),
|
|
21
|
+
agentsCacheFile: path.join(root, '.singleton', 'agents.json')
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
app.get('/api/health', (_req, res) => res.json({ ok: true, root }));
|
|
25
|
+
app.use('/api/agents', agentsRouter(ctx));
|
|
26
|
+
app.use('/api/pipelines', pipelinesRouter(ctx));
|
|
27
|
+
app.use('/api/files', filesRouter(ctx));
|
|
28
|
+
|
|
29
|
+
app.use(express.static(WEB_DIST));
|
|
30
|
+
app.get('*', (_req, res) => res.sendFile(path.join(WEB_DIST, 'index.html')));
|
|
31
|
+
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
const server = app.listen(port, () => {
|
|
34
|
+
logger(`Singleton server listening on http://localhost:${port}`);
|
|
35
|
+
logger(`Project root: ${root}`);
|
|
36
|
+
resolve(server);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
42
|
+
startServer({ port: Number(process.env.PORT) || 4317, root: process.cwd() });
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import { scanAgents } from '../../../cli/src/scanner.js';
|
|
4
|
+
|
|
5
|
+
export function agentsRouter(ctx) {
|
|
6
|
+
const r = Router();
|
|
7
|
+
|
|
8
|
+
r.get('/', async (_req, res) => {
|
|
9
|
+
try {
|
|
10
|
+
try {
|
|
11
|
+
const raw = await fs.readFile(ctx.agentsCacheFile, 'utf8');
|
|
12
|
+
return res.json(JSON.parse(raw));
|
|
13
|
+
} catch {
|
|
14
|
+
const agents = await scanAgents(ctx.root);
|
|
15
|
+
return res.json({ scannedAt: new Date().toISOString(), root: ctx.root, agents });
|
|
16
|
+
}
|
|
17
|
+
} catch (err) {
|
|
18
|
+
res.status(500).json({ error: err.message });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
r.post('/rescan', async (_req, res) => {
|
|
23
|
+
try {
|
|
24
|
+
const agents = await scanAgents(ctx.root);
|
|
25
|
+
res.json({ scannedAt: new Date().toISOString(), root: ctx.root, agents });
|
|
26
|
+
} catch (err) {
|
|
27
|
+
res.status(500).json({ error: err.message });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return r;
|
|
32
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', '.singleton', 'dist', 'build', '.next', '.cache']);
|
|
6
|
+
|
|
7
|
+
async function walk(root, rel = '', out = []) {
|
|
8
|
+
const abs = path.join(root, rel);
|
|
9
|
+
const entries = await fs.readdir(abs, { withFileTypes: true });
|
|
10
|
+
for (const e of entries) {
|
|
11
|
+
if (e.name.startsWith('.') && e.name !== '.singleton') {
|
|
12
|
+
if (e.isDirectory()) continue;
|
|
13
|
+
}
|
|
14
|
+
if (e.isDirectory()) {
|
|
15
|
+
if (SKIP_DIRS.has(e.name)) continue;
|
|
16
|
+
await walk(root, path.join(rel, e.name), out);
|
|
17
|
+
} else if (e.isFile()) {
|
|
18
|
+
out.push(path.join(rel, e.name));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function filesRouter(ctx) {
|
|
25
|
+
const r = Router();
|
|
26
|
+
|
|
27
|
+
r.get('/', async (req, res) => {
|
|
28
|
+
try {
|
|
29
|
+
const ext = String(req.query.ext || '').replace(/^\./, '').toLowerCase();
|
|
30
|
+
const all = await walk(ctx.root);
|
|
31
|
+
const filtered = ext
|
|
32
|
+
? all.filter((f) => f.toLowerCase().endsWith(`.${ext}`))
|
|
33
|
+
: all;
|
|
34
|
+
filtered.sort();
|
|
35
|
+
res.json({ root: ctx.root, files: filtered });
|
|
36
|
+
} catch (err) {
|
|
37
|
+
res.status(500).json({ error: err.message });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return r;
|
|
42
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
function safeName(name) {
|
|
6
|
+
return String(name).replace(/[^a-zA-Z0-9_-]+/g, '-').slice(0, 80) || 'pipeline';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function pipelinesRouter(ctx) {
|
|
10
|
+
const r = Router();
|
|
11
|
+
|
|
12
|
+
r.get('/', async (_req, res) => {
|
|
13
|
+
try {
|
|
14
|
+
await fs.mkdir(ctx.pipelinesDir, { recursive: true });
|
|
15
|
+
const files = (await fs.readdir(ctx.pipelinesDir)).filter((f) => f.endsWith('.json'));
|
|
16
|
+
const items = (await Promise.all(files.map(async (f) => {
|
|
17
|
+
try {
|
|
18
|
+
const raw = await fs.readFile(path.join(ctx.pipelinesDir, f), 'utf8');
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
if (!parsed.name) return null;
|
|
21
|
+
return { file: f, ...parsed };
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}))).filter(Boolean);
|
|
26
|
+
res.json({ pipelines: items });
|
|
27
|
+
} catch (err) {
|
|
28
|
+
res.status(500).json({ error: err.message });
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
r.get('/:name', async (req, res) => {
|
|
33
|
+
try {
|
|
34
|
+
const file = path.join(ctx.pipelinesDir, `${safeName(req.params.name)}.json`);
|
|
35
|
+
const raw = await fs.readFile(file, 'utf8');
|
|
36
|
+
res.json(JSON.parse(raw));
|
|
37
|
+
} catch (err) {
|
|
38
|
+
res.status(404).json({ error: 'not found' });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
r.post('/', async (req, res) => {
|
|
43
|
+
try {
|
|
44
|
+
const { name, steps = [], nodes, edges } = req.body || {};
|
|
45
|
+
if (!name) return res.status(400).json({ error: 'name required' });
|
|
46
|
+
await fs.mkdir(ctx.pipelinesDir, { recursive: true });
|
|
47
|
+
const safe = safeName(name);
|
|
48
|
+
const file = path.join(ctx.pipelinesDir, `${safe}.json`);
|
|
49
|
+
const payload = {
|
|
50
|
+
name: safe,
|
|
51
|
+
created: new Date().toISOString(),
|
|
52
|
+
steps,
|
|
53
|
+
nodes,
|
|
54
|
+
edges
|
|
55
|
+
};
|
|
56
|
+
await fs.writeFile(file, JSON.stringify(payload, null, 2));
|
|
57
|
+
res.json({ ok: true, file: `${safe}.json`, ...payload });
|
|
58
|
+
} catch (err) {
|
|
59
|
+
res.status(500).json({ error: err.message });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
r.delete('/:name', async (req, res) => {
|
|
64
|
+
try {
|
|
65
|
+
const file = path.join(ctx.pipelinesDir, `${safeName(req.params.name)}.json`);
|
|
66
|
+
await fs.unlink(file);
|
|
67
|
+
res.json({ ok: true });
|
|
68
|
+
} catch (err) {
|
|
69
|
+
res.status(404).json({ error: 'not found' });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return r;
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.sidebar[data-v-82474bb7]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;width:288px;border-right:1px solid #262b34;background-color:#14171c;flex-shrink:0}.sidebar__header[data-v-82474bb7]{padding:12px;border-bottom:1px solid #262b34}.sidebar__title[data-v-82474bb7]{margin:0;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:#94a3b8}.sidebar__hint[data-v-82474bb7]{margin:4px 0 0;font-size:11px;color:#64748b}.sidebar__list[data-v-82474bb7]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:8px}.sidebar__list[data-v-82474bb7]::-webkit-scrollbar{width:10px;height:10px}.sidebar__list[data-v-82474bb7]::-webkit-scrollbar-track{background:transparent}.sidebar__list[data-v-82474bb7]::-webkit-scrollbar-thumb{background:#262b34;border-radius:4px}.sidebar__list[data-v-82474bb7]::-webkit-scrollbar-thumb:hover{background:#3a4150}.sidebar__list[data-v-82474bb7]{flex:1;overflow-y:auto;padding:12px}.sidebar__state[data-v-82474bb7]{font-size:12px;color:#94a3b8}.sidebar__state--error[data-v-82474bb7]{color:#ef4444}.sidebar__state code[data-v-82474bb7]{font-size:11px;background:#1c2027;padding:2px 4px;border-radius:4px}.sidebar__nodes[data-v-82474bb7]{padding:12px;border-bottom:1px solid #262b34}.sidebar__subtitle[data-v-82474bb7]{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:#64748b;margin-bottom:4px}.sidebar__io-row[data-v-82474bb7]{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:8px}.io-card[data-v-82474bb7]{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:2px;flex:1;padding:8px;border-radius:6px;border:1px solid #3a4150;border-left:3px solid #7dd3fc;background-color:#1c2027;cursor:grab;transition:background-color .12s ease,border-color .12s ease}.io-card[data-v-82474bb7]:hover{background-color:#262b34;border-color:#38bdf8}.io-card[data-v-82474bb7]:active{cursor:grabbing}.io-card__label[data-v-82474bb7]{font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace;font-size:11px;color:#7dd3fc}.io-card__hint[data-v-82474bb7]{font-size:10px;color:#64748b}.agent-card[data-v-82474bb7]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:4px;padding:12px;border-radius:8px;border:1px solid #3a4150;background-color:#1c2027;cursor:grab;transition:background-color .12s ease,border-color .12s ease}.agent-card[data-v-82474bb7]:hover{background-color:#262b34;border-color:#38bdf8}.agent-card[data-v-82474bb7]:active{cursor:grabbing}.agent-card__head[data-v-82474bb7]{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:8px}.agent-card__id[data-v-82474bb7]{font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace;font-size:12px;color:#7dd3fc}.agent-card__model[data-v-82474bb7]{font-size:10px;color:#64748b}.agent-card__desc[data-v-82474bb7]{margin:0;font-size:11px;color:#94a3b8;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.agent-card__tags[data-v-82474bb7]{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:4px;flex-wrap:wrap}.agent-card__tag[data-v-82474bb7]{font-size:10px;padding:2px 6px;border-radius:4px;background:#3a4150;color:#94a3b8}.agent-card__ports[data-v-82474bb7]{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:12px;font-size:10px;color:#64748b}.node-agent[data-v-9ea2f689]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;min-width:220px;border-radius:8px;border:1px solid #3a4150;background-color:#1c2027;box-shadow:0 4px 12px #00000059;color:#f1f5f9}.node-agent--stale[data-v-9ea2f689]{border-color:#7f1d1d;background-color:#450a0a}.node-agent--stale .node-agent__head[data-v-9ea2f689]{background-color:#7f1d1d;border-bottom-color:#7f1d1d}.node-agent--stale .node-agent__id[data-v-9ea2f689]{color:#fee2e2}.node-agent__head[data-v-9ea2f689]{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:8px;padding:8px 12px;border-bottom:1px solid #3a4150;background-color:#14171c;border-radius:8px 8px 0 0}.node-agent__id[data-v-9ea2f689]{font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace;font-size:12px;color:#7dd3fc}.node-agent__badge[data-v-9ea2f689]{margin-left:4px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:#fee2e2}.node-agent__close[data-v-9ea2f689]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;font-size:11px;color:#64748b;transition:color .12s ease}.node-agent__close[data-v-9ea2f689]:hover{color:#ef4444}.node-agent__desc[data-v-9ea2f689]{margin:0;padding:8px 12px;font-size:11px;color:#94a3b8;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.node-agent__warn[data-v-9ea2f689]{margin:0;padding:8px 12px;font-size:11px;color:#fee2e2}.node-agent__ports[data-v-9ea2f689]{display:flex;flex-direction:row;align-items:stretch;justify-content:flex-start;gap:8px;padding:8px 12px;border-top:1px solid #3a4150}.node-agent__col[data-v-9ea2f689]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:4px;flex:1}.node-agent__col--out[data-v-9ea2f689]{text-align:right}.node-agent__col-title[data-v-9ea2f689]{font-size:10px;text-transform:uppercase;color:#64748b;margin-bottom:4px}.node-agent__port[data-v-9ea2f689]{position:relative;font-size:11px;color:#94a3b8;padding:4px 0}.node-agent__port--in[data-v-9ea2f689]{padding-left:8px}.node-agent__port--out[data-v-9ea2f689]{padding-right:8px}.file-picker[data-v-be890d20]{display:flex;align-items:center;justify-content:center;position:fixed;top:0;right:0;bottom:0;left:0;z-index:50;background-color:#0009}.file-picker__dialog[data-v-be890d20]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;width:720px;max-width:95vw;max-height:80vh;border-radius:8px;border:1px solid #3a4150;background-color:#14171c;box-shadow:0 8px 24px #00000073}.file-picker__head[data-v-be890d20]{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:12px;padding:12px;border-bottom:1px solid #262b34}.file-picker__title[data-v-be890d20]{margin:0;font-size:14px;font-weight:600}.file-picker__close[data-v-be890d20]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;color:#94a3b8}.file-picker__close[data-v-be890d20]:hover{color:#f1f5f9}.file-picker__body[data-v-be890d20]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:8px;padding:12px;overflow:hidden}.file-picker__search[data-v-be890d20]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;outline:0;font:inherit;color:inherit;padding:4px 8px;font-size:12px;border-radius:6px;border:1px solid #3a4150;background-color:#1c2027;color:#f1f5f9}.file-picker__search[data-v-be890d20]:focus{border-color:#38bdf8}.file-picker__state[data-v-be890d20]{padding:12px;font-size:12px;color:#94a3b8;text-align:center}.file-picker__state--error[data-v-be890d20]{color:#ef4444}.file-picker__list[data-v-be890d20]::-webkit-scrollbar{width:10px;height:10px}.file-picker__list[data-v-be890d20]::-webkit-scrollbar-track{background:transparent}.file-picker__list[data-v-be890d20]::-webkit-scrollbar-thumb{background:#262b34;border-radius:4px}.file-picker__list[data-v-be890d20]::-webkit-scrollbar-thumb:hover{background:#3a4150}.file-picker__list[data-v-be890d20]{list-style:none;margin:0;padding:0;overflow-y:auto;max-height:55vh;border:1px solid #262b34;border-radius:6px}.file-picker__item[data-v-be890d20]{padding:4px 8px;font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace;font-size:11px;color:#f1f5f9;cursor:pointer;border-bottom:1px solid #262b34}.file-picker__item[data-v-be890d20]:last-child{border-bottom:0}.file-picker__item[data-v-be890d20]:hover{background-color:#1c2027;color:#7dd3fc}.node-input[data-v-95ec91bc]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;min-width:220px;border-radius:8px;border:1px solid #3a4150;background-color:#1c2027;box-shadow:0 4px 12px #00000059;color:#f1f5f9;border-left:3px solid #7dd3fc}.node-input__head[data-v-95ec91bc]{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:8px;padding:8px 12px;border-bottom:1px solid #3a4150;background-color:#14171c;border-radius:8px 8px 0 0}.node-input__meta[data-v-95ec91bc]{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:8px}.node-input__kind[data-v-95ec91bc]{font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace;font-size:11px;color:#7dd3fc;text-transform:uppercase;letter-spacing:.06em}.node-input__subtypes[data-v-95ec91bc]{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:2px}.node-input__st[data-v-95ec91bc]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;font:inherit;color:inherit;cursor:pointer;font-size:10px;padding:1px 4px;border-radius:4px;color:#64748b;border:1px solid transparent;transition:color .12s ease,border-color .12s ease}.node-input__st[data-v-95ec91bc]:hover{color:#94a3b8}.node-input__st--active[data-v-95ec91bc]{color:#7dd3fc;border-color:#3a4150}.node-input__close[data-v-95ec91bc]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;font-size:11px;color:#64748b}.node-input__close[data-v-95ec91bc]:hover{color:#ef4444}.node-input__body[data-v-95ec91bc]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0}.node-input__field[data-v-95ec91bc]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;outline:0;font:inherit;color:inherit;padding:4px 12px;font-size:11px;color:#f1f5f9;border-bottom:1px solid #3a4150;background:transparent;transition:background-color .12s ease}.node-input__field[data-v-95ec91bc]::placeholder{color:#64748b;font-style:italic}.node-input__field[data-v-95ec91bc]:focus{background-color:#262b34}.node-input__field--dim[data-v-95ec91bc]{color:#94a3b8}.node-input__file-btn[data-v-95ec91bc]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;font:inherit;color:inherit;cursor:pointer;padding:8px 12px;text-align:left;font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace;font-size:11px;transition:background-color .12s ease;border-bottom:1px solid #3a4150}.node-input__file-btn[data-v-95ec91bc]:hover{background-color:#262b34}.node-input__path[data-v-95ec91bc]{color:#7dd3fc}.node-input__placeholder[data-v-95ec91bc]{color:#64748b;font-style:italic}.node-input__port[data-v-95ec91bc]{position:relative;font-size:11px;color:#94a3b8;padding:4px 12px;text-align:right}.canvas[data-v-b93ee04b]{width:100%;height:100%}.canvas[data-v-b93ee04b] .canvas__minimap{background-color:#14171c!important}.toolbar[data-v-770bf2a0]{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:8px}.toolbar__input[data-v-770bf2a0]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;outline:0;font:inherit;color:inherit;width:160px;padding:4px 8px;font-size:11px;border-radius:6px;border:1px solid #3a4150;background-color:#1c2027;color:#f1f5f9;transition:border-color .12s ease}.toolbar__input[data-v-770bf2a0]:focus{border-color:#38bdf8}.toolbar__btn[data-v-770bf2a0]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;font:inherit;color:inherit;cursor:pointer;padding:4px 12px;font-size:11px;border-radius:6px;border:1px solid #3a4150;background-color:#1c2027;color:#f1f5f9;transition:background-color .12s ease,border-color .12s ease}.toolbar__btn[data-v-770bf2a0]:hover{background-color:#262b34}.toolbar__btn--primary[data-v-770bf2a0]{background-color:#0ea5e9;border-color:#0ea5e9;color:#fff}.toolbar__btn--primary[data-v-770bf2a0]:hover{background-color:#38bdf8;border-color:#38bdf8}.toolbar__btn--danger[data-v-770bf2a0]:hover{background-color:#7f1d1d;border-color:#7f1d1d}.modal[data-v-770bf2a0]{display:flex;align-items:center;justify-content:center;position:fixed;top:0;right:0;bottom:0;left:0;z-index:50;background-color:#0009}.modal__dialog[data-v-770bf2a0]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;max-width:95vw;max-height:85vh;border-radius:8px;border:1px solid #3a4150;background-color:#14171c;box-shadow:0 8px 24px #00000073}.modal__dialog--sm[data-v-770bf2a0]{width:560px}.modal__dialog--md[data-v-770bf2a0]{width:720px}.modal__header[data-v-770bf2a0]{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:12px;padding:12px;border-bottom:1px solid #262b34}.modal__title[data-v-770bf2a0]{margin:0;font-size:14px;font-weight:600}.modal__close[data-v-770bf2a0]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;color:#94a3b8;transition:color .12s ease}.modal__close[data-v-770bf2a0]:hover{color:#f1f5f9}.modal__body[data-v-770bf2a0]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:16px}.modal__body[data-v-770bf2a0]::-webkit-scrollbar{width:10px;height:10px}.modal__body[data-v-770bf2a0]::-webkit-scrollbar-track{background:transparent}.modal__body[data-v-770bf2a0]::-webkit-scrollbar-thumb{background:#262b34;border-radius:4px}.modal__body[data-v-770bf2a0]::-webkit-scrollbar-thumb:hover{background:#3a4150}.modal__body[data-v-770bf2a0]{padding:16px;overflow-y:auto}.modal__section[data-v-770bf2a0]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:4px}.modal__section-head[data-v-770bf2a0]{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:8px}.modal__label[data-v-770bf2a0]{font-size:11px;text-transform:uppercase;color:#94a3b8}.modal__copy[data-v-770bf2a0]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:none;border:0;padding:0;font:inherit;color:inherit;cursor:pointer;font-size:11px;color:#7dd3fc}.modal__copy[data-v-770bf2a0]:hover{color:#38bdf8}.modal__code[data-v-770bf2a0]{margin:0;padding:8px 12px;font-size:11px;font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace;color:#7dd3fc;background-color:#0b0d10;border:1px solid #262b34;border-radius:6px}.modal__code--inline[data-v-770bf2a0]{white-space:pre-wrap}.modal__code--scroll[data-v-770bf2a0]{overflow:auto;max-height:50vh;color:#f1f5f9}.pipeline-list[data-v-770bf2a0]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;list-style:none;margin:0;padding:0}.pipeline-list__empty[data-v-770bf2a0]{margin:0;padding:16px;text-align:center;font-size:12px;color:#64748b}.pipeline-list__item[data-v-770bf2a0]{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:8px;padding:8px 0;border-bottom:1px solid #262b34}.pipeline-list__item[data-v-770bf2a0]:last-child{border-bottom:0}.pipeline-list__info[data-v-770bf2a0]{min-width:0;flex:1}.pipeline-list__name[data-v-770bf2a0]{font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace;font-size:12px;color:#7dd3fc;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pipeline-list__meta[data-v-770bf2a0]{font-size:10px;color:#64748b}.pipeline-list__actions[data-v-770bf2a0]{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:4px;flex-shrink:0}.toast-stack[data-v-ac6df039]{display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-start;gap:8px;position:fixed;right:16px;bottom:16px;z-index:60}.toast[data-v-ac6df039]{padding:8px 12px;max-width:360px;font-size:11px;border-radius:8px;border:1px solid;box-shadow:0 4px 12px #00000059}.toast--info[data-v-ac6df039]{background-color:#1c2027;border-color:#3a4150;color:#f1f5f9}.toast--warn[data-v-ac6df039]{background-color:#b45309d9;border-color:#d97706;color:#fde68a}.toast--error[data-v-ac6df039]{background-color:#7f1d1dd9;border-color:#dc2626;color:#fee2e2}.toast--success[data-v-ac6df039]{background-color:#064e3bd9;border-color:#10b981;color:#d1fae5}.app[data-v-599c0f81]{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;height:100vh}.app__header[data-v-599c0f81]{display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:12px;height:48px;padding:0 16px;border-bottom:1px solid #262b34;background-color:#14171c;flex-shrink:0}.app__brand[data-v-599c0f81]{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:8px}.app__logo[data-v-599c0f81]{color:#7dd3fc;font-weight:700}.app__title[data-v-599c0f81]{margin:0;font-size:14px;font-weight:600}.app__tagline[data-v-599c0f81]{font-size:11px;color:#64748b}.app__body[data-v-599c0f81]{display:flex;flex-direction:row;align-items:stretch;justify-content:flex-start;gap:0;flex:1;overflow:hidden}.app__canvas[data-v-599c0f81]{flex:1;position:relative}.vue-flow{position:relative;width:100%;height:100%;overflow:hidden;z-index:0;direction:ltr}.vue-flow__container{position:absolute;height:100%;width:100%;left:0;top:0}.vue-flow__pane{z-index:1}.vue-flow__pane.draggable{cursor:grab}.vue-flow__pane.selection{cursor:pointer}.vue-flow__pane.dragging{cursor:grabbing}.vue-flow__transformationpane{transform-origin:0 0;z-index:2;pointer-events:none}.vue-flow__viewport{z-index:4;overflow:clip}.vue-flow__selection{z-index:6}.vue-flow__edge-labels{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.vue-flow__nodesselection-rect:focus,.vue-flow__nodesselection-rect:focus-visible{outline:none}.vue-flow .vue-flow__edges{pointer-events:none;overflow:visible}.vue-flow__edge-path,.vue-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.vue-flow__edge{pointer-events:visibleStroke;cursor:pointer}.vue-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.vue-flow__edge.animated path.vue-flow__edge-interaction{stroke-dasharray:none;animation:none}.vue-flow__edge.inactive{pointer-events:none}.vue-flow__edge.selected,.vue-flow__edge:focus,.vue-flow__edge:focus-visible{outline:none}.vue-flow__edge.selected .vue-flow__edge-path,.vue-flow__edge:focus .vue-flow__edge-path,.vue-flow__edge:focus-visible .vue-flow__edge-path{stroke:#555}.vue-flow__edge-textwrapper{pointer-events:all}.vue-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.vue-flow__connection{pointer-events:none}.vue-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.vue-flow__connectionline{z-index:1001}.vue-flow__nodes{pointer-events:none;transform-origin:0 0}.vue-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.vue-flow__node.draggable{cursor:grab;pointer-events:all}.vue-flow__node.draggable.dragging{cursor:grabbing}.vue-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.vue-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.vue-flow__nodesselection-rect.dragging{cursor:grabbing}.vue-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px}.vue-flow__handle.connectable{pointer-events:all;cursor:crosshair}.vue-flow__handle-bottom{left:50%;bottom:0;transform:translate(-50%,50%)}.vue-flow__handle-top{left:50%;top:0;transform:translate(-50%,-50%)}.vue-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.vue-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.vue-flow__edgeupdater{cursor:move;pointer-events:all}.vue-flow__panel{position:absolute;z-index:5;margin:15px}.vue-flow__panel.top{top:0}.vue-flow__panel.bottom{bottom:0}.vue-flow__panel.left{left:0}.vue-flow__panel.right{right:0}.vue-flow__panel.center{left:50%;transform:translate(-50%)}@keyframes dashdraw{0%{stroke-dashoffset:10}}:root{--vf-node-bg: #fff;--vf-node-text: #222;--vf-connection-path: #b1b1b7;--vf-handle: #555}.vue-flow__edge.updating .vue-flow__edge-path{stroke:#777}.vue-flow__edge-text{font-size:10px}.vue-flow__edge-textbg{fill:#fff}.vue-flow__connection-path{stroke:var(--vf-connection-path)}.vue-flow__node{cursor:grab}.vue-flow__node.selectable:focus,.vue-flow__node.selectable:focus-visible{outline:none}.vue-flow__node-default,.vue-flow__node-input,.vue-flow__node-output{padding:10px;border-radius:3px;width:150px;font-size:12px;text-align:center;border-width:1px;border-style:solid;color:var(--vf-node-text);background-color:var(--vf-node-bg);border-color:var(--vf-node-color)}.vue-flow__node-default.selected,.vue-flow__node-default.selected:hover,.vue-flow__node-input.selected,.vue-flow__node-input.selected:hover,.vue-flow__node-output.selected,.vue-flow__node-output.selected:hover{box-shadow:0 0 0 .5px var(--vf-box-shadow)}.vue-flow__node-default.selected,.vue-flow__node-default:focus,.vue-flow__node-default:focus-visible,.vue-flow__node-input.selected,.vue-flow__node-input:focus,.vue-flow__node-input:focus-visible,.vue-flow__node-output.selected,.vue-flow__node-output:focus,.vue-flow__node-output:focus-visible{outline:none;border:1px solid #555}.vue-flow__node-default .vue-flow__handle,.vue-flow__node-input .vue-flow__handle,.vue-flow__node-output .vue-flow__handle{background:var(--vf-handle)}.vue-flow__node-default.selectable:hover,.vue-flow__node-input.selectable:hover,.vue-flow__node-output.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.vue-flow__node-input{--vf-node-color: var(--vf-node-color, #0041d0);--vf-handle: var(--vf-node-color, #0041d0);--vf-box-shadow: var(--vf-node-color, #0041d0);background:var(--vf-node-bg);border-color:var(--vf-node-color, #0041d0)}.vue-flow__node-input.selected,.vue-flow__node-input:focus,.vue-flow__node-input:focus-visible{outline:none;border:1px solid var(--vf-node-color, #0041d0)}.vue-flow__node-default{--vf-handle: var(--vf-node-color, #1a192b);--vf-box-shadow: var(--vf-node-color, #1a192b);background:var(--vf-node-bg);border-color:var(--vf-node-color, #1a192b)}.vue-flow__node-default.selected,.vue-flow__node-default:focus,.vue-flow__node-default:focus-visible{outline:none;border:1px solid var(--vf-node-color, #1a192b)}.vue-flow__node-output{--vf-handle: var(--vf-node-color, #ff0072);--vf-box-shadow: var(--vf-node-color, #ff0072);background:var(--vf-node-bg);border-color:var(--vf-node-color, #ff0072)}.vue-flow__node-output.selected,.vue-flow__node-output:focus,.vue-flow__node-output:focus-visible{outline:none;border:1px solid var(--vf-node-color, #ff0072)}.vue-flow__nodesselection-rect,.vue-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.vue-flow__nodesselection-rect:focus,.vue-flow__nodesselection-rect:focus-visible,.vue-flow__selection:focus,.vue-flow__selection:focus-visible{outline:none}.vue-flow__handle{width:6px;height:6px;background:var(--vf-handle);border:1px solid #fff;border-radius:100%}.vue-flow__controls{box-shadow:0 0 2px 1px #00000014}.vue-flow__controls-button{background:#fefefe;border:none;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;user-select:none;padding:5px}.vue-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.vue-flow__controls-button:hover{background:#f4f4f4}.vue-flow__controls-button:disabled{pointer-events:none}.vue-flow__controls-button:disabled svg{fill-opacity:.4}.vue-flow__minimap{background-color:#fff}.vue-flow__minimap.pannable{cursor:grab}.vue-flow__minimap.dragging{cursor:grabbing}.vue-flow__minimap-mask.pannable{cursor:grab}*,*:before,*:after{box-sizing:border-box}html,body,#app{height:100%;margin:0;padding:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:14px;background-color:#0b0d10;color:#f1f5f9;-webkit-font-smoothing:antialiased}a{color:#38bdf8}code,pre{font-family:SF Mono,JetBrains Mono,Fira Code,Menlo,monospace}.vue-flow__node-agent{padding:0;border:none;background:transparent;box-shadow:none}.vue-flow__handle{width:10px;height:10px;background:#38bdf8;border:2px solid #0b0d10}.vue-flow__edge-path{stroke:#38bdf8;stroke-width:2}.vue-flow__background{background-color:#0b0d10}.vue-flow__controls button{background:#1c2027;border-color:#3a4150;color:#f1f5f9}.vue-flow__controls button:hover{background:#262b34}.vue-flow__controls button svg{fill:#f1f5f9}.vue-flow__minimap{background:#14171c!important}
|