@formant/formant-cli 0.4.2 → 0.4.4
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 +85 -29
- package/dist/commands/device/list.d.ts +7 -1
- package/dist/commands/device/list.js +64 -10
- package/dist/commands/device/list.js.map +1 -1
- package/dist/commands/device/streams.d.ts +1 -0
- package/dist/commands/device/streams.js +78 -23
- package/dist/commands/device/streams.js.map +1 -1
- package/dist/commands/query/index.d.ts +1 -1
- package/dist/commands/query/index.js +20 -6
- package/dist/commands/query/index.js.map +1 -1
- package/dist/lib/presence.d.ts +46 -0
- package/dist/lib/presence.js +95 -0
- package/dist/lib/presence.js.map +1 -0
- package/oclif.manifest.json +1099 -1132
- package/package.json +1 -1
- package/dist/commands/device/last-seen.d.ts +0 -10
- package/dist/commands/device/last-seen.js +0 -25
- package/dist/commands/device/last-seen.js.map +0 -1
package/README.md
CHANGED
|
@@ -33,7 +33,9 @@ This CLI provides programmatic access to all Formant capabilities:
|
|
|
33
33
|
- List, create, rename, and delete robots/sensors
|
|
34
34
|
- Add tags for organization (environment, location, customer)
|
|
35
35
|
- View real-time device status and configuration
|
|
36
|
-
- List available telemetry streams
|
|
36
|
+
- List available telemetry streams with per-stream data presence (datapoint counts, last seen, freshness)
|
|
37
|
+
- Data-based last seen timestamps and datapoint counts on device listings
|
|
38
|
+
- Filter to devices that have ingested data (`--with-data`)
|
|
37
39
|
|
|
38
40
|
### Telemetry Queries
|
|
39
41
|
- Query historical sensor data (battery, temperature, position, custom metrics)
|
|
@@ -171,8 +173,8 @@ The CLI will automatically load credentials from your `.env` file.
|
|
|
171
173
|
# View your organization
|
|
172
174
|
fcli org get
|
|
173
175
|
|
|
174
|
-
# List
|
|
175
|
-
fcli device list --dev
|
|
176
|
+
# List devices with data
|
|
177
|
+
fcli device list --with-data --dev
|
|
176
178
|
|
|
177
179
|
# Get device details
|
|
178
180
|
fcli device get <device-id> --dev
|
|
@@ -214,15 +216,20 @@ Create, list, and manage robots/sensors in your fleet.
|
|
|
214
216
|
|
|
215
217
|
```bash
|
|
216
218
|
# Listing and filtering
|
|
217
|
-
fcli device list #
|
|
218
|
-
fcli device list --
|
|
219
|
+
fcli device list # Online devices (default)
|
|
220
|
+
fcli device list --include-offline # Include offline devices
|
|
221
|
+
fcli device list --with-data # Only devices with ingested data
|
|
222
|
+
fcli device list --with-data --days 90 # Widen the search window (default: 30d)
|
|
219
223
|
fcli device list --tag location=warehouse # Filter by tag
|
|
220
224
|
|
|
221
225
|
# Device details
|
|
222
226
|
fcli device get <device-id> # Get full device details
|
|
223
227
|
fcli device config <device-id> # Get device configuration
|
|
224
|
-
|
|
225
|
-
|
|
228
|
+
|
|
229
|
+
# Stream discovery with data presence
|
|
230
|
+
fcli device streams <device-id> # List streams with datapoint counts,
|
|
231
|
+
# last seen (ISO), and freshness
|
|
232
|
+
fcli device streams <device-id> --days 30 # Adjust presence lookback (default: 7d)
|
|
226
233
|
|
|
227
234
|
# Device management
|
|
228
235
|
fcli device create --name "robot-001" # Create a new device
|
|
@@ -258,6 +265,18 @@ Retrieve historical telemetry and sensor data.
|
|
|
258
265
|
fcli query --device <device-id> --stream battery_level \
|
|
259
266
|
--start 2026-01-01 --end 2026-01-02
|
|
260
267
|
|
|
268
|
+
# Multiple streams in one query
|
|
269
|
+
fcli query --device <device-id> --stream heat.current --stream power.available \
|
|
270
|
+
--start 2026-02-01 --end 2026-02-20
|
|
271
|
+
|
|
272
|
+
# Multiple devices in one query
|
|
273
|
+
fcli query --device <id1> --device <id2> --stream battery_level \
|
|
274
|
+
--start 2026-02-01 --end 2026-02-20
|
|
275
|
+
|
|
276
|
+
# Aggregated data (downsample to hourly)
|
|
277
|
+
fcli query --device <device-id> --stream temperature \
|
|
278
|
+
--start 2026-01-01 --end 2026-02-01 --aggregate hour
|
|
279
|
+
|
|
261
280
|
# Latest values
|
|
262
281
|
fcli query latest-values --device <device-id> --stream battery_level
|
|
263
282
|
|
|
@@ -454,14 +473,50 @@ These flags work with any command:
|
|
|
454
473
|
Human-readable tables optimized for terminal viewing:
|
|
455
474
|
|
|
456
475
|
```bash
|
|
457
|
-
$ fcli device list --dev
|
|
476
|
+
$ fcli device list --with-data --dev
|
|
477
|
+
|
|
478
|
+
Devices — with data (dev):
|
|
479
|
+
|
|
480
|
+
NAME ID ONLINE TYPE LAST SEEN (30D) DATAPOINTS (30D)
|
|
481
|
+
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
|
482
|
+
carter.1 a8840212-a0a4-4d0b-8a47-098563894748 false default 2026-02-19T19:05:37.536Z 720156
|
|
483
|
+
DJI Matrice 4E 10f78fd5-c00d-42d9-94c4-b310646f59be false default 2026-02-18T01:17:41.167Z 917051
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
```bash
|
|
487
|
+
$ fcli device streams <device-id> --dev
|
|
488
|
+
|
|
489
|
+
Device Streams (dev):
|
|
490
|
+
|
|
491
|
+
Device: Walt (020e59a1-...)
|
|
492
|
+
Configured: 10 | Discovered from data: 34
|
|
493
|
+
Presence (last 7d): 0 active, 32 recent, 0 stale, 0 dormant, 12 no data
|
|
494
|
+
|
|
495
|
+
NAME TYPE SOURCE DATAPOINTS LAST SEEN FRESHNESS
|
|
496
|
+
spot.robot_state.battery numeric data 20195 2026-02-19T23:37:01.281Z recent
|
|
497
|
+
spot.localization text data 16962 2026-02-19T23:37:01.271Z recent
|
|
498
|
+
spot.hand.image custom config — — —
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
$ fcli query --device <device-id> --stream heat.current --stream power.available \
|
|
503
|
+
--start 2026-02-01 --end 2026-02-20 --dev
|
|
504
|
+
|
|
505
|
+
Telemetry Data (dev environment):
|
|
506
|
+
|
|
507
|
+
Stream: heat.current (numeric) Device: 01397354-d17a-4b53-ac27-61753de7918c
|
|
508
|
+
Points: 1
|
|
458
509
|
|
|
459
|
-
|
|
510
|
+
TIME VALUE
|
|
511
|
+
──────────────────────────────────────────────────────────────────────────────
|
|
512
|
+
2026-02-18T21:15:38.491Z 47
|
|
460
513
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
514
|
+
Stream: power.available (numeric) Device: 01397354-d17a-4b53-ac27-61753de7918c
|
|
515
|
+
Points: 1
|
|
516
|
+
|
|
517
|
+
TIME VALUE
|
|
518
|
+
──────────────────────────────────────────────────────────────────────────────
|
|
519
|
+
2026-02-18T21:15:38.520Z 72
|
|
465
520
|
```
|
|
466
521
|
|
|
467
522
|
### JSON
|
|
@@ -469,17 +524,15 @@ robot-002 e5f6g7h8-... offline env=dev
|
|
|
469
524
|
Machine-readable JSON for scripting and automation:
|
|
470
525
|
|
|
471
526
|
```bash
|
|
472
|
-
$ fcli device list --dev --json
|
|
527
|
+
$ fcli device list --with-data --dev --json
|
|
473
528
|
{
|
|
474
529
|
"items": [
|
|
475
530
|
{
|
|
476
|
-
"id": "
|
|
477
|
-
"name": "
|
|
478
|
-
"online":
|
|
479
|
-
"
|
|
480
|
-
|
|
481
|
-
"location": "warehouse"
|
|
482
|
-
}
|
|
531
|
+
"id": "a8840212-...",
|
|
532
|
+
"name": "carter.1",
|
|
533
|
+
"online": false,
|
|
534
|
+
"last_seen": "2026-02-19T19:05:37.536Z",
|
|
535
|
+
"datapoints": 720156
|
|
483
536
|
}
|
|
484
537
|
]
|
|
485
538
|
}
|
|
@@ -496,17 +549,20 @@ fcli device list --json | jq '.items[] | select(.online==true) | .name'
|
|
|
496
549
|
### Monitor fleet health
|
|
497
550
|
|
|
498
551
|
```bash
|
|
552
|
+
# Find devices that have data
|
|
553
|
+
fcli device list --with-data
|
|
554
|
+
|
|
555
|
+
# Find devices with data, wider search window
|
|
556
|
+
fcli device list --with-data --days 90
|
|
557
|
+
|
|
499
558
|
# Check which devices are offline
|
|
500
|
-
fcli device list --json | jq '.items[] | select(.online==false) | .name'
|
|
559
|
+
fcli device list --include-offline --json | jq '.items[] | select(.online==false) | .name'
|
|
501
560
|
|
|
502
561
|
# View recent critical events across the fleet
|
|
503
562
|
fcli event list --severity critical --limit 50
|
|
504
563
|
|
|
505
|
-
#
|
|
506
|
-
|
|
507
|
-
echo "Device: $device"
|
|
508
|
-
fcli query latest-values --device $device --stream battery_level
|
|
509
|
-
done
|
|
564
|
+
# Discover what streams a device has and their data freshness
|
|
565
|
+
fcli device streams <device-id>
|
|
510
566
|
```
|
|
511
567
|
|
|
512
568
|
### Delegate a task to an AI persona
|
|
@@ -534,8 +590,8 @@ fcli investigation trigger <investigation-id> --input "Device stopped responding
|
|
|
534
590
|
# 3. Check investigation runs
|
|
535
591
|
fcli investigation runs <investigation-id>
|
|
536
592
|
|
|
537
|
-
# 4. Query relevant telemetry
|
|
538
|
-
fcli query --device <device-id> --stream temperature \
|
|
593
|
+
# 4. Query relevant telemetry (multiple streams and/or devices at once)
|
|
594
|
+
fcli query --device <device-id> --stream temperature --stream battery_level \
|
|
539
595
|
--start 2026-02-17T10:00:00Z --end 2026-02-17T12:00:00Z
|
|
540
596
|
```
|
|
541
597
|
|
|
@@ -3,15 +3,21 @@ export default class DevicesList extends BaseCommand<typeof DevicesList> {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
-
|
|
6
|
+
'include-offline': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
days: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
8
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'with-data': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
11
|
tag: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
12
|
};
|
|
11
13
|
static summary: string;
|
|
12
14
|
run(): Promise<{
|
|
13
15
|
items: unknown[];
|
|
14
16
|
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Query analytics SQL for per-device last seen and datapoint counts.
|
|
19
|
+
*/
|
|
20
|
+
private getLastSeenMap;
|
|
15
21
|
/**
|
|
16
22
|
* Try to get online device IDs from the presence service.
|
|
17
23
|
* Returns null if the endpoint fails or is unavailable.
|
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../base-command.js';
|
|
3
3
|
import { formatTable } from '../../lib/formatters.js';
|
|
4
|
+
import { buildLastSeenSQL, toIsoDateTime } from '../../lib/presence.js';
|
|
4
5
|
export default class DevicesList extends BaseCommand {
|
|
5
6
|
static description = `List robots and sensors in your fleet. Shows only online devices by default.
|
|
6
7
|
|
|
7
|
-
Use --
|
|
8
|
+
Use --include-offline to include offline devices. Use --with-data to show only
|
|
9
|
+
devices that have ingested data. Each device is enriched with data-based last
|
|
10
|
+
seen timestamp and datapoint count from analytics.`;
|
|
8
11
|
static examples = [
|
|
9
12
|
'<%= config.bin %> device list',
|
|
10
|
-
'<%= config.bin %> device list --
|
|
11
|
-
'<%= config.bin %> device list --
|
|
13
|
+
'<%= config.bin %> device list --include-offline',
|
|
14
|
+
'<%= config.bin %> device list --with-data',
|
|
15
|
+
'<%= config.bin %> device list --with-data --days 90',
|
|
16
|
+
'<%= config.bin %> device list --include-offline --limit 100',
|
|
12
17
|
'<%= config.bin %> device list --tag location=warehouse',
|
|
13
18
|
'<%= config.bin %> device list --name robot --dev --json',
|
|
14
19
|
];
|
|
15
20
|
static flags = {
|
|
16
|
-
|
|
21
|
+
'include-offline': Flags.boolean({
|
|
17
22
|
char: 'a',
|
|
18
|
-
description: '
|
|
23
|
+
description: 'Include offline devices',
|
|
24
|
+
}),
|
|
25
|
+
days: Flags.integer({
|
|
26
|
+
default: 30,
|
|
27
|
+
description: 'How many days back to search for last seen data',
|
|
19
28
|
}),
|
|
20
29
|
limit: Flags.integer({
|
|
21
30
|
char: 'l',
|
|
@@ -26,6 +35,10 @@ Use --all to include offline devices.`;
|
|
|
26
35
|
char: 'n',
|
|
27
36
|
description: 'Filter devices by name (search)',
|
|
28
37
|
}),
|
|
38
|
+
'with-data': Flags.boolean({
|
|
39
|
+
char: 'w',
|
|
40
|
+
description: 'Only show devices that have data in the last --days window',
|
|
41
|
+
}),
|
|
29
42
|
tag: Flags.string({
|
|
30
43
|
char: 't',
|
|
31
44
|
description: 'Filter by tag (key=value), can be specified multiple times',
|
|
@@ -55,7 +68,7 @@ Use --all to include offline devices.`;
|
|
|
55
68
|
}
|
|
56
69
|
const limit = this.flags.limit;
|
|
57
70
|
let filtered;
|
|
58
|
-
if (this.flags.
|
|
71
|
+
if (this.flags['include-offline'] || this.flags['with-data']) {
|
|
59
72
|
// --all: simple single fetch, enrich with online status
|
|
60
73
|
const result = await this.api('admin', 'devices/query', { body: { ...baseBody, count: limit } });
|
|
61
74
|
const onlineIds = await this.getOnlineDeviceIds();
|
|
@@ -96,14 +109,29 @@ Use --all to include offline devices.`;
|
|
|
96
109
|
}
|
|
97
110
|
}
|
|
98
111
|
}
|
|
112
|
+
// ── Enrich with analytics-based last seen + datapoint counts ─────────────
|
|
113
|
+
const deviceIds = filtered.map((d) => d.id).filter(Boolean);
|
|
114
|
+
const lastSeenMap = await this.getLastSeenMap(deviceIds);
|
|
115
|
+
for (const device of filtered) {
|
|
116
|
+
const info = lastSeenMap.get(device.id);
|
|
117
|
+
device.last_seen = info ? toIsoDateTime(info.last_seen) : null;
|
|
118
|
+
device.datapoints = info?.total_points ?? null;
|
|
119
|
+
}
|
|
120
|
+
// ── Filter to devices with data if requested ──────────────────────────────
|
|
121
|
+
if (this.flags['with-data']) {
|
|
122
|
+
filtered = filtered.filter((d) => d.last_seen !== null);
|
|
123
|
+
}
|
|
99
124
|
const output = { items: filtered };
|
|
100
125
|
if (!this.jsonEnabled()) {
|
|
101
|
-
const mode = this.flags.
|
|
126
|
+
const mode = this.flags['with-data'] ? 'with data' : this.flags['include-offline'] ? 'all' : 'online';
|
|
127
|
+
const days = this.flags.days;
|
|
102
128
|
const columns = [
|
|
103
|
-
{ key: 'name', label: 'NAME', width:
|
|
129
|
+
{ key: 'name', label: 'NAME', width: 24 },
|
|
104
130
|
{ key: 'id', label: 'ID', width: 40 },
|
|
105
|
-
{ key: 'online', label: 'ONLINE', width:
|
|
106
|
-
{ key: 'type', label: 'TYPE', width:
|
|
131
|
+
{ key: 'online', label: 'ONLINE', width: 8 },
|
|
132
|
+
{ key: 'type', label: 'TYPE', width: 10 },
|
|
133
|
+
{ key: 'last_seen', label: `LAST SEEN (${days}D)`, width: 28 },
|
|
134
|
+
{ key: 'datapoints', label: `DATAPOINTS (${days}D)`, width: 16 },
|
|
107
135
|
];
|
|
108
136
|
this.log(`\nDevices — ${mode} (${this.env}):\n`);
|
|
109
137
|
this.log(formatTable(filtered, columns));
|
|
@@ -111,6 +139,32 @@ Use --all to include offline devices.`;
|
|
|
111
139
|
}
|
|
112
140
|
return output;
|
|
113
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Query analytics SQL for per-device last seen and datapoint counts.
|
|
144
|
+
*/
|
|
145
|
+
async getLastSeenMap(deviceIds) {
|
|
146
|
+
const map = new Map();
|
|
147
|
+
if (deviceIds.length === 0)
|
|
148
|
+
return map;
|
|
149
|
+
try {
|
|
150
|
+
const sql = buildLastSeenSQL({
|
|
151
|
+
days: this.flags.days,
|
|
152
|
+
deviceIds,
|
|
153
|
+
});
|
|
154
|
+
const result = await this.api('query', 'analytics/rows', {
|
|
155
|
+
body: { sqlQuery: sql, type: 'advanced' },
|
|
156
|
+
});
|
|
157
|
+
if (!result.error && result.rows) {
|
|
158
|
+
for (const row of result.rows) {
|
|
159
|
+
map.set(row.device_id, row);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// non-fatal
|
|
165
|
+
}
|
|
166
|
+
return map;
|
|
167
|
+
}
|
|
114
168
|
/**
|
|
115
169
|
* Try to get online device IDs from the presence service.
|
|
116
170
|
* Returns null if the endpoint fails or is unavailable.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/device/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,aAAa,CAAA;AAEjC,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAc,WAAW,EAAC,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/device/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ export default class DevicesStreams extends BaseCommand<typeof DevicesStreams> {
|
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
9
|
days: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'with-data': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
};
|
|
11
12
|
static summary: string;
|
|
12
13
|
run(): Promise<{
|
|
@@ -1,27 +1,36 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../../base-command.js';
|
|
3
3
|
import { formatTable } from '../../lib/formatters.js';
|
|
4
|
+
import { buildPresenceSQL, getFreshness, toIsoDateTime, } from '../../lib/presence.js';
|
|
4
5
|
export default class DevicesStreams extends BaseCommand {
|
|
5
6
|
static args = {
|
|
6
7
|
id: Args.string({ description: 'Device ID (UUID)', required: true }),
|
|
7
8
|
};
|
|
8
|
-
static description = `List telemetry streams for a device.
|
|
9
|
+
static description = `List telemetry streams for a device with data presence.
|
|
9
10
|
|
|
10
|
-
Shows streams
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
Shows streams from the device configuration and any streams discovered from
|
|
12
|
+
ingested data. Each stream is enriched with presence info from the analytics
|
|
13
|
+
backend: data point count, last seen time, and a freshness indicator
|
|
14
|
+
(active/recent/stale/dormant).
|
|
15
|
+
|
|
16
|
+
Unconfigured streams (discovered from data only) are marked with SOURCE "data".`;
|
|
13
17
|
static examples = [
|
|
14
18
|
'<%= config.bin %> device streams <device-id>',
|
|
19
|
+
'<%= config.bin %> device streams <device-id> --with-data',
|
|
20
|
+
'<%= config.bin %> device streams <device-id> --with-data --days 30',
|
|
15
21
|
'<%= config.bin %> device streams <device-id> --json',
|
|
16
|
-
'<%= config.bin %> device streams <device-id> --days 30',
|
|
17
22
|
];
|
|
18
23
|
static flags = {
|
|
19
24
|
days: Flags.integer({
|
|
20
|
-
default:
|
|
21
|
-
description: 'How many days back to look for
|
|
25
|
+
default: 7,
|
|
26
|
+
description: 'How many days back to look for stream data presence',
|
|
27
|
+
}),
|
|
28
|
+
'with-data': Flags.boolean({
|
|
29
|
+
char: 'w',
|
|
30
|
+
description: 'Only show streams that have data in the --days window',
|
|
22
31
|
}),
|
|
23
32
|
};
|
|
24
|
-
static summary = 'List device streams';
|
|
33
|
+
static summary = 'List device streams with data presence';
|
|
25
34
|
async run() {
|
|
26
35
|
const deviceId = this.args.id;
|
|
27
36
|
// ── 1. Fetch config-based streams ────────────────────────────────────────
|
|
@@ -49,48 +58,94 @@ Unconfigured streams are marked with a SOURCE of "data" in the output.`;
|
|
|
49
58
|
start: since.toISOString(),
|
|
50
59
|
},
|
|
51
60
|
});
|
|
52
|
-
|
|
61
|
+
// Deduplicate metadata by stream name (the API may return multiple
|
|
62
|
+
// entries for the same stream with different tags)
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
for (const item of metaResult?.items ?? []) {
|
|
65
|
+
if (!seen.has(item.name)) {
|
|
66
|
+
seen.add(item.name);
|
|
67
|
+
metadataItems.push(item);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
53
70
|
}
|
|
54
71
|
catch {
|
|
55
72
|
// metadata endpoint failure is non-fatal; we still return config streams
|
|
56
73
|
}
|
|
57
|
-
// ── 3.
|
|
58
|
-
|
|
74
|
+
// ── 3. Fetch per-stream data presence via analytics SQL ──────────────────
|
|
75
|
+
let presenceMap = new Map();
|
|
76
|
+
try {
|
|
77
|
+
const sql = buildPresenceSQL({
|
|
78
|
+
days: this.flags.days,
|
|
79
|
+
deviceIds: [deviceId],
|
|
80
|
+
});
|
|
81
|
+
const presenceResult = await this.api('query', 'analytics/rows', {
|
|
82
|
+
body: { sqlQuery: sql, type: 'advanced' },
|
|
83
|
+
});
|
|
84
|
+
if (!presenceResult.error && presenceResult.rows) {
|
|
85
|
+
for (const row of presenceResult.rows) {
|
|
86
|
+
presenceMap.set(row.stream_name, row);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// presence failure is non-fatal
|
|
92
|
+
}
|
|
93
|
+
// ── 4. Build merged stream list ───────────────────────────────────────────
|
|
59
94
|
const rows = configStreams.map((s) => {
|
|
60
95
|
const conf = s.configuration;
|
|
96
|
+
const name = s.name;
|
|
97
|
+
const presence = presenceMap.get(name);
|
|
61
98
|
return {
|
|
62
|
-
|
|
63
|
-
|
|
99
|
+
datapoints: presence?.data_points ?? null,
|
|
100
|
+
first_seen: presence ? toIsoDateTime(presence.first_seen) : null,
|
|
101
|
+
freshness: presence ? getFreshness(presence.last_seen) : null,
|
|
102
|
+
last_seen: presence ? toIsoDateTime(presence.last_seen) : null,
|
|
103
|
+
name,
|
|
64
104
|
source: 'config',
|
|
65
|
-
topic: conf?.topic || '—',
|
|
66
105
|
type: conf?.type || '—',
|
|
67
106
|
};
|
|
68
107
|
});
|
|
69
|
-
// Append streams seen in data but absent from config
|
|
70
108
|
for (const item of metadataItems) {
|
|
71
109
|
if (!configuredStreamNames.has(item.name)) {
|
|
110
|
+
const presence = presenceMap.get(item.name);
|
|
72
111
|
rows.push({
|
|
112
|
+
datapoints: presence?.data_points ?? null,
|
|
113
|
+
first_seen: presence ? toIsoDateTime(presence.first_seen) : null,
|
|
114
|
+
freshness: presence ? getFreshness(presence.last_seen) : null,
|
|
115
|
+
last_seen: presence ? toIsoDateTime(presence.last_seen) : null,
|
|
73
116
|
name: item.name,
|
|
74
|
-
quality: '—',
|
|
75
117
|
source: 'data',
|
|
76
|
-
topic: '—',
|
|
77
118
|
type: item.type || '—',
|
|
78
119
|
});
|
|
79
120
|
}
|
|
80
121
|
}
|
|
122
|
+
// ── 5. Filter to streams with data if requested ────────────────────────
|
|
123
|
+
if (this.flags['with-data']) {
|
|
124
|
+
rows.splice(0, rows.length, ...rows.filter((r) => r.datapoints !== null));
|
|
125
|
+
}
|
|
81
126
|
if (!this.jsonEnabled()) {
|
|
82
127
|
const columns = [
|
|
83
|
-
{ key: 'name', label: 'NAME', width:
|
|
84
|
-
{ key: 'type', label: 'TYPE', width:
|
|
85
|
-
{ key: 'topic', label: 'TOPIC', width: 36 },
|
|
86
|
-
{ key: 'quality', label: 'QUALITY', width: 10 },
|
|
128
|
+
{ key: 'name', label: 'NAME', width: 34 },
|
|
129
|
+
{ key: 'type', label: 'TYPE', width: 14 },
|
|
87
130
|
{ key: 'source', label: 'SOURCE', width: 8 },
|
|
131
|
+
{ key: 'datapoints', label: `DATAPOINTS (${this.flags.days}D)`, width: 18 },
|
|
132
|
+
{ key: 'last_seen', label: `LAST SEEN (${this.flags.days}D)`, width: 28 },
|
|
133
|
+
{ key: 'freshness', label: 'FRESHNESS', width: 10 },
|
|
88
134
|
];
|
|
89
135
|
const configured = rows.filter((r) => r.source === 'config').length;
|
|
90
136
|
const discovered = rows.filter((r) => r.source === 'data').length;
|
|
91
|
-
|
|
137
|
+
const active = rows.filter((r) => r.freshness === 'active').length;
|
|
138
|
+
const recent = rows.filter((r) => r.freshness === 'recent').length;
|
|
139
|
+
const stale = rows.filter((r) => r.freshness === 'stale').length;
|
|
140
|
+
const dormant = rows.filter((r) => r.freshness === 'dormant').length;
|
|
141
|
+
const noData = rows.filter((r) => r.freshness === null).length;
|
|
142
|
+
const mode = this.flags['with-data'] ? ' — with data' : '';
|
|
143
|
+
this.log(`\nDevice Streams${mode} (${this.env}):\n`);
|
|
92
144
|
this.log(` Device: ${device.name} (${deviceId})`);
|
|
93
|
-
this.log(` Configured: ${configured} | Discovered from data
|
|
145
|
+
this.log(` Configured: ${configured} | Discovered from data: ${discovered}`);
|
|
146
|
+
this.log(` Presence (last ${this.flags.days}d): ${active} active, ${recent} recent, ${stale} stale, ${dormant} dormant` +
|
|
147
|
+
(noData > 0 ? `, ${noData} no data` : ''));
|
|
148
|
+
this.log('');
|
|
94
149
|
this.log(formatTable(rows, columns));
|
|
95
150
|
this.log('');
|
|
96
151
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streams.js","sourceRoot":"","sources":["../../../src/commands/device/streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAE,KAAK,EAAC,MAAM,aAAa,CAAA;AAEvC,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAc,WAAW,EAAC,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"streams.js","sourceRoot":"","sources":["../../../src/commands/device/streams.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ export default class Query extends BaseCommand<typeof Query> {
|
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
6
|
aggregate: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
device: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
device: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
8
8
|
end: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
latest: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
10
|
limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -15,6 +15,8 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
15
15
|
static examples = [
|
|
16
16
|
'<%= config.bin %> query --device <id> --stream battery_level --start 2026-01-01 --end 2026-01-02',
|
|
17
17
|
'<%= config.bin %> query --device <id> --stream speed --aggregate hour --start 2026-01-01 --end 2026-02-01',
|
|
18
|
+
'<%= config.bin %> query --device <id> --stream battery_level --stream temperature --start 2026-01-01 --end 2026-01-02',
|
|
19
|
+
'<%= config.bin %> query --device <id1> --device <id2> --stream battery_level --start 2026-01-01 --end 2026-01-02',
|
|
18
20
|
'<%= config.bin %> query --device <id> --stream battery_level --latest',
|
|
19
21
|
'<%= config.bin %> query --device <id> --stream temperature --type numeric --json',
|
|
20
22
|
];
|
|
@@ -25,7 +27,8 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
25
27
|
}),
|
|
26
28
|
device: Flags.string({
|
|
27
29
|
char: 'd',
|
|
28
|
-
description: 'Device ID
|
|
30
|
+
description: 'Device ID(s), can be specified multiple times',
|
|
31
|
+
multiple: true,
|
|
29
32
|
required: true,
|
|
30
33
|
}),
|
|
31
34
|
end: Flags.string({
|
|
@@ -58,7 +61,7 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
58
61
|
this.error('--start and --end are required (unless using --latest).');
|
|
59
62
|
}
|
|
60
63
|
const body = {
|
|
61
|
-
deviceIds:
|
|
64
|
+
deviceIds: this.flags.device,
|
|
62
65
|
names: this.flags.stream,
|
|
63
66
|
};
|
|
64
67
|
if (this.flags.start)
|
|
@@ -90,10 +93,21 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
90
93
|
{ key: 'time', label: 'TIME', width: 28 },
|
|
91
94
|
{ key: 'value', label: 'VALUE', width: 50 },
|
|
92
95
|
];
|
|
93
|
-
const rows = points.map((p) =>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
const rows = points.map((p) => {
|
|
97
|
+
// Points can be [timestamp_ms, value] tuples or {time, value} objects
|
|
98
|
+
if (Array.isArray(p)) {
|
|
99
|
+
const [ts, val] = p;
|
|
100
|
+
return {
|
|
101
|
+
time: new Date(ts).toISOString(),
|
|
102
|
+
value: typeof val === 'object' ? JSON.stringify(val) : val,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const obj = p;
|
|
106
|
+
return {
|
|
107
|
+
time: obj.time,
|
|
108
|
+
value: typeof obj.value === 'object' ? JSON.stringify(obj.value) : obj.value,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
97
111
|
this.log(formatTable(rows, columns));
|
|
98
112
|
this.log('');
|
|
99
113
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,aAAa,CAAA;AAEjC,OAAO,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAc,WAAW,EAAC,MAAM,yBAAyB,CAAA;AAEhE,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,WAAyB;IAC1D,MAAM,CAAU,WAAW,GAAG;;;;;;;;;iDASiB,CAAA;IAE/C,MAAM,CAAU,QAAQ,GAAG;QACzB,kGAAkG;QAClG,2GAA2G;QAC3G,uEAAuE;QACvE,kFAAkF;KACnF,CAAA;IAED,MAAM,CAAU,KAAK,GAAG;QACtB,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC;YACtB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,oCAAoC;SAClD,CAAC;QACF,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;YACnB,IAAI,EAAE,GAAG;YACT,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/query/index.ts"],"names":[],"mappings":"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"}
|