@formant/formant-cli 0.4.4 → 0.5.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/README.md +93 -6
- package/dist/base-command.d.ts +17 -0
- package/dist/base-command.js +33 -0
- package/dist/base-command.js.map +1 -1
- package/dist/commands/query/index.d.ts +6 -4
- package/dist/commands/query/index.js +76 -18
- package/dist/commands/query/index.js.map +1 -1
- package/dist/help.js +3 -1
- package/dist/help.js.map +1 -1
- package/oclif.manifest.json +1237 -728
- package/package.json +2 -1
- package/dist/commands/query/latest-values.d.ts +0 -15
- package/dist/commands/query/latest-values.js +0 -128
- package/dist/commands/query/latest-values.js.map +0 -1
package/README.md
CHANGED
|
@@ -39,8 +39,9 @@ This CLI provides programmatic access to all Formant capabilities:
|
|
|
39
39
|
|
|
40
40
|
### Telemetry Queries
|
|
41
41
|
- Query historical sensor data (battery, temperature, position, custom metrics)
|
|
42
|
-
- Retrieve latest values
|
|
43
|
-
-
|
|
42
|
+
- Retrieve latest values with `--latest-values-only`
|
|
43
|
+
- Auto-discover all streams with `--all-streams`
|
|
44
|
+
- Filter by time range, device, and stream type
|
|
44
45
|
- Export data as JSON for analysis
|
|
45
46
|
|
|
46
47
|
### Telemetry Ingestion
|
|
@@ -170,7 +171,7 @@ The CLI will automatically load credentials from your `.env` file.
|
|
|
170
171
|
## Quick Start
|
|
171
172
|
|
|
172
173
|
```bash
|
|
173
|
-
# View your organization
|
|
174
|
+
# View your organization (add --toon for compact output, --json for machine-readable)
|
|
174
175
|
fcli org get
|
|
175
176
|
|
|
176
177
|
# List devices with data
|
|
@@ -258,7 +259,7 @@ fcli event get <event-id> # Get event details
|
|
|
258
259
|
|
|
259
260
|
### Query
|
|
260
261
|
|
|
261
|
-
Retrieve historical telemetry and sensor data.
|
|
262
|
+
Retrieve historical telemetry and sensor data. `--start` and `--end` are always required.
|
|
262
263
|
|
|
263
264
|
```bash
|
|
264
265
|
# Historical data
|
|
@@ -277,8 +278,13 @@ fcli query --device <id1> --device <id2> --stream battery_level \
|
|
|
277
278
|
fcli query --device <device-id> --stream temperature \
|
|
278
279
|
--start 2026-01-01 --end 2026-02-01 --aggregate hour
|
|
279
280
|
|
|
280
|
-
# Latest
|
|
281
|
-
fcli query
|
|
281
|
+
# Latest value per stream (within the time range)
|
|
282
|
+
fcli query --device <device-id> --stream battery_level \
|
|
283
|
+
--start 2026-01-01 --end 2026-02-01 --latest-values-only
|
|
284
|
+
|
|
285
|
+
# Auto-discover and query all streams on a device
|
|
286
|
+
fcli query --device <device-id> --all-streams \
|
|
287
|
+
--start 2026-01-01 --end 2026-02-01 --latest-values-only
|
|
282
288
|
|
|
283
289
|
# Common streams: battery_level, temperature, cpu_usage, memory_usage, location
|
|
284
290
|
```
|
|
@@ -462,6 +468,7 @@ These flags work with any command:
|
|
|
462
468
|
- `--dev` — Target the dev environment (for testing)
|
|
463
469
|
- `--stage` — Target the stage environment (for staging)
|
|
464
470
|
- `--json` — Output raw JSON instead of formatted tables
|
|
471
|
+
- `--toon` — Output data in TOON (Token-Oriented Object Notation) format
|
|
465
472
|
- `-h, --help` — Show help for any command
|
|
466
473
|
|
|
467
474
|
**Default environment:** Production (unless `--dev` or `--stage` is specified)
|
|
@@ -544,6 +551,60 @@ Use with `jq` for advanced processing:
|
|
|
544
551
|
fcli device list --json | jq '.items[] | select(.online==true) | .name'
|
|
545
552
|
```
|
|
546
553
|
|
|
554
|
+
### TOON
|
|
555
|
+
|
|
556
|
+
**`--toon` is the recommended output format when working with AI tools, LLM prompts, or any context where token efficiency matters.**
|
|
557
|
+
|
|
558
|
+
TOON (Token-Oriented Object Notation) is a compact, schema-aware encoding of structured data that is both human-readable and significantly more token-efficient than JSON — making it ideal for pasting into LLM prompts, storing in context windows, and scripting with AI agents.
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
$ fcli org get --toon
|
|
562
|
+
name: Acme Robotics
|
|
563
|
+
id: 6380a48c-0847-4543-a67f-9b7ccc41ec21
|
|
564
|
+
plan: paid
|
|
565
|
+
daysDataRetained: 600
|
|
566
|
+
aiEnabled: true
|
|
567
|
+
investigationsEnabled: true
|
|
568
|
+
flags[19]: settings.role,settings.user,settings.device,...
|
|
569
|
+
billingInfo:
|
|
570
|
+
usagePrices:
|
|
571
|
+
devices: 1100
|
|
572
|
+
dataPoints: 0.00015
|
|
573
|
+
bytes: 3e-9
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
$ fcli user list --toon
|
|
578
|
+
items[2]{id,email,firstName,lastName,enabled}:
|
|
579
|
+
4f79f32b-...,alice@acme.io,Alice,Smith,true
|
|
580
|
+
9a1c22de-...,bob@acme.io,Bob,Jones,true
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Key properties of TOON output:
|
|
584
|
+
|
|
585
|
+
- **Arrays are schema-compressed** — `items[N]{field1,field2,...}:` header followed by rows, rather than repeating field names for every object
|
|
586
|
+
- **Scalars are unquoted** — only strings with special characters are quoted
|
|
587
|
+
- **Nested objects are indented** — structure is preserved but without JSON's punctuation overhead
|
|
588
|
+
- **Null and boolean values are compact** — `null`, `true`, `false` without quotes
|
|
589
|
+
|
|
590
|
+
**When to use `--toon` vs `--json`:**
|
|
591
|
+
|
|
592
|
+
| Use case | Recommended flag |
|
|
593
|
+
|---|---|
|
|
594
|
+
| Pasting into an LLM prompt | `--toon` |
|
|
595
|
+
| AI agent / agentic scripting | `--toon` |
|
|
596
|
+
| Shell scripting with `jq` | `--json` |
|
|
597
|
+
| Saving to a file for later processing | `--json` |
|
|
598
|
+
| Quick human inspection | (default) |
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
# Pipe directly into an LLM context or agent prompt
|
|
602
|
+
fcli device list --toon
|
|
603
|
+
fcli event list --severity critical --toon
|
|
604
|
+
fcli investigation runs <id> --toon
|
|
605
|
+
fcli org get --toon
|
|
606
|
+
```
|
|
607
|
+
|
|
547
608
|
## Examples
|
|
548
609
|
|
|
549
610
|
### Monitor fleet health
|
|
@@ -623,6 +684,32 @@ fcli analytics query --sql "SELECT * FROM events WHERE created_at > '2026-01-01'
|
|
|
623
684
|
--json > analytics_export.json
|
|
624
685
|
```
|
|
625
686
|
|
|
687
|
+
### Use with AI tools and LLMs
|
|
688
|
+
|
|
689
|
+
`--toon` produces compact, token-efficient output that is well-suited for LLM prompts and agentic workflows. Use it anywhere you would otherwise paste JSON into a prompt.
|
|
690
|
+
|
|
691
|
+
```bash
|
|
692
|
+
# Summarize your fleet for an LLM
|
|
693
|
+
fcli device list --with-data --toon
|
|
694
|
+
|
|
695
|
+
# Get org context in compact form
|
|
696
|
+
fcli org get --toon
|
|
697
|
+
|
|
698
|
+
# Pipe investigation results into an AI workflow
|
|
699
|
+
fcli investigation runs <investigation-id> --toon
|
|
700
|
+
|
|
701
|
+
# Combine with other tools for agentic scripting
|
|
702
|
+
fcli event list --severity critical --limit 20 --toon | your-ai-tool analyze
|
|
703
|
+
|
|
704
|
+
# Check recent events and device state together
|
|
705
|
+
{
|
|
706
|
+
echo "=== Critical Events ==="
|
|
707
|
+
fcli event list --severity critical --limit 10 --toon
|
|
708
|
+
echo "=== Fleet Status ==="
|
|
709
|
+
fcli device list --include-offline --toon
|
|
710
|
+
} | your-ai-tool "Summarize what's wrong with my fleet"
|
|
711
|
+
```
|
|
712
|
+
|
|
626
713
|
## Development
|
|
627
714
|
|
|
628
715
|
### Build from source
|
package/dist/base-command.d.ts
CHANGED
|
@@ -12,11 +12,28 @@ export declare abstract class BaseCommand<T extends typeof Command> extends Comm
|
|
|
12
12
|
static baseFlags: {
|
|
13
13
|
dev: Interfaces.BooleanFlag<boolean>;
|
|
14
14
|
stage: Interfaces.BooleanFlag<boolean>;
|
|
15
|
+
toon: Interfaces.BooleanFlag<boolean>;
|
|
15
16
|
};
|
|
16
17
|
protected flags: InferredFlags<T>;
|
|
17
18
|
protected args: InferredArgs<T>;
|
|
18
19
|
/** Resolved environment based on --dev / --stage flags. */
|
|
19
20
|
protected get env(): Environment;
|
|
21
|
+
/** Returns true when --toon flag is active. */
|
|
22
|
+
toonEnabled(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Returns true when either --json or --toon is active.
|
|
25
|
+
* Overriding this ensures human-readable output is suppressed for both flags,
|
|
26
|
+
* without requiring changes to individual command files.
|
|
27
|
+
*/
|
|
28
|
+
jsonEnabled(): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Called by oclif when jsonEnabled() is true and run() returns successfully.
|
|
31
|
+
* When --toon is active, encodes the result as TOON instead of JSON.
|
|
32
|
+
*
|
|
33
|
+
* Note: must write to process.stdout directly — this.log() is suppressed
|
|
34
|
+
* by oclif whenever jsonEnabled() returns true.
|
|
35
|
+
*/
|
|
36
|
+
protected logJson(json: unknown): void;
|
|
20
37
|
init(): Promise<void>;
|
|
21
38
|
/**
|
|
22
39
|
* Make an authenticated GET/POST request to a Formant API.
|
package/dist/base-command.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { encode } from '@toon-format/toon';
|
|
2
3
|
import { apiRequest } from './lib/api.js';
|
|
3
4
|
import { login } from './lib/auth.js';
|
|
4
5
|
/**
|
|
@@ -18,6 +19,11 @@ export class BaseCommand extends Command {
|
|
|
18
19
|
exclusive: ['dev'],
|
|
19
20
|
helpGroup: 'GLOBAL',
|
|
20
21
|
}),
|
|
22
|
+
toon: Flags.boolean({
|
|
23
|
+
description: 'Output data in TOON (Token-Oriented Object Notation) format',
|
|
24
|
+
exclusive: ['json'],
|
|
25
|
+
helpGroup: 'GLOBAL',
|
|
26
|
+
}),
|
|
21
27
|
};
|
|
22
28
|
flags;
|
|
23
29
|
args;
|
|
@@ -29,6 +35,33 @@ export class BaseCommand extends Command {
|
|
|
29
35
|
return 'stage';
|
|
30
36
|
return 'prod';
|
|
31
37
|
}
|
|
38
|
+
/** Returns true when --toon flag is active. */
|
|
39
|
+
toonEnabled() {
|
|
40
|
+
return Boolean(this.flags?.toon);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Returns true when either --json or --toon is active.
|
|
44
|
+
* Overriding this ensures human-readable output is suppressed for both flags,
|
|
45
|
+
* without requiring changes to individual command files.
|
|
46
|
+
*/
|
|
47
|
+
jsonEnabled() {
|
|
48
|
+
return super.jsonEnabled() || this.toonEnabled();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Called by oclif when jsonEnabled() is true and run() returns successfully.
|
|
52
|
+
* When --toon is active, encodes the result as TOON instead of JSON.
|
|
53
|
+
*
|
|
54
|
+
* Note: must write to process.stdout directly — this.log() is suppressed
|
|
55
|
+
* by oclif whenever jsonEnabled() returns true.
|
|
56
|
+
*/
|
|
57
|
+
logJson(json) {
|
|
58
|
+
if (this.toonEnabled()) {
|
|
59
|
+
process.stdout.write(encode(json) + '\n');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
super.logJson(json);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
32
65
|
async init() {
|
|
33
66
|
await super.init();
|
|
34
67
|
const { args, flags } = await this.parse({
|
package/dist/base-command.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-command.js","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"base-command.js","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,KAAK,EAAa,MAAM,aAAa,CAAA;AACtD,OAAO,EAAC,MAAM,EAAC,MAAM,mBAAmB,CAAA;AAExC,OAAO,EAAiB,UAAU,EAAC,MAAM,cAAc,CAAA;AACvD,OAAO,EAAC,KAAK,EAAC,MAAM,eAAe,CAAA;AAQnC;;;GAGG;AACH,MAAM,OAAgB,WAAsC,SAAQ,OAAO;IACzE,MAAM,CAAC,cAAc,GAAG,IAAI,CAAA;IAE5B,MAAM,CAAC,SAAS,GAAG;QACjB,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;YACjB,WAAW,EAAE,4BAA4B;YACzC,SAAS,EAAE,CAAC,OAAO,CAAC;YACpB,SAAS,EAAE,QAAQ;SACpB,CAAC;QACF,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC;YACnB,WAAW,EAAE,8BAA8B;YAC3C,SAAS,EAAE,CAAC,KAAK,CAAC;YAClB,SAAS,EAAE,QAAQ;SACpB,CAAC;QACF,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC;YAClB,WAAW,EAAE,6DAA6D;YAC1E,SAAS,EAAE,CAAC,MAAM,CAAC;YACnB,SAAS,EAAE,QAAQ;SACpB,CAAC;KACH,CAAA;IAES,KAAK,CAAmB;IACxB,IAAI,CAAkB;IAEhC,2DAA2D;IAC3D,IAAc,GAAG;QACf,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG;YAAE,OAAO,KAAK,CAAA;QAChC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK;YAAE,OAAO,OAAO,CAAA;QACpC,OAAO,MAAM,CAAA;IACf,CAAC;IAED,+CAA+C;IACxC,WAAW;QAChB,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAClC,CAAC;IAED;;;;OAIG;IACa,WAAW;QACzB,OAAO,KAAK,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,CAAA;IAClD,CAAC;IAED;;;;;;OAMG;IACgB,OAAO,CAAC,IAAa;QACtC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;QAC3C,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACrB,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;QAClB,MAAM,EAAC,IAAI,EAAE,KAAK,EAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC;YACrC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI;YACpB,SAAS,EAAG,KAAK,CAAC,IAA2B,CAAC,SAAS;YACvD,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc;YACxC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK;YACtB,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM;SACzB,CAAC,CAAA;QAEF,IAAI,CAAC,KAAK,GAAG,KAAyB,CAAA;QACtC,IAAI,CAAC,IAAI,GAAG,IAAuB,CAAA;IACrC,CAAC;IAED;;;OAGG;IACO,KAAK,CAAC,GAAG,CACjB,MAAiB,EACjB,IAAY,EACZ,OAAgH;QAEhH,OAAO,UAAU,CAAI,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;IACvD,CAAC;IAED;;;OAGG;IACO,KAAK,CAAC,QAAQ;QACtB,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAClC,OAAO,IAAI,CAAC,cAAc,CAAA;IAC5B,CAAC;IAEkB,KAAK,CAAC,KAAK,CAAC,GAAgC;QAC7D,MAAM,GAAG,CAAA;IACX,CAAC;IAED;;;OAGG;IACO,iBAAiB,CAAC,OAAe;QACzC,2EAA2E;QAC3E,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;YAC1D,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,2DAA2D;QAC3D,OAAO,GAAG,OAAO,YAAY,CAAA;IAC/B,CAAC"}
|
|
@@ -4,12 +4,14 @@ 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
|
+
'all-streams': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
days: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
9
|
device: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
end: import("@oclif/core/interfaces").OptionFlag<string
|
|
9
|
-
latest: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
end: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
'latest-values-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
12
|
limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
-
start: import("@oclif/core/interfaces").OptionFlag<string
|
|
12
|
-
stream: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
start: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
stream: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
15
|
type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
16
|
};
|
|
15
17
|
static summary: string;
|
|
@@ -5,7 +5,11 @@ export default class Query extends BaseCommand {
|
|
|
5
5
|
static description = `Query telemetry stream data for a device over a time range.
|
|
6
6
|
|
|
7
7
|
Returns time-series datapoints for the specified stream(s). Supports aggregation
|
|
8
|
-
for downsampling large datasets, and --latest to get only the most recent value.
|
|
8
|
+
for downsampling large datasets, and --latest-values-only to get only the most recent value.
|
|
9
|
+
|
|
10
|
+
Use --all-streams with --latest-values-only to automatically discover and query all
|
|
11
|
+
streams — both from the device configuration and from actual ingested data
|
|
12
|
+
(unconfigured streams included).
|
|
9
13
|
|
|
10
14
|
Stream types: numeric, text, image, video, location, json, bitset, battery, health,
|
|
11
15
|
"numeric set", "point cloud", localization, "transform tree", file.
|
|
@@ -17,7 +21,8 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
17
21
|
'<%= config.bin %> query --device <id> --stream speed --aggregate hour --start 2026-01-01 --end 2026-02-01',
|
|
18
22
|
'<%= config.bin %> query --device <id> --stream battery_level --stream temperature --start 2026-01-01 --end 2026-01-02',
|
|
19
23
|
'<%= config.bin %> query --device <id1> --device <id2> --stream battery_level --start 2026-01-01 --end 2026-01-02',
|
|
20
|
-
'<%= config.bin %> query --device <id> --stream battery_level --latest',
|
|
24
|
+
'<%= config.bin %> query --device <id> --stream battery_level --start 2026-01-01 --end 2026-02-01 --latest-values-only',
|
|
25
|
+
'<%= config.bin %> query --device <id> --all-streams --start 2026-01-01 --end 2026-02-01 --latest-values-only',
|
|
21
26
|
'<%= config.bin %> query --device <id> --stream temperature --type numeric --json',
|
|
22
27
|
];
|
|
23
28
|
static flags = {
|
|
@@ -25,6 +30,13 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
25
30
|
char: 'a',
|
|
26
31
|
description: 'Aggregation level for downsampling',
|
|
27
32
|
}),
|
|
33
|
+
'all-streams': Flags.boolean({
|
|
34
|
+
description: 'Query all streams for the device — from config and from ingested data (requires single --device and --latest-values-only)',
|
|
35
|
+
}),
|
|
36
|
+
days: Flags.integer({
|
|
37
|
+
default: 14,
|
|
38
|
+
description: 'How many days back to look for unconfigured streams when using --all-streams',
|
|
39
|
+
}),
|
|
28
40
|
device: Flags.string({
|
|
29
41
|
char: 'd',
|
|
30
42
|
description: 'Device ID(s), can be specified multiple times',
|
|
@@ -33,8 +45,9 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
33
45
|
}),
|
|
34
46
|
end: Flags.string({
|
|
35
47
|
description: 'End time (ISO 8601)',
|
|
48
|
+
required: true,
|
|
36
49
|
}),
|
|
37
|
-
latest: Flags.boolean({
|
|
50
|
+
'latest-values-only': Flags.boolean({
|
|
38
51
|
description: 'Only return the most recent value per stream',
|
|
39
52
|
}),
|
|
40
53
|
limit: Flags.integer({
|
|
@@ -43,12 +56,12 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
43
56
|
}),
|
|
44
57
|
start: Flags.string({
|
|
45
58
|
description: 'Start time (ISO 8601)',
|
|
59
|
+
required: true,
|
|
46
60
|
}),
|
|
47
61
|
stream: Flags.string({
|
|
48
62
|
char: 's',
|
|
49
63
|
description: 'Stream name(s), can be specified multiple times',
|
|
50
64
|
multiple: true,
|
|
51
|
-
required: true,
|
|
52
65
|
}),
|
|
53
66
|
type: Flags.string({
|
|
54
67
|
description: 'Filter by stream type',
|
|
@@ -56,31 +69,76 @@ hour, 4 hours, 12 hours, day, week, month, year.`;
|
|
|
56
69
|
};
|
|
57
70
|
static summary = 'Query telemetry stream data';
|
|
58
71
|
async run() {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
let streamNames = this.flags.stream ?? [];
|
|
73
|
+
// --all-streams validation
|
|
74
|
+
if (this.flags['all-streams']) {
|
|
75
|
+
if (!this.flags['latest-values-only']) {
|
|
76
|
+
this.error('--all-streams requires --latest-values-only');
|
|
77
|
+
}
|
|
78
|
+
if (this.flags.device.length !== 1) {
|
|
79
|
+
this.error('--all-streams requires exactly one --device');
|
|
80
|
+
}
|
|
81
|
+
const deviceId = this.flags.device[0];
|
|
82
|
+
const discovered = new Set();
|
|
83
|
+
// ── Config-based discovery ──────────────────────────────────────────────
|
|
84
|
+
const device = await this.api('admin', `devices/${deviceId}`, {
|
|
85
|
+
method: 'GET',
|
|
86
|
+
});
|
|
87
|
+
const configVersion = device.desiredConfigurationVersion;
|
|
88
|
+
if (configVersion) {
|
|
89
|
+
const config = await this.api('admin', `devices/${deviceId}/configurations/${configVersion}`, { method: 'GET' });
|
|
90
|
+
const doc = config.document;
|
|
91
|
+
const telemetry = doc?.telemetry;
|
|
92
|
+
const configStreams = telemetry?.streams || [];
|
|
93
|
+
for (const s of configStreams) {
|
|
94
|
+
const name = s.name;
|
|
95
|
+
if (name)
|
|
96
|
+
discovered.add(name);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// ── Data-based discovery (metadata endpoint) ───────────────────────────
|
|
100
|
+
const since = new Date();
|
|
101
|
+
since.setDate(since.getDate() - this.flags.days);
|
|
102
|
+
try {
|
|
103
|
+
const metaResult = await this.api('query', 'metadata', {
|
|
104
|
+
body: {
|
|
105
|
+
deviceIds: [deviceId],
|
|
106
|
+
start: since.toISOString(),
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
for (const item of metaResult?.items ?? []) {
|
|
110
|
+
if (item.name)
|
|
111
|
+
discovered.add(item.name);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// metadata endpoint failure is non-fatal
|
|
116
|
+
}
|
|
117
|
+
streamNames = [...discovered];
|
|
118
|
+
if (streamNames.length === 0) {
|
|
119
|
+
this.error('No streams found for this device (neither configured nor in ingested data)');
|
|
120
|
+
}
|
|
121
|
+
if (!this.jsonEnabled()) {
|
|
122
|
+
this.log(`\nDiscovered ${streamNames.length} streams (config + data, last ${this.flags.days}d).\n`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (streamNames.length === 0) {
|
|
126
|
+
this.error('Specify --stream names or use --all-streams with --latest-values-only');
|
|
62
127
|
}
|
|
63
128
|
const body = {
|
|
64
129
|
deviceIds: this.flags.device,
|
|
65
|
-
names:
|
|
130
|
+
names: streamNames,
|
|
131
|
+
start: this.normalizeDateTime(this.flags.start),
|
|
132
|
+
end: this.normalizeDateTime(this.flags.end),
|
|
66
133
|
};
|
|
67
|
-
if (this.flags.start)
|
|
68
|
-
body.start = this.normalizeDateTime(this.flags.start);
|
|
69
|
-
if (this.flags.end)
|
|
70
|
-
body.end = this.normalizeDateTime(this.flags.end);
|
|
71
134
|
if (this.flags.type)
|
|
72
135
|
body.types = [this.flags.type];
|
|
73
136
|
if (this.flags.aggregate)
|
|
74
137
|
body.aggregate = this.flags.aggregate;
|
|
75
|
-
if (this.flags
|
|
138
|
+
if (this.flags['latest-values-only'])
|
|
76
139
|
body.latestOnly = true;
|
|
77
140
|
if (this.flags.limit)
|
|
78
141
|
body.limit = this.flags.limit;
|
|
79
|
-
// If using --latest and no time range given, use a reasonable default
|
|
80
|
-
if (this.flags.latest && !this.flags.start) {
|
|
81
|
-
body.start = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
82
|
-
body.end = new Date().toISOString();
|
|
83
|
-
}
|
|
84
142
|
const result = await this.api('query', 'queries', { body });
|
|
85
143
|
if (!this.jsonEnabled()) {
|
|
86
144
|
this.log(`\nTelemetry Data (${this.env} environment):\n`);
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/query/index.ts"],"names":[],"mappings":"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"}
|
package/dist/help.js
CHANGED
|
@@ -51,6 +51,7 @@ GLOBAL FLAGS
|
|
|
51
51
|
--dev Target the dev environment (for testing)
|
|
52
52
|
--stage Target the stage environment (for staging)
|
|
53
53
|
--json Output raw JSON instead of formatted tables
|
|
54
|
+
--toon Output data in TOON (Token-Oriented Object Notation) format
|
|
54
55
|
-h, --help Show help for any command
|
|
55
56
|
|
|
56
57
|
AUTHENTICATION
|
|
@@ -75,7 +76,7 @@ EXAMPLES
|
|
|
75
76
|
|
|
76
77
|
# Telemetry queries
|
|
77
78
|
$ fcli query --device <id> --stream battery_level --start 2026-01-01 --end 2026-01-02
|
|
78
|
-
$ fcli query
|
|
79
|
+
$ fcli query --device <id> --stream battery_level --latest-values-only
|
|
79
80
|
$ fcli device streams <device-id> # List available streams
|
|
80
81
|
|
|
81
82
|
# Ingest telemetry data
|
|
@@ -133,6 +134,7 @@ EXAMPLES
|
|
|
133
134
|
TIPS
|
|
134
135
|
* Use --json with any command to get machine-readable output for scripting
|
|
135
136
|
* Pipe output to jq for advanced JSON processing: fcli device list --json | jq
|
|
137
|
+
* Use --toon for compact Token-Oriented Object Notation output (great for LLM prompts)
|
|
136
138
|
* Set --dev or --stage to target non-production environments
|
|
137
139
|
* Use fcli <command> --help to see detailed help for any command
|
|
138
140
|
|
package/dist/help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,aAAa,CAAA;AAEhC,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,IAAI;IACrB,KAAK,CAAC,YAAY;QACnC,MAAM,MAAM,GAAG;;kDAE+B,IAAI,CAAC,MAAM,CAAC,OAAO
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,aAAa,CAAA;AAEhC,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,IAAI;IACrB,KAAK,CAAC,YAAY;QACnC,MAAM,MAAM,GAAG;;kDAE+B,IAAI,CAAC,MAAM,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2IpE,CAAA;QAEG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAClB,CAAC;CACF"}
|