@datasynx/agentic-ai-cartography 0.1.8 → 0.2.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/dist/bookmarks-O7KNR7D3.js +8 -0
- package/dist/bookmarks-O7KNR7D3.js.map +1 -0
- package/dist/chunk-EVJP2FWQ.js +118 -0
- package/dist/chunk-EVJP2FWQ.js.map +1 -0
- package/dist/chunk-GUZXO6PM.js +647 -0
- package/dist/chunk-GUZXO6PM.js.map +1 -0
- package/dist/chunk-JAFRT2R6.js +97 -0
- package/dist/chunk-JAFRT2R6.js.map +1 -0
- package/dist/cli.js +657 -722
- package/dist/cli.js.map +1 -1
- package/dist/exporter-BDVDYA3K.js +24 -0
- package/dist/exporter-BDVDYA3K.js.map +1 -0
- package/dist/index.d.ts +15 -2
- package/dist/index.js +342 -3
- package/dist/index.js.map +1 -1
- package/dist/types-NKF6BRMZ.js +26 -0
- package/dist/types-NKF6BRMZ.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
exportAll,
|
|
4
|
+
exportBackstageYAML,
|
|
5
|
+
exportHTML,
|
|
6
|
+
exportJSON,
|
|
7
|
+
exportSOPDashboard,
|
|
8
|
+
exportSOPMarkdown,
|
|
9
|
+
generateDependencyMermaid,
|
|
10
|
+
generateTopologyMermaid,
|
|
11
|
+
generateWorkflowMermaid
|
|
12
|
+
} from "./chunk-GUZXO6PM.js";
|
|
13
|
+
export {
|
|
14
|
+
exportAll,
|
|
15
|
+
exportBackstageYAML,
|
|
16
|
+
exportHTML,
|
|
17
|
+
exportJSON,
|
|
18
|
+
exportSOPDashboard,
|
|
19
|
+
exportSOPMarkdown,
|
|
20
|
+
generateDependencyMermaid,
|
|
21
|
+
generateTopologyMermaid,
|
|
22
|
+
generateWorkflowMermaid
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=exporter-BDVDYA3K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -233,7 +233,7 @@ type ClientMessage = {
|
|
|
233
233
|
answer: string;
|
|
234
234
|
} | {
|
|
235
235
|
type: 'command';
|
|
236
|
-
command: 'new-task' | 'end-task' | 'status' | 'stop';
|
|
236
|
+
command: 'new-task' | 'end-task' | 'status' | 'stop' | 'pause' | 'resume';
|
|
237
237
|
} | {
|
|
238
238
|
type: 'task-description';
|
|
239
239
|
description: string;
|
|
@@ -252,10 +252,12 @@ interface ShadowStatus {
|
|
|
252
252
|
nodeCount: number;
|
|
253
253
|
eventCount: number;
|
|
254
254
|
taskCount: number;
|
|
255
|
+
sopCount: number;
|
|
255
256
|
pendingPrompts: number;
|
|
256
257
|
autoSave: boolean;
|
|
257
258
|
mode: 'foreground' | 'daemon';
|
|
258
259
|
agentActive: boolean;
|
|
260
|
+
paused: boolean;
|
|
259
261
|
cyclesRun: number;
|
|
260
262
|
cyclesSkipped: number;
|
|
261
263
|
}
|
|
@@ -316,6 +318,12 @@ declare class CartographyDB {
|
|
|
316
318
|
id: string;
|
|
317
319
|
workflowId: string;
|
|
318
320
|
}>;
|
|
321
|
+
markTaskAsSOPCandidate(taskId: string): void;
|
|
322
|
+
getAllSOPs(): Array<SOP & {
|
|
323
|
+
id: string;
|
|
324
|
+
workflowId: string;
|
|
325
|
+
generatedAt: string;
|
|
326
|
+
}>;
|
|
319
327
|
setApproval(pattern: string, action: 'save' | 'ignore' | 'auto'): void;
|
|
320
328
|
getApproval(pattern: string): string | undefined;
|
|
321
329
|
getStats(sessionId: string): {
|
|
@@ -365,9 +373,14 @@ declare function exportBackstageYAML(nodes: NodeRow[], edges: EdgeRow[], org?: s
|
|
|
365
373
|
declare function exportJSON(db: CartographyDB, sessionId: string): string;
|
|
366
374
|
declare function exportHTML(nodes: NodeRow[], edges: EdgeRow[]): string;
|
|
367
375
|
declare function exportSOPMarkdown(sop: SOP): string;
|
|
376
|
+
declare function exportSOPDashboard(sops: Array<SOP & {
|
|
377
|
+
id: string;
|
|
378
|
+
workflowId: string;
|
|
379
|
+
generatedAt?: string;
|
|
380
|
+
}>): string;
|
|
368
381
|
declare function exportAll(db: CartographyDB, sessionId: string, outputDir: string, formats?: string[]): void;
|
|
369
382
|
|
|
370
383
|
declare function checkPrerequisites(): void;
|
|
371
384
|
declare function checkPollInterval(intervalMs: number): number;
|
|
372
385
|
|
|
373
|
-
export { type ActivityEvent, type CartographyConfig, CartographyDB, type ClientMessage, type DaemonMessage, type DiscoveryEdge, type DiscoveryEvent, type DiscoveryNode, EDGE_RELATIONSHIPS, EVENT_TYPES, type EdgeRelationship, type EdgeRow, EdgeSchema, type EventRow, EventSchema, type EventType, MIN_POLL_INTERVAL_MS, NODE_TYPES, type NodeRow, NodeSchema, type NodeType, type PendingPrompt, type SOP, SOPSchema, type SOPStep, SOPStepSchema, type SessionRow, type ShadowStatus, type TaskRow, type WorkflowRow, checkPollInterval, checkPrerequisites, createCartographyTools, CartographyDB as default, defaultConfig, exportAll, exportBackstageYAML, exportHTML, exportJSON, exportSOPMarkdown, generateDependencyMermaid, generateSOPs, generateTopologyMermaid, generateWorkflowMermaid, runDiscovery, runShadowCycle, safetyHook, stripSensitive };
|
|
386
|
+
export { type ActivityEvent, type CartographyConfig, CartographyDB, type ClientMessage, type DaemonMessage, type DiscoveryEdge, type DiscoveryEvent, type DiscoveryNode, EDGE_RELATIONSHIPS, EVENT_TYPES, type EdgeRelationship, type EdgeRow, EdgeSchema, type EventRow, EventSchema, type EventType, MIN_POLL_INTERVAL_MS, NODE_TYPES, type NodeRow, NodeSchema, type NodeType, type PendingPrompt, type SOP, SOPSchema, type SOPStep, SOPStepSchema, type SessionRow, type ShadowStatus, type TaskRow, type WorkflowRow, checkPollInterval, checkPrerequisites, createCartographyTools, CartographyDB as default, defaultConfig, exportAll, exportBackstageYAML, exportHTML, exportJSON, exportSOPDashboard, exportSOPMarkdown, generateDependencyMermaid, generateSOPs, generateTopologyMermaid, generateWorkflowMermaid, runDiscovery, runShadowCycle, safetyHook, stripSensitive };
|
package/dist/index.js
CHANGED
|
@@ -385,6 +385,24 @@ var CartographyDB = class {
|
|
|
385
385
|
confidence: r["confidence"]
|
|
386
386
|
}));
|
|
387
387
|
}
|
|
388
|
+
markTaskAsSOPCandidate(taskId) {
|
|
389
|
+
this.db.prepare("UPDATE tasks SET is_sop_candidate = 1 WHERE id = ?").run(taskId);
|
|
390
|
+
}
|
|
391
|
+
getAllSOPs() {
|
|
392
|
+
const rows = this.db.prepare("SELECT * FROM sops ORDER BY generated_at DESC").all();
|
|
393
|
+
return rows.map((r) => ({
|
|
394
|
+
id: r["id"],
|
|
395
|
+
workflowId: r["workflow_id"],
|
|
396
|
+
title: r["title"],
|
|
397
|
+
description: r["description"],
|
|
398
|
+
steps: JSON.parse(r["steps"]),
|
|
399
|
+
involvedSystems: JSON.parse(r["involved_systems"]),
|
|
400
|
+
estimatedDuration: r["estimated_duration"],
|
|
401
|
+
frequency: r["frequency"],
|
|
402
|
+
confidence: r["confidence"],
|
|
403
|
+
generatedAt: r["generated_at"]
|
|
404
|
+
}));
|
|
405
|
+
}
|
|
388
406
|
// ── Approvals ───────────────────────────
|
|
389
407
|
setApproval(pattern, action) {
|
|
390
408
|
this.db.prepare(`
|
|
@@ -735,6 +753,124 @@ async function createCartographyTools(db, sessionId, opts = {}) {
|
|
|
735
753
|
}]
|
|
736
754
|
};
|
|
737
755
|
}),
|
|
756
|
+
tool("scan_k8s_resources", "Kubernetes-Cluster via kubectl scannen \u2014 100% readonly (get, describe)", {
|
|
757
|
+
namespace: z2.string().optional().describe("Namespace filtern \u2014 leer = alle Namespaces")
|
|
758
|
+
}, async (args) => {
|
|
759
|
+
const { execSync: execSync2 } = await import("child_process");
|
|
760
|
+
const ns = args["namespace"];
|
|
761
|
+
const nsFlag = ns ? `-n ${ns}` : "--all-namespaces";
|
|
762
|
+
const run = (cmd) => {
|
|
763
|
+
try {
|
|
764
|
+
return execSync2(cmd, { stdio: "pipe", timeout: 15e3, shell: "/bin/sh" }).toString().trim();
|
|
765
|
+
} catch (e) {
|
|
766
|
+
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
const sections = [
|
|
770
|
+
["CONTEXT", 'kubectl config current-context 2>/dev/null || echo "(kein Context gesetzt)"'],
|
|
771
|
+
["NODES", "kubectl get nodes -o wide"],
|
|
772
|
+
["NAMESPACES", "kubectl get namespaces"],
|
|
773
|
+
["SERVICES", `kubectl get services ${nsFlag}`],
|
|
774
|
+
["DEPLOYMENTS", `kubectl get deployments ${nsFlag}`],
|
|
775
|
+
["STATEFULSETS", `kubectl get statefulsets ${nsFlag}`],
|
|
776
|
+
["INGRESSES", `kubectl get ingress ${nsFlag} 2>/dev/null || echo "(keine)"`],
|
|
777
|
+
["PODS_RUNNING", `kubectl get pods ${nsFlag} --field-selector=status.phase=Running 2>/dev/null | head -60`],
|
|
778
|
+
["CONFIGMAPS_SYSTEM", "kubectl get configmaps -n kube-system 2>/dev/null | head -30"]
|
|
779
|
+
];
|
|
780
|
+
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
781
|
+
${run(c)}`).join("\n\n");
|
|
782
|
+
return { content: [{ type: "text", text: out }] };
|
|
783
|
+
}),
|
|
784
|
+
tool("scan_aws_resources", "AWS-Infrastruktur via AWS CLI scannen \u2014 100% readonly (describe, list)", {
|
|
785
|
+
region: z2.string().optional().describe("AWS Region \u2014 default: AWS_DEFAULT_REGION oder Profil"),
|
|
786
|
+
profile: z2.string().optional().describe("AWS CLI Profil")
|
|
787
|
+
}, async (args) => {
|
|
788
|
+
const { execSync: execSync2 } = await import("child_process");
|
|
789
|
+
const region = args["region"];
|
|
790
|
+
const profile = args["profile"];
|
|
791
|
+
const env = { ...process.env };
|
|
792
|
+
if (region) env["AWS_DEFAULT_REGION"] = region;
|
|
793
|
+
const pf = profile ? `--profile ${profile}` : "";
|
|
794
|
+
const run = (cmd) => {
|
|
795
|
+
try {
|
|
796
|
+
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh", env }).toString().trim();
|
|
797
|
+
} catch (e) {
|
|
798
|
+
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
const sections = [
|
|
802
|
+
["IDENTITY", `aws sts get-caller-identity ${pf} --output json`],
|
|
803
|
+
["EC2", `aws ec2 describe-instances ${pf} --query 'Reservations[*].Instances[*].[InstanceId,InstanceType,State.Name,PublicIpAddress,PrivateIpAddress,Tags[?Key==\`Name\`].Value|[0]]' --output table`],
|
|
804
|
+
["RDS", `aws rds describe-db-instances ${pf} --query 'DBInstances[*].[DBInstanceIdentifier,Engine,DBInstanceStatus,Endpoint.Address,Endpoint.Port]' --output table`],
|
|
805
|
+
["ELB_V2", `aws elbv2 describe-load-balancers ${pf} --query 'LoadBalancers[*].[LoadBalancerName,DNSName,Type,State.Code]' --output table`],
|
|
806
|
+
["EKS", `aws eks list-clusters ${pf} --output json`],
|
|
807
|
+
["ELASTICACHE", `aws elasticache describe-cache-clusters ${pf} --query 'CacheClusters[*].[CacheClusterId,Engine,CacheClusterStatus]' --output table 2>/dev/null || echo "(nicht verf\xFCgbar)"`],
|
|
808
|
+
["S3", `aws s3 ls ${pf} 2>/dev/null || echo "(nicht verf\xFCgbar)"`],
|
|
809
|
+
["VPC", `aws ec2 describe-vpcs ${pf} --query 'Vpcs[*].[VpcId,CidrBlock,IsDefault,Tags[?Key==\`Name\`].Value|[0]]' --output table`]
|
|
810
|
+
];
|
|
811
|
+
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
812
|
+
${run(c)}`).join("\n\n");
|
|
813
|
+
return { content: [{ type: "text", text: out }] };
|
|
814
|
+
}),
|
|
815
|
+
tool("scan_gcp_resources", "Google Cloud Platform via gcloud CLI scannen \u2014 100% readonly (list, describe)", {
|
|
816
|
+
project: z2.string().optional().describe("GCP Project ID \u2014 default: aktuelles gcloud-Projekt")
|
|
817
|
+
}, async (args) => {
|
|
818
|
+
const { execSync: execSync2 } = await import("child_process");
|
|
819
|
+
const project = args["project"];
|
|
820
|
+
const pf = project ? `--project ${project}` : "";
|
|
821
|
+
const run = (cmd) => {
|
|
822
|
+
try {
|
|
823
|
+
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh" }).toString().trim();
|
|
824
|
+
} catch (e) {
|
|
825
|
+
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
const sections = [
|
|
829
|
+
["IDENTITY", `gcloud config list account --format='value(core.account)' 2>/dev/null; gcloud config get-value project 2>/dev/null`],
|
|
830
|
+
["COMPUTE_INSTANCES", `gcloud compute instances list ${pf} 2>/dev/null || echo "(error)"`],
|
|
831
|
+
["SQL_INSTANCES", `gcloud sql instances list ${pf} 2>/dev/null || echo "(error)"`],
|
|
832
|
+
["GKE_CLUSTERS", `gcloud container clusters list ${pf} 2>/dev/null || echo "(error)"`],
|
|
833
|
+
["CLOUD_RUN", `gcloud run services list ${pf} --platform managed 2>/dev/null || echo "(error)"`],
|
|
834
|
+
["CLOUD_FUNCTIONS", `gcloud functions list ${pf} 2>/dev/null || echo "(error)"`],
|
|
835
|
+
["REDIS", `gcloud redis instances list ${pf} --regions=- 2>/dev/null || echo "(error)"`],
|
|
836
|
+
["PUBSUB", `gcloud pubsub topics list ${pf} 2>/dev/null || echo "(error)"`],
|
|
837
|
+
["SPANNER", `gcloud spanner instances list ${pf} 2>/dev/null || echo "(error)"`]
|
|
838
|
+
];
|
|
839
|
+
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
840
|
+
${run(c)}`).join("\n\n");
|
|
841
|
+
return { content: [{ type: "text", text: out }] };
|
|
842
|
+
}),
|
|
843
|
+
tool("scan_azure_resources", "Azure-Infrastruktur via az CLI scannen \u2014 100% readonly (list, show)", {
|
|
844
|
+
subscription: z2.string().optional().describe("Azure Subscription ID"),
|
|
845
|
+
resourceGroup: z2.string().optional().describe("Resource Group filtern")
|
|
846
|
+
}, async (args) => {
|
|
847
|
+
const { execSync: execSync2 } = await import("child_process");
|
|
848
|
+
const sub = args["subscription"];
|
|
849
|
+
const rg = args["resourceGroup"];
|
|
850
|
+
const sf = sub ? `--subscription ${sub}` : "";
|
|
851
|
+
const rf = rg ? `--resource-group ${rg}` : "";
|
|
852
|
+
const run = (cmd) => {
|
|
853
|
+
try {
|
|
854
|
+
return execSync2(cmd, { stdio: "pipe", timeout: 2e4, shell: "/bin/sh" }).toString().trim();
|
|
855
|
+
} catch (e) {
|
|
856
|
+
return `(error: ${e instanceof Error ? e.message.split("\n")[0] : String(e)})`;
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
const sections = [
|
|
860
|
+
["IDENTITY", `az account show --output json ${sf} 2>/dev/null || echo "(nicht eingeloggt \u2014 az login)"`],
|
|
861
|
+
["VMS", `az vm list ${sf} ${rf} --output table 2>/dev/null || echo "(error)"`],
|
|
862
|
+
["AKS", `az aks list ${sf} ${rf} --output table 2>/dev/null || echo "(error)"`],
|
|
863
|
+
["SQL_SERVERS", `az sql server list ${sf} ${rf} --output table 2>/dev/null || echo "(error)"`],
|
|
864
|
+
["POSTGRES", `az postgres server list ${sf} ${rf} --output table 2>/dev/null || echo "(error)"`],
|
|
865
|
+
["REDIS", `az redis list ${sf} ${rf} --output table 2>/dev/null || echo "(error)"`],
|
|
866
|
+
["WEBAPPS", `az webapp list ${sf} ${rf} --output table 2>/dev/null || echo "(error)"`],
|
|
867
|
+
["CONTAINER_APPS", `az containerapp list ${sf} ${rf} --output table 2>/dev/null || echo "(error)"`],
|
|
868
|
+
["FUNCTIONS", `az functionapp list ${sf} ${rf} --output table 2>/dev/null || echo "(error)"`]
|
|
869
|
+
];
|
|
870
|
+
const out = sections.map(([l, c]) => `=== ${l} ===
|
|
871
|
+
${run(c)}`).join("\n\n");
|
|
872
|
+
return { content: [{ type: "text", text: out }] };
|
|
873
|
+
}),
|
|
738
874
|
tool("save_sop", "Standard Operating Procedure speichern", {
|
|
739
875
|
workflowId: z2.string(),
|
|
740
876
|
title: z2.string(),
|
|
@@ -806,15 +942,22 @@ SCHRITT 2 \u2014 Lokale Infrastruktur:
|
|
|
806
942
|
ss -tlnp && ps aux \u2192 alle lauschenden Ports/Prozesse identifizieren
|
|
807
943
|
Jeden Service vertiefen: DB\u2192Schemas, API\u2192Endpoints, Queue\u2192Topics
|
|
808
944
|
|
|
809
|
-
SCHRITT 3 \u2014
|
|
945
|
+
SCHRITT 3 \u2014 Cloud & Kubernetes (falls CLI vorhanden):
|
|
946
|
+
scan_k8s_resources() \u2192 Nodes, Services, Pods, Deployments, Ingresses
|
|
947
|
+
scan_aws_resources() \u2192 EC2, RDS, ELB, EKS, ElastiCache, S3 (falls AWS CLI + Credentials)
|
|
948
|
+
scan_gcp_resources() \u2192 Compute, SQL, GKE, Cloud Run, Functions (falls gcloud + Auth)
|
|
949
|
+
scan_azure_resources() \u2192 VMs, AKS, SQL, Redis, WebApps (falls az CLI + Login)
|
|
950
|
+
Fehler / "nicht verf\xFCgbar" \u2192 ignorieren, weiter mit n\xE4chstem Tool
|
|
951
|
+
|
|
952
|
+
SCHRITT 4 \u2014 Config-Files:
|
|
810
953
|
.env, docker-compose.yml, application.yml, kubernetes/*.yml
|
|
811
954
|
Nur Host:Port extrahieren \u2014 KEINE Credentials
|
|
812
955
|
|
|
813
|
-
SCHRITT
|
|
956
|
+
SCHRITT 5 \u2014 R\xFCckfragen bei Unklarheit:
|
|
814
957
|
ask_user() nutzen wenn: Dienst unklar ist, Kontext fehlt, oder User Input sinnvoll w\xE4re
|
|
815
958
|
Beispiele: "Welche Umgebung ist das (dev/staging/prod)?", "Ist <host> ein internes Tool?"
|
|
816
959
|
|
|
817
|
-
SCHRITT
|
|
960
|
+
SCHRITT 6 \u2014 Fertig wenn alle Spuren ersch\xF6pft.
|
|
818
961
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
819
962
|
|
|
820
963
|
PORT-MAPPING: 5432=postgres, 3306=mysql, 27017=mongodb, 6379=redis,
|
|
@@ -849,6 +992,10 @@ Nutze ask_user wenn du Kontext vom User brauchst.`;
|
|
|
849
992
|
"mcp__cartograph__save_edge",
|
|
850
993
|
"mcp__cartograph__get_catalog",
|
|
851
994
|
"mcp__cartograph__scan_bookmarks",
|
|
995
|
+
"mcp__cartograph__scan_k8s_resources",
|
|
996
|
+
"mcp__cartograph__scan_aws_resources",
|
|
997
|
+
"mcp__cartograph__scan_gcp_resources",
|
|
998
|
+
"mcp__cartograph__scan_azure_resources",
|
|
852
999
|
"mcp__cartograph__ask_user"
|
|
853
1000
|
],
|
|
854
1001
|
hooks: {
|
|
@@ -1400,6 +1547,197 @@ function exportSOPMarkdown(sop) {
|
|
|
1400
1547
|
}
|
|
1401
1548
|
return lines.join("\n");
|
|
1402
1549
|
}
|
|
1550
|
+
function exportSOPDashboard(sops) {
|
|
1551
|
+
const sopsJson = JSON.stringify(sops.map((s) => ({
|
|
1552
|
+
id: s.id,
|
|
1553
|
+
title: s.title,
|
|
1554
|
+
description: s.description,
|
|
1555
|
+
steps: s.steps,
|
|
1556
|
+
systems: s.involvedSystems,
|
|
1557
|
+
duration: s.estimatedDuration,
|
|
1558
|
+
frequency: s.frequency,
|
|
1559
|
+
confidence: s.confidence,
|
|
1560
|
+
generatedAt: s.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1561
|
+
})));
|
|
1562
|
+
const systemCount = {};
|
|
1563
|
+
for (const sop of sops) {
|
|
1564
|
+
for (const sys of sop.involvedSystems) {
|
|
1565
|
+
systemCount[sys] = (systemCount[sys] ?? 0) + 1;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
const systemsJson = JSON.stringify(
|
|
1569
|
+
Object.entries(systemCount).sort((a, b) => b[1] - a[1])
|
|
1570
|
+
);
|
|
1571
|
+
return `<!DOCTYPE html>
|
|
1572
|
+
<html lang="de">
|
|
1573
|
+
<head>
|
|
1574
|
+
<meta charset="UTF-8">
|
|
1575
|
+
<title>Cartography \u2014 SOP Dashboard</title>
|
|
1576
|
+
<style>
|
|
1577
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
1578
|
+
body {
|
|
1579
|
+
background: #0d1117; color: #e6edf3;
|
|
1580
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace;
|
|
1581
|
+
padding: 0; line-height: 1.6;
|
|
1582
|
+
}
|
|
1583
|
+
.header {
|
|
1584
|
+
background: linear-gradient(135deg, #161b22 0%, #1a1f2e 100%);
|
|
1585
|
+
border-bottom: 1px solid #30363d; padding: 32px 40px;
|
|
1586
|
+
}
|
|
1587
|
+
.header h1 { font-size: 24px; color: #58a6ff; margin-bottom: 8px; }
|
|
1588
|
+
.header .subtitle { color: #8b949e; font-size: 14px; }
|
|
1589
|
+
.stats-row {
|
|
1590
|
+
display: flex; gap: 24px; margin-top: 16px; flex-wrap: wrap;
|
|
1591
|
+
}
|
|
1592
|
+
.stat-card {
|
|
1593
|
+
background: #21262d; border: 1px solid #30363d; border-radius: 8px;
|
|
1594
|
+
padding: 12px 20px; min-width: 140px;
|
|
1595
|
+
}
|
|
1596
|
+
.stat-card .value { font-size: 28px; font-weight: 700; color: #58a6ff; }
|
|
1597
|
+
.stat-card .label { font-size: 11px; color: #8b949e; text-transform: uppercase; letter-spacing: 0.5px; }
|
|
1598
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 24px 40px; }
|
|
1599
|
+
.section-title { font-size: 18px; color: #c9d1d9; margin: 32px 0 16px; border-bottom: 1px solid #21262d; padding-bottom: 8px; }
|
|
1600
|
+
/* Systems bar chart */
|
|
1601
|
+
.systems-grid { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 24px; }
|
|
1602
|
+
.sys-tag {
|
|
1603
|
+
background: #21262d; border: 1px solid #30363d; border-radius: 6px;
|
|
1604
|
+
padding: 6px 12px; font-size: 12px; cursor: default;
|
|
1605
|
+
}
|
|
1606
|
+
.sys-tag .count { color: #58a6ff; font-weight: 600; margin-left: 4px; }
|
|
1607
|
+
/* SOP cards */
|
|
1608
|
+
.sop-card {
|
|
1609
|
+
background: #161b22; border: 1px solid #30363d; border-radius: 8px;
|
|
1610
|
+
margin-bottom: 16px; overflow: hidden; transition: border-color 0.2s;
|
|
1611
|
+
}
|
|
1612
|
+
.sop-card:hover { border-color: #58a6ff; }
|
|
1613
|
+
.sop-header {
|
|
1614
|
+
padding: 16px 20px; cursor: pointer; display: flex;
|
|
1615
|
+
justify-content: space-between; align-items: center;
|
|
1616
|
+
}
|
|
1617
|
+
.sop-header h3 { font-size: 16px; color: #e6edf3; }
|
|
1618
|
+
.sop-meta { display: flex; gap: 16px; align-items: center; font-size: 12px; color: #8b949e; }
|
|
1619
|
+
.sop-meta .freq { color: #3fb950; font-weight: 600; }
|
|
1620
|
+
.sop-meta .dur { color: #d29922; }
|
|
1621
|
+
.sop-meta .conf {
|
|
1622
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
1623
|
+
}
|
|
1624
|
+
.conf-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
|
|
1625
|
+
.sop-body { display: none; padding: 0 20px 20px; border-top: 1px solid #21262d; }
|
|
1626
|
+
.sop-body.open { display: block; padding-top: 16px; }
|
|
1627
|
+
.sop-desc { color: #8b949e; font-size: 13px; margin-bottom: 12px; }
|
|
1628
|
+
.sop-systems { margin-bottom: 12px; }
|
|
1629
|
+
.sop-systems span { background: #0d419d33; color: #58a6ff; border-radius: 4px; padding: 2px 8px; font-size: 11px; margin-right: 4px; }
|
|
1630
|
+
.steps-list { list-style: none; counter-reset: step; }
|
|
1631
|
+
.steps-list li {
|
|
1632
|
+
counter-increment: step; position: relative;
|
|
1633
|
+
padding: 10px 12px 10px 44px; border-left: 2px solid #30363d;
|
|
1634
|
+
margin-left: 14px; font-size: 13px;
|
|
1635
|
+
}
|
|
1636
|
+
.steps-list li:last-child { border-left-color: transparent; }
|
|
1637
|
+
.steps-list li::before {
|
|
1638
|
+
content: counter(step);
|
|
1639
|
+
position: absolute; left: -14px; top: 8px;
|
|
1640
|
+
width: 26px; height: 26px; border-radius: 50%;
|
|
1641
|
+
background: #21262d; border: 2px solid #30363d;
|
|
1642
|
+
display: flex; align-items: center; justify-content: center;
|
|
1643
|
+
font-size: 12px; font-weight: 600; color: #58a6ff;
|
|
1644
|
+
}
|
|
1645
|
+
.step-tool { color: #d2a8ff; font-weight: 600; }
|
|
1646
|
+
.step-target { color: #7ee787; font-size: 12px; }
|
|
1647
|
+
.step-notes { color: #8b949e; font-style: italic; font-size: 12px; margin-top: 2px; }
|
|
1648
|
+
.step-instr { color: #c9d1d9; }
|
|
1649
|
+
.toggle-icon { color: #8b949e; font-size: 18px; transition: transform 0.2s; }
|
|
1650
|
+
.toggle-icon.open { transform: rotate(90deg); }
|
|
1651
|
+
.empty { color: #484f58; font-size: 14px; padding: 40px; text-align: center; }
|
|
1652
|
+
.gen-time { color: #484f58; font-size: 11px; margin-top: 8px; }
|
|
1653
|
+
</style>
|
|
1654
|
+
</head>
|
|
1655
|
+
<body>
|
|
1656
|
+
<div class="header">
|
|
1657
|
+
<h1>SOP Dashboard</h1>
|
|
1658
|
+
<div class="subtitle">Datasynx Cartography \u2014 Standard Operating Procedures</div>
|
|
1659
|
+
<div class="stats-row">
|
|
1660
|
+
<div class="stat-card"><div class="value" id="sop-count">0</div><div class="label">SOPs</div></div>
|
|
1661
|
+
<div class="stat-card"><div class="value" id="step-count">0</div><div class="label">Total Steps</div></div>
|
|
1662
|
+
<div class="stat-card"><div class="value" id="sys-count">0</div><div class="label">Systems</div></div>
|
|
1663
|
+
<div class="stat-card"><div class="value" id="avg-conf">\u2014</div><div class="label">Avg Confidence</div></div>
|
|
1664
|
+
</div>
|
|
1665
|
+
</div>
|
|
1666
|
+
<div class="container">
|
|
1667
|
+
<h2 class="section-title">Beteiligte Systeme</h2>
|
|
1668
|
+
<div class="systems-grid" id="systems"></div>
|
|
1669
|
+
|
|
1670
|
+
<h2 class="section-title">SOPs</h2>
|
|
1671
|
+
<div id="sop-list"></div>
|
|
1672
|
+
</div>
|
|
1673
|
+
<script>
|
|
1674
|
+
const sops = ${sopsJson};
|
|
1675
|
+
const systems = ${systemsJson};
|
|
1676
|
+
|
|
1677
|
+
document.getElementById('sop-count').textContent = sops.length;
|
|
1678
|
+
document.getElementById('step-count').textContent = sops.reduce((a, s) => a + s.steps.length, 0);
|
|
1679
|
+
document.getElementById('sys-count').textContent = systems.length;
|
|
1680
|
+
const avgConf = sops.length > 0
|
|
1681
|
+
? (sops.reduce((a, s) => a + s.confidence, 0) / sops.length * 100).toFixed(0) + '%'
|
|
1682
|
+
: '\u2014';
|
|
1683
|
+
document.getElementById('avg-conf').textContent = avgConf;
|
|
1684
|
+
|
|
1685
|
+
const sysDiv = document.getElementById('systems');
|
|
1686
|
+
systems.forEach(([name, count]) => {
|
|
1687
|
+
const el = document.createElement('div');
|
|
1688
|
+
el.className = 'sys-tag';
|
|
1689
|
+
el.innerHTML = name + '<span class="count">x' + count + '</span>';
|
|
1690
|
+
sysDiv.appendChild(el);
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
const listDiv = document.getElementById('sop-list');
|
|
1694
|
+
if (sops.length === 0) {
|
|
1695
|
+
listDiv.innerHTML = '<div class="empty">Keine SOPs vorhanden. Shadow-Daemon starten und Workflows beobachten.</div>';
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
sops.forEach((sop, i) => {
|
|
1699
|
+
const confColor = sop.confidence >= 0.8 ? '#3fb950' : sop.confidence >= 0.5 ? '#d29922' : '#f85149';
|
|
1700
|
+
const card = document.createElement('div');
|
|
1701
|
+
card.className = 'sop-card';
|
|
1702
|
+
card.innerHTML = \`
|
|
1703
|
+
<div class="sop-header" onclick="toggle(\${i})">
|
|
1704
|
+
<h3>\${sop.title}</h3>
|
|
1705
|
+
<div class="sop-meta">
|
|
1706
|
+
<span class="freq">\${sop.frequency}</span>
|
|
1707
|
+
<span class="dur">\${sop.duration}</span>
|
|
1708
|
+
<span class="conf"><span class="conf-dot" style="background:\${confColor}"></span>\${Math.round(sop.confidence*100)}%</span>
|
|
1709
|
+
<span class="toggle-icon" id="icon-\${i}">\u25B8</span>
|
|
1710
|
+
</div>
|
|
1711
|
+
</div>
|
|
1712
|
+
<div class="sop-body" id="body-\${i}">
|
|
1713
|
+
<div class="sop-desc">\${sop.description}</div>
|
|
1714
|
+
<div class="sop-systems">\${sop.systems.map(s => '<span>'+s+'</span>').join('')}</div>
|
|
1715
|
+
<ol class="steps-list">
|
|
1716
|
+
\${sop.steps.map(st => \`
|
|
1717
|
+
<li>
|
|
1718
|
+
<span class="step-tool">\${st.tool}</span>
|
|
1719
|
+
\${st.target ? '<span class="step-target"> \u2192 '+st.target+'</span>' : ''}
|
|
1720
|
+
<div class="step-instr">\${st.instruction}</div>
|
|
1721
|
+
\${st.notes ? '<div class="step-notes">'+st.notes+'</div>' : ''}
|
|
1722
|
+
</li>
|
|
1723
|
+
\`).join('')}
|
|
1724
|
+
</ol>
|
|
1725
|
+
<div class="gen-time">Generiert: \${sop.generatedAt ? sop.generatedAt.substring(0,19).replace('T',' ') : '\u2014'}</div>
|
|
1726
|
+
</div>
|
|
1727
|
+
\`;
|
|
1728
|
+
listDiv.appendChild(card);
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
function toggle(i) {
|
|
1732
|
+
const body = document.getElementById('body-'+i);
|
|
1733
|
+
const icon = document.getElementById('icon-'+i);
|
|
1734
|
+
body.classList.toggle('open');
|
|
1735
|
+
icon.classList.toggle('open');
|
|
1736
|
+
}
|
|
1737
|
+
</script>
|
|
1738
|
+
</body>
|
|
1739
|
+
</html>`;
|
|
1740
|
+
}
|
|
1403
1741
|
function exportAll(db, sessionId, outputDir, formats = ["mermaid", "json", "yaml", "html", "sops"]) {
|
|
1404
1742
|
mkdirSync2(outputDir, { recursive: true });
|
|
1405
1743
|
mkdirSync2(join2(outputDir, "sops"), { recursive: true });
|
|
@@ -1496,6 +1834,7 @@ export {
|
|
|
1496
1834
|
exportBackstageYAML,
|
|
1497
1835
|
exportHTML,
|
|
1498
1836
|
exportJSON,
|
|
1837
|
+
exportSOPDashboard,
|
|
1499
1838
|
exportSOPMarkdown,
|
|
1500
1839
|
generateDependencyMermaid,
|
|
1501
1840
|
generateSOPs,
|