update skills

This commit is contained in:
Oliver Kieselbach
2026-03-19 16:44:12 +01:00
parent 361b2a88b8
commit d41ef75040
5 changed files with 0 additions and 655 deletions
-176
View File
@@ -1,176 +0,0 @@
# Backend Logs Skill
Du analysierst Azure Functions Backend-Logs (Application Insights) um Probleme zu debuggen. Du baust KQL-Queries, fuehrst sie aus und korrelierst die Ergebnisse mit dem Source Code.
## Input
`$ARGUMENTS` enthaelt entweder:
- Eine Beschreibung des Problems (z.B. "warum schlagen die ingest calls fehl")
- Einen Funktionsnamen (z.B. "IngestEventsFunction" oder "IngestEvents")
- Einen Zeitraum und/oder spezifischen Fehler (z.B. "errors letzte Stunde")
- Leer = Health-Overview der letzten Stunde
## Hilfsskript
Nutze das Helper-Script `.claude/scripts/query-appinsights.sh` fuer alle Abfragen:
```bash
.claude/scripts/query-appinsights.sh "<KQL-Query>" [timespan]
```
- Timespan-Format: ISO 8601 Duration (z.B. PT1H, PT24H, P7D)
- Default-Timespan: PT1H (letzte Stunde)
- Das Script nutzt `$AUTOPILOT_MONITOR_APPINSIGHTS_ID` fuer die App Insights App-ID
- Ergebnis ist ein JSON-Array mit Objekten (Spaltenname → Wert)
## Wichtig: Datenmodell
Die `requests`-Tabelle ist bei diesem Projekt leer (.NET isolated worker Eigenheit). Alle Function-Ausfuehrungsdaten liegen in der `traces`-Tabelle:
- **FunctionStarted**: `message` beginnt mit `Executing 'Functions.<Name>'`
- **FunctionCompleted**: `message` beginnt mit `Executed 'Functions.<Name>'` und enthaelt `(Succeeded, ...)` oder `(Failed, ...)`
- `customDimensions` enthaelt JSON mit `prop__functionName`, `prop__status`, `prop__executionDuration`, `prop__invocationId`
- `operation_Name` = Function-Name (ohne "Functions." Prefix)
## Schritt 1: Problem verstehen und Strategie waehlen
Analysiere `$ARGUMENTS` und waehle die passende Strategie:
### Strategie A: Fehler-Analyse (User beschreibt ein Problem)
→ Weiter mit Schritt 2a
### Strategie B: Funktions-Analyse (User nennt eine Function)
→ Weiter mit Schritt 2b
### Strategie C: Allgemeine Uebersicht (Input leer oder User will Status sehen)
→ Weiter mit Schritt 2c
## Schritt 2a: Fehler-Uebersicht
### Exceptions pruefen:
```bash
.claude/scripts/query-appinsights.sh "exceptions | where timestamp > ago(1h) | summarize count() by outerMessage, problemId | order by count_ desc | take 20"
```
Falls leer, Zeitraum auf 24h erweitern:
```bash
.claude/scripts/query-appinsights.sh "exceptions | where timestamp > ago(24h) | summarize count() by outerMessage, problemId | order by count_ desc | take 20" PT24H
```
### Details zum relevantesten Fehler:
```bash
.claude/scripts/query-appinsights.sh "exceptions | where timestamp > ago(1h) | where outerMessage contains '<fehler_text>' | project timestamp, outerMessage, innermostMessage, details[0].rawStack, operation_Id | order by timestamp desc | take 5"
```
### Fehlgeschlagene Functions:
```bash
.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message startswith 'Executed' | where message contains 'Failed' | project timestamp, message, operation_Name, operation_Id, customDimensions | order by timestamp desc | take 20"
```
### Warnings und Errors in Traces:
```bash
.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where severityLevel >= 2 | summarize count() by message, severityLevel | order by count_ desc | take 20"
```
## Schritt 2b: Function-Requests analysieren
Uebersicht einer Function (Erfolge vs Fehler):
```bash
.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message startswith 'Executed' | where operation_Name == '<function_name>' | where message contains 'Succeeded' or message contains 'Failed' | extend status = iif(message contains 'Succeeded', 'Succeeded', 'Failed') | extend durationMs = toint(extract('Duration=([0-9]+)ms', 1, message)) | summarize total=count(), succeeded=countif(status == 'Succeeded'), failed=countif(status == 'Failed'), avgMs=avg(durationMs), p95Ms=percentile(durationMs, 95) by operation_Name"
```
Letzte fehlgeschlagene Ausfuehrungen:
```bash
.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message startswith 'Executed' | where operation_Name == '<function_name>' | where message contains 'Failed' | project timestamp, message, operation_Id | order by timestamp desc | take 10"
```
Alle Traces einer Function (letzte Ausfuehrungen):
```bash
.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where operation_Name == '<function_name>' | project timestamp, message, severityLevel, operation_Id | order by timestamp desc | take 50"
```
## Schritt 2c: Health-Overview
Alle Functions mit Erfolgsrate:
```bash
.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message startswith 'Executed' | where message contains 'Succeeded' or message contains 'Failed' | extend status = iif(message contains 'Succeeded', 'Succeeded', 'Failed') | extend durationMs = toint(extract('Duration=([0-9]+)ms', 1, message)) | summarize total=count(), failed=countif(status == 'Failed'), avgMs=avg(durationMs) by operation_Name | extend failRate=round(100.0 * failed / total, 1) | order by failed desc, total desc | take 30"
```
## Schritt 3: Tiefere Analyse mit Operation-ID
Wenn du eine operation_Id hast, hole ALLE Telemetrie dazu:
```bash
.claude/scripts/query-appinsights.sh "union traces, exceptions, dependencies | where operation_Id == '<op_id>' | order by timestamp asc | project timestamp, itemType, message, outerMessage, severityLevel, operation_Name, customDimensions" PT24H
```
## Schritt 4: Trace-Logs durchsuchen
Nach Suchbegriff:
```bash
.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message contains '<suchbegriff>' | project timestamp, message, severityLevel, operation_Name, operation_Id | order by timestamp desc | take 30"
```
Severity-Level: 0=Verbose, 1=Information, 2=Warning, 3=Error, 4=Critical
## Schritt 5: Dependency-Analyse (externe Aufrufe)
```bash
.claude/scripts/query-appinsights.sh "dependencies | where timestamp > ago(1h) | where success == false | summarize count() by name, type, target, resultCode | order by count_ desc | take 20"
```
## Schritt 6: Zeitreihen-Analyse (Pattern erkennen)
```bash
.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(24h) | where message startswith 'Executed' | where operation_Name == '<function_name>' | where message contains 'Succeeded' or message contains 'Failed' | extend status = iif(message contains 'Succeeded', 'Succeeded', 'Failed') | summarize total=count(), errors=countif(status == 'Failed') by bin(timestamp, 15m) | order by timestamp asc" PT24H
```
## Schritt 7: Code-Korrelation
Nachdem du die Fehler identifiziert hast, lies den relevanten Code:
### Backend Functions (nach Function-Name suchen)
- Alle Functions: `src/Backend/AutopilotMonitor.Functions/Functions/`
- Unterordner: Admin, Bootstrap, Config, Diagnostics, Feedback, Galactic, Infrastructure, Ingest, Metrics, Progress, Reports, Rules, Sessions
- Security: `src/Backend/AutopilotMonitor.Functions/Security/`
- Services: `src/Backend/AutopilotMonitor.Functions/Services/`
- Middleware: `src/Backend/AutopilotMonitor.Functions/Middleware/`
### Haeufige Fehlerquellen
- **401/403**: `SecurityValidationExtensions.cs`, `AuthenticationMiddleware.cs`, `PolicyEnforcementMiddleware.cs`
- **429 Rate Limiting**: `RateLimitService.cs`
- **Table Storage**: `TableStorageService*.cs`
- **Deserialization**: JSON-Parsing im Function Body
- **Tenant-Config**: `TenantConfigurationService.cs`
## Ausgabe-Format
1. **Problem-Zusammenfassung**: Was wurde gefunden (1-3 Saetze)
2. **Fehler-Details**: Exceptions, fehlgeschlagene Functions mit Kontext
3. **Timeline**: Wann das Problem begann/endet, ob es andauert
4. **Root-Cause Analyse**: Korrelation mit Code, was den Fehler verursacht
5. **Empfehlung**: Was getan werden sollte
## Hinweise
- Die Env-Variable `AUTOPILOT_MONITOR_APPINSIGHTS_ID` enthaelt die Application Insights App-ID
- `az monitor app-insights query` braucht aktiven Azure CLI Login (`az login`)
- Die `requests`-Tabelle ist leer — nutze immer `traces` fuer Function-Ausfuehrungen
- Bei grossen Ergebnismengen immer `| take N` verwenden
- `operation_Id` verbindet Traces → Exceptions → Dependencies einer einzelnen Ausfuehrung
- `operation_Name` = Function-Name (z.B. "IngestEvents", "GetAgentConfig")
- `cloud_RoleName` = `autopilotmonitor-api`
- Sampling ist aktiv — bei Traces koennen einzelne Eintraege fehlen
- Zeitraum-Mapping: PT1H=1h, PT6H=6h, PT24H=24h, P7D=7 Tage
- Wenn der User einen Zeitraum nennt (z.B. "letzte 6 Stunden"), passe sowohl den KQL-Filter (`ago(6h)`) als auch den Timespan-Parameter (`PT6H`) an
- **KQL-Escaping**: Vermeide `extract()` mit Regex-Backslashes — das Bash→az→KQL Escaping ist fragil. Nutze stattdessen `iif(message contains ..., ...)` oder `has`/`contains` Operatoren
- Nutze einfache Anfuehrungszeichen fuer die KQL-Query im Script-Aufruf, damit Bash keine Variablensubstitution macht
-231
View File
@@ -1,231 +0,0 @@
# Event Query Skill
Du suchst und analysierst Events aus Azure Table Storage fuer Debugging und Weiterentwicklung. Du kannst Events nach Typ suchen, Kontext um ein bestimmtes Event herum anzeigen, oder einzelne Events abrufen.
## Input
`$ARGUMENTS` enthaelt eine natuerlichsprachige Beschreibung was gesucht wird. Beispiele:
- `"finde app_tracking_summary events mit vielen app namen"` — Suche nach EventType mit Kriterien
- `"zeige mir die 5 events vor und nach event abc123... in session def456..."` — Kontext-Fenster
- `"event abc123... in session def456..."` — Einzelnes Event abrufen
- `"alle error_detected events in session def456..."` — Events nach Typ in einer Session
- `"finde phase_transition events"` — Globale Suche ueber alle Events
## Hilfsskript
Nutze das Helper-Script `.claude/scripts/query-table.sh` fuer alle Table-Abfragen:
```bash
.claude/scripts/query-table.sh <TableName> "<OData-Filter>" [select_fields] [top_count]
```
Das Script:
- Parst den Connection String aus `$AUTOPILOT_MONITOR_TABLE_CS`
- Baut die REST API URL zusammen
- Gibt JSON zurueck
- `top_count` begrenzt die Ergebnismenge serverseitig (optional)
## Schritt 1: Intent erkennen
Analysiere `$ARGUMENTS` und waehle die passende Strategie:
### Strategie A: Events nach Typ suchen (mit optionalen Kriterien)
User sucht Events eines bestimmten EventType, ggf. mit Inhaltskriterien.
→ Weiter mit Schritt 2a
### Strategie B: Kontext-Fenster (Events um ein bestimmtes Event herum)
User will Events VOR und NACH einem bestimmten Event sehen.
→ Weiter mit Schritt 2b
### Strategie C: Einzelnes Event abrufen
User will ein spezifisches Event mit allen Details sehen.
→ Weiter mit Schritt 2c
## TenantId ermitteln (nur wenn SessionId angegeben)
Wenn eine SessionId angegeben ist, zuerst TenantId aus der Sessions-Table holen:
```bash
.claude/scripts/query-table.sh Sessions "RowKey eq '${SESSION_ID}'" "PartitionKey,RowKey,Status"
```
- **PartitionKey** = TenantId
- Events-PartitionKey wird dann: `{TenantId}_{SessionId}`
Bei globaler Suche (kein SessionId) ist kein TenantId noetig.
## Schritt 2a: Events nach Typ suchen
### Standard: Globale Suche (ueber alle Events)
```bash
.claude/scripts/query-table.sh Events "EventType eq '${EVENT_TYPE}'" "" 20
```
Das ist ein Table Scan — in der Preview-Phase kein Problem wegen geringer Datenmenge. `$top=20` als Default-Limit, anpassen wenn User mehr oder weniger will.
### Optional: Eingeschraenkt auf eine Session
Wenn User eine SessionId angibt:
```bash
.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and EventType eq '${EVENT_TYPE}'"
```
### Optional: Eingeschraenkt auf einen Tenant
Alle Events des Typs holen und per `jq` nach TenantId filtern (Table Storage unterstuetzt kein `startswith`):
```bash
.claude/scripts/query-table.sh Events "EventType eq '${EVENT_TYPE}'" "" 50 | jq '[.value[] | select(.PartitionKey | startswith("'${TENANT_ID}'_"))]'
```
### Mit Inhaltskriterien (DataJson durchsuchen)
Wenn User nach Inhalten sucht (z.B. "mit vielen app namen"), erst Events holen, dann DataJson mit `jq` filtern:
```bash
# Beispiel: app_tracking_summary mit vielen Apps
.claude/scripts/query-table.sh Events "EventType eq 'app_tracking_summary'" "" 50 | jq '[.value[] | select(.DataJson | fromjson | .apps | length > 5)]'
```
Die `jq`-Filter muessen je nach Suchkriterium angepasst werden. Schau dir zuerst ein paar Beispiel-Events an um die DataJson-Struktur zu verstehen, bevor du filterst.
### Mit Severity-Filter
```bash
# Nur Warnings und hoeher
.claude/scripts/query-table.sh Events "EventType eq '${EVENT_TYPE}' and Severity ge 2" "" 20
```
## Schritt 2b: Kontext-Fenster
User will N Events vor und nach einem bestimmten Event sehen (Default: 5).
### 1. Ziel-Event finden
Per EventId:
```bash
.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and EventId eq '${EVENT_ID}'"
```
Oder per anderer Identifikation (z.B. EventType + ungefaehrer Zeitpunkt). Aus dem Ergebnis den **RowKey** merken.
### 2. Events davor holen
```bash
.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and RowKey lt '${TARGET_ROW_KEY}'" "" 200
```
Da Table Storage kein `$orderby` unterstuetzt und RowKeys aufsteigend zurueckkommen, kommen die Events chronologisch. Nutze `jq` um die letzten N zu extrahieren:
```bash
... | jq '[.value | sort_by(.RowKey) | .[-5:]]'
```
### 3. Events danach holen
```bash
.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and RowKey gt '${TARGET_ROW_KEY}'" "" 5
```
Hier funktioniert `$top=N` direkt, da RowKeys aufsteigend sind.
### 4. Kombinierte Timeline ausgeben
Zeige die Events in chronologischer Reihenfolge mit dem Ziel-Event deutlich markiert:
```
... (events davor)
>>> TARGET EVENT >>> EventType | Severity | Message
... (events danach)
```
## Schritt 2c: Einzelnes Event abrufen
```bash
.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and EventId eq '${EVENT_ID}'"
```
Wenn kein SessionId bekannt: globale Suche per EventId (Table Scan):
```bash
.claude/scripts/query-table.sh Events "EventId eq '${EVENT_ID}'" "" 1
```
Zeige ALLE Felder des Events, inklusive DataJson geparsed und formatiert.
## Ausgabe-Format
### Einzelnes Event
Alle Properties strukturiert anzeigen. DataJson IMMER parsen und als formatierten JSON-Block darstellen:
```
EventId: abc123...
EventType: app_tracking_summary
Severity: Info (1)
Phase: DeviceSetup (2)
Source: Agent
Timestamp: 2024-03-15T10:23:45.123Z
Message: App tracking summary collected
Session: def456...
Tenant: ghi789...
DataJson:
{
"apps": [...],
"totalApps": 12,
...
}
```
### Event-Liste (Suche/Kontext)
Kompakte Tabelle mit den wichtigsten Feldern:
```
RowKey (kurz) | EventType | Sev | Phase | Message (gekuerzt)
------------------|------------------------|------|--------------|--------------------
...0001 | phase_transition | Info | DeviceSetup | Phase changed to...
...0002 | app_install_started | Info | DeviceSetup | Installing App X
>>> ...0003 | error_detected | Err | DeviceSetup | Failed to install <<<
...0004 | app_install_failed | Err | DeviceSetup | App X failed
...0005 | phase_transition | Info | AccountSetup | Phase changed to...
```
Bei Interesse an einem bestimmten Event aus der Liste: DataJson auf Nachfrage anzeigen oder direkt wenn es fuer die Frage relevant ist.
## Bekannte EventTypes
Referenz der gaengigsten EventTypes:
**Phase & Lifecycle:** `phase_transition`, `esp_phase_changed`, `enrollment_complete`, `enrollment_failed`, `whiteglove_complete`, `whiteglove_resumed`, `completion_check`
**App Installation:** `app_install_started`, `app_install_completed`, `app_install_failed`, `app_install_skipped`, `app_download_started`, `download_progress`
**App Tracking:** `app_tracking_summary`, `app_tracking_started`, `app_tracking_completed`
**Network:** `network_state_change`, `network_connectivity_check`
**System:** `error_detected`, `log_entry`, `performance_snapshot`, `gather_result`, `gather_rules_collection_completed`, `agent_metrics_snapshot`
**ESP:** `esp_state_change`, `esp_ui_state`, `esp_failure`, `esp_provisioning_status`
**Andere:** `cert_validation`, `desktop_arrived`, `script_completed`, `script_failed`
## Enum-Mappings
**Severity:** Trace=-1, Debug=0, Info=1, Warning=2, Error=3, Critical=4
**Phase:** Unknown=0, DevicePreparation=1, DeviceSetup=2, AccountSetup=3, DeviceESP=4, UserESP=5, Complete=6, PreProvisioning=7
## Hinweise
- DataJson ist ein JSON-String als Table-Property — IMMER parsen und anzeigen
- `performance_snapshot` und `agent_metrics_snapshot` Events standardmaessig ausblenden (Hochfrequenz-Rauschen), ausser explizit angefragt
- RowKey-Format `yyyyMMddHHmmssfff_NNNNNNNNNN` — String-Vergleich funktioniert fuer chronologische Sortierung
- EventId ist eine Property, KEIN Key — Suche per EventId ohne PartitionKey ist ein Table Scan
- Bei grossen Ergebnismengen `$top` nutzen und User informieren wenn Ergebnisse abgeschnitten wurden
- Fuer cross-session Suchen ist Table Scan OK in der Preview — bei Wachstum spaeter einschraenken
- Die Env-Variable `AUTOPILOT_MONITOR_TABLE_CS` enthaelt den Connection String mit SAS Token
-116
View File
@@ -1,116 +0,0 @@
# Session Debug Skill
Du analysierst eine Autopilot-Monitor Session anhand ihrer SessionId. Du holst dir selbststaendig alle noetigen Daten aus Azure Table Storage via REST API und korrelierst sie mit dem Code.
## Input
Der User gibt eine SessionId (GUID) an, optional gefolgt von einer Frage.
Beispiel: `/session-debug abc12345-1234-1234-1234-123456789abc warum ist die Session failed?`
Die gesamte Eingabe wird als `$ARGUMENTS` uebergeben. Die SessionId ist das erste Wort (GUID-Format), alles danach ist die Frage des Users.
## Hilfsskript
Nutze das Helper-Script `.claude/scripts/query-table.sh` fuer alle Table-Abfragen:
```bash
.claude/scripts/query-table.sh <TableName> "<OData-Filter>" [select_fields]
```
Das Script:
- Parst den Connection String aus `$AUTOPILOT_MONITOR_TABLE_CS`
- Baut die REST API URL zusammen
- Gibt JSON zurueck
## Schritt 1: Session-Metadaten holen
Zuerst die Session aus der **Sessions** Table holen um die TenantId zu bekommen:
```bash
.claude/scripts/query-table.sh Sessions "RowKey eq '${SESSION_ID}'"
```
Aus dem Ergebnis:
- **PartitionKey** = TenantId
- **RowKey** = SessionId
- Wichtige Felder: `Status`, `CurrentPhase`, `EnrollmentType`, `StartedAt`, `CompletedAt`, `FailureReason`, `EventCount`, `DurationSeconds`, `AgentVersion`, `IsPreProvisioned`, `IsHybridJoin`, `IsUserDriven`, `OsBuild`, `SerialNumber`, `DeviceName`, `Manufacturer`, `Model`
Fasse die Session-Metadaten kurz zusammen bevor du weiter machst.
## Schritt 2: Events holen
Mit TenantId und SessionId die Events aus der **Events** Table holen:
```bash
.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}'"
```
Events-Struktur:
- **PartitionKey**: `{TenantId}_{SessionId}`
- **RowKey**: `{Timestamp:yyyyMMddHHmmssfff}_{Sequence:D10}` (chronologisch sortiert)
- **EventType**: z.B. `phase_transition`, `app_install_started`, `app_install_completed`, `error_detected`, `esp_page_change`, `hello_screen_detected`, `desktop_arrival`, `enrollment_complete`, `enrollment_failed`, `performance_snapshot`, `agent_metrics_snapshot`, `collector_started`, `collector_stopped`, `completion_check`, etc.
- **Severity**: Trace=-1, Debug=0, Info=1, Warning=2, Error=3, Critical=4
- **Source**: Agent, IME, Registry, WMI, Network
- **Phase**: 0=Unknown, 1=DevicePreparation, 2=DeviceSetup, 3=AccountSetup, 4=DeviceESP, 5=UserESP, 6=Complete, 7=PreProvisioning
- **Message**: Menschenlesbarer Text
- **DataJson**: Strukturierte Metadaten als JSON String (oft die wichtigsten Details!)
- **Sequence**: Reihenfolge bei gleichem Timestamp
## Schritt 3: Bei Bedarf weitere Tables abfragen
Nur abfragen wenn fuer die Frage relevant:
### RuleResults (automatische Analyse-Ergebnisse)
```bash
.claude/scripts/query-table.sh RuleResults "PartitionKey eq '${TENANT_ID}_${SESSION_ID}'"
```
### AppInstallSummaries (App-Installationen)
```bash
.claude/scripts/query-table.sh AppInstallSummaries "PartitionKey eq '${TENANT_ID}'" "PartitionKey,RowKey,AppName,Status,DurationSeconds,StartedAt,CompletedAt"
```
Dann filtern: nur Eintraege deren RowKey mit der SessionId beginnt.
### TenantConfiguration
```bash
.claude/scripts/query-table.sh TenantConfiguration "PartitionKey eq '${TENANT_ID}' and RowKey eq 'config'"
```
## Schritt 4: Analyse
Nachdem du die Daten hast:
1. **Chronologie aufbauen**: Events nach Timestamp sortiert durchgehen
2. **Phasen-Uebergaenge**: phase_transition Events zeigen den Ablauf
3. **Fehler identifizieren**: Events mit Severity >= 2 (Warning) hervorheben
4. **DataJson parsen**: Oft stecken die Details in DataJson - immer reinschauen bei relevanten Events
5. **Code-Korrelation**: Wenn der User eine Frage zum Verhalten hat, lies den relevanten Code im Repo und erklaere anhand der Events was passiert ist
6. **Completion-Logik**: Bei Fragen zur Session-Completion, schau in:
- `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker*.cs`
- Die 3 Completion-Pfade: IME pattern, ESP+Hello composite, Desktop+Hello (no-ESP)
## Wichtige Code-Pfade fuer Event-Analyse
- **Event-Ingestion**: `src/Backend/AutopilotMonitor.Functions/Functions/IngestEventsFunction.cs`
- **Session-Updates**: `src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Sessions.cs`
- **Enrollment Tracker**: `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker*.cs`
- **ESP/Hello Tracker**: `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/EspAndHelloTracker*.cs`
- **Desktop Arrival**: `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/DesktopArrivalDetector.cs`
- **Collectors**: `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/`
- **Web Session View**: `src/Web/autopilot-monitor-web/app/sessions/[sessionId]/`
- **Shared Models/Enums**: `src/Shared/AutopilotMonitor.Shared/`
## Ausgabe-Format
1. **Session-Uebersicht**: Status, Typ, Dauer, Geraet (kompakt, tabellarisch)
2. **Event-Timeline**: Wichtigste Events chronologisch mit Timestamp, EventType, Message (nicht alle - nur relevante, keine performance_snapshot/agent_metrics_snapshot)
3. **Auffaelligkeiten**: Errors, Warnings, ungewoehnliche Muster
4. **Antwort auf User-Frage**: Falls der User eine spezifische Frage hat, diese gezielt beantworten mit Verweis auf Events UND Code
## Hinweise
- Die Env-Variable `AUTOPILOT_MONITOR_TABLE_CS` enthaelt den Connection String mit SAS Token
- Bei vielen Events (>100) kann die Ausgabe gross werden - fokussiere auf relevante Events, filtere performance_snapshot und agent_metrics_snapshot raus wenn sie nicht relevant sind
- DataJson Felder enthalten oft die wichtigsten Details - IMMER reinschauen bei relevanten Events
- Wenn ein Event unklar ist, schau im Code nach wo dieser EventType erzeugt wird (grep nach dem EventType-String)
- Phase-Enum: Unknown=0, DevicePreparation=1, DeviceSetup=2, AccountSetup=3, DeviceESP=4, UserESP=5, Complete=6, PreProvisioning=7
-76
View File
@@ -1,76 +0,0 @@
#!/bin/bash
# Helper script to query Application Insights via Azure CLI
# Usage: query-appinsights.sh "<KQL-Query>" [timespan]
#
# Requires AUTOPILOT_MONITOR_APPINSIGHTS_ID environment variable (App Insights App-ID GUID)
# Timespan format: ISO 8601 Duration (e.g., PT1H, PT24H, P7D). Default: PT1H
QUERY="$1"
TIMESPAN="${2:-PT1H}"
if [ -z "$QUERY" ]; then
echo "Usage: query-appinsights.sh \"<KQL-Query>\" [timespan]"
exit 1
fi
if [ -z "$AUTOPILOT_MONITOR_APPINSIGHTS_ID" ]; then
echo "ERROR: AUTOPILOT_MONITOR_APPINSIGHTS_ID environment variable not set"
echo "Set it to the Application Insights App ID (Azure Portal > API Access > Application ID)"
exit 1
fi
# Ensure az CLI is in PATH (Windows/Git Bash may not have it)
if ! command -v az &>/dev/null; then
for AZ_DIR in \
"/c/Program Files/Microsoft SDKs/Azure/CLI2/wbin" \
"/c/Program Files (x86)/Microsoft SDKs/Azure/CLI2/wbin"; do
if [ -d "$AZ_DIR" ]; then
export PATH="$AZ_DIR:$PATH"
break
fi
done
if ! command -v az &>/dev/null; then
echo "ERROR: Azure CLI (az) not found. Install it or add it to PATH."
exit 1
fi
fi
# Execute query
RESULT=$(az monitor app-insights query \
--app "$AUTOPILOT_MONITOR_APPINSIGHTS_ID" \
--analytics-query "$QUERY" \
--offset "$TIMESPAN" \
2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
echo "ERROR: az query failed (exit code $EXIT_CODE)"
echo "$RESULT"
exit $EXIT_CODE
fi
# Transform nested table format (columns+rows) into readable JSON array of objects
python3 -c "
import json, sys
data = json.loads(sys.stdin.read())
tables = data.get('tables', data.get('Tables', []))
if not tables:
print('[]')
sys.exit(0)
table = tables[0]
cols = [c['name'] for c in table['columns']]
rows = table['rows']
result = []
for row in rows:
obj = {}
for i, col in enumerate(cols):
val = row[i] if i < len(row) else None
if val is not None:
obj[col] = val
result.append(obj)
print(json.dumps(result, indent=2, default=str))
" <<< "$RESULT"
-56
View File
@@ -1,56 +0,0 @@
#!/bin/bash
# Helper script to query Azure Table Storage via REST API
# Usage: query-table.sh <table_name> <odata_filter> [select_fields] [top_count]
#
# Requires AUTOPILOT_MONITOR_TABLE_CS environment variable with format:
# TableEndpoint=https://....table.core.windows.net/;SharedAccessSignature=sv=...
TABLE_NAME="$1"
FILTER="$2"
SELECT="$3"
if [ -z "$TABLE_NAME" ] || [ -z "$FILTER" ]; then
echo "Usage: query-table.sh <table_name> <odata_filter> [select_fields]"
exit 1
fi
if [ -z "$AUTOPILOT_MONITOR_TABLE_CS" ]; then
echo "ERROR: AUTOPILOT_MONITOR_TABLE_CS environment variable not set"
exit 1
fi
# Parse connection string
ENDPOINT=$(echo "$AUTOPILOT_MONITOR_TABLE_CS" | sed -n 's/.*TableEndpoint=\([^;]*\).*/\1/p' | sed 's:/*$::')
SAS=$(echo "$AUTOPILOT_MONITOR_TABLE_CS" | sed -n 's/.*SharedAccessSignature=\(.*\)/\1/p')
if [ -z "$ENDPOINT" ] || [ -z "$SAS" ]; then
echo "ERROR: Could not parse connection string (need TableEndpoint and SharedAccessSignature)"
exit 1
fi
# URL-encode the OData filter for use in query string
ENCODED_FILTER=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$FILTER', safe=''))" 2>/dev/null)
if [ -z "$ENCODED_FILTER" ]; then
# Fallback: manual encoding of common OData chars
ENCODED_FILTER=$(echo "$FILTER" | sed "s/ /%20/g; s/'/%27/g")
fi
# Build full URL with query params
QUERY_URL="${ENDPOINT}/${TABLE_NAME}()?${SAS}&\$filter=${ENCODED_FILTER}"
if [ -n "$SELECT" ]; then
ENCODED_SELECT=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$SELECT', safe=''))" 2>/dev/null)
if [ -z "$ENCODED_SELECT" ]; then
ENCODED_SELECT="$SELECT"
fi
QUERY_URL="${QUERY_URL}&\$select=${ENCODED_SELECT}"
fi
TOP="$4"
if [ -n "$TOP" ]; then
QUERY_URL="${QUERY_URL}&\$top=${TOP}"
fi
# Execute query
curl -s -H "Accept: application/json;odata=nometadata" \
-H "x-ms-version: 2024-11-04" \
--globoff \
"$QUERY_URL"