@framedash/cli 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -5
- package/dist/commands/alerts.d.ts.map +1 -1
- package/dist/commands/alerts.js +2 -1
- package/dist/commands/alerts.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +25 -2
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/builds.d.ts +2 -0
- package/dist/commands/builds.d.ts.map +1 -0
- package/dist/commands/builds.js +31 -0
- package/dist/commands/builds.js.map +1 -0
- package/dist/commands/content.d.ts.map +1 -1
- package/dist/commands/content.js +2 -2
- package/dist/commands/content.js.map +1 -1
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +3 -3
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/funnel.d.ts.map +1 -1
- package/dist/commands/funnel.js +7 -8
- package/dist/commands/funnel.js.map +1 -1
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +142 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +79 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/map-capture.d.ts +2 -1
- package/dist/commands/map-capture.d.ts.map +1 -1
- package/dist/commands/map-capture.js +42 -12
- package/dist/commands/map-capture.js.map +1 -1
- package/dist/commands/perf-diff.d.ts +2 -0
- package/dist/commands/perf-diff.d.ts.map +1 -0
- package/dist/commands/perf-diff.js +125 -0
- package/dist/commands/perf-diff.js.map +1 -0
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +12 -0
- package/dist/commands/query.js.map +1 -1
- package/dist/commands/retention.d.ts.map +1 -1
- package/dist/commands/retention.js +3 -3
- package/dist/commands/retention.js.map +1 -1
- package/dist/commands/run-profile-test.d.ts +7 -0
- package/dist/commands/run-profile-test.d.ts.map +1 -0
- package/dist/commands/run-profile-test.js +503 -0
- package/dist/commands/run-profile-test.js.map +1 -0
- package/dist/commands/threshold-profiles.d.ts +2 -0
- package/dist/commands/threshold-profiles.d.ts.map +1 -0
- package/dist/commands/threshold-profiles.js +24 -0
- package/dist/commands/threshold-profiles.js.map +1 -0
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +42 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +44 -14
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/create-client.d.ts +27 -1
- package/dist/lib/create-client.d.ts.map +1 -1
- package/dist/lib/create-client.js +167 -24
- package/dist/lib/create-client.js.map +1 -1
- package/dist/lib/formatters.js +93 -1
- package/dist/lib/formatters.js.map +1 -1
- package/dist/lib/logger.d.ts +2 -0
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +4 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/oauth/loopback-server.d.ts +27 -0
- package/dist/lib/oauth/loopback-server.d.ts.map +1 -0
- package/dist/lib/oauth/loopback-server.js +164 -0
- package/dist/lib/oauth/loopback-server.js.map +1 -0
- package/dist/lib/oauth/manager.d.ts +36 -0
- package/dist/lib/oauth/manager.d.ts.map +1 -0
- package/dist/lib/oauth/manager.js +130 -0
- package/dist/lib/oauth/manager.js.map +1 -0
- package/dist/lib/oauth/pkce.d.ts +15 -0
- package/dist/lib/oauth/pkce.d.ts.map +1 -0
- package/dist/lib/oauth/pkce.js +24 -0
- package/dist/lib/oauth/pkce.js.map +1 -0
- package/dist/lib/oauth/token-endpoint.d.ts +39 -0
- package/dist/lib/oauth/token-endpoint.d.ts.map +1 -0
- package/dist/lib/oauth/token-endpoint.js +117 -0
- package/dist/lib/oauth/token-endpoint.js.map +1 -0
- package/dist/lib/oauth/token-store.d.ts +34 -0
- package/dist/lib/oauth/token-store.d.ts.map +1 -0
- package/dist/lib/oauth/token-store.js +164 -0
- package/dist/lib/oauth/token-store.js.map +1 -0
- package/dist/lib/perf-diff-eval.d.ts +58 -0
- package/dist/lib/perf-diff-eval.d.ts.map +1 -0
- package/dist/lib/perf-diff-eval.js +49 -0
- package/dist/lib/perf-diff-eval.js.map +1 -0
- package/dist/lib/run-command.js +2 -2
- package/dist/lib/run-command.js.map +1 -1
- package/dist/lib/run-profile-test-lib.d.ts +156 -0
- package/dist/lib/run-profile-test-lib.d.ts.map +1 -0
- package/dist/lib/run-profile-test-lib.js +171 -0
- package/dist/lib/run-profile-test-lib.js.map +1 -0
- package/dist/lib/uploader.d.ts +14 -1
- package/dist/lib/uploader.d.ts.map +1 -1
- package/dist/lib/uploader.js +26 -12
- package/dist/lib/uploader.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -6,22 +6,52 @@ Framedash CLI tool for CI/CD pipelines, analytics queries, and coding agent inte
|
|
|
6
6
|
|
|
7
7
|
| Command | Description |
|
|
8
8
|
|---------|-------------|
|
|
9
|
-
| `framedash
|
|
9
|
+
| `framedash login` | Sign in via the browser (interactive OAuth; not for CI) |
|
|
10
|
+
| `framedash logout` | Revoke and remove a stored browser login |
|
|
11
|
+
| `framedash auth` | Verify credentials, show their source, and list projects |
|
|
10
12
|
| `framedash query` | Run SQL analytics queries against ClickHouse |
|
|
11
13
|
| `framedash dashboard` | Fetch dashboard KPI summary |
|
|
12
14
|
| `framedash retention` | Fetch retention cohort data |
|
|
13
15
|
| `framedash funnel` | Fetch funnel conversion data |
|
|
16
|
+
| `framedash builds` | List builds seen for the project (for `perf-diff`) |
|
|
17
|
+
| `framedash perf-diff` | Compare two builds and gate CI on a perf regression |
|
|
18
|
+
| `framedash run-profile-test` | Run a profiling build, wait for ingest, then gate on a regression |
|
|
14
19
|
| `framedash alerts` | Manage alert rules and channels |
|
|
15
20
|
| `framedash maps` | List and manage map overlays |
|
|
21
|
+
| `framedash threshold-profiles` | List threshold profiles (perf budgets for alerts) |
|
|
16
22
|
| `framedash content` | Manage content registry entries |
|
|
17
23
|
| `framedash status` | Check project ingestion status |
|
|
18
24
|
| `framedash map-capture` | Upload captured map images |
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
`framedash alerts create` requires an existing map and threshold profile: pass
|
|
27
|
+
their UUIDs via `--map-id` and `--threshold-profile-id`. Discover those UUIDs
|
|
28
|
+
without opening the dashboard by running `framedash maps list` and
|
|
29
|
+
`framedash threshold-profiles list`.
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
## Authentication
|
|
32
|
+
|
|
33
|
+
Two credential types are supported:
|
|
34
|
+
|
|
35
|
+
- API keys (recommended for CI and any non-interactive use): pass via
|
|
36
|
+
`--api-key-file` (supports `-` for piped stdin), `--api-key`, or the
|
|
37
|
+
`FRAMEDASH_API_KEY` environment variable. Prefer files or piped stdin over
|
|
38
|
+
`--api-key` for local runs so raw keys do not appear in shell history.
|
|
39
|
+
- Interactive browser login: `framedash login` runs an OAuth 2.1
|
|
40
|
+
authorization-code + PKCE flow against the Framedash authorization server
|
|
41
|
+
(loopback redirect on `127.0.0.1`). Tokens are stored per base-URL origin in
|
|
42
|
+
`$XDG_CONFIG_HOME/framedash/credentials.json` (default
|
|
43
|
+
`~/.config/framedash/credentials.json`; the same `~/.config` path is used on
|
|
44
|
+
Windows) and refreshed automatically, including refresh-token rotation.
|
|
45
|
+
`framedash logout` revokes the session server-side (best effort) and removes
|
|
46
|
+
the stored tokens; `framedash logout --all` clears every stored login.
|
|
47
|
+
|
|
48
|
+
Precedence: `--api-key` > `--api-key-file` > `FRAMEDASH_API_KEY` > stored
|
|
49
|
+
OAuth login for the resolved base URL. `framedash auth` reports which source
|
|
50
|
+
is active.
|
|
51
|
+
|
|
52
|
+
CI/non-interactive environments should keep using `FRAMEDASH_API_KEY` with a
|
|
53
|
+
project API key: `framedash login` requires a browser and stores per-user
|
|
54
|
+
refresh tokens, neither of which fits ephemeral CI runners.
|
|
25
55
|
|
|
26
56
|
Project-scoped commands such as `dashboard` also require a project ID from
|
|
27
57
|
`--project-id` or `FRAMEDASH_PROJECT_ID`. The `auth` command only requires an
|
|
@@ -56,6 +86,33 @@ node . dashboard --api-key-file ../../framedash.key --format table
|
|
|
56
86
|
node . dashboard --api-key-file ../../framedash.key --format csv
|
|
57
87
|
```
|
|
58
88
|
|
|
89
|
+
## CI performance gating
|
|
90
|
+
|
|
91
|
+
`run-profile-test` ties the SDK's automated-session API to the `perf-diff` gate
|
|
92
|
+
into a single CI step. It exports the `FRAMEDASH_*` session contract, launches
|
|
93
|
+
your profiling build, waits for its performance data to ingest, then fails the
|
|
94
|
+
job on a build-over-build regression:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
framedash run-profile-test \
|
|
98
|
+
--command "./Build/Game.exe -nullrhi -ExecCmds='Automation RunTest Perf'" \
|
|
99
|
+
--scenario nightly --baseline "$BASE_SHA" --threshold 5 --fail-on-regression
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The launched command inherits `FRAMEDASH_BUILD_ID`, `FRAMEDASH_GIT_BRANCH`,
|
|
103
|
+
`FRAMEDASH_GIT_COMMIT`, and `FRAMEDASH_TEST_SCENARIO`; an SDK that calls
|
|
104
|
+
`BeginAutomatedSessionFromEnvironment()` stamps them onto every event with no
|
|
105
|
+
in-game code change. The build id (defaulting to the git commit) becomes the
|
|
106
|
+
`perf-diff` candidate. Because the build id reaches the game only through the
|
|
107
|
+
exported `FRAMEDASH_BUILD_ID`, a build that hardcodes its SDK build id will never
|
|
108
|
+
match the awaited build and the ingest wait will time out.
|
|
109
|
+
|
|
110
|
+
The launched process is bounded by `--command-timeout` (default 1800s, `0`
|
|
111
|
+
disables): if the game does not exit on its own, its whole process tree is killed
|
|
112
|
+
and the run fails without gating -- so a non-self-quitting build cannot hang the
|
|
113
|
+
CI job forever. Run `framedash run-profile-test --help` for all options, or use
|
|
114
|
+
`framedash builds` / `framedash perf-diff` to drive the steps manually.
|
|
115
|
+
|
|
59
116
|
## Development
|
|
60
117
|
|
|
61
118
|
Run these commands from `packages/cli`:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/commands/alerts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../../src/commands/alerts.ts"],"names":[],"mappings":"AAoCA,eAAO,MAAM,MAAM,mCAKjB,CAAC"}
|
package/dist/commands/alerts.js
CHANGED
|
@@ -19,8 +19,9 @@ Create a new alert rule.
|
|
|
19
19
|
|
|
20
20
|
Required:
|
|
21
21
|
--name <name> Alert rule name
|
|
22
|
-
--map-id <uuid> Map ID
|
|
22
|
+
--map-id <uuid> Map ID (list IDs with 'framedash maps list')
|
|
23
23
|
--threshold-profile-id <uuid> Threshold profile ID
|
|
24
|
+
(list IDs with 'framedash threshold-profiles list')
|
|
24
25
|
--metric <metric> Metric name
|
|
25
26
|
--threshold-level <level> Threshold level
|
|
26
27
|
--fail-percentage <n> Failure percentage (0-100)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/commands/alerts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,IAAI,GAAG;;;;;;;;;;0DAU6C,CAAC;AAE3D,MAAM,WAAW,GAAG
|
|
1
|
+
{"version":3,"file":"alerts.js","sourceRoot":"","sources":["../../src/commands/alerts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,IAAI,GAAG;;;;;;;;;;0DAU6C,CAAC;AAE3D,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;8EAiB0D,CAAC;AAE/E,MAAM,CAAC,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,EAAE,IAAI,EAAE;IACrD,IAAI,EAAE,UAAU;IAChB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;CACpB,CAAC,CAAC;AAEH,KAAK,UAAU,UAAU,CAAC,IAAc;IACvC,MAAM,UAAU,CACf,EAAE,IAAI,EAAE,IAAI,EAAE,yEAAyE,EAAE,EACzF,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC5D,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAc;IACzC,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE;YACR,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC5B,sBAAsB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1C,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACrC,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACrC,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACrC,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC/B,kBAAkB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACtC,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACjC;KACD,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,MAAM,QAAQ,GAAG;YAChB,MAAM;YACN,QAAQ;YACR,sBAAsB;YACtB,QAAQ;YACR,iBAAiB;YACjB,iBAAiB;YACjB,iBAAiB;YACjB,WAAW;YACX,kBAAkB;SACT,CAAC;QAEX,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnB,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,CAAC;gBAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjB,CAAC;QACF,CAAC;QAED,MAAM,IAAI,GAA4B;YACrC,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC;YACvB,kBAAkB,EAAE,MAAM,CAAC,sBAAsB,CAAC;YAClD,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,cAAc,EAAE,MAAM,CAAC,iBAAiB,CAAC;YACzC,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,iBAAiB,CAAW,EAAE,iBAAiB,CAAC;YACnF,cAAc,EAAE,gBAAgB,CAAC,MAAM,CAAC,iBAAiB,CAAW,EAAE,iBAAiB,CAAC;YACxF,QAAQ,EAAE,gBAAgB,CAAC,MAAM,CAAC,WAAW,CAAW,EAAE,WAAW,CAAC;YACtE,eAAe,EAAE,gBAAgB,CAAC,MAAM,CAAC,kBAAkB,CAAW,EAAE,kBAAkB,CAAC;SAC3F,CAAC;QAEF,IAAI,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,aAAa,CAAW,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;QACnE,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAC9B,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAc;IACzC,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,yGAAyG;QAC/G,OAAO,EAAE;YACR,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC/B,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACjC,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACrC,iBAAiB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACrC,kBAAkB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACtC;QACD,gBAAgB,EAAE,IAAI;KACtB,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;QACjD,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,KAAK,CAAC,0DAA0D,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,MAAM,IAAI,GAA4B,EAAE,CAAC;QACzC,IAAI,MAAM,CAAC,IAAI;YAAE,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACzC,IAAI,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,CAAW,CAAC;YACxC,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;gBACnC,KAAK,CAAC,+CAA+C,CAAC,EAAE,CAAC,CAAC;gBAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjB,CAAC;YACD,IAAI,CAAC,QAAQ,GAAG,CAAC,KAAK,MAAM,CAAC;QAC9B,CAAC;QACD,IAAI,MAAM,CAAC,aAAa,CAAC;YAAE,IAAI,CAAC,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,aAAa,CAAW,CAAC,CAAC;QAC9F,IAAI,MAAM,CAAC,iBAAiB,CAAC;YAC5B,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,MAAM,CAAC,iBAAiB,CAAW,EAAE,iBAAiB,CAAC,CAAC;QAC3F,IAAI,MAAM,CAAC,iBAAiB,CAAC;YAC5B,IAAI,CAAC,cAAc,GAAG,gBAAgB,CACrC,MAAM,CAAC,iBAAiB,CAAW,EACnC,iBAAiB,CACjB,CAAC;QACH,IAAI,MAAM,CAAC,kBAAkB,CAAC;YAC7B,IAAI,CAAC,eAAe,GAAG,gBAAgB,CACtC,MAAM,CAAC,kBAAkB,CAAW,EACpC,kBAAkB,CAClB,CAAC;QAEH,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpC,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,KAAK,CAC9B,MAAM,CAAC,WAAW,CAAC,UAAU,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,EAC3D,IAAI,CACJ,CAAC;QACF,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAC9B,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAc;IACzC,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,4DAA4D;QAClE,gBAAgB,EAAE,IAAI;KACtB,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;QACjC,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,KAAK,CAAC,0DAA0D,CAAC,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,cAAc,OAAO,UAAU,CAAC,CAAC;IAC1C,CAAC,CACD,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,GAAW;IACnC,MAAM,GAAG,GAAG,GAAG;SACb,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AA0BA,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxD"}
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,19 +1,42 @@
|
|
|
1
1
|
import { formatOutput } from "../lib/formatters.js";
|
|
2
2
|
import { log, success } from "../lib/logger.js";
|
|
3
3
|
import { runCommand } from "../lib/run-command.js";
|
|
4
|
+
/**
|
|
5
|
+
* Human-readable description of the active credential source. For a stored
|
|
6
|
+
* OAuth login this shows scope and expiry ONLY -- never token material.
|
|
7
|
+
*/
|
|
8
|
+
function describeCredential(credential) {
|
|
9
|
+
if (credential.kind === "api-key") {
|
|
10
|
+
switch (credential.source) {
|
|
11
|
+
case "flag":
|
|
12
|
+
return "API key from --api-key flag";
|
|
13
|
+
case "file":
|
|
14
|
+
return "API key from --api-key-file";
|
|
15
|
+
case "env":
|
|
16
|
+
return "API key from FRAMEDASH_API_KEY env";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const expires = new Date(credential.entry.expires_at).toISOString();
|
|
20
|
+
return `stored OAuth login for ${credential.origin} (framedash login; scopes: ${credential.entry.scope || "unknown"}; access token expires: ${expires})`;
|
|
21
|
+
}
|
|
4
22
|
export async function auth(args) {
|
|
5
23
|
await runCommand({ args, help: HELP, noProject: true }, async ({ client, config }) => {
|
|
6
24
|
const projects = await client.get("/api/v1/projects");
|
|
7
|
-
success("API key is valid");
|
|
25
|
+
success(config.credential.kind === "api-key" ? "API key is valid" : "OAuth login is valid");
|
|
26
|
+
success(`Credential source: ${describeCredential(config.credential)}`);
|
|
8
27
|
log(formatOutput(projects, config.format));
|
|
9
28
|
});
|
|
10
29
|
}
|
|
11
30
|
const HELP = `Usage: framedash auth [options]
|
|
12
31
|
|
|
13
|
-
Verify your
|
|
32
|
+
Verify your credentials and list the projects they can read. Also reports
|
|
33
|
+
which credential source is active: --api-key flag, --api-key-file,
|
|
34
|
+
FRAMEDASH_API_KEY env, or a stored OAuth login from 'framedash login'
|
|
35
|
+
(API keys always take precedence over a stored login).
|
|
14
36
|
|
|
15
37
|
Options:
|
|
16
38
|
--api-key <key> API key (or FRAMEDASH_API_KEY env)
|
|
39
|
+
--api-key-file <path> Read the API key from a file ('-' for stdin)
|
|
17
40
|
--base-url <url> API base URL (default: https://app.framedash.dev)
|
|
18
41
|
--format <fmt> Output format: json, table, csv (default: json)
|
|
19
42
|
-h, --help Show help`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD;;;GAGG;AACH,SAAS,kBAAkB,CAAC,UAAyB;IACpD,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACnC,QAAQ,UAAU,CAAC,MAAM,EAAE,CAAC;YAC3B,KAAK,MAAM;gBACV,OAAO,6BAA6B,CAAC;YACtC,KAAK,MAAM;gBACV,OAAO,6BAA6B,CAAC;YACtC,KAAK,KAAK;gBACT,OAAO,oCAAoC,CAAC;QAC9C,CAAC;IACF,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,OAAO,0BAA0B,UAAU,CAAC,MAAM,8BACjD,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,SAC3B,2BAA2B,OAAO,GAAG,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAc;IACxC,MAAM,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAiC,kBAAkB,CAAC,CAAC;QACtF,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC;QAC5F,OAAO,CAAC,sBAAsB,kBAAkB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QACvE,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,IAAI,GAAG;;;;;;;;;;;;mCAYsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builds.d.ts","sourceRoot":"","sources":["../../src/commands/builds.ts"],"names":[],"mappings":"AAKA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB1D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { buildBuildsPath } from "@framedash/api-client";
|
|
2
|
+
import { formatOutput } from "../lib/formatters.js";
|
|
3
|
+
import { log } from "../lib/logger.js";
|
|
4
|
+
import { runCommand } from "../lib/run-command.js";
|
|
5
|
+
export async function builds(args) {
|
|
6
|
+
await runCommand({
|
|
7
|
+
args,
|
|
8
|
+
help: HELP,
|
|
9
|
+
options: {
|
|
10
|
+
days: { type: "string" },
|
|
11
|
+
},
|
|
12
|
+
}, async ({ client, config, values }) => {
|
|
13
|
+
const data = await client.get(client.projectPath(buildBuildsPath({ days: values.days })));
|
|
14
|
+
log(formatOutput(data, config.format));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const HELP = `Usage: framedash builds [options]
|
|
18
|
+
|
|
19
|
+
List the builds seen for the project (newest first), so you can pick build IDs
|
|
20
|
+
to compare with 'framedash perf-diff'. A build is any non-empty build_id that
|
|
21
|
+
produced performance events in the window (set build_id from your CI, e.g. the
|
|
22
|
+
git SHA, when initializing the SDK).
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--days <n> Time period in days: 7, 14, 30, 90 (default: 30)
|
|
26
|
+
--api-key <key> API key (or FRAMEDASH_API_KEY env)
|
|
27
|
+
--project-id <uuid> Project ID (or FRAMEDASH_PROJECT_ID env)
|
|
28
|
+
--base-url <url> API base URL (default: https://app.framedash.dev)
|
|
29
|
+
--format <fmt> Output format: json, table, csv (default: json)
|
|
30
|
+
-h, --help Show help`;
|
|
31
|
+
//# sourceMappingURL=builds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builds.js","sourceRoot":"","sources":["../../src/commands/builds.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAc;IAC1C,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACR,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACxB;KACD,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC5B,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAA0B,EAAE,CAAC,CAAC,CAChF,CAAC;QACF,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC;AAED,MAAM,IAAI,GAAG;;;;;;;;;;;;;mCAasB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/commands/content.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/commands/content.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,OAAO,mCAIlB,CAAC"}
|
package/dist/commands/content.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { buildContentPath } from "@framedash/api-client";
|
|
2
3
|
import { formatOutput } from "../lib/formatters.js";
|
|
3
4
|
import { error, log, success } from "../lib/logger.js";
|
|
4
5
|
import { runCommand, withSubcommands } from "../lib/run-command.js";
|
|
@@ -27,8 +28,7 @@ async function contentList(args) {
|
|
|
27
28
|
help: "Usage: framedash content list [--type <content-type>] [--format json|table|csv] [global options]",
|
|
28
29
|
options: { type: { type: "string" } },
|
|
29
30
|
}, async ({ client, config, values }) => {
|
|
30
|
-
const
|
|
31
|
-
const data = await client.get(`/api/v1/content${params}`);
|
|
31
|
+
const data = await client.get(buildContentPath({ type: values.type }));
|
|
32
32
|
log(formatOutput(data, config.format));
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/commands/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,IAAI,GAAG;;;;;;;;;2DAS8C,CAAC;AAE5D,MAAM,WAAW,GAAG;;;6DAGyC,CAAC;AAE9D,MAAM,CAAC,MAAM,OAAO,GAAG,eAAe,CAAC,SAAS,EAAE,IAAI,EAAE;IACvD,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,aAAa;IACrB,MAAM,EAAE,aAAa;CACrB,CAAC,CAAC;AAEH,KAAK,UAAU,WAAW,CAAC,IAAc;IACxC,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,kGAAkG;QACxG,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KACrC,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,MAAM,
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/commands/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,IAAI,GAAG;;;;;;;;;2DAS8C,CAAC;AAE5D,MAAM,WAAW,GAAG;;;6DAGyC,CAAC;AAE9D,MAAM,CAAC,MAAM,OAAO,GAAG,eAAe,CAAC,SAAS,EAAE,IAAI,EAAE;IACvD,IAAI,EAAE,WAAW;IACjB,MAAM,EAAE,aAAa;IACrB,MAAM,EAAE,aAAa;CACrB,CAAC,CAAC;AAEH,KAAK,UAAU,WAAW,CAAC,IAAc;IACxC,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,kGAAkG;QACxG,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KACrC,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAA0B,EAAE,CAAC,CAAC,CAAC;QAC7F,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,IAAc;IAC1C,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,8DAA8D;QACpE,gBAAgB,EAAE,IAAI;KACtB,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;QACzC,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,KAAK,CAAC,kEAAkE,CAAC,CAAC;YAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACJ,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACjE,KAAK,CAAC,wBAAwB,QAAQ,KAAK,GAAG,GAAG,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,IAAI,OAAgB,CAAC;QACrB,IAAI,CAAC;YACJ,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACR,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,iDAAiD;QACjD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QAE/D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;QAC3D,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAC5B,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,IAAc;IAC1C,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE;YACR,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAChC;QACD,gBAAgB,EAAE,IAAI;KACtB,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE;QACzC,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;YACjD,KAAK,CAAC,kEAAkE,CAAC,CAAC;YAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QACD,IAAI,EAAE,EAAE,CAAC;YACR,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC3C,MAAM,MAAM,CAAC,MAAM,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;YACjD,OAAO,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;QACxC,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;gBAClC,WAAW,EAAE,MAAM,CAAC,IAAc;gBAClC,SAAS,EAAE,MAAM,CAAC,YAAY,CAAW;aACzC,CAAC,CAAC;YACH,MAAM,MAAM,CAAC,MAAM,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;YACjD,OAAO,CAAC,uBAAuB,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,gDAAgD,CAAC,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC,CACD,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"AAKA,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7D"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { buildDashboardPath } from "@framedash/api-client";
|
|
1
2
|
import { formatOutput } from "../lib/formatters.js";
|
|
2
3
|
import { log } from "../lib/logger.js";
|
|
3
4
|
import { runCommand } from "../lib/run-command.js";
|
|
4
5
|
export async function dashboard(args) {
|
|
5
6
|
await runCommand({ args, help: HELP, options: { days: { type: "string" } } }, async ({ client, config, values }) => {
|
|
6
|
-
const
|
|
7
|
-
const data = await client.get(client.projectPath(`dashboard?${params}`));
|
|
7
|
+
const data = await client.get(client.projectPath(buildDashboardPath({ days: values.days ?? "30" })));
|
|
8
8
|
log(formatOutput(data, config.format));
|
|
9
9
|
});
|
|
10
10
|
}
|
|
@@ -13,7 +13,7 @@ const HELP = `Usage: framedash dashboard [options]
|
|
|
13
13
|
Show project dashboard metrics.
|
|
14
14
|
|
|
15
15
|
Options:
|
|
16
|
-
--days <n> Time period in days:
|
|
16
|
+
--days <n> Time period in days: 7, 14, 30, 90 (default: 30)
|
|
17
17
|
--api-key <key> API key (or FRAMEDASH_API_KEY env)
|
|
18
18
|
--project-id <uuid> Project ID (or FRAMEDASH_PROJECT_ID env)
|
|
19
19
|
--base-url <url> API base URL (default: https://app.framedash.dev)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc;IAC7C,MAAM,UAAU,CACf,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAC3D,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,MAAM,
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc;IAC7C,MAAM,UAAU,CACf,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAC3D,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC5B,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAG,MAAM,CAAC,IAAe,IAAI,IAAI,EAAE,CAAC,CAAC,CACjF,CAAC;QACF,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC;AAED,MAAM,IAAI,GAAG;;;;;;;;;;mCAUsB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"funnel.d.ts","sourceRoot":"","sources":["../../src/commands/funnel.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"funnel.d.ts","sourceRoot":"","sources":["../../src/commands/funnel.ts"],"names":[],"mappings":"AAKA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6B1D"}
|
package/dist/commands/funnel.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildFunnelPath } from "@framedash/api-client";
|
|
1
2
|
import { formatOutput } from "../lib/formatters.js";
|
|
2
3
|
import { error, log } from "../lib/logger.js";
|
|
3
4
|
import { runCommand } from "../lib/run-command.js";
|
|
@@ -15,13 +16,11 @@ export async function funnel(args) {
|
|
|
15
16
|
error('--steps is required (comma-separated event names, e.g. "login,tutorial,purchase")');
|
|
16
17
|
process.exit(1);
|
|
17
18
|
}
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
params.set("window", values.window);
|
|
24
|
-
const data = await client.get(client.projectPath(`funnels?${params}`));
|
|
19
|
+
const data = await client.get(client.projectPath(buildFunnelPath({
|
|
20
|
+
steps: values.steps,
|
|
21
|
+
days: values.days,
|
|
22
|
+
window: values.window,
|
|
23
|
+
})));
|
|
25
24
|
log(formatOutput(data, config.format));
|
|
26
25
|
});
|
|
27
26
|
}
|
|
@@ -33,7 +32,7 @@ Required:
|
|
|
33
32
|
--steps <events> Comma-separated event names (2-8 steps)
|
|
34
33
|
|
|
35
34
|
Options:
|
|
36
|
-
--days <n> Time period in days:
|
|
35
|
+
--days <n> Time period in days: 7, 14, 30, 90 (default: 30)
|
|
37
36
|
--window <seconds> Conversion window: 3600, 21600, 86400, 604800 (default: 86400)
|
|
38
37
|
--api-key <key> API key (or FRAMEDASH_API_KEY env)
|
|
39
38
|
--project-id <uuid> Project ID (or FRAMEDASH_PROJECT_ID env)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"funnel.js","sourceRoot":"","sources":["../../src/commands/funnel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAc;IAC1C,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACR,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC1B;KACD,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,CAAC,mFAAmF,CAAC,CAAC;YAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,MAAM,
|
|
1
|
+
{"version":3,"file":"funnel.js","sourceRoot":"","sources":["../../src/commands/funnel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAc;IAC1C,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACR,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC1B;KACD,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACpC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACnB,KAAK,CAAC,mFAAmF,CAAC,CAAC;YAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAC5B,MAAM,CAAC,WAAW,CACjB,eAAe,CAAC;YACf,KAAK,EAAE,MAAM,CAAC,KAAe;YAC7B,IAAI,EAAE,MAAM,CAAC,IAA0B;YACvC,MAAM,EAAE,MAAM,CAAC,MAA4B;SAC3C,CAAC,CACF,CACD,CAAC;QACF,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC;AAED,MAAM,IAAI,GAAG;;;;;;;;;;;;;;mCAcsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAwEA,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAmFzD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { GLOBAL_OPTIONS, resolveBaseAndFormat } from "../lib/config.js";
|
|
4
|
+
import { error, log, success } from "../lib/logger.js";
|
|
5
|
+
import { startLoopbackServer } from "../lib/oauth/loopback-server.js";
|
|
6
|
+
import { computeS256CodeChallenge, generateCodeVerifier, generateState, } from "../lib/oauth/pkce.js";
|
|
7
|
+
import { CLI_OAUTH_CLIENT_ID, exchangeAuthorizationCode, revokeToken, toStoredEntry, } from "../lib/oauth/token-endpoint.js";
|
|
8
|
+
import { credentialsFilePath, saveStoredEntry } from "../lib/oauth/token-store.js";
|
|
9
|
+
const HELP = `Usage: framedash login [options]
|
|
10
|
+
|
|
11
|
+
Sign in interactively via your browser (OAuth 2.1 authorization code + PKCE).
|
|
12
|
+
Tokens are stored in ${"~"}/.config/framedash/credentials.json (honors
|
|
13
|
+
XDG_CONFIG_HOME; the same path is used on Windows) and refreshed
|
|
14
|
+
automatically. Commands use the stored login only when no API key is given
|
|
15
|
+
(--api-key / --api-key-file / FRAMEDASH_API_KEY take precedence).
|
|
16
|
+
|
|
17
|
+
For CI and other non-interactive use, do NOT use login: set the
|
|
18
|
+
FRAMEDASH_API_KEY environment variable to a project API key instead.
|
|
19
|
+
|
|
20
|
+
Options:
|
|
21
|
+
--scopes <scopes> Space-delimited scopes to request
|
|
22
|
+
(default: analytics:read)
|
|
23
|
+
--no-browser Do not try to open the browser; print the URL only
|
|
24
|
+
--base-url <url> API base URL (default: https://app.framedash.dev)
|
|
25
|
+
-h, --help Show help`;
|
|
26
|
+
/** Give the user five minutes to finish the browser consent. */
|
|
27
|
+
const CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
28
|
+
/** RFC 6749 scope tokens: printable ASCII except space, quote, backslash. */
|
|
29
|
+
const SCOPE_RE = /^[\x21\x23-\x5B\x5D-\x7E]+$/;
|
|
30
|
+
/**
|
|
31
|
+
* Best-effort system browser launch; the URL is always printed as fallback.
|
|
32
|
+
* Windows uses rundll32's FileProtocolHandler instead of `cmd /c start`
|
|
33
|
+
* because start parses `&` (which every authorize URL contains) as a shell
|
|
34
|
+
* operator unless escaped.
|
|
35
|
+
*/
|
|
36
|
+
function openBrowser(url) {
|
|
37
|
+
let command;
|
|
38
|
+
let args;
|
|
39
|
+
if (process.platform === "win32") {
|
|
40
|
+
command = "rundll32";
|
|
41
|
+
args = ["url.dll,FileProtocolHandler", url];
|
|
42
|
+
}
|
|
43
|
+
else if (process.platform === "darwin") {
|
|
44
|
+
command = "open";
|
|
45
|
+
args = [url];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
command = "xdg-open";
|
|
49
|
+
args = [url];
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const child = spawn(command, args, { stdio: "ignore", detached: true });
|
|
53
|
+
child.on("error", () => {
|
|
54
|
+
// Best effort: the printed URL is the fallback.
|
|
55
|
+
});
|
|
56
|
+
child.unref();
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Best effort only.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function login(args) {
|
|
63
|
+
const { values } = parseArgs({
|
|
64
|
+
args,
|
|
65
|
+
options: {
|
|
66
|
+
...GLOBAL_OPTIONS,
|
|
67
|
+
scopes: { type: "string" },
|
|
68
|
+
"no-browser": { type: "boolean" },
|
|
69
|
+
},
|
|
70
|
+
allowPositionals: false,
|
|
71
|
+
});
|
|
72
|
+
if (values.help) {
|
|
73
|
+
log(HELP);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const { baseUrl } = resolveBaseAndFormat(values);
|
|
77
|
+
const origin = new URL(baseUrl).origin;
|
|
78
|
+
// Normalize whitespace: trim, split on runs of any whitespace, and drop
|
|
79
|
+
// empty tokens, so "a b" or tab-separated input works. An empty token
|
|
80
|
+
// list must fail explicitly ([].every() is vacuously true).
|
|
81
|
+
const scopeList = (values.scopes ?? "analytics:read").trim().split(/\s+/).filter(Boolean);
|
|
82
|
+
if (scopeList.length === 0 || !scopeList.every((scope) => SCOPE_RE.test(scope))) {
|
|
83
|
+
error(`Invalid --scopes value. Use space-delimited scopes, e.g. "analytics:read".`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
const scopes = scopeList.join(" ");
|
|
87
|
+
const codeVerifier = generateCodeVerifier();
|
|
88
|
+
const state = generateState();
|
|
89
|
+
const server = await startLoopbackServer(state);
|
|
90
|
+
try {
|
|
91
|
+
const authorizeUrl = new URL("/oauth/authorize", origin);
|
|
92
|
+
authorizeUrl.searchParams.set("response_type", "code");
|
|
93
|
+
authorizeUrl.searchParams.set("client_id", CLI_OAUTH_CLIENT_ID);
|
|
94
|
+
authorizeUrl.searchParams.set("redirect_uri", server.redirectUri);
|
|
95
|
+
authorizeUrl.searchParams.set("scope", scopes);
|
|
96
|
+
authorizeUrl.searchParams.set("state", state);
|
|
97
|
+
authorizeUrl.searchParams.set("code_challenge", computeS256CodeChallenge(codeVerifier));
|
|
98
|
+
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
99
|
+
log("Open this URL in your browser to sign in:");
|
|
100
|
+
log("");
|
|
101
|
+
log(` ${authorizeUrl.toString()}`);
|
|
102
|
+
log("");
|
|
103
|
+
if (!values["no-browser"]) {
|
|
104
|
+
openBrowser(authorizeUrl.toString());
|
|
105
|
+
}
|
|
106
|
+
success("Waiting for the browser sign-in to complete (5 minute timeout)...");
|
|
107
|
+
const { code } = await server.waitForCallback(CALLBACK_TIMEOUT_MS);
|
|
108
|
+
const tokens = await exchangeAuthorizationCode(baseUrl, {
|
|
109
|
+
code,
|
|
110
|
+
codeVerifier,
|
|
111
|
+
redirectUri: server.redirectUri,
|
|
112
|
+
});
|
|
113
|
+
// Last-writer-wins: of two racing logins for the same origin, the later
|
|
114
|
+
// save is the one that sticks (see token-store.ts concurrency model).
|
|
115
|
+
const entry = toStoredEntry(tokens);
|
|
116
|
+
try {
|
|
117
|
+
await saveStoredEntry(origin, entry);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
// The exchange already minted a LIVE server-side grant, and we are
|
|
121
|
+
// about to discard the only refresh token that could disconnect it
|
|
122
|
+
// (ENOSPC/permissions on the save). Best-effort revoke so no orphaned
|
|
123
|
+
// grant lingers on the server, then surface the original error.
|
|
124
|
+
try {
|
|
125
|
+
await revokeToken(baseUrl, tokens.refresh_token);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Best effort: the user can also revoke from Connected apps.
|
|
129
|
+
}
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
success(`Logged in to ${origin}`);
|
|
133
|
+
// Scope + expiry only -- NEVER print token values.
|
|
134
|
+
log(`Scopes: ${entry.scope || "(none reported)"}`);
|
|
135
|
+
log(`Access token expires: ${new Date(entry.expires_at).toISOString()}`);
|
|
136
|
+
log(`Credentials stored in: ${credentialsFilePath()}`);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
await server.close();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAyCA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAiD1D"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { GLOBAL_OPTIONS, resolveBaseAndFormat } from "../lib/config.js";
|
|
4
|
+
import { log, success, warn } from "../lib/logger.js";
|
|
5
|
+
import { revokeToken } from "../lib/oauth/token-endpoint.js";
|
|
6
|
+
import { clearTokenStore, credentialsFilePath, deleteStoredEntry, readStoredEntry, readTokenStore, } from "../lib/oauth/token-store.js";
|
|
7
|
+
const HELP = `Usage: framedash logout [options]
|
|
8
|
+
|
|
9
|
+
Sign out of a stored OAuth login (created by 'framedash login'): revokes the
|
|
10
|
+
session server-side (best effort) and removes the locally stored tokens for
|
|
11
|
+
the resolved base URL. API keys (FRAMEDASH_API_KEY etc.) are not affected.
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--all Revoke and remove ALL stored logins for every origin
|
|
15
|
+
--base-url <url> API base URL (default: https://app.framedash.dev)
|
|
16
|
+
-h, --help Show help`;
|
|
17
|
+
/**
|
|
18
|
+
* Best-effort server-side revocation of the refresh token (RFC 7009 kills
|
|
19
|
+
* the whole grant). A transport failure only warns: the local credentials
|
|
20
|
+
* are removed regardless, and an unreachable server must not block logout.
|
|
21
|
+
*/
|
|
22
|
+
async function bestEffortRevoke(origin, refreshToken) {
|
|
23
|
+
try {
|
|
24
|
+
await revokeToken(origin, refreshToken);
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
warn(`Could not revoke the server-side session for ${origin} ` +
|
|
28
|
+
`(${err instanceof Error ? err.message : String(err)}); removing local credentials anyway. ` +
|
|
29
|
+
`You can also revoke it from the dashboard's Connected apps page.`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function logout(args) {
|
|
33
|
+
const { values } = parseArgs({
|
|
34
|
+
args,
|
|
35
|
+
options: { ...GLOBAL_OPTIONS, all: { type: "boolean" } },
|
|
36
|
+
allowPositionals: false,
|
|
37
|
+
});
|
|
38
|
+
if (values.help) {
|
|
39
|
+
log(HELP);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (values.all) {
|
|
43
|
+
const store = readTokenStore();
|
|
44
|
+
const origins = Object.keys(store);
|
|
45
|
+
const hadFile = existsSync(credentialsFilePath());
|
|
46
|
+
for (const origin of origins) {
|
|
47
|
+
const entry = store[origin];
|
|
48
|
+
if (entry) {
|
|
49
|
+
await bestEffortRevoke(origin, entry.refresh_token);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Always clear: this removes the file (including the corrupt/unparseable
|
|
53
|
+
// case, which still holds token material) AND reaps any orphaned
|
|
54
|
+
// credentials.json.*.tmp files from crashed writes.
|
|
55
|
+
await clearTokenStore();
|
|
56
|
+
if (origins.length > 0) {
|
|
57
|
+
success(`Logged out of ${origins.length} origin(s); credentials file removed.`);
|
|
58
|
+
}
|
|
59
|
+
else if (hadFile) {
|
|
60
|
+
success("Removed the local credentials file (it contained no parseable logins; " +
|
|
61
|
+
"server-side revocation was skipped).");
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
log("No stored logins.");
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const { baseUrl } = resolveBaseAndFormat(values);
|
|
69
|
+
const origin = new URL(baseUrl).origin;
|
|
70
|
+
const entry = readStoredEntry(origin);
|
|
71
|
+
if (!entry) {
|
|
72
|
+
log(`No stored login for ${origin}.`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
await bestEffortRevoke(origin, entry.refresh_token);
|
|
76
|
+
await deleteStoredEntry(origin);
|
|
77
|
+
success(`Logged out of ${origin}`);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"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"}
|