@tailglow/cli 0.7.1 → 0.9.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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # @tailglow/cli
2
2
 
3
- `tglow` is the Tailglow command line. Every command is generated from the API's own routes and
4
- validations, so the CLI, the docs site, `llms.txt` and the in-product AI assistant all describe the
5
- same API by construction.
3
+ `tglow` is the Tailglow command line. API resource commands are generated from the API's own
4
+ routes and validations, so the CLI, docs site and in-product AI assistant describe the same API.
5
+ Local commands also support sign-in, bundled documentation and file imports.
6
6
 
7
7
  ```bash
8
8
  npm install -g @tailglow/cli
@@ -10,7 +10,9 @@ tglow login
10
10
  tglow projects list
11
11
  ```
12
12
 
13
- Node 18.3 or newer. `bun add -g` and `pnpm add -g` work the same way.
13
+ Node 22 or newer. `bun add -g` and `pnpm add -g` work the same way. Update with
14
+ `npm install -g @tailglow/cli@latest`. When a newer version is out, `tglow` says so in one line on
15
+ stderr; it asks npm at most once a day.
14
16
 
15
17
  Prefer a shorter name? Alias it rather than installing over one:
16
18
 
@@ -135,8 +137,86 @@ Pages print as markdown even when piped; `--json` returns `{ path, url, content
135
137
  bun run tglow <resource> <command> # run from source, without installing
136
138
  bun run test:cli # unit tests, no network
137
139
  bun run build:llms # regenerate src/commands.generated.js and src/docs.generated.js
140
+ bun run build:import # regenerate the bundled importer and its XML worker
138
141
  ```
139
142
 
140
143
  `src/commands.generated.js` and `src/docs.generated.js` are written by `scripts/build/llms.build.js`
141
144
  and are never edited by hand. Adding a route to the API adds its command here with no change to this
