een-api-toolkit 0.3.47 → 0.3.51
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/.claude/agents/een-events-agent.md +34 -1
- package/.claude/agents/een-jobs-agent.md +676 -0
- package/CHANGELOG.md +7 -8
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1172 -28
- package/dist/index.js +796 -333
- package/dist/index.js.map +1 -1
- package/docs/AI-CONTEXT.md +22 -1
- package/docs/ai-reference/AI-AUTH.md +1 -1
- package/docs/ai-reference/AI-AUTOMATIONS.md +1 -1
- package/docs/ai-reference/AI-DEVICES.md +1 -1
- package/docs/ai-reference/AI-EVENTS.md +114 -4
- package/docs/ai-reference/AI-GROUPING.md +1 -1
- package/docs/ai-reference/AI-JOBS.md +1084 -0
- package/docs/ai-reference/AI-MEDIA.md +1 -1
- package/docs/ai-reference/AI-SETUP.md +1 -1
- package/docs/ai-reference/AI-USERS.md +1 -1
- package/examples/vue-events/src/components/EventsModal.vue +328 -3
- package/examples/vue-jobs/.env.example +11 -0
- package/examples/vue-jobs/README.md +245 -0
- package/examples/vue-jobs/e2e/app.spec.ts +79 -0
- package/examples/vue-jobs/e2e/auth.spec.ts +382 -0
- package/examples/vue-jobs/e2e/delete-features.spec.ts +564 -0
- package/examples/vue-jobs/e2e/timelapse.spec.ts +361 -0
- package/examples/vue-jobs/index.html +13 -0
- package/examples/vue-jobs/package-lock.json +1722 -0
- package/examples/vue-jobs/package.json +28 -0
- package/examples/vue-jobs/playwright.config.ts +47 -0
- package/examples/vue-jobs/src/App.vue +154 -0
- package/examples/vue-jobs/src/main.ts +25 -0
- package/examples/vue-jobs/src/router/index.ts +82 -0
- package/examples/vue-jobs/src/views/Callback.vue +76 -0
- package/examples/vue-jobs/src/views/CreateExport.vue +284 -0
- package/examples/vue-jobs/src/views/Files.vue +424 -0
- package/examples/vue-jobs/src/views/Home.vue +195 -0
- package/examples/vue-jobs/src/views/JobDetail.vue +392 -0
- package/examples/vue-jobs/src/views/Jobs.vue +297 -0
- package/examples/vue-jobs/src/views/Login.vue +33 -0
- package/examples/vue-jobs/src/views/Logout.vue +59 -0
- package/examples/vue-jobs/src/vite-env.d.ts +1 -0
- package/examples/vue-jobs/tsconfig.json +25 -0
- package/examples/vue-jobs/vite.config.ts +12 -0
- package/package.json +1 -1
package/docs/AI-CONTEXT.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# EEN API Toolkit - AI Reference
|
|
2
2
|
|
|
3
|
-
> **Version:** 0.3.
|
|
3
|
+
> **Version:** 0.3.51
|
|
4
4
|
>
|
|
5
5
|
> This documentation is optimized for AI assistants. It provides focused, domain-specific
|
|
6
6
|
> references to help you understand and use the een-api-toolkit efficiently.
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
| Live video, images, HLS playback | [AI-MEDIA.md](./ai-reference/AI-MEDIA.md) | ~4K |
|
|
22
22
|
| Events, alerts, metrics, SSE | [AI-EVENTS.md](./ai-reference/AI-EVENTS.md) | ~3.5K |
|
|
23
23
|
| Automation rules, alert actions | [AI-AUTOMATIONS.md](./ai-reference/AI-AUTOMATIONS.md) | ~4K |
|
|
24
|
+
| Jobs, exports, files, downloads | [AI-JOBS.md](./ai-reference/AI-JOBS.md) | ~3.5K |
|
|
24
25
|
|
|
25
26
|
## Specialized Agents
|
|
26
27
|
|
|
@@ -36,6 +37,7 @@ Specialized agents are available in `.claude/agents/` for domain-specific tasks:
|
|
|
36
37
|
| `een-media-agent` | Live video, camera previews, HLS playback, recorded images |
|
|
37
38
|
| `een-events-agent` | Events, alerts, metrics, real-time SSE subscriptions |
|
|
38
39
|
| `een-automations-agent` | Automation rules, alert condition rules, alert actions |
|
|
40
|
+
| `een-jobs-agent` | Jobs, exports, files, downloads, video export workflows |
|
|
39
41
|
|
|
40
42
|
**How to Use Agents:**
|
|
41
43
|
|
|
@@ -76,6 +78,7 @@ Then follow the context files and instructions specified within.
|
|
|
76
78
|
| [vue-alerts-metrics](../examples/vue-alerts-metrics/) | Event metrics and alerts | `src/components/MetricsChart.vue` |
|
|
77
79
|
| [vue-event-subscriptions](../examples/vue-event-subscriptions/) | Real-time SSE streaming | `src/views/LiveEvents.vue` |
|
|
78
80
|
| [vue-automations](../examples/vue-automations/) | Automation rules listing | `src/views/Automations.vue` |
|
|
81
|
+
| [vue-jobs](../examples/vue-jobs/) | Jobs, exports, file downloads | `src/views/Jobs.vue` |
|
|
79
82
|
|
|
80
83
|
---
|
|
81
84
|
|
|
@@ -180,6 +183,24 @@ Then follow the context files and instructions specified within.
|
|
|
180
183
|
| `listAlertActions(params?)` | List alert actions |
|
|
181
184
|
| `getAlertAction(id)` | Get a specific alert action |
|
|
182
185
|
|
|
186
|
+
### Exports & Jobs
|
|
187
|
+
| Function | Purpose |
|
|
188
|
+
|----------|---------|
|
|
189
|
+
| `createExportJob(params)` | Create video/timelapse export job |
|
|
190
|
+
| `listJobs(params?)` | List jobs with filtering |
|
|
191
|
+
| `getJob(jobId)` | Get a specific job |
|
|
192
|
+
|
|
193
|
+
### Files & Downloads
|
|
194
|
+
| Function | Purpose |
|
|
195
|
+
|----------|---------|
|
|
196
|
+
| `listFiles(params?)` | List available files |
|
|
197
|
+
| `getFile(fileId)` | Get a specific file |
|
|
198
|
+
| `addFile(params)` | Add a new file |
|
|
199
|
+
| `downloadFile(fileId)` | Download file content |
|
|
200
|
+
| `listDownloads(params?)` | List available downloads |
|
|
201
|
+
| `getDownload(downloadId)` | Get a specific download |
|
|
202
|
+
| `downloadDownload(downloadId)` | Download from downloads endpoint |
|
|
203
|
+
|
|
183
204
|
### Utilities
|
|
184
205
|
| Function | Purpose |
|
|
185
206
|
|----------|---------|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Events, Alerts & Real-Time Streaming - EEN API Toolkit
|
|
2
2
|
|
|
3
|
-
> **Version:** 0.3.
|
|
3
|
+
> **Version:** 0.3.51
|
|
4
4
|
>
|
|
5
5
|
> Complete reference for events, alerts, metrics, and SSE subscriptions.
|
|
6
6
|
> Load this document when implementing event-driven features.
|
|
@@ -292,6 +292,7 @@ import {
|
|
|
292
292
|
listEventFieldValues,
|
|
293
293
|
listEventTypes,
|
|
294
294
|
getRecordedImage,
|
|
295
|
+
getEvent,
|
|
295
296
|
type Camera,
|
|
296
297
|
type Event,
|
|
297
298
|
type EventType,
|
|
@@ -374,6 +375,13 @@ const imageLoadingIds = ref<Set<string>>(new Set()) // Track which images are cu
|
|
|
374
375
|
const boundingBoxCache = ref<Map<string, BoundingBox[]>>(new Map()) // Cache bounding boxes per event
|
|
375
376
|
const enlargedEventId = ref<string | null>(null)
|
|
376
377
|
|
|
378
|
+
// JSON viewer state
|
|
379
|
+
const jsonViewerEventId = ref<string | null>(null)
|
|
380
|
+
const jsonViewerFullEvent = ref<Event | null>(null)
|
|
381
|
+
const jsonViewerLoading = ref(false)
|
|
382
|
+
const jsonViewerError = ref<string | null>(null)
|
|
383
|
+
const copySuccess = ref(false)
|
|
384
|
+
|
|
377
385
|
// Lightbox media state
|
|
378
386
|
const showVideo = ref(false)
|
|
379
387
|
const hdImageUrl = ref<string | null>(null)
|
|
@@ -396,6 +404,21 @@ const enlargedBoundingBoxes = computed(() => {
|
|
|
396
404
|
if (!enlargedEvent.value) return []
|
|
397
405
|
return getBoundingBoxes(enlargedEvent.value)
|
|
398
406
|
})
|
|
407
|
+
const jsonViewerEvent = computed(() => {
|
|
408
|
+
if (!jsonViewerEventId.value) return null
|
|
409
|
+
return events.value.find(e => e.id === jsonViewerEventId.value) || null
|
|
410
|
+
})
|
|
411
|
+
const jsonViewerContent = computed(() => {
|
|
412
|
+
// Use full event if loaded, otherwise fall back to list event
|
|
413
|
+
const eventToShow = jsonViewerFullEvent.value || jsonViewerEvent.value
|
|
414
|
+
if (!eventToShow) return ''
|
|
415
|
+
try {
|
|
416
|
+
return JSON.stringify(eventToShow, null, 2)
|
|
417
|
+
} catch (err) {
|
|
418
|
+
// Safely handle any JSON serialization errors
|
|
419
|
+
return `Error serializing event data: ${String(err)}`
|
|
420
|
+
}
|
|
421
|
+
})
|
|
399
422
|
|
|
400
423
|
// Get start timestamp based on time range
|
|
401
424
|
function getStartTimestamp(range: TimeRange): string {
|
|
@@ -698,6 +721,77 @@ function closeEnlargedImage() {
|
|
|
698
721
|
resetVideo()
|
|
699
722
|
}
|
|
700
723
|
|
|
724
|
+
// Open JSON viewer and fetch full event details
|
|
725
|
+
async function openJsonViewer(eventId: string) {
|
|
726
|
+
jsonViewerEventId.value = eventId
|
|
727
|
+
jsonViewerFullEvent.value = null
|
|
728
|
+
jsonViewerError.value = null
|
|
729
|
+
copySuccess.value = false
|
|
730
|
+
|
|
731
|
+
// Find the event in the list to get its dataSchemas
|
|
732
|
+
const listEvent = events.value.find(e => e.id === eventId)
|
|
733
|
+
if (!listEvent) {
|
|
734
|
+
jsonViewerError.value = 'Event not found in current list'
|
|
735
|
+
return
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Build include array from dataSchemas (prefix with "data.")
|
|
739
|
+
const includes = listEvent.dataSchemas?.map(schema => `data.${schema}`) || []
|
|
740
|
+
|
|
741
|
+
if (includes.length === 0) {
|
|
742
|
+
// No additional data to fetch, use the list event as-is
|
|
743
|
+
return
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Fetch full event details with all data schemas
|
|
747
|
+
jsonViewerLoading.value = true
|
|
748
|
+
const { data, error } = await getEvent(eventId, { include: includes })
|
|
749
|
+
jsonViewerLoading.value = false
|
|
750
|
+
|
|
751
|
+
if (error) {
|
|
752
|
+
jsonViewerError.value = error.message
|
|
753
|
+
return
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (data) {
|
|
757
|
+
jsonViewerFullEvent.value = data
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Close JSON viewer
|
|
762
|
+
function closeJsonViewer() {
|
|
763
|
+
jsonViewerEventId.value = null
|
|
764
|
+
jsonViewerFullEvent.value = null
|
|
765
|
+
jsonViewerError.value = null
|
|
766
|
+
copySuccess.value = false
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Copy JSON to clipboard
|
|
770
|
+
async function copyJsonToClipboard() {
|
|
771
|
+
if (!jsonViewerContent.value) return
|
|
772
|
+
try {
|
|
773
|
+
await navigator.clipboard.writeText(jsonViewerContent.value)
|
|
774
|
+
copySuccess.value = true
|
|
775
|
+
setTimeout(() => {
|
|
776
|
+
copySuccess.value = false
|
|
777
|
+
}, 2000)
|
|
778
|
+
} catch {
|
|
779
|
+
// Fallback for older browsers
|
|
780
|
+
const textarea = document.createElement('textarea')
|
|
781
|
+
textarea.value = jsonViewerContent.value
|
|
782
|
+
textarea.style.position = 'fixed'
|
|
783
|
+
textarea.style.opacity = '0'
|
|
784
|
+
document.body.appendChild(textarea)
|
|
785
|
+
textarea.select()
|
|
786
|
+
document.execCommand('copy')
|
|
787
|
+
document.body.removeChild(textarea)
|
|
788
|
+
copySuccess.value = true
|
|
789
|
+
setTimeout(() => {
|
|
790
|
+
copySuccess.value = false
|
|
791
|
+
}, 2000)
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
701
795
|
// Switch to preview mode
|
|
702
796
|
function showPreview() {
|
|
703
797
|
currentMediaType.value = 'preview'
|
|
@@ -747,8 +841,12 @@ async function showVideoPlayer() {
|
|
|
747
841
|
|
|
748
842
|
// Handle keyboard events for accessibility
|
|
749
843
|
function handleKeydown(event: KeyboardEvent) {
|
|
750
|
-
if (event.key === 'Escape'
|
|
751
|
-
|
|
844
|
+
if (event.key === 'Escape') {
|
|
845
|
+
if (jsonViewerEventId.value) {
|
|
846
|
+
closeJsonViewer()
|
|
847
|
+
} else if (enlargedEventId.value) {
|
|
848
|
+
closeEnlargedImage()
|
|
849
|
+
}
|
|
752
850
|
}
|
|
753
851
|
}
|
|
754
852
|
|
|
@@ -759,6 +857,9 @@ onMounted(() => {
|
|
|
759
857
|
|
|
760
858
|
onUnmounted(() => {
|
|
761
859
|
window.removeEventListener('keydown', handleKeydown)
|
|
860
|
+
// Clean up JSON viewer state to prevent memory leaks
|
|
861
|
+
jsonViewerFullEvent.value = null
|
|
862
|
+
jsonViewerEventId.value = null
|
|
762
863
|
})
|
|
763
864
|
|
|
764
865
|
// Watch for modal open/close
|
|
@@ -784,6 +885,7 @@ watch(() => props.isOpen, async (isOpen) => {
|
|
|
784
885
|
boundingBoxCache.value.clear()
|
|
785
886
|
events.value = []
|
|
786
887
|
enlargedEventId.value = null
|
|
888
|
+
jsonViewerEventId.value = null
|
|
787
889
|
}
|
|
788
890
|
}, { immediate: true })
|
|
789
891
|
|
|
@@ -883,7 +985,15 @@ watch([timeRange, selectedEventTypes], () => {
|
|
|
883
985
|
</div>
|
|
884
986
|
</div>
|
|
885
987
|
<div class="event-info">
|
|
886
|
-
<div class="event-type">
|
|
988
|
+
<div class="event-type-row">
|
|
989
|
+
<span class="event-type">{{ getEventTypeName(event.type) }}</span>
|
|
990
|
+
<button
|
|
991
|
+
class="json-button"
|
|
992
|
+
@click="openJsonViewer(event.id)"
|
|
993
|
+
title="View JSON data"
|
|
994
|
+
data-testid="json-button"
|
|
995
|
+
>{ }</button>
|
|
996
|
+
</div>
|
|
887
997
|
<div class="event-time">{{ formatTimestamp(event.startTimestamp) }}</div>
|
|
888
998
|
<div v-if="getBoundingBoxCount(event) > 0" class="event-detections" data-testid="event-detections">
|
|
889
999
|
{{ getBoundingBoxCount(event) }} detection{{ getBoundingBoxCount(event) !== 1 ? 's' : '' }}
|