@trillboards/edge-sdk 0.2.2 → 0.2.3
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 +147 -2
- package/deploy/docker/Dockerfile.cpu +132 -0
- package/deploy/docker/Dockerfile.cuda +134 -0
- package/deploy/docker/Dockerfile.openvino +131 -0
- package/deploy/docker/README.md +358 -0
- package/deploy/helm/README.md +508 -0
- package/deploy/helm/trillboards-edge/Chart.yaml +19 -0
- package/deploy/helm/trillboards-edge/templates/_helpers.tpl +40 -0
- package/deploy/helm/trillboards-edge/templates/daemonset.yaml +120 -0
- package/deploy/helm/trillboards-edge/templates/service.yaml +15 -0
- package/deploy/helm/trillboards-edge/values.yaml +95 -0
- package/deploy/k8s/daemonset.yaml +144 -0
- package/dist/CommandRouter.d.ts +113 -0
- package/dist/CommandRouter.d.ts.map +1 -0
- package/dist/CommandRouter.js +392 -0
- package/dist/CommandRouter.js.map +1 -0
- package/dist/EdgeAgent.d.ts +6 -1
- package/dist/EdgeAgent.d.ts.map +1 -1
- package/dist/EdgeAgent.js +277 -10
- package/dist/EdgeAgent.js.map +1 -1
- package/dist/cli.js +60 -8
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/demo.d.ts +111 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/demo.js +483 -0
- package/dist/demo.js.map +1 -0
- package/dist/diagnose.d.ts +59 -0
- package/dist/diagnose.d.ts.map +1 -0
- package/dist/diagnose.js +651 -0
- package/dist/diagnose.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +19 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +364 -0
- package/dist/init.js.map +1 -0
- package/dist/mcp-server.d.ts +27 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +1264 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/status.d.ts +11 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +343 -0
- package/dist/status.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Default values for trillboards-edge Helm chart
|
|
2
|
+
|
|
3
|
+
# Image configuration
|
|
4
|
+
image:
|
|
5
|
+
repository: trillboards/edge-sdk
|
|
6
|
+
tag: "latest"
|
|
7
|
+
pullPolicy: Always
|
|
8
|
+
|
|
9
|
+
# Available tags: latest (CPU), cuda (NVIDIA GPU), openvino (Intel GPU)
|
|
10
|
+
# Override per-node using nodeSelector + separate releases if heterogeneous fleet
|
|
11
|
+
|
|
12
|
+
# Trillboards API configuration
|
|
13
|
+
api:
|
|
14
|
+
baseUrl: "https://api.trillboards.com"
|
|
15
|
+
|
|
16
|
+
# Device token — create a Kubernetes secret:
|
|
17
|
+
# kubectl create secret generic trillboards-edge --from-literal=device-token=YOUR_TOKEN
|
|
18
|
+
deviceToken:
|
|
19
|
+
existingSecret: "trillboards-edge"
|
|
20
|
+
secretKey: "device-token"
|
|
21
|
+
|
|
22
|
+
# Sensing configuration
|
|
23
|
+
sensing:
|
|
24
|
+
camera:
|
|
25
|
+
enabled: true
|
|
26
|
+
audio:
|
|
27
|
+
enabled: true
|
|
28
|
+
kiosk:
|
|
29
|
+
enabled: false
|
|
30
|
+
url: "https://screen.trillboards.com"
|
|
31
|
+
executionProvider: "cpu" # cpu, openvino, directml, cuda
|
|
32
|
+
logLevel: "info"
|
|
33
|
+
|
|
34
|
+
# Resource limits
|
|
35
|
+
resources:
|
|
36
|
+
requests:
|
|
37
|
+
cpu: "250m"
|
|
38
|
+
memory: "256Mi"
|
|
39
|
+
limits:
|
|
40
|
+
cpu: "1000m"
|
|
41
|
+
memory: "1Gi"
|
|
42
|
+
|
|
43
|
+
# GPU resources (for CUDA/OpenVINO images)
|
|
44
|
+
gpu:
|
|
45
|
+
enabled: false
|
|
46
|
+
# nvidia.com/gpu: 1
|
|
47
|
+
|
|
48
|
+
# Status server
|
|
49
|
+
statusServer:
|
|
50
|
+
port: 9090
|
|
51
|
+
|
|
52
|
+
# Prometheus scraping
|
|
53
|
+
prometheus:
|
|
54
|
+
enabled: true
|
|
55
|
+
port: 9090
|
|
56
|
+
path: "/metrics"
|
|
57
|
+
|
|
58
|
+
# Health probes
|
|
59
|
+
probes:
|
|
60
|
+
liveness:
|
|
61
|
+
initialDelaySeconds: 30
|
|
62
|
+
periodSeconds: 30
|
|
63
|
+
readiness:
|
|
64
|
+
initialDelaySeconds: 10
|
|
65
|
+
periodSeconds: 10
|
|
66
|
+
|
|
67
|
+
# Model storage — hostPath for persistent models across restarts
|
|
68
|
+
models:
|
|
69
|
+
hostPath: "/opt/trillboards/models"
|
|
70
|
+
|
|
71
|
+
# Data storage — hostPath for signal buffer, device identity, etc.
|
|
72
|
+
data:
|
|
73
|
+
hostPath: "/opt/trillboards/data"
|
|
74
|
+
|
|
75
|
+
# Node scheduling
|
|
76
|
+
nodeSelector:
|
|
77
|
+
trillboards.com/edge-device: "true"
|
|
78
|
+
|
|
79
|
+
tolerations:
|
|
80
|
+
- key: "edge"
|
|
81
|
+
operator: "Exists"
|
|
82
|
+
effect: "NoSchedule"
|
|
83
|
+
|
|
84
|
+
affinity: {}
|
|
85
|
+
|
|
86
|
+
# DaemonSet update strategy
|
|
87
|
+
updateStrategy:
|
|
88
|
+
type: RollingUpdate
|
|
89
|
+
rollingUpdate:
|
|
90
|
+
maxUnavailable: 1
|
|
91
|
+
|
|
92
|
+
# Security context
|
|
93
|
+
securityContext:
|
|
94
|
+
privileged: false
|
|
95
|
+
readOnlyRootFilesystem: false
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Trillboards Edge SDK — Kubernetes DaemonSet
|
|
2
|
+
# Deploys one edge agent per node for fleet-wide audience sensing
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# kubectl create secret generic trillboards-edge \
|
|
6
|
+
# --from-literal=device-token=YOUR_TOKEN
|
|
7
|
+
# kubectl apply -f daemonset.yaml
|
|
8
|
+
#
|
|
9
|
+
# For GPU nodes, use the CUDA image and add NVIDIA tolerations.
|
|
10
|
+
|
|
11
|
+
apiVersion: apps/v1
|
|
12
|
+
kind: DaemonSet
|
|
13
|
+
metadata:
|
|
14
|
+
name: trillboards-edge-agent
|
|
15
|
+
namespace: default
|
|
16
|
+
labels:
|
|
17
|
+
app: trillboards-edge
|
|
18
|
+
component: agent
|
|
19
|
+
spec:
|
|
20
|
+
selector:
|
|
21
|
+
matchLabels:
|
|
22
|
+
app: trillboards-edge
|
|
23
|
+
updateStrategy:
|
|
24
|
+
type: RollingUpdate
|
|
25
|
+
rollingUpdate:
|
|
26
|
+
maxUnavailable: 1
|
|
27
|
+
template:
|
|
28
|
+
metadata:
|
|
29
|
+
labels:
|
|
30
|
+
app: trillboards-edge
|
|
31
|
+
component: agent
|
|
32
|
+
annotations:
|
|
33
|
+
prometheus.io/scrape: "true"
|
|
34
|
+
prometheus.io/port: "9090"
|
|
35
|
+
prometheus.io/path: "/metrics"
|
|
36
|
+
spec:
|
|
37
|
+
terminationGracePeriodSeconds: 30
|
|
38
|
+
containers:
|
|
39
|
+
- name: edge-agent
|
|
40
|
+
image: trillboards/edge-sdk:latest
|
|
41
|
+
imagePullPolicy: Always
|
|
42
|
+
ports:
|
|
43
|
+
- name: status
|
|
44
|
+
containerPort: 9090
|
|
45
|
+
protocol: TCP
|
|
46
|
+
env:
|
|
47
|
+
- name: DEVICE_TOKEN
|
|
48
|
+
valueFrom:
|
|
49
|
+
secretKeyRef:
|
|
50
|
+
name: trillboards-edge
|
|
51
|
+
key: device-token
|
|
52
|
+
- name: SCREEN_ID
|
|
53
|
+
valueFrom:
|
|
54
|
+
fieldRef:
|
|
55
|
+
fieldPath: spec.nodeName
|
|
56
|
+
- name: API_BASE_URL
|
|
57
|
+
value: "https://api.trillboards.com"
|
|
58
|
+
- name: CAMERA_ENABLED
|
|
59
|
+
value: "true"
|
|
60
|
+
- name: AUDIO_ENABLED
|
|
61
|
+
value: "true"
|
|
62
|
+
- name: KIOSK_ENABLED
|
|
63
|
+
value: "false"
|
|
64
|
+
- name: EXECUTION_PROVIDER
|
|
65
|
+
value: "cpu"
|
|
66
|
+
- name: LOG_LEVEL
|
|
67
|
+
value: "info"
|
|
68
|
+
- name: NODE_NAME
|
|
69
|
+
valueFrom:
|
|
70
|
+
fieldRef:
|
|
71
|
+
fieldPath: spec.nodeName
|
|
72
|
+
resources:
|
|
73
|
+
requests:
|
|
74
|
+
cpu: "250m"
|
|
75
|
+
memory: "256Mi"
|
|
76
|
+
limits:
|
|
77
|
+
cpu: "1000m"
|
|
78
|
+
memory: "1Gi"
|
|
79
|
+
livenessProbe:
|
|
80
|
+
httpGet:
|
|
81
|
+
path: /health
|
|
82
|
+
port: status
|
|
83
|
+
initialDelaySeconds: 30
|
|
84
|
+
periodSeconds: 30
|
|
85
|
+
timeoutSeconds: 5
|
|
86
|
+
failureThreshold: 3
|
|
87
|
+
readinessProbe:
|
|
88
|
+
httpGet:
|
|
89
|
+
path: /health
|
|
90
|
+
port: status
|
|
91
|
+
initialDelaySeconds: 10
|
|
92
|
+
periodSeconds: 10
|
|
93
|
+
timeoutSeconds: 3
|
|
94
|
+
failureThreshold: 2
|
|
95
|
+
volumeMounts:
|
|
96
|
+
- name: models
|
|
97
|
+
mountPath: /app/models
|
|
98
|
+
- name: data
|
|
99
|
+
mountPath: /app/data
|
|
100
|
+
# Mount camera device if available
|
|
101
|
+
- name: video-devices
|
|
102
|
+
mountPath: /dev
|
|
103
|
+
readOnly: true
|
|
104
|
+
securityContext:
|
|
105
|
+
privileged: false
|
|
106
|
+
readOnlyRootFilesystem: false
|
|
107
|
+
volumes:
|
|
108
|
+
- name: models
|
|
109
|
+
hostPath:
|
|
110
|
+
path: /opt/trillboards/models
|
|
111
|
+
type: DirectoryOrCreate
|
|
112
|
+
- name: data
|
|
113
|
+
hostPath:
|
|
114
|
+
path: /opt/trillboards/data
|
|
115
|
+
type: DirectoryOrCreate
|
|
116
|
+
- name: video-devices
|
|
117
|
+
hostPath:
|
|
118
|
+
path: /dev
|
|
119
|
+
type: Directory
|
|
120
|
+
# Schedule on edge nodes only
|
|
121
|
+
nodeSelector:
|
|
122
|
+
trillboards.com/edge-device: "true"
|
|
123
|
+
tolerations:
|
|
124
|
+
- key: "edge"
|
|
125
|
+
operator: "Exists"
|
|
126
|
+
effect: "NoSchedule"
|
|
127
|
+
---
|
|
128
|
+
# Service for status endpoint access
|
|
129
|
+
apiVersion: v1
|
|
130
|
+
kind: Service
|
|
131
|
+
metadata:
|
|
132
|
+
name: trillboards-edge-status
|
|
133
|
+
namespace: default
|
|
134
|
+
labels:
|
|
135
|
+
app: trillboards-edge
|
|
136
|
+
spec:
|
|
137
|
+
selector:
|
|
138
|
+
app: trillboards-edge
|
|
139
|
+
ports:
|
|
140
|
+
- name: status
|
|
141
|
+
port: 9090
|
|
142
|
+
targetPort: status
|
|
143
|
+
protocol: TCP
|
|
144
|
+
clusterIP: None # Headless — each pod addressable individually
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandRouter — Routes incoming `deviceCommand` socket events to
|
|
3
|
+
* the appropriate handler and reports results back to the caller.
|
|
4
|
+
*
|
|
5
|
+
* Supported commands:
|
|
6
|
+
* config.push — Hot-push sensing/VAS/full config
|
|
7
|
+
* device.screenshot — Capture + upload a screenshot to S3
|
|
8
|
+
* power.sleep — DPMS display off
|
|
9
|
+
* power.wake — DPMS display on
|
|
10
|
+
* kiosk.reload — Reload kiosk browser
|
|
11
|
+
* device.restart — Restart the edge agent process
|
|
12
|
+
* device.ping — Heartbeat ping/pong
|
|
13
|
+
* device.info — Return host system information
|
|
14
|
+
*/
|
|
15
|
+
import { EventEmitter } from 'events';
|
|
16
|
+
/**
|
|
17
|
+
* Capabilities injected by EdgeAgent when constructing the CommandRouter.
|
|
18
|
+
* Each function encapsulates a side-effect the router delegates to rather
|
|
19
|
+
* than performing directly — this keeps the router unit-testable.
|
|
20
|
+
*/
|
|
21
|
+
export interface CommandContext {
|
|
22
|
+
reloadKiosk: () => Promise<void>;
|
|
23
|
+
captureScreenshot: () => Promise<Buffer | null>;
|
|
24
|
+
getUploadUrl: (type: string) => Promise<string | null>;
|
|
25
|
+
uploadToS3: (url: string, data: Buffer, contentType: string) => Promise<boolean>;
|
|
26
|
+
setSleepMode: (sleep: boolean) => Promise<void>;
|
|
27
|
+
restartDevice: () => Promise<void>;
|
|
28
|
+
updateConfig: (configPatch: Record<string, unknown>) => Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Shape of the `deviceCommand` event payload received from the backend.
|
|
32
|
+
* Matches the server-side dispatcher: `{ commandId, command, payload, ... }`.
|
|
33
|
+
*/
|
|
34
|
+
export interface DeviceCommand {
|
|
35
|
+
commandId: string;
|
|
36
|
+
command: string;
|
|
37
|
+
payload?: Record<string, unknown>;
|
|
38
|
+
timestamp?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Outcome of executing a single command — sent back to the server
|
|
42
|
+
* via `deviceCommandStatus`.
|
|
43
|
+
*/
|
|
44
|
+
export interface CommandResult {
|
|
45
|
+
commandId: string;
|
|
46
|
+
command: string;
|
|
47
|
+
status: 'completed' | 'failed' | 'unsupported';
|
|
48
|
+
result?: unknown;
|
|
49
|
+
error?: string;
|
|
50
|
+
durationMs: number;
|
|
51
|
+
}
|
|
52
|
+
export declare class CommandRouter extends EventEmitter {
|
|
53
|
+
private readonly context;
|
|
54
|
+
private readonly commandHistory;
|
|
55
|
+
private readonly maxHistory;
|
|
56
|
+
private readonly startedAt;
|
|
57
|
+
constructor(context: CommandContext, maxHistory?: number);
|
|
58
|
+
/**
|
|
59
|
+
* Route and execute an incoming device command.
|
|
60
|
+
* Returns the `CommandResult` which the caller should send back via socket.
|
|
61
|
+
*
|
|
62
|
+
* This method **never throws** — every failure path is captured in
|
|
63
|
+
* the result with `status: 'failed'` and a human-readable error message.
|
|
64
|
+
*/
|
|
65
|
+
handleCommand(cmd: DeviceCommand): Promise<CommandResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Return a shallow copy of the recent command history, newest-first.
|
|
68
|
+
*/
|
|
69
|
+
getHistory(): CommandResult[];
|
|
70
|
+
private dispatch;
|
|
71
|
+
/**
|
|
72
|
+
* config.push — Update sensing/VAS config.
|
|
73
|
+
* Payload: `{ type: 'sensing' | 'vas' | 'full', config: {...} }`
|
|
74
|
+
*/
|
|
75
|
+
private handleConfigPush;
|
|
76
|
+
/**
|
|
77
|
+
* device.screenshot — Capture kiosk screenshot, upload to S3.
|
|
78
|
+
* 1. captureScreenshot() -> Buffer | null
|
|
79
|
+
* 2. getUploadUrl('screenshot') -> presigned URL
|
|
80
|
+
* 3. uploadToS3(url, buffer, 'image/png')
|
|
81
|
+
*/
|
|
82
|
+
private handleScreenshot;
|
|
83
|
+
/**
|
|
84
|
+
* power.sleep — Put the display to sleep via DPMS.
|
|
85
|
+
*/
|
|
86
|
+
private handlePowerSleep;
|
|
87
|
+
/**
|
|
88
|
+
* power.wake — Wake the display from DPMS sleep.
|
|
89
|
+
*/
|
|
90
|
+
private handlePowerWake;
|
|
91
|
+
/**
|
|
92
|
+
* kiosk.reload — Reload the kiosk browser page.
|
|
93
|
+
*/
|
|
94
|
+
private handleKioskReload;
|
|
95
|
+
/**
|
|
96
|
+
* device.restart — Restart the edge agent process.
|
|
97
|
+
*
|
|
98
|
+
* Note: after calling `context.restartDevice()`, this process may terminate
|
|
99
|
+
* before the result can be sent. The caller should emit the ack/status
|
|
100
|
+
* *before* actually restarting if reliable delivery matters.
|
|
101
|
+
*/
|
|
102
|
+
private handleDeviceRestart;
|
|
103
|
+
/**
|
|
104
|
+
* device.ping — Simple liveness check. Returns a pong with server timestamp.
|
|
105
|
+
*/
|
|
106
|
+
private handleDevicePing;
|
|
107
|
+
/**
|
|
108
|
+
* device.info — Return system information useful for remote diagnostics.
|
|
109
|
+
*/
|
|
110
|
+
private handleDeviceInfo;
|
|
111
|
+
private recordHistory;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=CommandRouter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommandRouter.d.ts","sourceRoot":"","sources":["../src/CommandRouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAKtC;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAChD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvD,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjF,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,aAAa,CAAC;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAiBD,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuB;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,cAAc,EAAE,UAAU,SAAM;IASrD;;;;;;OAMG;IACG,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAuB/D;;OAEG;IACH,UAAU,IAAI,aAAa,EAAE;YAMf,QAAQ;IA+BtB;;;OAGG;YACW,gBAAgB;IAoC9B;;;;;OAKG;YACW,gBAAgB;IAwD9B;;OAEG;YACW,gBAAgB;IAqB9B;;OAEG;YACW,eAAe;IAqB7B;;OAEG;YACW,iBAAiB;IAqB/B;;;;;;OAMG;YACW,mBAAmB;IAqBjC;;OAEG;YACW,gBAAgB;IAU9B;;OAEG;YACW,gBAAgB;IAsC9B,OAAO,CAAC,aAAa;CAMtB"}
|