142
- package, and editing a guide changes the bundled page the same way.
145
+ package, and editing a guide changes the bundled page the same way. `src/import.generated.js`, its
146
+ declarations and `src/import-worker.generated.js` are self-contained bundles of the workspace
147
+ importer written by `scripts/build/import.build.js`.
148
+
149
+ ## Import files
150
+
151
+ Scan and validate locally without credentials:
152
+
153
+ ```bash
154
+ tglow import scan export.xml
155
+ tglow import scan export.xml --record-path /HealthData/Record --full
156
+ tglow import validate events.jsonl
157
+ ```
158
+
159
+ Content detection supports XML, JSONL, JSON, CSV and TSV; use `--format` to resolve ambiguity.
160
+ Scan defaults to a bounded sample, reports its coverage, and discovers literal XML element paths.
161
+ XML automatically imports every direct child of the document root, including different record
162
+ types. Root attributes and meaningful direct text become a separate document record; a root-only
163
+ document becomes one record. `--record-path` is an optional filter for importing just part of a
164
+ file. Rows preserve the `{name, attributes, children}` tree, strings, qualified names and child
165
+ order. Line-break indentation in element-only containers is removed. Mixed text, inline spaces,
166
+ whitespace-only values, CDATA, character references and inherited `xml:space="preserve"` stay intact;
167
+ formatting separators between top-level records are omitted.
168
+ There is no Apple-specific flattening. DTDs use the documented ignore profile; declarations are not
169
+ applied and declared entities are not expanded. JSON documents have a 50 MiB limit; the other
170
+ formats stream in bounded memory. Every serialized row must fit within 1 MiB including its
171
+ newline.
172
+
173
+ Upload using an exact project, source and ingest key, with normal CLI authentication:
174
+
175
+ ```bash
176
+ tglow import events.jsonl --project prj_x --source-id src_x --ingest-key-id ik_x --collection events
177
+ ```
178
+
179
+ Alternatively pass an explicit `--ingest-url` containing its key and `--project prj_x` to verify
180
+ the expected project. Treat the URL as a credential: passing it on the command line exposes it
181
+ to shell history and process listings. The CLI never prints the URL or API credential. The
182
+ project can come from the existing environment/config default; `--project-id` is an alias for
183
+ `--project`. Source and ingest-key IDs never default or select themselves. A localhost API cannot
184
+ automatically direct an upload to a remote URL; use an explicit ingest URL for that destination.
185
+
186
+ Upload reads a bounded preview, checks destination readiness, then validates records and sends
187
+ batches as it reads. It holds one file descriptor and checks for file changes before every request
188
+ and at EOF. These checks detect ordinary replacement, edits, append and truncation; they are not
189
+ an immutable snapshot guarantee. Do not edit the input during import. No full temporary copy is
190
+ needed by default. A late parse error can leave earlier batches accepted.
191
+
192
+ Add `--validate-first` to copy the file into a private temporary directory and check the whole
193
+ snapshot before any upload. This needs free temporary disk space approximately equal to the input
194
+ size. The same snapshot is used for checking and delivery, then removed on completion, failure or
195
+ graceful Ctrl-C. A cleanup failure is reported separately as `cleanup_error` and a warning; it does
196
+ not change delivery status or acknowledged counts. Changes to the original after copying do not change the uploaded bytes. A failed
197
+ full check sends no rows; successful checking cannot prevent a later delivery failure.
198
+
199
+ Records keep their own fields, including their dates; Tailglow records when each batch arrived.
200
+ Batches stay within 500 rows and 1 MiB.
201
+
202
+ Only HTTP 202 acknowledges a batch. Upload stops on rejection or uncertain delivery and never
203
+ retries automatically. The report includes `accepted_count` and the affected batch's inclusive
204
+ `first_row`/`last_row`; these indices refer to the selected output rows, not physical file lines.
205
+ A timeout, cancellation during a request, or missing acknowledgement may mean that batch arrived.
206
+ Do not blindly rerun a partial upload: import idempotency is not provided yet.
207
+
208
+ Full scans, validation and upload read large XML files with worker threads. `--workers auto`, the
209
+ default, starts two for files of 32 MiB or more on machines with at least four logical processors;
210
+ `--workers 0` reads on the main thread, and `--workers 1` to `16` sets the count for any XML file.
211
+ Each pass checks that its workers start and answer before reading, falls back to the main thread
212
+ if they cannot, and reports what ran on stderr (`XML workers: 2.`). Reports, rows, batches and
213
+ errors are identical with any worker count; JSONL, JSON, CSV, TSV and sampled scans read on the
214
+ main thread.
215
+
216
+ Progress is written to stderr. In `--json` mode or when piped, import always writes its final
217
+ JSON report to stdout, including failures and partial uploads. `total_rows` is `null` until a full
218
+ check or clean EOF establishes it. `accepted_count` counts only acknowledged rows. The `outcome`
219
+ field distinguishes validation failures, changed files, destination failures, rejected batches,
220
+ uncertain delivery and cancellation. Exit code is 0 on success, 1 on
221
+ failure, and 130 after cancellation. Other CLI commands retain the standard failure-to-stderr
222
+ behavior described above. See `tglow import --help` for all flags.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailglow/cli",
3
- "version": "0.7.1",
3
+ "version": "0.9.4",
4
4
  "type": "module",
5
5
  "description": "The Tailglow command line. Every command is generated from the Tailglow API reference, so the CLI and the documentation can never describe different APIs.",
6
6
  "bin": {
@@ -15,7 +15,7 @@
15
15
  "src"
16
16
  ],
17
17
  "engines": {
18
- "node": ">=18.3.0"
18
+ "node": ">=22.0.0"
19
19
  },
20
20
  "homepage": "https://docs.tailglow.io",
21
21
  "bugs": {
@@ -35,6 +35,6 @@
35
35
  "@clack/prompts": "^1.2.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@types/bun": "^1.3.6"
38
+ "@types/bun": "^1.4.3"
39
39
  }
40
40
  }
@@ -3334,6 +3334,7 @@ export const CLI_COMMANDS = [
3334
3334
  "`transform_script` is TGL and must compile. A script that does not parse is rejected rather than stored, because an unparseable script would fail on every record it later touched.",
3335
3335
  "The script must write every field the view's output schema declares.",
3336
3336
  "Omitting the script queues the transform for generation instead of rejecting it.",
3337
+ "`collection_schema_version` must be settled. A candidate stays out of views until it settles, so naming one is refused with a 409; settle it with Update Collection Schema first.",
3337
3338
  "A transform on a collection-backed view must name the `collection_schema_version` it covers. View-to-view transforms pass null, because they read another view's output rather than a collection schema.",
3338
3339
  "`transform_script` cannot be whitespace-only. Omit it to have TGL generated instead."
3339
3340
  ],
