project-state-manager 1.0.1
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 +51 -0
- package/dist/shared/schemas/process.schema.d.ts +969 -0
- package/dist/shared/schemas/process.schema.js +119 -0
- package/dist/src/core/error-formatter.d.ts +31 -0
- package/dist/src/core/error-formatter.js +97 -0
- package/dist/src/core/signal-calculator.d.ts +12 -0
- package/dist/src/core/signal-calculator.js +19 -0
- package/dist/src/core/yaml-engine.d.ts +15 -0
- package/dist/src/core/yaml-engine.js +162 -0
- package/dist/src/gui-worker.d.ts +1 -0
- package/dist/src/gui-worker.js +221 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +281 -0
- package/dist/src/infra/ipc-hub.d.ts +18 -0
- package/dist/src/infra/ipc-hub.js +71 -0
- package/dist/src/infra/proxy-client.d.ts +32 -0
- package/dist/src/infra/proxy-client.js +134 -0
- package/dist/src/tools/check-readiness.d.ts +17 -0
- package/dist/src/tools/check-readiness.js +48 -0
- package/dist/src/tools/define-process.d.ts +14 -0
- package/dist/src/tools/define-process.js +39 -0
- package/dist/src/tools/discover-process.d.ts +7 -0
- package/dist/src/tools/discover-process.js +41 -0
- package/dist/src/tools/get-health-status.d.ts +11 -0
- package/dist/src/tools/get-health-status.js +62 -0
- package/dist/src/tools/list-existing-processes.d.ts +7 -0
- package/dist/src/tools/list-existing-processes.js +25 -0
- package/dist/src/tools/scan-node.d.ts +14 -0
- package/dist/src/tools/scan-node.js +47 -0
- package/dist/src/tools/submit-node-data.d.ts +18 -0
- package/dist/src/tools/submit-node-data.js +86 -0
- package/gui/README.md +73 -0
- package/gui/dist/assets/index-BpHg-2LI.css +1 -0
- package/gui/dist/assets/index-Cq6Ut9u2.js +9 -0
- package/gui/dist/index.html +14 -0
- package/gui/dist/vite.svg +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DiscoverProcessArgsSchema } from '../../shared/schemas/process.schema.js';
|
|
2
|
+
export async function discoverProcess(yamlEngine, args) {
|
|
3
|
+
const { context } = DiscoverProcessArgsSchema.parse(args);
|
|
4
|
+
const processes = await yamlEngine.listProcesses();
|
|
5
|
+
const matches = [];
|
|
6
|
+
for (const id of processes) {
|
|
7
|
+
const p = await yamlEngine.loadProcess(id);
|
|
8
|
+
if (p) {
|
|
9
|
+
const searchTerm = context.toLowerCase();
|
|
10
|
+
const inName = p.name.toLowerCase().includes(searchTerm);
|
|
11
|
+
const inId = id.toLowerCase().includes(searchTerm);
|
|
12
|
+
const inEntry = p.entryPoint.toLowerCase().includes(searchTerm);
|
|
13
|
+
// Extended search in node content
|
|
14
|
+
const inInput = p.nodes.input.content.toLowerCase().includes(searchTerm);
|
|
15
|
+
const inFlow = p.nodes.flow.content.toLowerCase().includes(searchTerm);
|
|
16
|
+
const inOutput = p.nodes.output.content.toLowerCase().includes(searchTerm);
|
|
17
|
+
if (inName || inId || inEntry || inInput || inFlow || inOutput) {
|
|
18
|
+
matches.push(p);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (matches.length === 0) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: 'text', text: `No process found matching context: "${context}"` }]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const results = matches.map(p => {
|
|
28
|
+
const manualFlags = [];
|
|
29
|
+
if (p.nodes.input.manuallyModified)
|
|
30
|
+
manualFlags.push('Input');
|
|
31
|
+
if (p.nodes.flow.manuallyModified)
|
|
32
|
+
manualFlags.push('Flow');
|
|
33
|
+
if (p.nodes.output.manuallyModified)
|
|
34
|
+
manualFlags.push('Output');
|
|
35
|
+
const manualSuffix = manualFlags.length > 0 ? ` [MODIFIÉ MANUELLEMENT: ${manualFlags.join(', ')}]` : '';
|
|
36
|
+
return `- ${p.name} (ID: ${p.id}) [Entry: ${p.entryPoint}]${manualSuffix}`;
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: 'text', text: `Found relevant processes for "${context}":\n${results.join('\n')}` }]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IpcHub } from '../infra/ipc-hub.js';
|
|
2
|
+
/**
|
|
3
|
+
* MCP Tool to get health status of Master and Worker processes.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getHealthStatus(ipcHub: IpcHub): Promise<{
|
|
6
|
+
isError: boolean;
|
|
7
|
+
content: {
|
|
8
|
+
type: string;
|
|
9
|
+
text: string;
|
|
10
|
+
}[];
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool to get health status of Master and Worker processes.
|
|
3
|
+
*/
|
|
4
|
+
export async function getHealthStatus(ipcHub) {
|
|
5
|
+
const masterMemory = process.memoryUsage();
|
|
6
|
+
const health = {
|
|
7
|
+
master: {
|
|
8
|
+
status: 'ok',
|
|
9
|
+
uptime: process.uptime(),
|
|
10
|
+
memory: {
|
|
11
|
+
rss: masterMemory.rss,
|
|
12
|
+
heapUsed: masterMemory.heapUsed,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
try {
|
|
17
|
+
const workerHealth = await ipcHub.request({ type: 'GET_HEALTH' }, 1000);
|
|
18
|
+
health.worker = {
|
|
19
|
+
status: workerHealth.data.status,
|
|
20
|
+
connected: true,
|
|
21
|
+
uptime: workerHealth.data.uptime,
|
|
22
|
+
memory: workerHealth.data.memory,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
health.worker = {
|
|
27
|
+
status: 'error',
|
|
28
|
+
connected: false,
|
|
29
|
+
error: err instanceof Error ? err.message : String(err),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
isError: false,
|
|
34
|
+
content: [{
|
|
35
|
+
type: 'text',
|
|
36
|
+
text: formatHealthMarkdown(health)
|
|
37
|
+
}],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function formatHealthMarkdown(health) {
|
|
41
|
+
let md = `## Infrastructure Health Status\n\n`;
|
|
42
|
+
md += `### Master Process\n`;
|
|
43
|
+
md += `- **Status**: \`${health.master.status}\`\n`;
|
|
44
|
+
md += `- **Uptime**: ${Math.floor(health.master.uptime)}s\n`;
|
|
45
|
+
md += `- **Memory**: RSS: ${(health.master.memory.rss / 1024 / 1024).toFixed(2)}MB, Heap: ${(health.master.memory.heapUsed / 1024 / 1024).toFixed(2)}MB\n\n`;
|
|
46
|
+
md += `### Worker Process (GUI)\n`;
|
|
47
|
+
if (health.worker) {
|
|
48
|
+
md += `- **Status**: \`${health.worker.status}\`\n`;
|
|
49
|
+
md += `- **IPC**: ${health.worker.connected ? '✅ Connected' : '❌ Disconnected'}\n`;
|
|
50
|
+
if (health.worker.connected) {
|
|
51
|
+
md += `- **Uptime**: ${Math.floor(health.worker.uptime)}s\n`;
|
|
52
|
+
md += `- **Memory**: RSS: ${(health.worker.memory.rss / 1024 / 1024).toFixed(2)}MB, Heap: ${(health.worker.memory.heapUsed / 1024 / 1024).toFixed(2)}MB\n`;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
md += `- **Error**: ${health.worker.error}\n`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
md += `*Worker status unavailable*\n`;
|
|
60
|
+
}
|
|
61
|
+
return md;
|
|
62
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export async function listExistingProcesses(yamlEngine) {
|
|
2
|
+
const processes = await yamlEngine.listProcesses();
|
|
3
|
+
if (processes.length === 0) {
|
|
4
|
+
return {
|
|
5
|
+
content: [{ type: 'text', text: 'No processes found.' }]
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
const results = await Promise.all(processes.map(async (id) => {
|
|
9
|
+
const p = await yamlEngine.loadProcess(id);
|
|
10
|
+
if (!p)
|
|
11
|
+
return `- (ID: ${id}) [Error loading]`;
|
|
12
|
+
const manualFlags = [];
|
|
13
|
+
if (p.nodes.input.manuallyModified)
|
|
14
|
+
manualFlags.push('Input');
|
|
15
|
+
if (p.nodes.flow.manuallyModified)
|
|
16
|
+
manualFlags.push('Flow');
|
|
17
|
+
if (p.nodes.output.manuallyModified)
|
|
18
|
+
manualFlags.push('Output');
|
|
19
|
+
const manualSuffix = manualFlags.length > 0 ? ` [MODIFIÉ MANUELLEMENT: ${manualFlags.join(', ')}]` : '';
|
|
20
|
+
return `- ${p.name} (ID: ${id}) [Entry: ${p.entryPoint}]${manualSuffix}`;
|
|
21
|
+
}));
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: 'text', text: `Existing processes:\n${results.join('\n')}` }]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { YamlEngine } from '../core/yaml-engine.js';
|
|
2
|
+
export declare function scanNode(yamlEngine: YamlEngine, args: any): Promise<{
|
|
3
|
+
content: {
|
|
4
|
+
type: string;
|
|
5
|
+
text: string;
|
|
6
|
+
}[];
|
|
7
|
+
isError: boolean;
|
|
8
|
+
} | {
|
|
9
|
+
content: {
|
|
10
|
+
type: string;
|
|
11
|
+
text: string;
|
|
12
|
+
}[];
|
|
13
|
+
isError?: undefined;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ScanNodeArgsSchema } from '../../shared/schemas/process.schema.js';
|
|
2
|
+
const PROMPTS = {
|
|
3
|
+
entry: `Analyse les points d'entrée de la feature. Identifie :
|
|
4
|
+
1. Les sources de données (APIs, fichiers, input utilisateur).
|
|
5
|
+
2. La structure des données entrantes.
|
|
6
|
+
3. Les schémas de validation appliqués (ex: Zod, Joi).
|
|
7
|
+
4. Les contraintes de sécurité (auth, rate limiting).`,
|
|
8
|
+
flow: `Analyse la logique de traitement. Identifie :
|
|
9
|
+
1. Le cheminement des données entre les modules.
|
|
10
|
+
2. Les transformations appliquées.
|
|
11
|
+
3. Les services ou helpers sollicités.
|
|
12
|
+
4. Les effets de bord (écriture DB, appels externes, logs).`,
|
|
13
|
+
exit: `Analyse les résultats et retours. Identifie :
|
|
14
|
+
1. Le format de la réponse (JSON, Status Codes).
|
|
15
|
+
2. La destination des données (Client, Cache, DB).
|
|
16
|
+
3. Les mécanismes de gestion d'erreurs et les codes d'erreur spécifiques.
|
|
17
|
+
4. Les garanties de succès (retries, transactions).`
|
|
18
|
+
};
|
|
19
|
+
export async function scanNode(yamlEngine, args) {
|
|
20
|
+
const validatedArgs = ScanNodeArgsSchema.parse(args);
|
|
21
|
+
const { process_name, node_type } = validatedArgs;
|
|
22
|
+
const process = await yamlEngine.loadProcess(process_name);
|
|
23
|
+
if (!process) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: 'text', text: `Processus "${process_name}" non trouvé.` }],
|
|
26
|
+
isError: true
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Map node type to check for manual modification
|
|
30
|
+
let targetKey;
|
|
31
|
+
if (node_type === 'entry')
|
|
32
|
+
targetKey = 'input';
|
|
33
|
+
else if (node_type === 'flow')
|
|
34
|
+
targetKey = 'flow';
|
|
35
|
+
else
|
|
36
|
+
targetKey = 'output';
|
|
37
|
+
let prompt = PROMPTS[node_type];
|
|
38
|
+
if (process.nodes[targetKey].manuallyModified) {
|
|
39
|
+
prompt = `⚠️ ATTENTION : Ce nœud a été modifié manuellement par l'utilisateur.
|
|
40
|
+
Prends en compte les corrections humaines dans ton analyse et évite d'écraser les informations importantes.
|
|
41
|
+
|
|
42
|
+
${prompt}`;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
content: [{ type: 'text', text: prompt }]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { YamlEngine } from '../core/yaml-engine.js';
|
|
2
|
+
/**
|
|
3
|
+
* Submits and validates findings for a specific process node.
|
|
4
|
+
*
|
|
5
|
+
* This tool validates the findings against the appropriate Zod schema
|
|
6
|
+
* for the node type, then persists the data via YamlEngine.
|
|
7
|
+
*
|
|
8
|
+
* @param yamlEngine - The YamlEngine instance for persistence
|
|
9
|
+
* @param args - The tool arguments (process_name, node_type, findings)
|
|
10
|
+
* @returns MCP tool response with success/error status
|
|
11
|
+
*/
|
|
12
|
+
export declare function submitNodeData(yamlEngine: YamlEngine, args: unknown): Promise<{
|
|
13
|
+
isError: boolean;
|
|
14
|
+
content: {
|
|
15
|
+
type: string;
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { SubmitNodeDataArgsSchema, NodeFindingsSchema } from '../../shared/schemas/process.schema.js';
|
|
2
|
+
import { ZodError } from 'zod';
|
|
3
|
+
import { ErrorFormatter } from '../core/error-formatter.js';
|
|
4
|
+
/**
|
|
5
|
+
* Submits and validates findings for a specific process node.
|
|
6
|
+
*
|
|
7
|
+
* This tool validates the findings against the appropriate Zod schema
|
|
8
|
+
* for the node type, then persists the data via YamlEngine.
|
|
9
|
+
*
|
|
10
|
+
* @param yamlEngine - The YamlEngine instance for persistence
|
|
11
|
+
* @param args - The tool arguments (process_name, node_type, findings)
|
|
12
|
+
* @returns MCP tool response with success/error status
|
|
13
|
+
*/
|
|
14
|
+
export async function submitNodeData(yamlEngine, args) {
|
|
15
|
+
try {
|
|
16
|
+
const validatedArgs = SubmitNodeDataArgsSchema.parse(args);
|
|
17
|
+
const { process_name, node_type, findings } = validatedArgs;
|
|
18
|
+
const process = await yamlEngine.loadProcess(process_name);
|
|
19
|
+
if (!process) {
|
|
20
|
+
return {
|
|
21
|
+
isError: true,
|
|
22
|
+
content: [{ type: 'text', text: `Processus "${process_name}" non trouvé.` }],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Prepare findings for validation by adding the 'type' field to match the discriminated union
|
|
26
|
+
const findingsWithType = { ...findings, type: node_type };
|
|
27
|
+
try {
|
|
28
|
+
const validatedFindings = NodeFindingsSchema.parse(findingsWithType);
|
|
29
|
+
// Update the specific node
|
|
30
|
+
const timestamp = new Date().toISOString();
|
|
31
|
+
let targetNode;
|
|
32
|
+
let warning = "";
|
|
33
|
+
if (node_type === 'entry')
|
|
34
|
+
targetNode = process.nodes.input;
|
|
35
|
+
else if (node_type === 'flow')
|
|
36
|
+
targetNode = process.nodes.flow;
|
|
37
|
+
else
|
|
38
|
+
targetNode = process.nodes.output;
|
|
39
|
+
if (targetNode.manuallyModified) {
|
|
40
|
+
warning = "\n⚠️ NOTE : Tu viens d'écraser une modification manuelle effectuée par l'utilisateur sur ce nœud.";
|
|
41
|
+
}
|
|
42
|
+
const updatedNode = {
|
|
43
|
+
...targetNode,
|
|
44
|
+
status: 'done',
|
|
45
|
+
lastUpdated: timestamp,
|
|
46
|
+
data: validatedFindings,
|
|
47
|
+
manuallyModified: false // Reset flag on agent submission
|
|
48
|
+
};
|
|
49
|
+
if (node_type === 'entry')
|
|
50
|
+
process.nodes.input = updatedNode;
|
|
51
|
+
else if (node_type === 'flow')
|
|
52
|
+
process.nodes.flow = updatedNode;
|
|
53
|
+
else
|
|
54
|
+
process.nodes.output = updatedNode;
|
|
55
|
+
await yamlEngine.saveProcess(process);
|
|
56
|
+
const updatedProcess = await yamlEngine.loadProcess(process_name);
|
|
57
|
+
const statusMsg = updatedProcess?.is_executable ? "TRUE ✅" : "false ❌";
|
|
58
|
+
return {
|
|
59
|
+
isError: false,
|
|
60
|
+
content: [{
|
|
61
|
+
type: 'text',
|
|
62
|
+
text: `Données soumises et validées pour le nœud "${node_type}" du processus "${process_name}".${warning}\n\n[GUARDRAIL] État actuel de is_executable : ${statusMsg}`
|
|
63
|
+
}],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (err instanceof ZodError) {
|
|
68
|
+
return {
|
|
69
|
+
isError: true,
|
|
70
|
+
content: [{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: `Échec de la validation des données pour le nœud "${node_type}":\n${ErrorFormatter.format(err, node_type)}`
|
|
73
|
+
}],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
81
|
+
return {
|
|
82
|
+
isError: true,
|
|
83
|
+
content: [{ type: 'text', text: `Erreur lors de la soumission : ${message}` }],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
package/gui/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# React + TypeScript + Vite
|
|
2
|
+
|
|
3
|
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
|
4
|
+
|
|
5
|
+
Currently, two official plugins are available:
|
|
6
|
+
|
|
7
|
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
|
|
8
|
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
|
9
|
+
|
|
10
|
+
## React Compiler
|
|
11
|
+
|
|
12
|
+
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
|
|
13
|
+
|
|
14
|
+
## Expanding the ESLint configuration
|
|
15
|
+
|
|
16
|
+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
export default defineConfig([
|
|
20
|
+
globalIgnores(['dist']),
|
|
21
|
+
{
|
|
22
|
+
files: ['**/*.{ts,tsx}'],
|
|
23
|
+
extends: [
|
|
24
|
+
// Other configs...
|
|
25
|
+
|
|
26
|
+
// Remove tseslint.configs.recommended and replace with this
|
|
27
|
+
tseslint.configs.recommendedTypeChecked,
|
|
28
|
+
// Alternatively, use this for stricter rules
|
|
29
|
+
tseslint.configs.strictTypeChecked,
|
|
30
|
+
// Optionally, add this for stylistic rules
|
|
31
|
+
tseslint.configs.stylisticTypeChecked,
|
|
32
|
+
|
|
33
|
+
// Other configs...
|
|
34
|
+
],
|
|
35
|
+
languageOptions: {
|
|
36
|
+
parserOptions: {
|
|
37
|
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
38
|
+
tsconfigRootDir: import.meta.dirname,
|
|
39
|
+
},
|
|
40
|
+
// other options...
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
])
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
// eslint.config.js
|
|
50
|
+
import reactX from 'eslint-plugin-react-x'
|
|
51
|
+
import reactDom from 'eslint-plugin-react-dom'
|
|
52
|
+
|
|
53
|
+
export default defineConfig([
|
|
54
|
+
globalIgnores(['dist']),
|
|
55
|
+
{
|
|
56
|
+
files: ['**/*.{ts,tsx}'],
|
|
57
|
+
extends: [
|
|
58
|
+
// Other configs...
|
|
59
|
+
// Enable lint rules for React
|
|
60
|
+
reactX.configs['recommended-typescript'],
|
|
61
|
+
// Enable lint rules for React DOM
|
|
62
|
+
reactDom.configs.recommended,
|
|
63
|
+
],
|
|
64
|
+
languageOptions: {
|
|
65
|
+
parserOptions: {
|
|
66
|
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
67
|
+
tsconfigRootDir: import.meta.dirname,
|
|
68
|
+
},
|
|
69
|
+
// other options...
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
])
|
|
73
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{font-family:system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a{font-weight:500;color:#646cff;text-decoration:inherit}a:hover{color:#535bf2}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}h1{font-size:3.2em;line-height:1.1}button{border-radius:8px;border:1px solid transparent;padding:.6em 1.2em;font-size:1em;font-weight:500;font-family:inherit;background-color:#1a1a1a;cursor:pointer;transition:border-color .25s}button:hover{border-color:#646cff}button:focus,button:focus-visible{outline:4px auto -webkit-focus-ring-color}@media(prefers-color-scheme:light){:root{color:#213547;background-color:#fff}a:hover{color:#747bff}button{background-color:#f9f9f9}}:root{--bg-color: #0f172a;--sidebar-bg: #1e293b;--card-bg: #334155;--text-primary: #f8fafc;--text-secondary: #94a3b8;--accent: #3b82f6;--success: #22c55e;--error: #ef4444;--warning: #f59e0b;--muted: #64748b;--border: #334155}.dashboard-layout{display:flex;flex-direction:column;height:100vh;background-color:var(--bg-color);color:var(--text-primary);font-family:Inter,system-ui,-apple-system,sans-serif}.main-header{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1.5rem;background-color:var(--sidebar-bg);border-bottom:1px solid var(--border)}.main-header h1{font-size:1.25rem;margin:0;color:var(--accent)}.connection-status{display:flex;align-items:center;gap:.5rem;font-size:.875rem;padding:.25rem .75rem;border-radius:9999px}.connection-status.connected{background-color:#22c55e1a;color:var(--success)}.connection-status.disconnected{background-color:#ef44441a;color:var(--error)}.main-content{display:flex;flex:1;overflow:hidden}.sidebar{width:260px;background-color:var(--sidebar-bg);border-right:1px solid var(--border);display:flex;flex-direction:column}.sidebar-header{display:flex;align-items:center;gap:.75rem;padding:1.25rem;border-bottom:1px solid var(--border)}.sidebar-header h2{font-size:1rem;margin:0}.process-list{flex:1;overflow-y:auto;padding:.5rem}.process-item{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem;border-radius:.5rem;cursor:pointer;transition:all .2s;margin-bottom:.25rem}.process-item:hover{background-color:#ffffff0d}.process-item.active{background-color:#3b82f61a;color:var(--accent)}.process-name{flex:1;font-size:.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.node-card.selected{border-color:var(--accent);box-shadow:0 0 10px #3b82f64d;transform:translateY(-4px)}.node-editor-overlay{position:fixed;inset:0;background-color:#000000bf;display:flex;justify-content:center;align-items:center;z-index:1000}.node-editor-container{width:800px;max-width:90vw;height:600px;max-height:80vh;background-color:var(--sidebar-bg);border-radius:1rem;border:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden;box-shadow:0 20px 25px -5px #00000080}.editor-header{padding:1.5rem;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center}.editor-title h2{font-size:1.25rem;margin:0}.editor-title .process-name{font-size:.875rem;color:var(--text-secondary)}.close-btn{background:none;border:none;color:var(--text-secondary);cursor:pointer;padding:.5rem;display:flex;align-items:center;justify-content:center;border-radius:.375rem;transition:background-color .2s}.close-btn:hover{background-color:#ffffff1a;color:var(--text-primary)}.editor-content{flex:1;display:flex;flex-direction:column;padding:1.5rem;gap:1rem}.yaml-textarea{flex:1;background-color:var(--bg-color);color:var(--text-primary);border:1px solid var(--border);border-radius:.5rem;padding:1rem;font-family:JetBrains Mono,Fira Code,"ui-monospace",monospace;font-size:.875rem;line-height:1.6;resize:none;outline:none}.yaml-textarea:focus{border-color:var(--accent)}.error-banner{background-color:#ef44441a;border:1px solid var(--error);color:#fca5a5;padding:.75rem 1rem;border-radius:.5rem;display:flex;align-items:center;gap:.75rem;font-size:.875rem}.editor-footer{padding:1.5rem;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:1rem}.cancel-btn{background:none;border:1px solid var(--border);color:var(--text-primary)}.cancel-btn:hover{background-color:#ffffff0d}.save-btn{background-color:var(--accent);color:#fff;display:flex;align-items:center;gap:.5rem}.save-btn:hover{background-color:#2563eb}.save-btn:disabled{opacity:.5;cursor:not-allowed}.process-view{flex:1;display:flex;flex-direction:column;padding:2rem;overflow-y:auto}.view-header{margin-bottom:2rem;display:flex;justify-content:space-between;align-items:flex-start}.execution-signal{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;border-radius:.5rem;background-color:#1e293b80;border:1px solid var(--border)}.execution-signal.executable{border-color:var(--success)}.execution-signal.executable .signal-dot{width:10px;height:10px;background-color:var(--success);border-radius:50%;box-shadow:0 0 8px var(--success)}.execution-signal.not-executable .signal-dot{width:10px;height:10px;background-color:var(--muted);border-radius:50%}.signal-label{font-size:.875rem;font-weight:500}.signal-label.executable{color:var(--success)}.nodes-container{display:flex;align-items:flex-start;gap:1rem}.connector{flex:1;height:2px;background-color:var(--border);margin-top:3rem;min-width:2rem}.node-card{width:300px;background-color:var(--sidebar-bg);border-radius:.75rem;border:1px solid var(--border);padding:1.25rem;display:flex;flex-direction:column;gap:1rem;transition:transform .2s,border-color .2s}.node-card:hover{transform:translateY(-4px)}.node-card.status-done{border-left:4px solid var(--success)}.node-card.status-error{border-left:4px solid var(--error)}.node-card.status-progress{border-left:4px solid var(--warning)}.node-card.status-todo{border-left:4px solid var(--muted)}.node-header{display:flex;justify-content:space-between;align-items:center}.node-title{display:flex;align-items:center;gap:.75rem}.node-title h3{font-size:1rem;margin:0}.node-content{background-color:#0f172a80;border-radius:.5rem;padding:1rem;min-height:80px}.content-text{font-size:.875rem;line-height:1.5;color:var(--text-secondary);margin:0}.node-footer{font-size:.75rem;color:var(--muted)}.empty-view{flex:1;display:flex;justify-content:center;align-items:center;color:var(--text-secondary)}.status-icon.success{color:var(--success)}.status-icon.error{color:var(--error)}.status-icon.warning{color:var(--warning)}.status-icon.muted{color:var(--muted)}
|