flipstream 0.4.0 → 0.6.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 +357 -27
- package/dist/commands/auth/login.js +7 -1
- package/dist/commands/auth/status.js +29 -3
- package/dist/commands/catalog.d.ts +15 -0
- package/dist/commands/catalog.js +110 -0
- package/dist/commands/connections/list.d.ts +1 -0
- package/dist/commands/connections/list.js +31 -4
- package/dist/commands/contract.d.ts +11 -0
- package/dist/commands/contract.js +35 -0
- package/dist/commands/health.d.ts +10 -0
- package/dist/commands/health.js +31 -0
- package/dist/commands/log/add.d.ts +16 -0
- package/dist/commands/log/add.js +48 -0
- package/dist/commands/log/list.d.ts +19 -0
- package/dist/commands/log/list.js +43 -0
- package/dist/commands/query.d.ts +15 -2
- package/dist/commands/query.js +255 -42
- package/dist/commands/skills/install.d.ts +16 -0
- package/dist/commands/skills/install.js +55 -0
- package/dist/commands/workspaces/connections.js +6 -3
- package/dist/commands/workspaces/get.js +4 -2
- package/dist/commands/workspaces/list.js +3 -0
- package/dist/lib/api/admin-client.d.ts +5 -0
- package/dist/lib/api/admin-client.js +19 -0
- package/dist/lib/api/connections.d.ts +0 -1
- package/dist/lib/api/connections.js +0 -25
- package/dist/lib/api/errors.d.ts +1 -0
- package/dist/lib/api/errors.js +13 -2
- package/dist/lib/api/http.d.ts +2 -0
- package/dist/lib/api/http.js +40 -4
- package/dist/lib/api/hydrate.d.ts +10 -0
- package/dist/lib/api/hydrate.js +46 -0
- package/dist/lib/api/ids.d.ts +1 -0
- package/dist/lib/api/ids.js +5 -0
- package/dist/lib/api/log.d.ts +22 -0
- package/dist/lib/api/log.js +56 -0
- package/dist/lib/api/projections.d.ts +1 -0
- package/dist/lib/api/projections.js +23 -0
- package/dist/lib/api/short-uuid.d.ts +1 -0
- package/dist/lib/api/short-uuid.js +30 -0
- package/dist/lib/auth/claims.js +3 -3
- package/dist/lib/auth/flow.js +8 -1
- package/dist/lib/auth/headless.js +14 -10
- package/dist/lib/auth/refresh.js +21 -1
- package/dist/lib/command/admin.d.ts +1 -0
- package/dist/lib/command/admin.js +21 -0
- package/dist/lib/command/base.d.ts +4 -0
- package/dist/lib/command/base.js +97 -3
- package/dist/lib/command/flags.d.ts +4 -0
- package/dist/lib/command/flags.js +11 -0
- package/dist/lib/command/planner.d.ts +9 -0
- package/dist/lib/command/planner.js +14 -0
- package/dist/lib/config/constants.d.ts +3 -1
- package/dist/lib/config/constants.js +14 -1
- package/dist/lib/config/xdg.d.ts +4 -0
- package/dist/lib/config/xdg.js +56 -1
- package/dist/lib/errors.d.ts +20 -1
- package/dist/lib/errors.js +132 -13
- package/dist/lib/output/dialogs.d.ts +27 -0
- package/dist/lib/output/dialogs.js +94 -0
- package/dist/lib/output/interactivity.d.ts +11 -0
- package/dist/lib/output/interactivity.js +48 -0
- package/dist/lib/output/redact.d.ts +1 -0
- package/dist/lib/output/redact.js +12 -0
- package/dist/lib/output/runlog.d.ts +3 -0
- package/dist/lib/output/runlog.js +72 -0
- package/dist/lib/output/sanitize.d.ts +2 -0
- package/dist/lib/output/sanitize.js +57 -0
- package/dist/lib/output/sidecar.d.ts +30 -0
- package/dist/lib/output/sidecar.js +58 -0
- package/dist/lib/output/table.js +5 -1
- package/dist/lib/output/trace.d.ts +11 -0
- package/dist/lib/output/trace.js +89 -0
- package/dist/lib/planner/catalog.d.ts +26 -0
- package/dist/lib/planner/catalog.js +60 -0
- package/dist/lib/planner/client.d.ts +14 -0
- package/dist/lib/planner/client.js +47 -0
- package/dist/lib/planner/connection.d.ts +14 -0
- package/dist/lib/planner/connection.js +139 -0
- package/dist/lib/planner/diagnose.d.ts +8 -0
- package/dist/lib/planner/diagnose.js +50 -0
- package/dist/lib/planner/errors.d.ts +14 -0
- package/dist/lib/planner/errors.js +129 -0
- package/dist/lib/planner/filters.d.ts +8 -0
- package/dist/lib/planner/filters.js +74 -0
- package/dist/lib/planner/request.d.ts +24 -0
- package/dist/lib/planner/request.js +51 -0
- package/dist/lib/planner/suggest.d.ts +2 -0
- package/dist/lib/planner/suggest.js +45 -0
- package/dist/lib/planner/vocabulary.d.ts +9 -0
- package/dist/lib/planner/vocabulary.js +95 -0
- package/dist/lib/skills/install.d.ts +24 -0
- package/dist/lib/skills/install.js +69 -0
- package/dist/lib/store/keyring.d.ts +3 -0
- package/dist/lib/store/keyring.js +45 -2
- package/dist/lib/store/memory-store.d.ts +1 -0
- package/dist/lib/store/memory-store.js +5 -0
- package/docs/AGENT-CONTRACT.md +238 -0
- package/oclif.manifest.json +606 -8
- package/package.json +22 -3
- package/skill/SKILL.md +55 -0
- package/dist/lib/auth/register.d.ts +0 -4
- package/dist/lib/auth/register.js +0 -43
package/README.md
CHANGED
|
@@ -6,25 +6,20 @@ authenticates once and prints clean **structured JSON** for AI coding agents.
|
|
|
6
6
|
> **Architecture:** this supersedes `flipstream-cli-kickoff.md`. See [`CLAUDE.md`](./CLAUDE.md)
|
|
7
7
|
> for the corrected model (thin client, two hosts, **no natural-language "ask"**).
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
- [Bun](https://bun.sh) (development toolchain)
|
|
12
|
-
- Node.js **20+** (the published CLI runs under Node)
|
|
13
|
-
|
|
14
|
-
## Install (development)
|
|
9
|
+
## Install
|
|
15
10
|
|
|
16
11
|
```sh
|
|
17
|
-
|
|
18
|
-
bun run build
|
|
12
|
+
npm i -g flipstream # requires Node.js 20+
|
|
19
13
|
```
|
|
20
14
|
|
|
21
15
|
## Usage
|
|
22
16
|
|
|
23
17
|
```sh
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
flipstream --help
|
|
19
|
+
flipstream --version
|
|
20
|
+
flipstream version --json # {version,node,platform,contractVersion}
|
|
21
|
+
flipstream auth login # opens the browser to sign in (OAuth2 + PKCE)
|
|
22
|
+
flipstream ws list # list your organization's workspaces
|
|
28
23
|
```
|
|
29
24
|
|
|
30
25
|
## For agents (Claude Code / Codex / MCP)
|
|
@@ -46,19 +41,30 @@ taxonomy, per-command shapes, `CONTRACT_VERSION`) lives in
|
|
|
46
41
|
[`docs/AGENT-CONTRACT.md`](./docs/AGENT-CONTRACT.md) and is enforced by a
|
|
47
42
|
conformance suite.
|
|
48
43
|
|
|
49
|
-
##
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
Built with [Bun](https://bun.sh); the published CLI runs under **Node.js 20+**.
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
bun install
|
|
50
|
+
bun run build
|
|
51
|
+
./bin/run.js --help # run from source (or during dev: bun bin/dev.js --help)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Scripts:
|
|
50
55
|
|
|
51
56
|
- `bun run build` — compile TypeScript to `dist/`
|
|
52
57
|
- `bun run lint` — eslint (flat config)
|
|
53
58
|
- `bun run check:secrets` — static deny-list gate over `src/lib/auth` (CI-enforced)
|
|
54
59
|
- `bun run format` — prettier
|
|
55
60
|
- `bun test` — run the test suite
|
|
61
|
+
- `bun run smoke` — pack the tarball + run the packaged binary (E5-4)
|
|
56
62
|
- `bun run dev` — run the CLI from source (`./bin/dev.js`)
|
|
57
63
|
|
|
58
64
|
## Security
|
|
59
65
|
|
|
60
|
-
The CLI is a **public OAuth2 client** (Authorization Code + PKCE
|
|
61
|
-
|
|
66
|
+
The CLI is a **public OAuth2 client** (Authorization Code + PKCE, a fixed
|
|
67
|
+
first-party `client_id`) — it holds **no client secret**.
|
|
62
68
|
|
|
63
69
|
- **Tokens live only in the OS keychain** (`@napi-rs/keyring`, service id
|
|
64
70
|
`io.flipstream.cli`); nothing is written to disk in plaintext, and the
|
|
@@ -101,10 +107,18 @@ If you already removed the binary, clean up manually:
|
|
|
101
107
|
* [`flipstream auth login`](#flipstream-auth-login)
|
|
102
108
|
* [`flipstream auth logout`](#flipstream-auth-logout)
|
|
103
109
|
* [`flipstream auth status`](#flipstream-auth-status)
|
|
110
|
+
* [`flipstream catalog [SOURCE]`](#flipstream-catalog-source)
|
|
104
111
|
* [`flipstream commands`](#flipstream-commands)
|
|
105
112
|
* [`flipstream conn list`](#flipstream-conn-list)
|
|
106
113
|
* [`flipstream connections list`](#flipstream-connections-list)
|
|
114
|
+
* [`flipstream contract`](#flipstream-contract)
|
|
115
|
+
* [`flipstream health`](#flipstream-health)
|
|
116
|
+
* [`flipstream log add MESSAGE`](#flipstream-log-add-message)
|
|
117
|
+
* [`flipstream log list`](#flipstream-log-list)
|
|
118
|
+
* [`flipstream logs add MESSAGE`](#flipstream-logs-add-message)
|
|
119
|
+
* [`flipstream logs list`](#flipstream-logs-list)
|
|
107
120
|
* [`flipstream query`](#flipstream-query)
|
|
121
|
+
* [`flipstream skills install`](#flipstream-skills-install)
|
|
108
122
|
* [`flipstream version`](#flipstream-version)
|
|
109
123
|
* [`flipstream workspaces connections ID`](#flipstream-workspaces-connections-id)
|
|
110
124
|
* [`flipstream workspaces get ID`](#flipstream-workspaces-get-id)
|
|
@@ -230,6 +244,45 @@ EXAMPLES
|
|
|
230
244
|
$ flipstream auth status --json
|
|
231
245
|
```
|
|
232
246
|
|
|
247
|
+
## `flipstream catalog [SOURCE]`
|
|
248
|
+
|
|
249
|
+
Show the dimensions and metrics a source accepts.
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
USAGE
|
|
253
|
+
$ flipstream catalog [SOURCE] [--json] [-H <value>] [--ndjson] [--timeout <value>] [--verbose] [--auth-host
|
|
254
|
+
<value>] [--url <value>]
|
|
255
|
+
|
|
256
|
+
ARGUMENTS
|
|
257
|
+
[SOURCE] A source name (omit to list every source).
|
|
258
|
+
|
|
259
|
+
FLAGS
|
|
260
|
+
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
261
|
+
--auth-host=<value> OAuth issuer host for credentials/refresh (defaults to the prod issuer).
|
|
262
|
+
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
263
|
+
--timeout=<value> Network timeout in milliseconds.
|
|
264
|
+
--url=<value> Query-planner base URL (default: the prod planner; env FLIPSTREAM_PLANNER_URL).
|
|
265
|
+
--verbose Print redacted diagnostics to stderr.
|
|
266
|
+
|
|
267
|
+
GLOBAL FLAGS
|
|
268
|
+
--json Format output as json.
|
|
269
|
+
|
|
270
|
+
DESCRIPTION
|
|
271
|
+
Show the dimensions and metrics a source accepts.
|
|
272
|
+
|
|
273
|
+
Print the request vocabulary the query planner accepts. The names shown ARE what `query` takes: `-d` and `-m` and
|
|
274
|
+
filter keys use an item's `name` (the wire name, e.g. search_date), never its `label` (what a human reads, e.g. Date).
|
|
275
|
+
Read from the SERVICE, never from a local data-model checkout — the two differ exactly when it matters most, right
|
|
276
|
+
after a version bump.
|
|
277
|
+
|
|
278
|
+
EXAMPLES
|
|
279
|
+
$ flipstream catalog
|
|
280
|
+
|
|
281
|
+
$ flipstream catalog gsc
|
|
282
|
+
|
|
283
|
+
$ flipstream catalog gsc --json
|
|
284
|
+
```
|
|
285
|
+
|
|
233
286
|
## `flipstream commands`
|
|
234
287
|
|
|
235
288
|
List all flipstream commands.
|
|
@@ -341,20 +394,297 @@ EXAMPLES
|
|
|
341
394
|
$ flipstream conn list --all
|
|
342
395
|
```
|
|
343
396
|
|
|
397
|
+
## `flipstream contract`
|
|
398
|
+
|
|
399
|
+
Print the agent contract (machine-facing promises) to stdout.
|
|
400
|
+
|
|
401
|
+
```
|
|
402
|
+
USAGE
|
|
403
|
+
$ flipstream contract [--json] [-H <value>] [--ndjson] [--timeout <value>] [--verbose]
|
|
404
|
+
|
|
405
|
+
FLAGS
|
|
406
|
+
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
407
|
+
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
408
|
+
--timeout=<value> Network timeout in milliseconds.
|
|
409
|
+
--verbose Print redacted diagnostics to stderr.
|
|
410
|
+
|
|
411
|
+
GLOBAL FLAGS
|
|
412
|
+
--json Format output as json.
|
|
413
|
+
|
|
414
|
+
DESCRIPTION
|
|
415
|
+
Print the agent contract (machine-facing promises) to stdout.
|
|
416
|
+
|
|
417
|
+
Print docs/AGENT-CONTRACT.md — the frozen machine contract (output modes, exit codes, error envelope, per-command JSON
|
|
418
|
+
shapes) — to stdout, from the installed package, no network needed. Context self-service for agents: read this before
|
|
419
|
+
driving the CLI programmatically.
|
|
420
|
+
|
|
421
|
+
EXAMPLES
|
|
422
|
+
$ flipstream contract
|
|
423
|
+
|
|
424
|
+
$ flipstream contract --json
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## `flipstream health`
|
|
428
|
+
|
|
429
|
+
Check that the query planner is reachable.
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
USAGE
|
|
433
|
+
$ flipstream health [--json] [-H <value>] [--ndjson] [--timeout <value>] [--verbose] [--url <value>]
|
|
434
|
+
|
|
435
|
+
FLAGS
|
|
436
|
+
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
437
|
+
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
438
|
+
--timeout=<value> Network timeout in milliseconds.
|
|
439
|
+
--url=<value> Query-planner base URL (default: the prod planner; env FLIPSTREAM_PLANNER_URL).
|
|
440
|
+
--verbose Print redacted diagnostics to stderr.
|
|
441
|
+
|
|
442
|
+
GLOBAL FLAGS
|
|
443
|
+
--json Format output as json.
|
|
444
|
+
|
|
445
|
+
DESCRIPTION
|
|
446
|
+
Check that the query planner is reachable.
|
|
447
|
+
|
|
448
|
+
Check that the query planner is answering. This is the ONE endpoint that takes no token, so it separates "the service
|
|
449
|
+
is down" from "my credentials are wrong" before any auth debugging starts.
|
|
450
|
+
|
|
451
|
+
EXAMPLES
|
|
452
|
+
$ flipstream health
|
|
453
|
+
|
|
454
|
+
$ flipstream health --json
|
|
455
|
+
|
|
456
|
+
$ flipstream health --url http://localhost:8080
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## `flipstream log add MESSAGE`
|
|
460
|
+
|
|
461
|
+
Add an entry to a workspace's log.
|
|
462
|
+
|
|
463
|
+
```
|
|
464
|
+
USAGE
|
|
465
|
+
$ flipstream log add MESSAGE --workspace <value> [--json] [-H <value>] [--ndjson] [--timeout <value>]
|
|
466
|
+
[--verbose] [--end <value>] [--start <value>]
|
|
467
|
+
|
|
468
|
+
ARGUMENTS
|
|
469
|
+
MESSAGE The log entry text.
|
|
470
|
+
|
|
471
|
+
FLAGS
|
|
472
|
+
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
473
|
+
--end=<value> End date (ISO 8601). Defaults to now.
|
|
474
|
+
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
475
|
+
--start=<value> Start date (ISO 8601). Defaults to now.
|
|
476
|
+
--timeout=<value> Network timeout in milliseconds.
|
|
477
|
+
--verbose Print redacted diagnostics to stderr.
|
|
478
|
+
--workspace=<value> (required) Workspace id (UUID) the entry belongs to.
|
|
479
|
+
|
|
480
|
+
GLOBAL FLAGS
|
|
481
|
+
--json Format output as json.
|
|
482
|
+
|
|
483
|
+
DESCRIPTION
|
|
484
|
+
Add an entry to a workspace's log.
|
|
485
|
+
|
|
486
|
+
Add an entry to a workspace's log (the logbook). You give the workspace (--workspace/--ws) and the message; the
|
|
487
|
+
organization is derived from the workspace, and the dates default to now (override with --start/--end). Returns the
|
|
488
|
+
created entry. Targets the OAuth/admin host.
|
|
489
|
+
|
|
490
|
+
ALIASES
|
|
491
|
+
$ flipstream logs add
|
|
492
|
+
|
|
493
|
+
EXAMPLES
|
|
494
|
+
$ flipstream log add "shipped the new pipeline" --workspace <id>
|
|
495
|
+
|
|
496
|
+
$ flipstream log add "kickoff call done" --ws <id> --json
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## `flipstream log list`
|
|
500
|
+
|
|
501
|
+
Read a workspace's log entries.
|
|
502
|
+
|
|
503
|
+
```
|
|
504
|
+
USAGE
|
|
505
|
+
$ flipstream log list --workspace <value> [--json] [-H <value>] [--ndjson] [--timeout <value>] [--verbose]
|
|
506
|
+
[--all] [--limit <value>] [--offset <value>] [--q <value>] [--sort <value>]
|
|
507
|
+
|
|
508
|
+
FLAGS
|
|
509
|
+
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
510
|
+
--all Fetch every page (drains pagination).
|
|
511
|
+
--limit=<value> [default: 50] Page size.
|
|
512
|
+
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
513
|
+
--offset=<value> Starting offset.
|
|
514
|
+
--q=<value> Free-text filter.
|
|
515
|
+
--sort=<value> Sort field.
|
|
516
|
+
--timeout=<value> Network timeout in milliseconds.
|
|
517
|
+
--verbose Print redacted diagnostics to stderr.
|
|
518
|
+
--workspace=<value> (required) The workspace whose log to read (UUID).
|
|
519
|
+
|
|
520
|
+
GLOBAL FLAGS
|
|
521
|
+
--json Format output as json.
|
|
522
|
+
|
|
523
|
+
DESCRIPTION
|
|
524
|
+
Read a workspace's log entries.
|
|
525
|
+
|
|
526
|
+
Read a workspace's log entries (the logbook). --workspace is required — a log entry only makes sense in a workspace's
|
|
527
|
+
context, so there is no account-wide read. Shows each entry's description, dates, and author; the full record
|
|
528
|
+
(including any new fields like tags) is under --json. Targets the OAuth/admin host.
|
|
529
|
+
|
|
530
|
+
ALIASES
|
|
531
|
+
$ flipstream logs list
|
|
532
|
+
|
|
533
|
+
EXAMPLES
|
|
534
|
+
$ flipstream log list --workspace <id>
|
|
535
|
+
|
|
536
|
+
$ flipstream logs list --ws <id> --json
|
|
537
|
+
|
|
538
|
+
$ flipstream log list --ws <id> --all
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## `flipstream logs add MESSAGE`
|
|
542
|
+
|
|
543
|
+
Add an entry to a workspace's log.
|
|
544
|
+
|
|
545
|
+
```
|
|
546
|
+
USAGE
|
|
547
|
+
$ flipstream logs add MESSAGE --workspace <value> [--json] [-H <value>] [--ndjson] [--timeout <value>]
|
|
548
|
+
[--verbose] [--end <value>] [--start <value>]
|
|
549
|
+
|
|
550
|
+
ARGUMENTS
|
|
551
|
+
MESSAGE The log entry text.
|
|
552
|
+
|
|
553
|
+
FLAGS
|
|
554
|
+
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
555
|
+
--end=<value> End date (ISO 8601). Defaults to now.
|
|
556
|
+
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
557
|
+
--start=<value> Start date (ISO 8601). Defaults to now.
|
|
558
|
+
--timeout=<value> Network timeout in milliseconds.
|
|
559
|
+
--verbose Print redacted diagnostics to stderr.
|
|
560
|
+
--workspace=<value> (required) Workspace id (UUID) the entry belongs to.
|
|
561
|
+
|
|
562
|
+
GLOBAL FLAGS
|
|
563
|
+
--json Format output as json.
|
|
564
|
+
|
|
565
|
+
DESCRIPTION
|
|
566
|
+
Add an entry to a workspace's log.
|
|
567
|
+
|
|
568
|
+
Add an entry to a workspace's log (the logbook). You give the workspace (--workspace/--ws) and the message; the
|
|
569
|
+
organization is derived from the workspace, and the dates default to now (override with --start/--end). Returns the
|
|
570
|
+
created entry. Targets the OAuth/admin host.
|
|
571
|
+
|
|
572
|
+
ALIASES
|
|
573
|
+
$ flipstream logs add
|
|
574
|
+
|
|
575
|
+
EXAMPLES
|
|
576
|
+
$ flipstream log add "shipped the new pipeline" --workspace <id>
|
|
577
|
+
|
|
578
|
+
$ flipstream log add "kickoff call done" --ws <id> --json
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
## `flipstream logs list`
|
|
582
|
+
|
|
583
|
+
Read a workspace's log entries.
|
|
584
|
+
|
|
585
|
+
```
|
|
586
|
+
USAGE
|
|
587
|
+
$ flipstream logs list --workspace <value> [--json] [-H <value>] [--ndjson] [--timeout <value>] [--verbose]
|
|
588
|
+
[--all] [--limit <value>] [--offset <value>] [--q <value>] [--sort <value>]
|
|
589
|
+
|
|
590
|
+
FLAGS
|
|
591
|
+
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
592
|
+
--all Fetch every page (drains pagination).
|
|
593
|
+
--limit=<value> [default: 50] Page size.
|
|
594
|
+
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
595
|
+
--offset=<value> Starting offset.
|
|
596
|
+
--q=<value> Free-text filter.
|
|
597
|
+
--sort=<value> Sort field.
|
|
598
|
+
--timeout=<value> Network timeout in milliseconds.
|
|
599
|
+
--verbose Print redacted diagnostics to stderr.
|
|
600
|
+
--workspace=<value> (required) The workspace whose log to read (UUID).
|
|
601
|
+
|
|
602
|
+
GLOBAL FLAGS
|
|
603
|
+
--json Format output as json.
|
|
604
|
+
|
|
605
|
+
DESCRIPTION
|
|
606
|
+
Read a workspace's log entries.
|
|
607
|
+
|
|
608
|
+
Read a workspace's log entries (the logbook). --workspace is required — a log entry only makes sense in a workspace's
|
|
609
|
+
context, so there is no account-wide read. Shows each entry's description, dates, and author; the full record
|
|
610
|
+
(including any new fields like tags) is under --json. Targets the OAuth/admin host.
|
|
611
|
+
|
|
612
|
+
ALIASES
|
|
613
|
+
$ flipstream logs list
|
|
614
|
+
|
|
615
|
+
EXAMPLES
|
|
616
|
+
$ flipstream log list --workspace <id>
|
|
617
|
+
|
|
618
|
+
$ flipstream logs list --ws <id> --json
|
|
619
|
+
|
|
620
|
+
$ flipstream log list --ws <id> --all
|
|
621
|
+
```
|
|
622
|
+
|
|
344
623
|
## `flipstream query`
|
|
345
624
|
|
|
346
|
-
Run
|
|
625
|
+
Run a logical query against the Flipstream query planner.
|
|
347
626
|
|
|
348
627
|
```
|
|
349
628
|
USAGE
|
|
350
629
|
$ flipstream query [--json] [-H <value>] [--ndjson] [--timeout <value>] [--verbose] [--auth-host <value>]
|
|
351
|
-
[--body <value> | --body-file <value>]
|
|
630
|
+
[--url <value>] [--body <value> | --body-file <value>] [--connection-id <value>] [-d <value>...] [--dry-run] [-f
|
|
631
|
+
<value>...] [-m <value>...] [--offset <value>] [--rows <value>] [-s <value>...] [--source <value>] [--table <value>]
|
|
632
|
+
|
|
633
|
+
FLAGS
|
|
634
|
+
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
635
|
+
-d, --dimension=<value>... Dimension name (repeatable).
|
|
636
|
+
-f, --filter=<value>... Filter as col=a,b | col=from..to | col=<json> (repeatable). 'col=' is refused.
|
|
637
|
+
-m, --metric=<value>... Metric name (repeatable).
|
|
638
|
+
-s, --sort=<value>... FIELD[:asc|desc] (repeatable).
|
|
639
|
+
--auth-host=<value> OAuth issuer host for credentials/refresh (defaults to the prod issuer).
|
|
640
|
+
--body=<value> Send this JSON body verbatim.
|
|
641
|
+
--body-file=<value> Path to a file holding the JSON body verbatim ("-" reads stdin).
|
|
642
|
+
--connection-id=<value> Connection UUID, or a name to look up.
|
|
643
|
+
--dry-run Print the request body and send nothing.
|
|
644
|
+
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
645
|
+
--offset=<value> Row offset.
|
|
646
|
+
--rows=<value> Row limit (default 100).
|
|
647
|
+
--source=<value> The dataset discriminator, e.g. gsc.
|
|
648
|
+
--table=<value> Physical table — honoured only for an unmodelled source.
|
|
649
|
+
--timeout=<value> Network timeout in milliseconds.
|
|
650
|
+
--url=<value> Query-planner base URL (default: the prod planner; env FLIPSTREAM_PLANNER_URL).
|
|
651
|
+
--verbose Print redacted diagnostics to stderr.
|
|
652
|
+
|
|
653
|
+
GLOBAL FLAGS
|
|
654
|
+
--json Format output as json.
|
|
655
|
+
|
|
656
|
+
DESCRIPTION
|
|
657
|
+
Run a logical query against the Flipstream query planner.
|
|
658
|
+
|
|
659
|
+
Send a LOGICAL query (source + dimensions + metrics + filters) to the Flipstream query planner, which resolves which
|
|
660
|
+
physical table answers it and returns the rows. Run `flipstream catalog <source>` first — the names it prints ARE what
|
|
661
|
+
-d, -m and filter keys take. --body/--body-file/stdin still forwards a JSON body verbatim for shapes the flags cannot
|
|
662
|
+
express.
|
|
663
|
+
|
|
664
|
+
EXAMPLES
|
|
665
|
+
$ flipstream catalog gsc
|
|
666
|
+
|
|
667
|
+
$ flipstream query --source gsc --connection-id <id|name> -d search_date -d query -m clicks -f 'search_date=2026-01-01..2026-02-01' --rows 20
|
|
668
|
+
|
|
669
|
+
$ flipstream query --source gsc --connection-id <id> -d query -m clicks --dry-run
|
|
670
|
+
|
|
671
|
+
$ flipstream query --body-file request.json --json
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
## `flipstream skills install`
|
|
675
|
+
|
|
676
|
+
Install the flipstream skill for detected AI coding agents.
|
|
677
|
+
|
|
678
|
+
```
|
|
679
|
+
USAGE
|
|
680
|
+
$ flipstream skills install [--json] [-H <value>] [--ndjson] [--timeout <value>] [--verbose] [--agent
|
|
681
|
+
claude-code|codex|cursor|opencode...] [-y]
|
|
352
682
|
|
|
353
683
|
FLAGS
|
|
354
684
|
-H, --host=<value> OAuth provider host (defaults to the prod issuer).
|
|
355
|
-
|
|
356
|
-
--
|
|
357
|
-
|
|
685
|
+
-y, --yes Install without asking.
|
|
686
|
+
--agent=<option>... Install for this agent id only (repeatable). Known: claude-code, codex, cursor, opencode.
|
|
687
|
+
<options: claude-code|codex|cursor|opencode>
|
|
358
688
|
--ndjson Stream output as NDJSON (one compact JSON object per line).
|
|
359
689
|
--timeout=<value> Network timeout in milliseconds.
|
|
360
690
|
--verbose Print redacted diagnostics to stderr.
|
|
@@ -363,18 +693,18 @@ GLOBAL FLAGS
|
|
|
363
693
|
--json Format output as json.
|
|
364
694
|
|
|
365
695
|
DESCRIPTION
|
|
366
|
-
|
|
696
|
+
Install the flipstream skill for detected AI coding agents.
|
|
367
697
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
698
|
+
Install the flipstream skill (SKILL.md) into the global skills directory of every detected AI coding agent (Claude
|
|
699
|
+
Code, Codex, Cursor, OpenCode). Detection is local env/dir inspection only — nothing is reported anywhere. The skill
|
|
700
|
+
teaches an agent to retrieve the command surface and data vocabulary from the CLI instead of guessing.
|
|
371
701
|
|
|
372
702
|
EXAMPLES
|
|
373
|
-
$ flipstream
|
|
703
|
+
$ flipstream skills install
|
|
374
704
|
|
|
375
|
-
|
|
705
|
+
$ flipstream skills install --yes
|
|
376
706
|
|
|
377
|
-
$ flipstream
|
|
707
|
+
$ flipstream skills install --agent claude-code --agent codex
|
|
378
708
|
```
|
|
379
709
|
|
|
380
710
|
## `flipstream version`
|
|
@@ -44,6 +44,12 @@ export default class AuthLogin extends BaseCommand {
|
|
|
44
44
|
}
|
|
45
45
|
// Emit only the (redacted) decoded claims — never the raw token.
|
|
46
46
|
const claims = redact(result.claims);
|
|
47
|
-
return this.respond(claims, (data) =>
|
|
47
|
+
return this.respond(claims, (data) => {
|
|
48
|
+
this.log(JSON.stringify(data));
|
|
49
|
+
// Close the loop (E11-3, #102): a fresh login has exactly two sensible
|
|
50
|
+
// next moves — prove the pipe works, then learn the vocabulary.
|
|
51
|
+
this.footer(`\nNext: ${this.config.bin} health (checks the query planner is reachable)\n` +
|
|
52
|
+
` ${this.config.bin} catalog (lists the sources you can query)`);
|
|
53
|
+
});
|
|
48
54
|
}
|
|
49
55
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { tokenClaims } from '../../lib/auth/claims.js';
|
|
2
2
|
import { BaseCommand } from '../../lib/command/base.js';
|
|
3
3
|
import { ExitCode } from '../../lib/exit-codes.js';
|
|
4
|
-
import {
|
|
4
|
+
import { isCI } from '../../lib/output/interactivity.js';
|
|
5
|
+
import { maskAccount, redact } from '../../lib/output/redact.js';
|
|
5
6
|
import { createStore } from '../../lib/store/index.js';
|
|
7
|
+
// 'expires in 25m' below an hour, 'expires in 2h' above — never a rounded-to-0h.
|
|
8
|
+
function formatRemaining(seconds) {
|
|
9
|
+
if (seconds < 3600)
|
|
10
|
+
return `${Math.max(1, Math.round(seconds / 60))}m`;
|
|
11
|
+
return `${Math.round(seconds / 3600)}h`;
|
|
12
|
+
}
|
|
6
13
|
export default class AuthStatus extends BaseCommand {
|
|
7
14
|
static description = 'Show the current Flipstream authentication status for a host.';
|
|
8
15
|
static examples = ['<%= config.bin %> auth status', '<%= config.bin %> auth status --json'];
|
|
@@ -37,11 +44,30 @@ export default class AuthStatus extends BaseCommand {
|
|
|
37
44
|
scopes: creds.scopes,
|
|
38
45
|
});
|
|
39
46
|
return this.respond(status, () => {
|
|
40
|
-
|
|
47
|
+
// ONE formatter for both the stdout line and the stderr note — a status
|
|
48
|
+
// command must not contradict itself about its own subject (E11-2 review).
|
|
49
|
+
const remaining = formatRemaining(creds.expiresAt - nowSeconds);
|
|
50
|
+
const relative = expired ? 'expired' : `expires in ${remaining}`;
|
|
51
|
+
// Disclosure tier (E11-4): mask the account in CI human output. This is
|
|
52
|
+
// HYGIENE for the human line, not a privacy control — the --json path
|
|
53
|
+
// (the one agents/CI actually consume) keeps the stable value by
|
|
54
|
+
// contract, so a public build log is not made safe by this alone.
|
|
55
|
+
const shown = typeof displayAccount === 'string' ? displayAccount : 'unknown';
|
|
41
56
|
this.log(`Logged in to ${host}`);
|
|
42
|
-
this.log(` account: ${
|
|
57
|
+
this.log(` account: ${isCI() ? maskAccount(shown) : shown}`);
|
|
43
58
|
this.log(` expires: ${expiresAtIso} (${relative})`);
|
|
44
59
|
this.log(` scopes: ${creds.scopes.join(' ')}`);
|
|
60
|
+
// Expiry narration (E11-2, #101): state the consequence, not just the fact.
|
|
61
|
+
// An expired ACCESS token is normal — commands refresh it on use — so the
|
|
62
|
+
// warning names the failure mode that actually needs the human (refresh
|
|
63
|
+
// rejected → session_expired) rather than crying wolf.
|
|
64
|
+
if (expired) {
|
|
65
|
+
this.note('the access token has expired; commands refresh it automatically — if one fails with ' +
|
|
66
|
+
'session_expired, run `flipstream auth login`');
|
|
67
|
+
}
|
|
68
|
+
else if (creds.expiresAt - nowSeconds < 24 * 3600) {
|
|
69
|
+
this.note(`the access token expires in ${remaining} and will refresh automatically on use`);
|
|
70
|
+
}
|
|
45
71
|
});
|
|
46
72
|
}
|
|
47
73
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseCommand } from '../lib/command/base.js';
|
|
2
|
+
export default class Catalog extends BaseCommand<typeof Catalog> {
|
|
3
|
+
static args: {
|
|
4
|
+
source: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
'auth-host': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
static summary: string;
|
|
13
|
+
run(): Promise<unknown>;
|
|
14
|
+
private renderSource;
|
|
15
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../lib/command/base.js';
|
|
3
|
+
import { plannerFlags } from '../lib/command/flags.js';
|
|
4
|
+
import { authedPlannerClient } from '../lib/command/planner.js';
|
|
5
|
+
import { resolveHost, resolvePlannerUrl } from '../lib/config/xdg.js';
|
|
6
|
+
import { AuthRequiredError } from '../lib/errors.js';
|
|
7
|
+
import { redact } from '../lib/output/redact.js';
|
|
8
|
+
import { sanitizeTerminal } from '../lib/output/sanitize.js';
|
|
9
|
+
import { renderTable } from '../lib/output/table.js';
|
|
10
|
+
import { parseCatalogIndex, parseCatalogSource } from '../lib/planner/catalog.js';
|
|
11
|
+
import { mapCatalogError } from '../lib/planner/errors.js';
|
|
12
|
+
import { createStore } from '../lib/store/index.js';
|
|
13
|
+
export default class Catalog extends BaseCommand {
|
|
14
|
+
static args = {
|
|
15
|
+
source: Args.string({
|
|
16
|
+
description: 'A source name (omit to list every source).',
|
|
17
|
+
// oclif fills a MISSING positional from stdin when stdin is not a TTY, so
|
|
18
|
+
// `echo x | flipstream catalog` would silently become `catalog x` — and a
|
|
19
|
+
// spawned process (the normal case for an agent) has a non-TTY stdin it may
|
|
20
|
+
// never close, which makes that read hang forever. Nothing here wants stdin.
|
|
21
|
+
ignoreStdin: true,
|
|
22
|
+
required: false,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
static description = 'Print the request vocabulary the query planner accepts. The names shown ARE what `query` takes: `-d` and ' +
|
|
26
|
+
"`-m` and filter keys use an item's `name` (the wire name, e.g. search_date), never its `label` (what a human " +
|
|
27
|
+
'reads, e.g. Date). Read from the SERVICE, never from a local data-model checkout — the two differ exactly ' +
|
|
28
|
+
'when it matters most, right after a version bump.';
|
|
29
|
+
static examples = [
|
|
30
|
+
'<%= config.bin %> catalog',
|
|
31
|
+
'<%= config.bin %> catalog gsc',
|
|
32
|
+
'<%= config.bin %> catalog gsc --json',
|
|
33
|
+
];
|
|
34
|
+
static flags = { ...plannerFlags };
|
|
35
|
+
static summary = 'Show the dimensions and metrics a source accepts.';
|
|
36
|
+
async run() {
|
|
37
|
+
const url = resolvePlannerUrl({ urlFlag: this.flags.url });
|
|
38
|
+
const authHost = resolveHost({ hostFlag: this.flags['auth-host'] ?? this.flags.host });
|
|
39
|
+
const store = createStore();
|
|
40
|
+
if (!store.load(authHost))
|
|
41
|
+
throw AuthRequiredError.notLoggedIn();
|
|
42
|
+
const client = authedPlannerClient({ authHost, store, timeoutMs: this.flags.timeout, url });
|
|
43
|
+
const { source } = this.args;
|
|
44
|
+
let raw;
|
|
45
|
+
const started = Date.now();
|
|
46
|
+
try {
|
|
47
|
+
raw = await client.getCatalog(source, { timeoutMs: this.flags.timeout });
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw mapCatalogError(error, { bin: this.config.bin, source, url });
|
|
51
|
+
}
|
|
52
|
+
// Which host answered (E11-2): the planner and the admin API are different
|
|
53
|
+
// origins, and "which one did I just talk to" is the first debugging question.
|
|
54
|
+
this.verboseLog(`GET ${url}/catalog${source === undefined ? '' : `/${source}`} (${Date.now() - started} ms)`);
|
|
55
|
+
// Redact BEFORE the document becomes output, exactly as `query` does. The
|
|
56
|
+
// served catalog is an allowlist projection built by data-model, so nothing
|
|
57
|
+
// secret should be in it — but "should" is the service's promise, and this is
|
|
58
|
+
// our own output boundary. A projection here would instead drop fields the
|
|
59
|
+
// service legitimately adds, so redaction (which masks secret-NAMED keys and
|
|
60
|
+
// leaves the rest untouched) is the right tool: it keeps `--json` faithful to
|
|
61
|
+
// the document as served while still refusing to print a credential.
|
|
62
|
+
const payload = redact(raw);
|
|
63
|
+
if (source === undefined) {
|
|
64
|
+
const index = parseCatalogIndex(payload);
|
|
65
|
+
return this.respond(payload, () => {
|
|
66
|
+
renderTable(index.sources.map((entry) => ({ ...entry })), [
|
|
67
|
+
{ key: 'name', name: 'Source' },
|
|
68
|
+
{ key: 'label', name: 'Label' },
|
|
69
|
+
{ key: 'dimensions', name: 'Dimensions' },
|
|
70
|
+
{ key: 'metrics', name: 'Metrics' },
|
|
71
|
+
], 'No sources.');
|
|
72
|
+
// Footer to STDERR: it is guidance, not data, and stdout must stay clean.
|
|
73
|
+
const hint = index.defaultSource === undefined ? '' : `, default ${index.defaultSource}`;
|
|
74
|
+
this.footer(`\n${index.sources.length} source(s)${hint}; \`${this.config.bin} catalog ${index.defaultSource ?? '<source>'}\` for its names`);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const entry = parseCatalogSource(payload);
|
|
78
|
+
return this.respond(payload, () => this.renderSource(entry));
|
|
79
|
+
}
|
|
80
|
+
renderSource(entry) {
|
|
81
|
+
this.log(sanitizeTerminal(`${entry.name} — ${entry.label}`));
|
|
82
|
+
if (entry.description)
|
|
83
|
+
this.log(sanitizeTerminal(entry.description));
|
|
84
|
+
for (const [heading, items] of [
|
|
85
|
+
['DIMENSIONS', entry.dimensions],
|
|
86
|
+
['METRICS', entry.metrics],
|
|
87
|
+
]) {
|
|
88
|
+
this.log(`\n${heading}`);
|
|
89
|
+
renderTable(items.map((item) => ({ ...item })), [
|
|
90
|
+
{ key: 'name', name: 'Name' },
|
|
91
|
+
{ key: 'label', name: 'Label' },
|
|
92
|
+
{ key: 'type', name: 'Type' },
|
|
93
|
+
{ key: 'description', name: 'Description' },
|
|
94
|
+
], '(none)');
|
|
95
|
+
}
|
|
96
|
+
if (entry.customDimensionsMax !== undefined) {
|
|
97
|
+
this.log(`\nCUSTOM DIMENSIONS up to ${entry.customDimensionsMax} per connection`);
|
|
98
|
+
}
|
|
99
|
+
// A runnable next command built from THIS source's real names (E11-3, #102):
|
|
100
|
+
// the reader of a catalog is one edit away from a working query, so hand
|
|
101
|
+
// them that edit. stderr — guidance, not data.
|
|
102
|
+
const dimension = entry.dimensions[0]?.name;
|
|
103
|
+
const metric = entry.metrics[0]?.name;
|
|
104
|
+
if (dimension !== undefined && metric !== undefined) {
|
|
105
|
+
this.footer(`\nTry: ${this.config.bin} query --source ${entry.name} --connection-id <id|name> ` +
|
|
106
|
+
`-d ${dimension} -m ${metric} --json\n` +
|
|
107
|
+
` (${this.config.bin} connections list prints the ids)`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|