@@ -3830,11 +3831,20 @@ export const CLI_COMMANDS = [
3830
3831
  "`complete_through_at` is the instant before which every number is final. Null means completeness is unknown, and then every bucket is incomplete.",
3831
3832
  "For complete buckets only, leave out every key at or after `first_incomplete_bucket_at`.",
3832
3833
  "`coverage_reasons` names unresolved view scopes; `excluded` is informational.",
3834
+ "Series are ranked by their total over the range (the count for a `count` metric, the sum of Last readings for `last`, the sum otherwise), ties broken by series hash, and `limit` of them come back per page, the metric's `ui_chart_max_series` by default. Walk the pages with `pagination.next_cursor` and `pagination.prev_cursor`; `pagination.total` counts every series.",
3835
+ "`__other__` folds in every series that is not on the current page, so the page plus Other always adds up to the range total and a share is right on every page.",
3836
+ "When the metric has pinned series, they make up the whole first page and later pages walk the other series by rank.",
3837
+ "`series` returns exactly the listed series on one page, unranked and without `__other__`.",
3838
+ "A metric with a `time_dimension` returns its range folded instead of as a timeline: every hour merges into its hour of the day, weekday or hour of the week. Record keys are then the category as a decimal string (`\"0\"` to `\"23\"`, `\"1\"` for Monday to `\"7\"` for Sunday, `\"0\"` to `\"167\"`), every category is present whatever `normalize` says, and `time_dimension` echoes it.",
3839
+ "Data is stored by UTC hour. A fold places each hour in its category in the request `timezone` (echoed as `timezone`), one hour at a time, so a daylight saving change moves only the hours after it.",
3840
+ "A fold reads hourly data whatever `interval` says, covers at most 400 days (a longer range is refused), and includes the current hour's data so far.",
3841
+ "In a fold an empty category is `0` when `empty_bucket_handling` is `zero` and `null` when it is `gaps`, cumulative values are `null`, and there is no trend line.",
3833
3842
  "The aggregation window is resolved in order: an explicit `start_at` plus `end_at` wins; otherwise `time_range` is used; otherwise the metric's saved chart range is used.",
3834
3843
  "`start_at` and `end_at` only take effect when sent together. Sending one without the other has no effect on the window.",
3835
3844
  "When both are provided, `start_at` must be earlier than `end_at`.",
3836
3845
  "`time_range=custom` without explicit dates falls back to the saved custom range of `page_id`, then the metric, then the project.",
3837
- "`timezone` must be a valid IANA timezone name, or `UTC`."
3846
+ "`timezone` must be a valid IANA timezone name, or `UTC`.",
3847
+ "`after` and `before` are mutually exclusive."
3838
3848
  ],
3839
3849
  "path_params": [
3840
3850
  {
@@ -3875,7 +3885,7 @@ export const CLI_COMMANDS = [
3875
3885
  "name": "interval",
3876
3886
  "type": "ChartInterval",
3877
3887
  "required": false,
3878
- "description": "Requested aggregation bucket interval. Optional.",
3888
+ "description": "Requested aggregation bucket interval. A metric with a time dimension ignores it and always folds hourly data. Optional.",
3879
3889
  "allowed_values": [
3880
3890
  "minute",
3881
3891
  "hour",
@@ -3920,7 +3930,7 @@ export const CLI_COMMANDS = [
3920
3930
  "name": "timezone",
3921
3931
  "type": "string",
3922
3932
  "required": false,
3923
- "description": "IANA timezone used to calculate date boundaries. Optional.",
3933
+ "description": "IANA timezone used to calculate date boundaries and to place each hour in a time dimension's categories. Optional.",
3924
3934
  "allowed_values": [],
3925
3935
  "deprecated": null
3926
3936
  },
@@ -3949,20 +3959,47 @@ export const CLI_COMMANDS = [
3949
3959
  "deprecated": null
3950
3960
  },
3951
3961
  {
3952
- "name": "max_series",
3962
+ "name": "series",
3963
+ "type": "string",
3964
+ "required": false,
3965
+ "description": "Comma-separated series hashes to return. Optional.",
3966
+ "allowed_values": [],
3967
+ "deprecated": null
3968
+ },
3969
+ {
3970
+ "name": "limit",
3953
3971
  "type": "integer",
3954
3972
  "required": false,
3955
- "description": "Maximum number of individual series to return. Optional. Minimum: `1`. Maximum: `50`.",
3973
+ "description": "Number of series to return per page. Defaults to the metric's `ui_chart_max_series`. Optional. Minimum: `1`. Maximum: `200`.",
3956
3974
  "allowed_values": [],
3957
3975
  "deprecated": null
3958
3976
  },
3959
3977
  {
3960
- "name": "series",
3978
+ "name": "after",
3961
3979
  "type": "string",
3962
3980
  "required": false,
3963
- "description": "Comma-separated series hashes to return. Optional.",
3981
+ "description": "Cursor from `pagination.next_cursor` of a previous response. Returns the resources after that page. Optional.",
3964
3982
  "allowed_values": [],
3965
3983
  "deprecated": null
3984
+ },
3985
+ {
3986
+ "name": "before",
3987
+ "type": "string",
3988
+ "required": false,
3989
+ "description": "Cursor from `pagination.prev_cursor` of a previous response. Returns the resources before that page. Optional.",
3990
+ "allowed_values": [],
3991
+ "deprecated": null
3992
+ },
3993
+ {
3994
+ "name": "sort",
3995
+ "type": "string",
3996
+ "required": false,
3997
+ "description": "Ranking direction of the series. `desc` returns the largest first. Optional. Defaults to `\"desc\"`.",
3998
+ "allowed_values": [
3999
+ "asc",
4000
+ "desc"
4001
+ ],
4002
+ "deprecated": null
3966
4003
  }
3967
4004
  ],
3968
4005
  "body_fields": []
@@ -3984,6 +4021,11 @@ export const CLI_COMMANDS = [
3984
4021
  "Field references and filters must be compatible with the selected view.",
3985
4022
  "Custom chart ranges require both dates, with an interval of at least 30 minutes.",
3986
4023
  "Color-mode configuration must match the selected chart color mode.",
4024
+ "`ui_chart_color_mode` must be a mode the chart type supports. A metric created without a `ui_chart_type` is a `bar` chart and is checked as one.",
4025
+ "`time_dimension` must be one the chart type supports: `breakdown` takes `hour_of_day` or `weekday`, `heatmap` takes `hour_of_week`, `hour_of_day` or `weekday`, and every other chart type takes none.",
4026
+ "A `heatmap` chart requires a `time_dimension`.",
4027
+ "A `time_dimension` cannot be combined with a cumulative `ui_chart_value` (`cumulative_sum` or `cumulative_count`).",
4028
+ "`ui_chart_order` is read by `breakdown` charts only; other chart types ignore it.",
3987
4029
  "`filters` must be a valid filter expression.",
3988
4030
  "`timestamp_field` cannot reference `__meta__` metadata.",
3989
4031
  "`group_by_display_fields` is set at create and cannot be changed afterwards, because each series takes its label from the first record it sees. Map a `group_by` field to a view field that carries a readable value for the same record."
@@ -4101,7 +4143,10 @@ export const CLI_COMMANDS = [
4101
4143
  "stat",
4102
4144
  "gauge",
4103
4145
  "calendar",
4104
- "uptime"
4146
+ "uptime",
4147
+ "breakdown",
4148
+ "heatmap",
4149
+ "table"
4105
4150
  ],
4106
4151
  "deprecated": null
4107
4152
  },
@@ -4305,6 +4350,29 @@ export const CLI_COMMANDS = [
4305
4350
  ],
4306
4351
  "deprecated": null
4307
4352
  },
4353
+ {
4354
+ "name": "time_dimension",
4355
+ "type": "TimeDimension",
4356
+ "required": false,
4357
+ "description": "Calendar position the chart folds the metric's hourly data into, such as the hour of the day. Null charts the data over time. Optional.",
4358
+ "allowed_values": [
4359
+ "hour_of_day",
4360
+ "weekday",
4361
+ "hour_of_week"
4362
+ ],
4363
+ "deprecated": null
4364
+ },
4365
+ {
4366
+ "name": "ui_chart_order",
4367
+ "type": "ChartOrder",
4368
+ "required": false,
4369
+ "description": "Order a breakdown chart shows its groups in. Null keeps a time dimension in its natural order and ranks field groups by value. Optional.",
4370
+ "allowed_values": [
4371
+ "natural",
4372
+ "value"
4373
+ ],
4374
+ "deprecated": null
4375
+ },
4308
4376
  {
4309
4377
  "name": "ui_chart_custom_range_start_at",
4310
4378
  "type": "ISODateString",
@@ -4325,7 +4393,7 @@ export const CLI_COMMANDS = [
4325
4393
  "name": "ui_chart_max_series",
4326
4394
  "type": "integer",
4327
4395
  "required": false,
4328
- "description": "Maximum number of individual series displayed by default. Optional. Minimum: `1`. Maximum: `50`.",
4396
+ "description": "Number of series an aggregation returns per page when the request sends no `limit`. Optional. Minimum: `1`. Maximum: `50`.",
4329
4397
  "allowed_values": [],
4330
4398
  "deprecated": null
4331
4399
  },
@@ -4359,6 +4427,11 @@ export const CLI_COMMANDS = [
4359
4427
  "Changing the metric's data feed rebuilds its historical rollups.",
4360
4428
  "Custom chart ranges require both dates, with an interval of at least 30 minutes.",
4361
4429
  "Color-mode configuration must match the selected chart color mode.",
4430
+ "`ui_chart_color_mode` must be a mode the chart type supports. Changing `ui_chart_type` keeps the current mode, so send a supported one with it.",
4431
+ "`time_dimension` must be one the chart type supports: `breakdown` takes `hour_of_day` or `weekday`, `heatmap` takes `hour_of_week`, `hour_of_day` or `weekday`, and every other chart type takes none. Send `time_dimension: null` when changing to a type that takes none.",
4432
+ "A `heatmap` chart requires a `time_dimension`.",
4433
+ "A `time_dimension` cannot be combined with a cumulative `ui_chart_value` (`cumulative_sum` or `cumulative_count`).",
4434
+ "`ui_chart_order` is read by `breakdown` charts only; other chart types ignore it.",
4362
4435
  "`filters` must be a valid filter expression.",
4363
4436
  "`timestamp_field` cannot reference `__meta__` metadata."
4364
4437
  ],
@@ -4475,7 +4548,10 @@ export const CLI_COMMANDS = [
4475
4548
  "stat",
4476
4549
  "gauge",
4477
4550
  "calendar",
4478
- "uptime"
4551
+ "uptime",
4552
+ "breakdown",
4553
+ "heatmap",
4554
+ "table"
4479
4555
  ],
4480
4556
  "deprecated": null
4481
4557
  },
@@ -4679,6 +4755,29 @@ export const CLI_COMMANDS = [
4679
4755
  ],
4680
4756
  "deprecated": null
4681
4757
  },
4758
+ {
4759
+ "name": "time_dimension",
4760
+ "type": "TimeDimension",
4761
+ "required": false,
4762
+ "description": "Calendar position the chart folds the metric's hourly data into, such as the hour of the day. Null charts the data over time. Optional.",
4763
+ "allowed_values": [
4764
+ "hour_of_day",
4765
+ "weekday",
4766
+ "hour_of_week"
4767
+ ],
4768
+ "deprecated": null
4769
+ },
4770
+ {
4771
+ "name": "ui_chart_order",
4772
+ "type": "ChartOrder",
4773
+ "required": false,
4774
+ "description": "Order a breakdown chart shows its groups in. Null keeps a time dimension in its natural order and ranks field groups by value. Optional.",
4775
+ "allowed_values": [
4776
+ "natural",
4777
+ "value"
4778
+ ],
4779
+ "deprecated": null
4780
+ },
4682
4781
  {
4683
4782
  "name": "ui_chart_custom_range_start_at",
4684
4783
  "type": "ISODateString",
@@ -4699,7 +4798,7 @@ export const CLI_COMMANDS = [
4699
4798
  "name": "ui_chart_max_series",
4700
4799
  "type": "integer",
4701
4800
  "required": false,
4702
- "description": "Maximum number of individual series displayed by default. Optional. Minimum: `1`. Maximum: `50`.",
4801
+ "description": "Number of series an aggregation returns per page when the request sends no `limit`. Optional. Minimum: `1`. Maximum: `50`.",
4703
4802
  "allowed_values": [],
4704
4803
  "deprecated": null
4705
4804
  },
@@ -6953,7 +7052,10 @@ export const CLI_COMMANDS = [
6953
7052
  ],
6954
7053
  "notes": [
6955
7054
  "A component is a card on one page showing one to six metrics together.",
6956
- "To change a whole page's layout in one write, send `components` to Update Page instead."
7055
+ "To change a whole page's layout in one write, send `components` to Update Page instead.",
7056
+ "Every metric must belong to the same project as the page, and none can already be on another card of the page.",
7057
+ "A metric can be listed only once in `metric_ids`.",
7058
+ "A page holds a limited number of components. Adding one past the limit is rejected."
6957
7059
  ],
6958
7060
  "path_params": [
6959
7061
  {
@@ -6974,7 +7076,51 @@ export const CLI_COMMANDS = [
6974
7076
  }
6975
7077
  ],
6976
7078
  "query_params": [],
6977
- "body_fields": []
7079
+ "body_fields": [
7080
+ {
7081
+ "name": "name",
7082
+ "type": "string | null",
7083
+ "required": false,
7084
+ "description": "Title of a card that shows several metrics. Send null to title it with their names. Optional.",
7085
+ "allowed_values": [],
7086
+ "deprecated": null
7087
+ },
7088
+ {
7089
+ "name": "ui_sort_index",
7090
+ "type": "integer",
7091
+ "required": false,
7092
+ "description": "Position of the component on the page. Lower appears first. Optional.",
7093
+ "allowed_values": [],
7094
+ "deprecated": null
7095
+ },
7096
+ {
7097
+ "name": "ui_card_size",
7098
+ "type": "ComponentCardSize",
7099
+ "required": false,
7100
+ "description": "How much of the page width the component occupies. Optional.",
7101
+ "allowed_values": [
7102
+ "1/1",
7103
+ "1/2"
7104
+ ],
7105
+ "deprecated": null
7106
+ },
7107
+ {
7108
+ "name": "metric_ids",
7109
+ "type": "string[]",
7110
+ "required": true,
7111
+ "description": "Metrics the card shows together, in order.",
7112
+ "allowed_values": [],
7113
+ "deprecated": null
7114
+ },
7115
+ {
7116
+ "name": "page_id",
7117
+ "type": "string",
7118
+ "required": false,
7119
+ "description": "Page the component is added to. Optional.",
7120
+ "allowed_values": [],
7121
+ "deprecated": null
7122
+ }
7123
+ ]
6978
7124
  },
6979
7125
  {
6980
7126
  "resource": "components",
@@ -7286,10 +7432,10 @@ export const CLI_COMMANDS = [
7286
7432
  "notes": [
7287
7433
  "Teams cannot be created with an API key. Use a signed-in user session.",
7288
7434
  "`name` defaults to `Team` and `billing_email` defaults to the authenticated user's email address when they are omitted.",
7435
+ "`billing_email` cannot use the `@tailglow.io` domain.",
7289
7436
  "The team starts on the `pro_v1` plan with a generated logo. `billing_plan`, `status`, `logo_url`, `is_mfa_required`, `billing_address`, and `max_servers_per_project` are ignored on create. Send them to the update endpoint instead.",
7290
7437
  "There is a maximum number of teams a single user can create.",
7291
7438
  "The authenticated user is added to the new team as its Owner.",
7292
- "`billing_email` cannot use the `@tailglow.io` domain.",
7293
7439
  "`logo_url` must be a JPEG or PNG base64 data URI of at most 5 MB."
7294
7440
  ],
7295
7441
  "path_params": [],
@@ -7441,7 +7587,7 @@ export const CLI_COMMANDS = [
7441
7587
  "Teams cannot be updated with an API key. Use a signed-in user session.",
7442
7588
  "At least one field must be provided.",
7443
7589
  "Only team Owners can change billing, security, or account settings. Other members can update `name` and `logo_url` only.",
7444
- "`status`, `max_servers_per_project`, and `deleted_at` are managed by Tailglow and cannot be set by team members.",
7590
+ "`status`, `max_servers_per_project`, and `deleted_at` are managed by Tailglow and cannot be set by team members, with one exception: an Owner restores a team scheduled for deletion by sending only `deleted_at` set to null.",
7445
7591
  "`status` requires `expected_status`, the status you loaded; the change is refused with a conflict if the team's status has changed since.",
7446
7592
  "Changing `is_mfa_required` requires a recently verified multi-factor sign-in.",
7447
7593
  "Moving to a plan that bills a payment method requires a valid payment method on the team.",
@@ -7615,7 +7761,7 @@ export const CLI_COMMANDS = [
7615
7761
  "name": "deleted_at",
7616
7762
  "type": "ISODateString | null",
7617
7763
  "required": false,
7618
- "description": "Date and time when the team is scheduled to be permanently deleted. Optional.",
7764
+ "description": "Set to null, as the only field, to restore a team scheduled for deletion. Only Tailglow can set a date. Optional.",
7619
7765
  "allowed_values": [],
7620
7766
  "deprecated": null
7621
7767
  }
@@ -7635,7 +7781,8 @@ export const CLI_COMMANDS = [
7635
7781
  "Teams cannot be deleted with an API key. Use a signed-in user session.",
7636
7782
  "Only the team Owner can delete a team.",
7637
7783
  "The team must be active. Contact support to delete a suspended or blocked team.",
7638
- "Deletion is scheduled, not immediate. Permanent removal happens 40 days later, the team's projects and API keys are cancelled, and every signed-in member is signed out. An owner who signs back in before then cancels the deletion."
7784
+ "Deletion is scheduled, not immediate. Permanent removal happens 40 days later. The team's API keys are deleted, its ingest keys are revoked, and its servers are scaled to zero.",
7785
+ "Members lose access to the team at once but stay signed in to their other teams. Until permanent removal, an Owner can restore the team by updating it with `deleted_at` set to null."
7639
7786
  ],
7640
7787
  "path_params": [
7641
7788
  {
@@ -9108,6 +9255,9 @@ export const CLI_COMMANDS = [
9108
9255
  "notes": [
9109
9256
  "A server is a dedicated machine that receives and processes the project's data. Every project runs its own fleet.",
9110
9257
  "A server that is still coming up is returned with a status of `provisioning`, and a server on its way out with a status of `terminating`.",
9258
+ "During a platform update, the server being replaced is returned with a status of `upgrading`. The `status` filter matches the status stored before the update, so it finds that server under `active`.",
9259
+ "A platform update also adds a temporary server that takes each server's records while that server is replaced. It is not billed and is left out of this list; `is_deploy_surge=true` lists it instead.",
9260
+ "`is_deploy_surge` accepts `true`, `false`, `1` or `0`.",
9111
9261
  "`after` and `before` are mutually exclusive."
9112
9262
  ],
9113
9263
  "path_params": [
@@ -9147,6 +9297,19 @@ export const CLI_COMMANDS = [
9147
9297
  ],
9148
9298
  "deprecated": null
9149
9299
  },
9300
+ {
9301
+ "name": "is_deploy_surge",
9302
+ "type": "string",
9303
+ "required": false,
9304
+ "description": "Set true to list the temporary server a platform update adds instead of the project's own servers. Optional.",
9305
+ "allowed_values": [
9306
+ "true",
9307
+ "false",
9308
+ "1",
9309
+ "0"
9310
+ ],
9311
+ "deprecated": null
9312
+ },
9150
9313
  {
9151
9314
  "name": "limit",
9152
9315
  "type": "number",
package/src/config.js CHANGED
@@ -17,7 +17,14 @@ export const CLI_CONFIGS = {
17
17
  DIRECTORY_MODE: 0o700,
18
18
  LOCK_RETRY_MS: 50,
19
19
  // The lock covers one small read and write, so waiting this long means its holder stopped.
20
- LOCK_TIMEOUT_MS: 10_000
20
+ LOCK_TIMEOUT_MS: 10_000,
21
+ UPDATE_CHECK_URL: "https://registry.npmjs.org/@tailglow/cli/latest",
22
+ UPDATE_CHECK_PATH: join(homedir(), ".tailglow", "update-check.json"),
23
+ UPDATE_CHECK_INTERVAL_MS: 24 * 60 * 60 * 1_000,
24
+ // The check runs alongside the command, so this only delays a command that finishes sooner.
25
+ UPDATE_CHECK_TIMEOUT_MS: 1_500,
26
+ // Caps the stored check and npm's answer, which is about 2 KB.
27
+ UPDATE_CHECK_MAX_BYTES: 65_536
21
28
  };
22
29
 
23
30
  export const LOGIN_CONFIGS = {