metergraph-cli 0.2.0-preview.3 → 0.2.0-preview.5
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 +155 -902
- package/assets/skill/SKILL.md +32 -2
- package/assets/skill/manifest.json +3 -3
- package/assets/skills/manifest.json +61 -0
- package/assets/skills/metergraph-analyze/SKILL.md +159 -0
- package/assets/skills/metergraph-candidates/SKILL.md +84 -0
- package/assets/skills/metergraph-evals/SKILL.md +99 -0
- package/assets/skills/metergraph-investigate/SKILL.md +105 -0
- package/assets/skills/metergraph-iterate/SKILL.md +73 -0
- package/assets/skills/metergraph-model-swap/SKILL.md +81 -0
- package/assets/skills/metergraph-onboarding/SKILL.md +155 -0
- package/assets/skills/metergraph-report/SKILL.md +91 -0
- package/assets/skills/metergraph-workloads/SKILL.md +94 -0
- package/package.json +3 -1
- package/src/args.js +43 -3
- package/src/cli.js +16 -1
- package/src/constants.js +4 -0
- package/src/output.js +118 -0
- package/src/skill-bundle.js +68 -1
- package/src/skill.js +175 -14
package/README.md
CHANGED
|
@@ -1,603 +1,148 @@
|
|
|
1
1
|
# metergraph-cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/metergraph-cli)
|
|
4
|
+
[](https://github.com/metergraph/cli/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/metergraph/cli/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
Connect a project to [Metergraph](https://www.metergraph.dev/) from the terminal or from
|
|
8
|
+
your coding agent. Metergraph learns from your application's LLM traces, benchmarks
|
|
9
|
+
models against your real usage and routes each task to the model that clears your
|
|
10
|
+
quality bar. This CLI signs a project in, writes its ingest key, installs the Metergraph
|
|
11
|
+
skill for Codex, Claude Code or Cursor, proves that a trace arrived, and reads cost,
|
|
12
|
+
token and route metadata back. Every command has stable `--json` output and exit codes,
|
|
13
|
+
so agents can run it as well as people.
|
|
7
14
|
|
|
8
15
|
```sh
|
|
9
16
|
npx --yes metergraph-cli@next --help
|
|
10
|
-
npx --yes metergraph-cli@next doctor --json
|
|
11
|
-
npm install -g metergraph-cli@next
|
|
12
17
|
```
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
**Availability:**
|
|
18
|
-
|
|
19
|
-
- `metergraph-cli@0.1.0` contains only `doctor` and `skill install` /
|
|
20
|
-
`skill update`. It has no sign in commands.
|
|
21
|
-
- `metergraph-cli@0.2.0-preview.0` adds `login`, `logout`, `setup`, `verify`,
|
|
22
|
-
`status`, `context`, `capabilities`, `usage`, `routes` and `traces`.
|
|
23
|
-
- `metergraph-cli@0.2.0-preview.1` makes `setup` write the SDK service root as
|
|
24
|
-
`METERGRAPH_INGEST_URL` and repairs the full ingest endpoint that
|
|
25
|
-
`0.2.0-preview.0` wrote, after checking the saved key. It also keeps validated,
|
|
26
|
-
workspace-bound trace links in Metadata reads.
|
|
27
|
-
- `metergraph-cli@0.2.0-preview.2` makes `verify` exit 11 `verification_failed`
|
|
28
|
-
when its deadline expires after the service reported the trace as pending, lets
|
|
29
|
-
`login` and `setup` reruns that need no approval succeed with `--json
|
|
30
|
-
--no-browser`, and reports why `verify --open` did not launch a browser.
|
|
31
|
-
- `metergraph-cli@0.2.0-preview.3` makes a `verify` deadline that expires after
|
|
32
|
-
the service reported the trace as pending return `trace_not_found_within_bounds`,
|
|
33
|
-
the same result as running out of attempts, instead of `verification_timeout`.
|
|
34
|
-
`setup --json --no-browser` now returns a `run_in_terminal` next action when
|
|
35
|
-
approval is needed.
|
|
36
|
-
`login` and `setup` use a pre-registered CLI client when the service names one
|
|
37
|
-
in `metergraph_cli_client_id`, instead of registering a new client, and still
|
|
38
|
-
register one on services that do not.
|
|
39
|
-
- Sign in needs a Metergraph service that offers Metadata-only CLI grants and grant
|
|
40
|
-
revocation. A service without them is reported as unsupported, and the CLI never
|
|
41
|
-
falls back to broader access.
|
|
42
|
-
- The read commands need a project signed in with `login`. `setup` also needs the
|
|
43
|
-
deployment's ingest bootstrap API and browser approval by a workspace member.
|
|
44
|
-
`verify` checks one exact trace identity in an explicit invocation window using
|
|
45
|
-
Metadata access. It never sends application data.
|
|
46
|
-
|
|
47
|
-
The preview includes:
|
|
48
|
-
|
|
49
|
-
- `doctor` checks whether a Metergraph service is reachable, healthy and supported.
|
|
50
|
-
- `skill install` and `skill update` copy the Metergraph agent skill bundled with the
|
|
51
|
-
CLI into one coding agent's project skill directory.
|
|
52
|
-
- `login` and `logout` sign a project in to one workspace through your
|
|
53
|
-
browser with a delegated, Metadata-only grant, and sign it out again.
|
|
54
|
-
- The read commands use that grant to read bounded workspace Metadata:
|
|
55
|
-
connection status, workspace context, capabilities, daily usage, routes and one page
|
|
56
|
-
of trace metadata.
|
|
57
|
-
- `setup` guides browser sign in and workspace choice, asks for
|
|
58
|
-
ingest-only approval, writes a private project env file, confirms delivery, and
|
|
59
|
-
installs the selected client skill.
|
|
60
|
-
- `verify` polls for one exact processed trace in a bounded window.
|
|
61
|
-
It does not infer application provenance from a Metadata match.
|
|
62
|
-
|
|
63
|
-
It does not read retained content, replay traces, call model providers or send
|
|
64
|
-
application data. Setup does not prove that the application sent a trace.
|
|
65
|
-
|
|
66
|
-
## Requirements
|
|
19
|
+
This is a preview, published on the `next` npm tag. `latest` is still `0.1.0`, which has
|
|
20
|
+
only `doctor` and `skill`. See the [changelog](https://github.com/metergraph/cli/blob/main/CHANGELOG.md).
|
|
67
21
|
|
|
68
|
-
|
|
69
|
-
| --- | --- |
|
|
70
|
-
| Node.js | 22 and 24 |
|
|
71
|
-
| Operating systems | Linux, macOS and Windows (each in the CI matrix) |
|
|
72
|
-
| Runtime dependencies | None |
|
|
22
|
+
## Quickstart
|
|
73
23
|
|
|
74
|
-
|
|
24
|
+
**1. Set up the project.** Run this in your project directory. It opens your browser to
|
|
25
|
+
sign in and choose a workspace, asks a workspace member to approve an ingest-only key,
|
|
26
|
+
writes `METERGRAPH_APP_TOKEN` and `METERGRAPH_INGEST_URL` to a private `.env`, and
|
|
27
|
+
installs the skill for your coding agent.
|
|
75
28
|
|
|
76
29
|
```sh
|
|
77
|
-
metergraph --
|
|
78
|
-
metergraph help doctor [--json]
|
|
79
|
-
metergraph --version [--json]
|
|
80
|
-
metergraph doctor [--url ORIGIN] [--timeout-ms N] [--json]
|
|
81
|
-
metergraph help skill [--json]
|
|
82
|
-
metergraph skill install --client CLIENT --runtime RUNTIME [--project DIR] [--json]
|
|
83
|
-
metergraph skill update --client CLIENT --runtime RUNTIME [--project DIR] [--json]
|
|
84
|
-
metergraph help login [--json]
|
|
85
|
-
metergraph login --runtime local [--url ORIGIN] [--workspace UUID] [--project DIR] [--config-dir DIR] [--timeout-ms N] [--signup] [--no-browser] [--reconnect] [--json]
|
|
86
|
-
metergraph logout [--project DIR] [--config-dir DIR] [--json]
|
|
87
|
-
metergraph setup --runtime local (--client codex|claude|cursor | --skip-skill) [--deployment managed|customer-local|byoc|oss] [--url ORIGIN] [--workspace UUID] [--confirm-prerequisites] [--agent-token-file FILE] [--project DIR] [--config-dir DIR] [--env-file .env] [--timeout-ms N] [--signup] [--reconnect] [--no-browser] [--repair] [--json]
|
|
88
|
-
metergraph status [--project DIR] [--config-dir DIR] [--timeout-ms N] [--json]
|
|
89
|
-
metergraph context [--project DIR] [--config-dir DIR] [--timeout-ms N] [--json]
|
|
90
|
-
metergraph capabilities [--project DIR] [--config-dir DIR] [--timeout-ms N] [--json]
|
|
91
|
-
metergraph usage [--days N] [--limit N] [--project DIR] [--config-dir DIR] [--timeout-ms N] [--json]
|
|
92
|
-
metergraph routes [--limit N] [--project DIR] [--config-dir DIR] [--timeout-ms N] [--json]
|
|
93
|
-
metergraph traces [--days N] [--limit N] [--route NAME] [--status success|error] [--cursor CURSOR] [--project DIR] [--config-dir DIR] [--timeout-ms N] [--json]
|
|
94
|
-
metergraph verify (--trace-id ID | --request-id ID) --since TIME --until TIME [--source application|synthetic|demo|import|unspecified] [--days N] [--timeout-ms N] [--poll-ms N] [--max-attempts N] [--open] [--no-browser] [--project DIR] [--config-dir DIR] [--json]
|
|
30
|
+
npx --yes metergraph-cli@next setup --runtime local --client claude
|
|
95
31
|
```
|
|
96
32
|
|
|
97
|
-
`--
|
|
98
|
-
requests. `login`, `logout`, `setup`, `verify` and the read commands are not in the published `0.1.0`
|
|
99
|
-
package.
|
|
100
|
-
|
|
101
|
-
### Exact trace verification
|
|
102
|
-
|
|
103
|
-
After an application invocation, pass its exact trace ID or request ID and the
|
|
104
|
-
invocation start and end timestamps to `verify`. The optional `--source` label is a
|
|
105
|
-
caller assertion. A matching Metadata row proves a processed trace is visible in the
|
|
106
|
-
bound workspace, but does not independently prove that it came from your application.
|
|
107
|
-
The result therefore keeps `application_traffic_verified: false` until a separate
|
|
108
|
-
application instrumentation check supplies that evidence. A missing, ambiguous, stale
|
|
109
|
-
or wrong-workspace result fails closed. The command neither creates an ingest key nor
|
|
110
|
-
sends a test event.
|
|
111
|
-
|
|
112
|
-
`--open` launches only a server-provided link carrying the exact trace and the
|
|
113
|
-
verified workspace ID. The dashboard must check that ID against its signed-in
|
|
114
|
-
workspace before displaying traces. Older links without a workspace remain a
|
|
115
|
-
manual handoff; a conflicting workspace or unsafe link is refused. The result's
|
|
116
|
-
`browser` field is `launcher_started`, `launcher_unavailable`, `not_requested`
|
|
117
|
-
(no `--open`), or `suppressed_by_json` / `suppressed_by_no_browser` when `--open`
|
|
118
|
-
was given but that flag returned the link instead of launching it.
|
|
119
|
-
|
|
120
|
-
If `--timeout-ms` expires after the service has answered at least once with a
|
|
121
|
-
pending result, `verify` exits 11 `verification_failed` with reason
|
|
122
|
-
`trace_not_found_within_bounds`, the same result as running out of
|
|
123
|
-
`--max-attempts`: the origin is reachable and the trace is not visible yet. Exit 4
|
|
124
|
-
`connection_failed` is kept for transport failures. Its reason is
|
|
125
|
-
`verification_timeout` when no valid answer arrived before the deadline. A deadline
|
|
126
|
-
while `--open` launches a trace that was already found exits 11 with
|
|
127
|
-
`verification_timeout`.
|
|
128
|
-
|
|
129
|
-
### doctor
|
|
130
|
-
|
|
131
|
-
`doctor` sends three unauthenticated, read-only `GET` requests to one origin, in order,
|
|
132
|
-
and stops at the first problem:
|
|
133
|
-
|
|
134
|
-
1. `/healthz` must answer `200` with the JSON body `{"ok": true}`.
|
|
135
|
-
2. `/v1/deployment` must answer `200` with a JSON `deployment_profile` this CLI supports.
|
|
136
|
-
3. `/v1/agent/capabilities` must answer `401` with a `WWW-Authenticate: Bearer` challenge.
|
|
137
|
-
|
|
138
|
-
Options:
|
|
139
|
-
|
|
140
|
-
| Option | Default | Notes |
|
|
141
|
-
| --- | --- | --- |
|
|
142
|
-
| `--url ORIGIN` | `https://app.metergraph.dev` | Bare origin only, see [Safe origins](#safe-origins). |
|
|
143
|
-
| `--timeout-ms N` | `5000` | Whole number from 100 to 30000. Covers the whole probe, not each request. |
|
|
144
|
-
| `--json` | off | Print exactly one JSON line on stdout and nothing on stderr. |
|
|
145
|
-
|
|
146
|
-
A healthy, supported service exits with code **3, `authentication_required`**. That is
|
|
147
|
-
the best result this preview can report: the service is reachable, but this CLI holds
|
|
148
|
-
no credentials, so `authenticated` is always `false` and `workspace` is always `null`.
|
|
149
|
-
A reachable service is not a working workspace connection. To connect an application,
|
|
150
|
-
follow the [connection guide](https://www.metergraph.dev/docs/guides/agent-access/).
|
|
151
|
-
|
|
152
|
-
`doctor` never opens a browser, never prompts and never reads stdin, so it is safe in
|
|
153
|
-
scripts and CI.
|
|
154
|
-
|
|
155
|
-
### skill install and skill update
|
|
156
|
-
|
|
157
|
-
`skill install` copies the Metergraph skill bundled with this CLI into one client's
|
|
158
|
-
native project skill directory. Both `--client` and `--runtime` are required, so the
|
|
159
|
-
command never guesses where the skill will be used.
|
|
160
|
-
|
|
161
|
-
| `--client` | Client | Skill file written | Client documentation |
|
|
162
|
-
| --- | --- | --- | --- |
|
|
163
|
-
| `codex` | Codex | `.agents/skills/metergraph/SKILL.md` | [Build skills](https://learn.chatgpt.com/docs/build-skills) |
|
|
164
|
-
| `claude` | Claude Code | `.claude/skills/metergraph/SKILL.md` | [Skills](https://code.claude.com/docs/en/skills) |
|
|
165
|
-
| `cursor` | Cursor | `.cursor/skills/metergraph/SKILL.md` | [Skills](https://cursor.com/docs/skills) |
|
|
33
|
+
Use `--client codex` or `--client cursor` for the other agents.
|
|
166
34
|
|
|
167
|
-
|
|
168
|
-
|
|
35
|
+
**2. Run your application once**, instrumented with a Metergraph SDK (see the
|
|
36
|
+
[first trace guide](https://www.metergraph.dev/docs/start/first-trace/)). Note the trace
|
|
37
|
+
ID or request ID and when the call started and ended.
|
|
169
38
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
| `--client CLIENT` | required | `codex`, `claude` or `cursor`. |
|
|
173
|
-
| `--runtime RUNTIME` | required | `local` when the client runs on this machine, `cloud` when it runs in a cloud environment with a shell and a checkout of the project. Recorded, not detected. |
|
|
174
|
-
| `--project DIR` | current directory | Must be an existing directory. Symbolic links in this path are resolved once; nothing below it is followed. |
|
|
175
|
-
| `--json` | off | Print exactly one JSON line on stdout and nothing on stderr. |
|
|
176
|
-
|
|
177
|
-
What it writes, and nothing else:
|
|
178
|
-
|
|
179
|
-
1. The skill file in the table above, plus any of its missing parent directories.
|
|
180
|
-
2. `.metergraph/skill-installations.json`, a small ownership receipt. For each client it
|
|
181
|
-
records the relative skill path, the skill name, the source revision and SHA-256,
|
|
182
|
-
and the runtimes requested. It contains no credentials, user names or absolute
|
|
183
|
-
paths, so it is safe to commit.
|
|
184
|
-
|
|
185
|
-
Ownership rules:
|
|
186
|
-
|
|
187
|
-
- `install` never replaces a skill it did not install, even one with identical bytes.
|
|
188
|
-
- Running `install` again on an unchanged skill it installed changes nothing.
|
|
189
|
-
- A skill it installed that was edited since is never overwritten, by `install` or by
|
|
190
|
-
`update`. Restore or remove the file first.
|
|
191
|
-
- `update` is the only way to replace an older revision this CLI installed. There is no
|
|
192
|
-
force option.
|
|
193
|
-
- A skill file this CLI installed that has gone missing is written again.
|
|
194
|
-
- Symbolic links and other non-regular entries on the skill or receipt path are
|
|
195
|
-
refused.
|
|
196
|
-
- Files are written to an exclusive temporary file and renamed into place, under a lock
|
|
197
|
-
file, `.metergraph/skill-installations.lock`. The receipt is written only after the
|
|
198
|
-
skill file, and a failed receipt write puts the skill file back as it was. If the
|
|
199
|
-
process is killed between the two writes, the skill is left without a receipt entry,
|
|
200
|
-
so later runs refuse to touch it, and the lock stays until you delete it.
|
|
201
|
-
- It never changes client settings, MCP configuration, `AGENTS.md`, `CLAUDE.md` or any
|
|
202
|
-
other file, and keeps the permissions of a file it replaces.
|
|
203
|
-
|
|
204
|
-
`--runtime cloud` writes the same project file. A cloud client sees it only through
|
|
205
|
-
its own checkout of the project, so commit the file if you installed it elsewhere.
|
|
206
|
-
Writing a skill file in a cloud checkout does not connect MCP, sign in or copy anything
|
|
207
|
-
from your own machine. Local skills do not sync to desktop or cloud apps on their own.
|
|
208
|
-
|
|
209
|
-
Clients and runtimes that cannot load project skill files get a pointer to the
|
|
210
|
-
[connection guide](https://www.metergraph.dev/docs/guides/agent-access/) with exit code
|
|
211
|
-
6, and nothing is written: `--client claude-desktop`, `--client chatgpt` and
|
|
212
|
-
`--runtime cloud-no-shell` (a cloud runtime without a shell or project checkout).
|
|
213
|
-
|
|
214
|
-
A successful run exits `0` with `discovery: "pending"` and `authenticated: false`.
|
|
215
|
-
Writing the file does not prove that a client has loaded it. Start or reload the client
|
|
216
|
-
in the project and check that it lists the `metergraph` skill. The skill itself is
|
|
217
|
-
instructions for the agent; it holds no credentials and does not connect a workspace.
|
|
218
|
-
|
|
219
|
-
#### Bundled skill source
|
|
220
|
-
|
|
221
|
-
`assets/skill/SKILL.md` is a byte-for-byte copy of the public skill at
|
|
222
|
-
<https://www.metergraph.dev/SKILL.md>. The source has no version number of its own, so
|
|
223
|
-
`assets/skill/manifest.json` records its source URL, size and SHA-256, and a revision
|
|
224
|
-
derived from that hash (`sha256-` followed by the first 12 hex digits). The package
|
|
225
|
-
version is not the skill version. At runtime the CLI checks the bundled file against a
|
|
226
|
-
hash pinned in its code and in the manifest, and refuses to write anything if either
|
|
227
|
-
does not match. It never downloads the skill or runs a remote script. A new skill
|
|
228
|
-
revision ships only in a new CLI release; `skill update` then upgrades projects that
|
|
229
|
-
hold an unchanged earlier revision.
|
|
230
|
-
|
|
231
|
-
### login and logout
|
|
232
|
-
|
|
233
|
-
`login` binds a project directory to one Metergraph workspace. Your browser does the
|
|
234
|
-
sign in, sign up, invitation and workspace consent on the service's own pages and
|
|
235
|
-
keeps its own session. The CLI receives only a delegated OAuth grant limited to the
|
|
236
|
-
Metadata scope, `agent:metadata`, checks it with the service and saves it privately.
|
|
39
|
+
**3. Prove the trace arrived.** Setup only proves the key works; `verify` checks that this
|
|
40
|
+
exact trace is visible in your workspace.
|
|
237
41
|
|
|
238
42
|
```sh
|
|
239
|
-
metergraph
|
|
240
|
-
|
|
43
|
+
npx --yes metergraph-cli@next verify --trace-id TRACE_ID \
|
|
44
|
+
--since 2026-01-08T12:00:00Z --until 2026-01-08T12:00:05Z
|
|
241
45
|
```
|
|
242
46
|
|
|
243
|
-
|
|
244
|
-
| --- | --- | --- |
|
|
245
|
-
| `--runtime RUNTIME` | required | `local`: the browser runs on this machine. `cloud` and `cloud-no-shell` get a handoff to the [connection guide](https://www.metergraph.dev/docs/guides/agent-access/) with exit code 6. |
|
|
246
|
-
| `--url ORIGIN` | `https://app.metergraph.dev` | Bare origin only, see [Safe origins](#safe-origins). |
|
|
247
|
-
| `--workspace UUID` | none | The workspace you expect. Sign in fails unless the browser grants exactly this one. Without it, the workspace you choose in the browser is used after the service confirms it. |
|
|
248
|
-
| `--project DIR` | current directory | Existing project directory to bind. |
|
|
249
|
-
| `--config-dir DIR` | see below | Private per-user directory for the saved grant. |
|
|
250
|
-
| `--timeout-ms N` | `300000` | How long to wait for the browser, 1000 to 900000. |
|
|
251
|
-
| `--signup` | off | Start at the hosted sign up page, which returns to the same authorization request. Managed service only; other profiles exit 6. |
|
|
252
|
-
| `--no-browser` | off | Print the authorization URL on stderr for you to open on this machine, then wait. With `--json`, a rerun that needs no approval succeeds; one that needs approval exits 6 `no_browser_requires_terminal` with a `run_in_terminal` next action. |
|
|
253
|
-
| `--reconnect` | off | Allow replacing a binding to a different origin or workspace. |
|
|
254
|
-
| `--json` | off | Print exactly one JSON line on stdout and nothing on stderr. |
|
|
255
|
-
|
|
256
|
-
What `login` does, in order:
|
|
257
|
-
|
|
258
|
-
1. Refuses cloud runtimes, SSH sessions, cloud development environments and CI (by the
|
|
259
|
-
presence of variables such as `SSH_CONNECTION`, `CODESPACES` or `CI`; values are
|
|
260
|
-
never read into output) before any request, listener or file write.
|
|
261
|
-
2. Reads the project binding. A project bound to another origin or workspace is refused
|
|
262
|
-
with exit code 8 unless you pass `--reconnect`. A project that is already signed in
|
|
263
|
-
and still verified is left as it is, with no browser and no new client.
|
|
264
|
-
3. Runs the same checks as `doctor`, then reads the service's OAuth metadata from the
|
|
265
|
-
same origin. Every endpoint must be a fixed path on that origin, and the service must
|
|
266
|
-
offer `agent:metadata`, PKCE with `S256`, public clients and revocation.
|
|
267
|
-
4. Chooses the OAuth client for a loopback redirect, `http://127.0.0.1:PORT/callback`
|
|
268
|
-
on an ephemeral port. When the service's OAuth metadata names a pre-registered
|
|
269
|
-
Metergraph CLI client in `metergraph_cli_client_id`, the CLI uses that client and
|
|
270
|
-
registers nothing; the service accepts the loopback redirect on any port (RFC 8252
|
|
271
|
-
section 7.3). Otherwise it registers a new public client named `Metergraph CLI` for
|
|
272
|
-
that one redirect, as servers without the field expect. A malformed value stops
|
|
273
|
-
sign in. The CLI then creates a random state and PKCE verifier, and arms the callback
|
|
274
|
-
listener and its timeout before the browser opens.
|
|
275
|
-
5. Opens the authorization URL with the operating system's launcher (no shell). The
|
|
276
|
-
listener accepts one `GET` with the exact host, path and state. Other requests get a
|
|
277
|
-
fixed page and do not end the wait.
|
|
278
|
-
6. Exchanges the code and accepts only a Bearer grant for exactly `agent:metadata` whose
|
|
279
|
-
claims name this issuer, resource, client and one workspace. The claims are a sanity
|
|
280
|
-
check; the CLI does not verify token signatures.
|
|
281
|
-
7. Asks the service, with the new token, for `/v1/agent/workspace` and
|
|
282
|
-
`/v1/agent/capabilities`. The workspace ID, its provenance and the token must agree,
|
|
283
|
-
the deployment profile must match step 3, the access scopes must be exactly
|
|
284
|
-
`agent:metadata`, and content, evidence and replay capabilities must be unavailable.
|
|
285
|
-
Nothing else is read.
|
|
286
|
-
8. Saves the grant in the config directory and writes `.metergraph/project.json`. The
|
|
287
|
-
saved grant keeps the client ID it was issued to, so a grant from a registered client
|
|
288
|
-
still refreshes and revokes after the service starts offering a pre-registered one.
|
|
289
|
-
|
|
290
|
-
Once the token response has been validated and holds a usable refresh token, a grant
|
|
291
|
-
the CLI decides not to keep (a workspace other than the one expected or bound, failed
|
|
292
|
-
verification, cancellation) is sent to the revocation endpoint before the command
|
|
293
|
-
exits. If the grant cannot be saved or the binding cannot be written, the saved grant
|
|
294
|
-
is removed, revocation is requested the same way, and the command exits 9. This is best
|
|
295
|
-
effort: the service may not answer or may not confirm, and the CLI does not retry. A
|
|
296
|
-
token response that fails validation is dropped without a revocation request, because
|
|
297
|
-
the CLI cannot safely use anything in it; a server-side grant may remain active until it
|
|
298
|
-
expires or is revoked from the service.
|
|
299
|
-
|
|
300
|
-
The config directory is `--config-dir`, else `METERGRAPH_CONFIG_DIR` (an absolute
|
|
301
|
-
path), else `~/.config/metergraph` on Linux and macOS or `AppData\Roaming\Metergraph` in
|
|
302
|
-
your Windows profile. It holds `credentials/SLOT.json`:
|
|
303
|
-
|
|
304
|
-
- On Linux and macOS the directories must be `0700` and the file `0600`, all owned by
|
|
305
|
-
you. Existing paths with other permissions, other owners or symbolic links are
|
|
306
|
-
refused and never changed.
|
|
307
|
-
- On Windows the grant is encrypted with DPAPI for the current user before it is
|
|
308
|
-
written. File permissions alone are not relied on there.
|
|
309
|
-
|
|
310
|
-
`.metergraph/project.json` holds the origin, workspace ID, deployment profile and the
|
|
311
|
-
name of the credential slot. It holds no token, user name or absolute path, so it is
|
|
312
|
-
safe to commit. Other files in `.metergraph`, such as the skill receipt, are kept.
|
|
313
|
-
|
|
314
|
-
Access tokens near expiry are refreshed once, under a lock, and the new refresh token
|
|
315
|
-
is saved before it is used. If a refresh request may have reached the service but its
|
|
316
|
-
result was not saved (a timeout after sending, a dropped connection, a server error or
|
|
317
|
-
an unusable answer), the old refresh token is never sent again: the next use asks you
|
|
318
|
-
to run `login` again. A revoked grant or lost workspace access fails closed with exit
|
|
319
|
-
code 12.
|
|
320
|
-
|
|
321
|
-
`logout` asks the service to revoke the project's grant through its revocation
|
|
322
|
-
endpoint, then removes the saved grant and `.metergraph/project.json`. Other credential
|
|
323
|
-
slots and project files are kept. A `200` from the service means it accepted the
|
|
324
|
-
revocation request. If it does not answer `200`, local sign out still happens and the
|
|
325
|
-
command exits 13 with `revocation: "unconfirmed"`. A project that is not signed in
|
|
326
|
-
exits 0 without any request.
|
|
327
|
-
|
|
328
|
-
### setup
|
|
329
|
-
|
|
330
|
-
Run setup once from a project directory, choosing the coding client that will use
|
|
331
|
-
the skill:
|
|
47
|
+
Then read what Metergraph has recorded:
|
|
332
48
|
|
|
333
49
|
```sh
|
|
334
|
-
metergraph
|
|
50
|
+
npx --yes metergraph-cli@next usage --days 7 # calls, errors, cost and tokens per route per day
|
|
51
|
+
npx --yes metergraph-cli@next traces --limit 20 # one page of trace metadata
|
|
335
52
|
```
|
|
336
53
|
|
|
337
|
-
|
|
338
|
-
|
|
54
|
+
To install it globally instead, run `npm install -g metergraph-cli@next`. The command is
|
|
55
|
+
`metergraph`. Pin an exact version, such as `metergraph-cli@0.2.0-preview.5`, when a
|
|
56
|
+
script must not change with the `next` tag.
|
|
339
57
|
|
|
340
|
-
|
|
341
|
-
metergraph setup --runtime local --deployment customer-local --url http://localhost:8080 --workspace 11111111-1111-4111-8111-111111111111 --confirm-prerequisites --client codex
|
|
342
|
-
```
|
|
58
|
+
## Requirements
|
|
343
59
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
resolve the skill file conflict and rerun setup without another approval.
|
|
404
|
-
With `--no-browser`, setup prints the approval URL on stderr instead of opening a
|
|
405
|
-
browser. Under `--json` it cannot show that URL, so a rerun that needs no approval
|
|
406
|
-
(a working saved key) still succeeds, and one that needs approval exits 6
|
|
407
|
-
`no_browser_requires_terminal` before any approval request or env write, with
|
|
408
|
-
`data.next_action` set to `{"kind": "run_in_terminal", "message": "..."}`. The
|
|
409
|
-
message is fixed text and holds no URL or credential. Non-hosted operator
|
|
410
|
-
handoffs carry their own deployment `next_action`; every other setup result has
|
|
411
|
-
`next_action: null`.
|
|
412
|
-
Success means the key was delivered and the project is ready to instrument.
|
|
413
|
-
It does **not** mean application traffic has arrived. Run your application and
|
|
414
|
-
verify one exact trace afterward. The hosted service advertises the setup
|
|
415
|
-
contract, but each non-hosted deployment must be checked at its own origin.
|
|
416
|
-
Local protocol tests do not prove browser approval or real application traffic.
|
|
417
|
-
|
|
418
|
-
#### Repository identity
|
|
419
|
-
|
|
420
|
-
After the key is ready, setup records which repository the project belongs to,
|
|
421
|
-
so traces from several repositories in one workspace stay apart. The SDKs read
|
|
422
|
-
`METERGRAPH_REPOSITORY`, then the nearest `.metergraph/config.json`:
|
|
423
|
-
|
|
424
|
-
```json
|
|
425
|
-
{
|
|
426
|
-
"version": 2,
|
|
427
|
-
"repository": "example-org/example-app"
|
|
428
|
-
}
|
|
60
|
+
| | Supported |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| Node.js | 22 and 24 |
|
|
63
|
+
| Operating systems | Linux, macOS and Windows, each tested in CI |
|
|
64
|
+
| Runtime dependencies | None |
|
|
65
|
+
| Service | Hosted Metergraph, or a self-hosted deployment (see [Deployments](#deployments)) |
|
|
66
|
+
|
|
67
|
+
Sign in needs a service that offers Metadata-only CLI grants and grant revocation; a
|
|
68
|
+
service without them is reported as unsupported, and the CLI never falls back to
|
|
69
|
+
broader access. `setup` also needs the deployment's ingest bootstrap API and approval by
|
|
70
|
+
a workspace member. Sign in needs a browser on the same machine. From SSH sessions,
|
|
71
|
+
cloud development environments and CI, `login` and `setup` hand off to a local machine
|
|
72
|
+
instead (exit 6).
|
|
73
|
+
|
|
74
|
+
## Using it with a coding agent
|
|
75
|
+
|
|
76
|
+
The CLI is built to be run by Codex, Claude Code and Cursor as well as by people:
|
|
77
|
+
|
|
78
|
+
- **Skill.** `setup` installs the Metergraph skill, which tells the agent how to set up
|
|
79
|
+
and query Metergraph. To install or update only the skill, with no sign in and no
|
|
80
|
+
network access:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
npx --yes metergraph-cli@next skill install --client codex --runtime local
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Python users can run `pip install metergraph-skills` and
|
|
87
|
+
`metergraph-skills install --client codex --runtime local` instead
|
|
88
|
+
([details](https://github.com/metergraph/cli/blob/main/python/metergraph-skills/README.md)).
|
|
89
|
+
Both write the same files and recognise each other's installs.
|
|
90
|
+
- **Model-swap skills.** `skills install` adds the workflow skills from
|
|
91
|
+
[metergraph/skills](https://github.com/metergraph/skills): choose a workload, choose
|
|
92
|
+
traces and models, define the eval, run the analysis, read the report and rerun,
|
|
93
|
+
all from the agent. Start with `metergraph-model-swap`. In Claude Desktop, add
|
|
94
|
+
the `metergraph/skills` plugin marketplace instead.
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
npx --yes metergraph-cli@next skills install --client claude --runtime local
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- **Machine output.** Every command takes `--json` and then prints exactly one JSON
|
|
101
|
+
line on stdout and nothing on stderr. All results share one envelope:
|
|
102
|
+
`schema_version`, `command`, `ok`, `outcome`, `exit_code`, `data` and `error`.
|
|
103
|
+
Failures carry a fixed `error.reason` and, where useful, a `data.next_action` that
|
|
104
|
+
tells the agent what to do next.
|
|
105
|
+
- **No hidden prompts.** No command reads stdin or asks a question. `--no-browser`
|
|
106
|
+
prints the sign in URL for a person to open instead of launching a browser; with
|
|
107
|
+
`--json`, a step that needs approval exits 6 with a `run_in_terminal` next action.
|
|
108
|
+
- **MCP.** Workspace tools for agents are served by the hosted MCP endpoint, not by this
|
|
109
|
+
CLI. See the [MCP server guide](https://www.metergraph.dev/docs/guides/mcp-server/)
|
|
110
|
+
and the [agent access guide](https://www.metergraph.dev/docs/guides/agent-access/).
|
|
111
|
+
|
|
112
|
+
Suggested `AGENTS.md` or `CLAUDE.md` lines for a project that uses Metergraph:
|
|
113
|
+
|
|
114
|
+
```md
|
|
115
|
+
- Metergraph CLI: `npx --yes metergraph-cli@next <command> --json`. Check exit codes;
|
|
116
|
+
follow `data.next_action` on failure.
|
|
117
|
+
- After changing instrumented code, run the app once and confirm the trace with
|
|
118
|
+
`metergraph verify` before reporting that tracing works.
|
|
429
119
|
```
|
|
430
120
|
|
|
431
|
-
|
|
432
|
-
remote (or the only remote). It writes `.metergraph/config.json` in the project
|
|
433
|
-
directory only when no identity exists yet. The file holds no secret; review
|
|
434
|
-
and commit it. An identity already set in `METERGRAPH_REPOSITORY` in the env
|
|
435
|
-
file, or in a `.metergraph/config.json` in the project or a parent directory, is
|
|
436
|
-
kept. If it differs from the remote or `--repository`, setup reports a mismatch
|
|
437
|
-
and changes nothing. Setup never rewrites a config file the SDKs cannot use.
|
|
438
|
-
Remotes with nested groups are not guessed; pass `--repository OWNER/NAME`.
|
|
439
|
-
`--no-repository` records nothing.
|
|
440
|
-
|
|
441
|
-
The JSON result reports the outcome in `data.repository`: `status` is
|
|
442
|
-
`written`, `existing`, `mismatch`, `invalid_config`, `not_inferred`,
|
|
443
|
-
`write_failed` or `skipped`, with the `repository`, its `source`
|
|
444
|
-
(`git_remote`, `flag`, `config` or `env_file`), the config `path`, and, for a
|
|
445
|
-
mismatch, the `expected` value. Only `owner/name` is reported, never a remote
|
|
446
|
-
URL. Recording an identity never fails setup.
|
|
447
|
-
|
|
448
|
-
With an identity configured, the SDKs exchange the ingest key for a
|
|
449
|
-
repository-scoped session before sending traces. Without one they keep the
|
|
450
|
-
older ingestion path and log that the identity is not configured; capture
|
|
451
|
-
continues either way.
|
|
452
|
-
|
|
453
|
-
Several repositories can send to one workspace. Run `setup` once in each
|
|
454
|
-
repository, so each has its own project binding and private `.env`; pass the
|
|
455
|
-
same `--workspace UUID` each time to pin the workspace; and give each
|
|
456
|
-
repository its own identity.
|
|
457
|
-
|
|
458
|
-
### Read commands
|
|
459
|
-
|
|
460
|
-
The read commands use the grant `login` saved for this project. They never open a
|
|
461
|
-
browser, never sign in on their own and never request another scope. Each one:
|
|
462
|
-
|
|
463
|
-
- reads `.metergraph/project.json` and the saved grant, and refreshes the grant at most
|
|
464
|
-
once, under the same lock and rules as `login` (an interrupted refresh is never
|
|
465
|
-
retried with a possibly used token);
|
|
466
|
-
- asks the service for `/v1/agent/workspace` and `/v1/agent/capabilities` and checks,
|
|
467
|
-
as `login` does, that the workspace, deployment profile and `agent:metadata` scope
|
|
468
|
-
still match the binding and that content, evidence and replay are unavailable;
|
|
469
|
-
- sends only `GET` requests to fixed paths on the bound origin, follows no redirects and
|
|
470
|
-
reads at most 1 MiB of a response;
|
|
471
|
-
- runs every request, including a refresh and any wait for another command that is
|
|
472
|
-
refreshing the same grant, within one total `--timeout-ms` deadline (1000 to 60000,
|
|
473
|
-
default 15000), which is never reset per request or page. A deadline exits 4
|
|
474
|
-
(`timeout`) and Ctrl+C exits 17, also while waiting for that lock; a lock held by
|
|
475
|
-
another process is never removed or taken over;
|
|
476
|
-
- prints only fields it validated. Unknown response fields are ignored and never named.
|
|
477
|
-
A metadata row that holds a field such as `prompt`, `messages`, `tool_calls` or
|
|
478
|
-
`access_token` is refused as a whole (exit 11). Names that contain control or
|
|
479
|
-
formatting characters are shown as `null`. Service warning and error text is not
|
|
480
|
-
printed. If any printed value, such as a workspace name, route name, cursor or
|
|
481
|
-
provenance source, contains a token the CLI holds for this project, nothing is
|
|
482
|
-
printed and the command exits 11 with `credential_in_metadata_response`.
|
|
483
|
-
|
|
484
|
-
Read commands do not change workspace configuration or telemetry, send no ingest data
|
|
485
|
-
and call no model provider. They are not side-effect free on the service: a command may
|
|
486
|
-
refresh its own saved grant, and the service may update its audit records and last used
|
|
487
|
-
times for the grant.
|
|
488
|
-
|
|
489
|
-
| Command | Request | What it prints |
|
|
490
|
-
| --- | --- | --- |
|
|
491
|
-
| `status` | `GET /healthz` and `GET /v1/deployment` (no credentials, checked as `doctor` does), then the two checks above | `configured`, `reachable`, `healthy`, `authenticated`, the bound (`intended`) and verified (`actual`) workspace, the bound deployment profile and `deployment_profile_verified`, scopes and capability flags. A `/v1/deployment` profile that differs from the binding exits 11 with `profile_mismatch` before the grant is used. `application_traffic_verified` is always `false`: a signed in project, existing data or a configured SDK does not prove that your application sends traffic. |
|
|
492
|
-
| `context` | the two checks above | Workspace ID, slug, name and creation time, Metadata retention days, whether the workspace captures content (never included here) and the access scope. |
|
|
493
|
-
| `capabilities` | the two checks above | Each known agent capability with `available`, `privacy_class`, `required_scope` and flags, and the service's bounds. Privacy class descriptions are not printed. |
|
|
494
|
-
| `usage` | `GET /v1/agent/usage?days=N&limit=N` | Daily rows per route: calls, errors, cost, tokens and latency (latency may be `null`), the window, evidence completeness, warning codes and totals of the returned rows. |
|
|
495
|
-
| `routes` | `GET /v1/agent/routes` | Route name, calls, replay eligible calls, evaluation contract version and hash, and whether a description or contract exists. |
|
|
496
|
-
| `traces` | `GET /v1/agent/traces?days=N&limit=N[&route=&status=&cursor=]` | One page of trace metadata: IDs, name, status, times, span count, tokens, cost (may be `null`), routes, providers and models, plus `next_cursor`. |
|
|
497
|
-
|
|
498
|
-
Bounds and honesty rules:
|
|
499
|
-
|
|
500
|
-
- `--days` is 1 to 90 (default 7) and `--limit` 1 to 200 (default 50, or 20 for
|
|
501
|
-
`traces`). Values outside these ranges exit 2. A value above the service's own
|
|
502
|
-
advertised `max_days` or `max_rows` exits 6 with `exceeds_service_bounds`; it is never
|
|
503
|
-
reduced silently.
|
|
504
|
-
- `usage` and `traces` report `truncated` and `complete`. Totals are sums of the
|
|
505
|
-
returned rows only; when `complete` is `false` they are not workspace totals. An
|
|
506
|
-
empty window is a successful result with `empty: true`.
|
|
507
|
-
- `GET /v1/agent/routes` takes no limit or window. The CLI validates every returned row,
|
|
508
|
-
keeps the first `--limit`, and reports `server_rows`, `truncated` and
|
|
509
|
-
`truncation: "local"`. Route descriptions, constraints and evaluation contract bodies
|
|
510
|
-
are never printed; `omitted_fields` lists them.
|
|
511
|
-
- `traces` fetches exactly one page. When more exist it returns `next_cursor`; pass it
|
|
512
|
-
back with `--cursor` to read the next page. The cursor is opaque and at most 512
|
|
513
|
-
printable characters. A page whose `limit` differs from the request, or rows that do
|
|
514
|
-
not match `--status` or `--route`, exit 11. Free-text filter values are not printed
|
|
515
|
-
back.
|
|
516
|
-
- The `traces` listing prints no trace links; each row has `link: null` and the
|
|
517
|
-
page reports `link_status: "server_link_unavailable"`. Exact-trace
|
|
518
|
-
`verify --open` uses a server link only when it includes the verified
|
|
519
|
-
workspace binding.
|
|
520
|
-
- `--environment`, `--workload`, `--since`, `--until`, `--sql`, `--query`, `--content`,
|
|
521
|
-
`--include-content`, `--debug` and `--replay` are recognized and refused with exit 6
|
|
522
|
-
before any request. The agent access contract has no environment selector or
|
|
523
|
-
absolute time range, and these commands never read content or replay. `--workload`
|
|
524
|
-
is refused by this CLI version because the returned trace rows do not show which
|
|
525
|
-
workload they belong to, so a filtered page could not be verified.
|
|
526
|
-
- A capability the service does not offer to this grant exits 14 without a read. A
|
|
527
|
-
refused read exits 15 (`insufficient_scope` or `forbidden`), rate limiting exits 16
|
|
528
|
-
with `retry_after_seconds` when the service sends a whole number of seconds, and a
|
|
529
|
-
token refused during the read exits 12. Nothing is retried. Ctrl+C exits 17.
|
|
530
|
-
|
|
531
|
-
A successful `usage`, shortened:
|
|
532
|
-
|
|
533
|
-
```json
|
|
534
|
-
{
|
|
535
|
-
"schema_version": 1,
|
|
536
|
-
"command": "usage",
|
|
537
|
-
"ok": true,
|
|
538
|
-
"outcome": "ok",
|
|
539
|
-
"exit_code": 0,
|
|
540
|
-
"data": {
|
|
541
|
-
"origin": "https://metergraph.example.com",
|
|
542
|
-
"workspace": { "id": "0b5c7c1e-1a2b-4c3d-8e4f-5a6b7c8d9e01" },
|
|
543
|
-
"deployment_profile": "managed",
|
|
544
|
-
"authenticated": true,
|
|
545
|
-
"scopes": ["agent:metadata"],
|
|
546
|
-
"result": {
|
|
547
|
-
"provenance": {
|
|
548
|
-
"deployment_profile": "managed",
|
|
549
|
-
"workspace_id": "0b5c7c1e-1a2b-4c3d-8e4f-5a6b7c8d9e01",
|
|
550
|
-
"generated_at": "2026-01-08T12:00:00Z",
|
|
551
|
-
"source": "example-source"
|
|
552
|
-
},
|
|
553
|
-
"window": { "days": 7, "since": "2026-01-01T12:00:00Z", "until": "2026-01-08T12:00:00Z" },
|
|
554
|
-
"evidence": { "sources": ["telemetry"], "rows": 1, "complete": true },
|
|
555
|
-
"warnings": [],
|
|
556
|
-
"content_included": false,
|
|
557
|
-
"truncated": false,
|
|
558
|
-
"complete": true,
|
|
559
|
-
"empty": false,
|
|
560
|
-
"rows": 1,
|
|
561
|
-
"items": [
|
|
562
|
-
{
|
|
563
|
-
"date": "2026-01-02",
|
|
564
|
-
"route": "checkout-summary",
|
|
565
|
-
"calls": 40,
|
|
566
|
-
"error_calls": 2,
|
|
567
|
-
"cost_usd": 0.0125,
|
|
568
|
-
"input_tokens": 12000,
|
|
569
|
-
"output_tokens": 3400,
|
|
570
|
-
"avg_latency_ms": 820,
|
|
571
|
-
"p95_latency_ms": null
|
|
572
|
-
}
|
|
573
|
-
],
|
|
574
|
-
"totals": {
|
|
575
|
-
"scope": "returned_rows",
|
|
576
|
-
"complete": true,
|
|
577
|
-
"calls": 40,
|
|
578
|
-
"error_calls": 2,
|
|
579
|
-
"cost_usd": 0.0125,
|
|
580
|
-
"input_tokens": 12000,
|
|
581
|
-
"output_tokens": 3400
|
|
582
|
-
}
|
|
583
|
-
},
|
|
584
|
-
"retry_after_seconds": null,
|
|
585
|
-
"notices": [],
|
|
586
|
-
"next_action": null
|
|
587
|
-
},
|
|
588
|
-
"error": null
|
|
589
|
-
}
|
|
590
|
-
```
|
|
121
|
+
## Commands
|
|
591
122
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
`
|
|
595
|
-
`
|
|
123
|
+
| Command | What it does | Network |
|
|
124
|
+
| --- | --- | --- |
|
|
125
|
+
| `setup` | Sign in, choose a workspace, write the ingest key to `.env`, record the repository, install the skill | Yes |
|
|
126
|
+
| `verify` | Confirm that one exact trace, by trace ID or request ID, reached the workspace | Yes |
|
|
127
|
+
| `usage` | Daily calls, errors, cost, tokens and latency per route | Yes |
|
|
128
|
+
| `traces` | One page of trace metadata, with a cursor for the next page | Yes |
|
|
129
|
+
| `routes` | Routes with call counts and evaluation contract status | Yes |
|
|
130
|
+
| `status` | Whether the project is signed in, and to which workspace | Yes |
|
|
131
|
+
| `context` | Workspace details and retention | Yes |
|
|
132
|
+
| `capabilities` | What the workspace makes available to this project | Yes |
|
|
133
|
+
| `login`, `logout` | Sign a project in or out without the rest of `setup` | Yes |
|
|
134
|
+
| `doctor` | Check that a service is reachable, healthy and supported, without credentials | Yes |
|
|
135
|
+
| `skill install`, `skill update` | Install or update the Metergraph skill for one agent | No |
|
|
136
|
+
| `skills install`, `skills update`, `skills list` | Install, update or list the workflow skills, such as the model-swap loop | No |
|
|
137
|
+
|
|
138
|
+
`metergraph help COMMAND` lists a command's options. The
|
|
139
|
+
[CLI reference](https://github.com/metergraph/cli/blob/main/docs/reference.md) covers every option, what each command reads and
|
|
140
|
+
writes, and full JSON examples.
|
|
596
141
|
|
|
597
142
|
## Exit codes
|
|
598
143
|
|
|
599
|
-
Exit codes are stable. Changing one is a breaking change. Codes 10 to 17
|
|
600
|
-
|
|
144
|
+
Exit codes are stable. Changing one is a breaking change. Codes 10 to 17 are not in
|
|
145
|
+
`0.1.0`.
|
|
601
146
|
|
|
602
147
|
| Code | Outcome | Meaning |
|
|
603
148
|
| --- | --- | --- |
|
|
@@ -620,339 +165,47 @@ added in `0.2.0-preview.0` and are unavailable in `0.1.0`.
|
|
|
620
165
|
| 16 | `rate_limited` | The service asked the CLI to slow down. Nothing was retried. Try again later. |
|
|
621
166
|
| 17 | `cancelled` | A read command was interrupted before it finished. Read commands never change workspace configuration or telemetry. |
|
|
622
167
|
|
|
623
|
-
##
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
- `profile_status` is `supported`, `unrecognized`, `unavailable` (the server has no
|
|
664
|
-
`/v1/deployment` endpoint) or `unknown` (not checked).
|
|
665
|
-
- Checks that did not run have `result: "skipped"`.
|
|
666
|
-
- `--help --json` includes the command list and the exit code table.
|
|
667
|
-
|
|
668
|
-
A successful `skill install`:
|
|
669
|
-
|
|
670
|
-
```json
|
|
671
|
-
{
|
|
672
|
-
"schema_version": 1,
|
|
673
|
-
"command": "skill install",
|
|
674
|
-
"ok": true,
|
|
675
|
-
"outcome": "ok",
|
|
676
|
-
"exit_code": 0,
|
|
677
|
-
"data": {
|
|
678
|
-
"client": "claude",
|
|
679
|
-
"runtime": "local",
|
|
680
|
-
"path": ".claude/skills/metergraph/SKILL.md",
|
|
681
|
-
"status": "installed",
|
|
682
|
-
"source": {
|
|
683
|
-
"name": "metergraph",
|
|
684
|
-
"revision": "sha256-57b920677adf",
|
|
685
|
-
"sha256": "57b920677adf62759c7221629327192a2d16b7e6034f7948ffd96cee402d4891"
|
|
686
|
-
},
|
|
687
|
-
"discovery": "pending",
|
|
688
|
-
"authenticated": false,
|
|
689
|
-
"next_action": {
|
|
690
|
-
"kind": "reload_client",
|
|
691
|
-
"message": "Start or restart Claude Code in this project, then confirm that it lists the metergraph skill."
|
|
692
|
-
}
|
|
693
|
-
},
|
|
694
|
-
"error": null
|
|
695
|
-
}
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
- `status` is `installed`, `updated` or `reused` (already in place, nothing rewritten).
|
|
699
|
-
- `path` is always relative to the project. Absolute paths are never printed.
|
|
700
|
-
- On failure `status` and `discovery` are `null`, and `error.reason` is a fixed token
|
|
701
|
-
such as `not_owned`, `modified`, `update_required`, `not_installed`, `unsafe_path`,
|
|
702
|
-
`receipt_invalid`, `locked`, `invalid_project`, `client_not_supported`,
|
|
703
|
-
`write_failed` or `bundled_skill_invalid`.
|
|
704
|
-
|
|
705
|
-
A successful `login`:
|
|
706
|
-
|
|
707
|
-
```json
|
|
708
|
-
{
|
|
709
|
-
"schema_version": 1,
|
|
710
|
-
"command": "login",
|
|
711
|
-
"ok": true,
|
|
712
|
-
"outcome": "ok",
|
|
713
|
-
"exit_code": 0,
|
|
714
|
-
"data": {
|
|
715
|
-
"origin": "https://metergraph.example.com",
|
|
716
|
-
"runtime": "local",
|
|
717
|
-
"deployment_profile": "managed",
|
|
718
|
-
"workspace": { "id": "0b5c7c1e-1a2b-4c3d-8e4f-5a6b7c8d9e01" },
|
|
719
|
-
"scopes": ["agent:metadata"],
|
|
720
|
-
"authenticated": true,
|
|
721
|
-
"configured": true,
|
|
722
|
-
"status": "signed_in",
|
|
723
|
-
"binding": ".metergraph/project.json",
|
|
724
|
-
"credential_protection": "owner_only_file",
|
|
725
|
-
"previous_grant_revocation": null,
|
|
726
|
-
"next_action": {
|
|
727
|
-
"kind": "connected",
|
|
728
|
-
"message": "This project is signed in with Metadata access. Run \"metergraph logout\" to sign out."
|
|
729
|
-
}
|
|
730
|
-
},
|
|
731
|
-
"error": null
|
|
732
|
-
}
|
|
733
|
-
```
|
|
734
|
-
|
|
735
|
-
- `status` is `signed_in`, `reused` (already signed in and verified, nothing changed)
|
|
736
|
-
or `reconnected` (a new grant replaced the previous one; `previous_grant_revocation`
|
|
737
|
-
is then `accepted`, `unconfirmed` or `not_attempted`).
|
|
738
|
-
- `credential_protection` is `owner_only_file` or `dpapi`.
|
|
739
|
-
- On failure `authenticated` and `configured` are `false`, `scopes` is empty, and
|
|
740
|
-
`next_action` is `null` or a handoff such as `connection_guide`, `reconnect`,
|
|
741
|
-
`run_in_terminal` or `no_browser`.
|
|
742
|
-
- The schema version `1` is the version of this CLI's own JSON output. It is unrelated
|
|
743
|
-
to the service's agent access contract version, `metergraph.agent-access/v1`.
|
|
744
|
-
- `login` and `logout` never print tokens, the authorization code, the PKCE verifier,
|
|
745
|
-
user names, email addresses, workspace names, absolute paths or server text. The
|
|
746
|
-
read commands never print tokens, email addresses, absolute paths or server error
|
|
747
|
-
text either; `context` prints the workspace slug and name, and the read commands
|
|
748
|
-
print validated route, trace, provider and model names, as described above.
|
|
749
|
-
|
|
750
|
-
`logout` prints `local_credentials` (`removed` or `none`), `binding` (`removed`,
|
|
751
|
-
`kept` or `none`) and `revocation` (`accepted`, `unconfirmed` or `not_attempted`).
|
|
752
|
-
|
|
753
|
-
Without `--json`, results are printed as text on stdout and usage errors go to stderr.
|
|
754
|
-
`login` prints progress lines, and with `--no-browser` the authorization URL, on
|
|
755
|
-
stderr.
|
|
756
|
-
|
|
757
|
-
## Safe origins
|
|
758
|
-
|
|
759
|
-
`--url` accepts only a bare origin, with an optional trailing slash:
|
|
760
|
-
|
|
761
|
-
- `https://` origins on any host, for example `https://metergraph.example.com`.
|
|
762
|
-
- `http://` only for `localhost`, `127.0.0.1` and `[::1]`, with an optional port.
|
|
763
|
-
|
|
764
|
-
Usernames, passwords, paths, queries and fragments are rejected before any request is
|
|
765
|
-
made. Invalid values and unknown arguments are not printed back, because a mistyped
|
|
766
|
-
argument can contain a credential. An accepted origin is printed in the output and sent
|
|
767
|
-
to the network, so do not put secrets in a hostname. See [Security](#security).
|
|
768
|
-
|
|
769
|
-
## Deployment profiles
|
|
770
|
-
|
|
771
|
-
The CLI recognizes these `deployment_profile` values: `local`, `managed` and
|
|
772
|
-
`byoc-core`. Managed staging uses the `managed` profile. Any other value is reported as `unsupported` and is not echoed. A server
|
|
773
|
-
without `/v1/deployment`, such as a self-hosted open source server, is reported as
|
|
774
|
-
`unsupported` with `profile_status: "unavailable"` until a dedicated adapter ships. The
|
|
775
|
-
CLI never assumes such a server is hosted.
|
|
776
|
-
|
|
777
|
-
## What doctor does not do
|
|
778
|
-
|
|
779
|
-
- It reads no credentials from environment variables, files, arguments or cookies, and
|
|
780
|
-
sends no `Authorization` or `Cookie` header.
|
|
781
|
-
- It follows no redirects.
|
|
782
|
-
- It reads at most 32 KiB of any response body and stops at the `--timeout-ms` limit.
|
|
783
|
-
- It never prints response bodies, response headers, authentication challenges,
|
|
784
|
-
server-supplied URLs or error text from the network stack.
|
|
785
|
-
- It makes no model provider calls, sends no usage data and reads no stored traces.
|
|
786
|
-
|
|
787
|
-
## What skill install does not do
|
|
788
|
-
|
|
789
|
-
- It makes no network requests and no model provider calls. The skill comes from this
|
|
790
|
-
package, not from a download.
|
|
791
|
-
- It does not sign in, store credentials, configure MCP or edit client settings.
|
|
792
|
-
- It does not claim a client has loaded the skill. `discovery` stays `pending`.
|
|
793
|
-
- It never prints file contents, absolute paths or raw error text.
|
|
794
|
-
|
|
795
|
-
## What login does not do
|
|
796
|
-
|
|
797
|
-
- It never asks for Debug (`agent:read`) or Replay (`agent:replay`) access, and never
|
|
798
|
-
falls back to them when the service does not offer `agent:metadata`.
|
|
799
|
-
- It never copies browser cookies or the browser's sign in.
|
|
800
|
-
- It does not create an application ingest key, and no manual API key is required. The
|
|
801
|
-
service records the grant as an OAuth connection on its own side; that connection
|
|
802
|
-
can only use `agent:metadata`, is separate from any API or ingest key you manage, and
|
|
803
|
-
is what `logout` asks the service to revoke.
|
|
804
|
-
- It reads no telemetry, retained content or traces, and makes no model provider calls.
|
|
805
|
-
- It sends the grant only to the origin it came from, follows no redirects and reads
|
|
806
|
-
bounded responses within fixed time limits.
|
|
807
|
-
- It accepts no credential on the command line, in the environment or on stdin.
|
|
808
|
-
|
|
809
|
-
## What the read commands do not do
|
|
810
|
-
|
|
811
|
-
- They never sign in, open a browser, create a grant or change the project binding.
|
|
812
|
-
The only file they may write is the saved grant, when a refresh rotates it.
|
|
813
|
-
- They never request `agent:read` or `agent:replay`, never read retained content,
|
|
814
|
-
evidence or replays, and never call a model provider.
|
|
815
|
-
- They make only `GET` requests to the read endpoints (a grant refresh, when needed, is
|
|
816
|
-
the only `POST`). They send no ingest data and change no evaluations, provider
|
|
817
|
-
settings, workspace configuration or telemetry. The service may still record the
|
|
818
|
-
access, for example audit entries and the grant's last used time.
|
|
819
|
-
- They never print a token the CLI holds, even inside an otherwise valid name.
|
|
820
|
-
- They never follow a cursor or page on their own, and never widen a request: an
|
|
821
|
-
unsupported option, an out of range value or a capability the grant lacks fails
|
|
822
|
-
instead.
|
|
823
|
-
- They accept no credential on the command line, in the environment or on stdin.
|
|
824
|
-
|
|
825
|
-
## Development
|
|
826
|
-
|
|
827
|
-
```sh
|
|
828
|
-
npm test # unit tests and CLI subprocess tests against loopback servers
|
|
829
|
-
npm run test:package # npm pack into a temporary directory, clean install, run the installed bin
|
|
830
|
-
node bin/metergraph.js --help
|
|
831
|
-
node bin/metergraph.js doctor --url http://127.0.0.1:8080 --json
|
|
832
|
-
node bin/metergraph.js skill install --client claude --runtime local --project /path/to/project --json
|
|
833
|
-
node bin/metergraph.js login --runtime local --url http://127.0.0.1:8080 --project /path/to/project
|
|
834
|
-
```
|
|
835
|
-
|
|
836
|
-
The sign in and read command tests run against a synthetic loopback service and a
|
|
837
|
-
test-only browser stand-in loaded with `--import`. They prove the protocol, file
|
|
838
|
-
handling and output rules, not the real service, a real browser, real workspace
|
|
839
|
-
consent or real workspace data. The Windows DPAPI round trip
|
|
840
|
-
runs only on the Windows CI runner.
|
|
841
|
-
|
|
842
|
-
To try a packed artifact without publishing:
|
|
843
|
-
|
|
844
|
-
```sh
|
|
845
|
-
npm pack --pack-destination "$(mktemp -d)"
|
|
846
|
-
npx --yes --package=/path/to/metergraph-cli-0.2.0-preview.3.tgz -- metergraph --version
|
|
847
|
-
```
|
|
848
|
-
|
|
849
|
-
Do not commit tarballs or other generated files.
|
|
850
|
-
|
|
851
|
-
## Releasing
|
|
852
|
-
|
|
853
|
-
The source of truth is the public repository
|
|
854
|
-
[github.com/metergraph/cli](https://github.com/metergraph/cli), licensed Apache-2.0.
|
|
855
|
-
`0.2.0-preview.3` is published on the `next` npm tag; `latest` remains on
|
|
856
|
-
`0.1.0`. The hosted service supports sign in, Metadata reads and ingest bootstrap.
|
|
857
|
-
Subsequent releases must pass the checks below before publication.
|
|
858
|
-
|
|
859
|
-
Releases are manual. The `Release CLI` workflow (`.github/workflows/release.yml`) runs
|
|
860
|
-
only when a maintainer starts it from `main`. It does not run on tags, pushes or a
|
|
861
|
-
schedule. It:
|
|
862
|
-
|
|
863
|
-
1. checks that `commit_sha` equals the commit the run started from (see
|
|
864
|
-
[Exact revision rule](#exact-revision-rule)), that it is on `main`, and that
|
|
865
|
-
`version` equals `package.json`;
|
|
866
|
-
2. asks the npm registry for that exact version and continues only on a `404`. An
|
|
867
|
-
existing version, any other status or a network failure stops the run;
|
|
868
|
-
3. runs `npm test` and `npm run test:package` at that commit;
|
|
869
|
-
4. packs the tarball and records its SHA-256;
|
|
870
|
-
5. only if `publish` is true, waits for approval on the `npm-release` environment,
|
|
871
|
-
checks out the same commit again, verifies the checksum and runs
|
|
872
|
-
`npm publish --provenance` for that exact tarball.
|
|
873
|
-
|
|
874
|
-
Leave `publish` false for a dry run that validates and packs without publishing.
|
|
875
|
-
|
|
876
|
-
### Exact revision rule
|
|
877
|
-
|
|
878
|
-
npm provenance records the commit that triggered the workflow (`GITHUB_SHA`) as the
|
|
879
|
-
source of the package. To keep that statement true, the workflow only releases that
|
|
880
|
-
commit:
|
|
881
|
-
|
|
882
|
-
- `commit_sha` must be the full 40 character SHA of the current head of `main`, and it
|
|
883
|
-
must equal `GITHUB_SHA` for the run. Older commits on `main` are rejected even though
|
|
884
|
-
they are ancestors of `main`.
|
|
885
|
-
- Both the validate job and the publish job check out that commit and confirm it.
|
|
886
|
-
- If `main` moves after you copy the SHA, the run fails. Start a new run with the new
|
|
887
|
-
head. To release an older state, land it on `main` first.
|
|
888
|
-
|
|
889
|
-
### First package bootstrap (completed for 0.1.0)
|
|
890
|
-
|
|
891
|
-
npm trusted publishing is configured on a package that already exists, so the very
|
|
892
|
-
first version could not come from this workflow. The steps below describe the
|
|
893
|
-
completed bootstrap of `0.1.0`; they are not part of subsequent releases. No npm
|
|
894
|
-
token or secret is stored here.
|
|
895
|
-
|
|
896
|
-
1. Confirm the intended npm maintainer accounts and that `metergraph-cli` is
|
|
897
|
-
available. The first approved publish establishes package ownership.
|
|
898
|
-
2. Run the `Release CLI` workflow with `publish` false. Download the
|
|
899
|
-
`metergraph-cli-release` artifact and check its SHA-256 against the run summary.
|
|
900
|
-
3. From a maintainer machine with npm two-factor authentication, publish that exact
|
|
901
|
-
tarball manually using `npm publish /path/to/metergraph-cli-0.1.0.tgz --access public
|
|
902
|
-
--tag next --provenance=false --ignore-scripts`. This bootstrap version has no
|
|
903
|
-
provenance attestation. Use a new version for the first trusted release.
|
|
904
|
-
4. Configure trusted publishing as described below.
|
|
905
|
-
|
|
906
|
-
### Trusted publishing
|
|
907
|
-
|
|
908
|
-
After the package exists, follow the official npm guide,
|
|
909
|
-
[Trusted publishing for npm packages](https://docs.npmjs.com/trusted-publishers/), and
|
|
910
|
-
add a GitHub Actions trusted publisher with exactly these values:
|
|
911
|
-
|
|
912
|
-
| Field | Value |
|
|
913
|
-
| --- | --- |
|
|
914
|
-
| Organization or user | `metergraph` |
|
|
915
|
-
| Repository | `cli` |
|
|
916
|
-
| Workflow filename | `release.yml` |
|
|
917
|
-
| Environment name | `npm-release` |
|
|
918
|
-
|
|
919
|
-
Trusted publishing requires npm 11.5.1 or newer. The publish job checks this before it
|
|
920
|
-
publishes. After the trusted publisher works, consider restricting the package to
|
|
921
|
-
trusted publishing so that long lived tokens cannot publish it.
|
|
922
|
-
|
|
923
|
-
### Release configuration
|
|
924
|
-
|
|
925
|
-
Before an automated publish, confirm the following settings are still in place:
|
|
926
|
-
|
|
927
|
-
- the [trusted publisher](#trusted-publishing) matches this repository and workflow;
|
|
928
|
-
- the `npm-release` environment has required reviewers;
|
|
929
|
-
- the repository variable `METERGRAPH_CLI_PUBLISH_ENABLED` is `true`.
|
|
930
|
-
|
|
931
|
-
Until all of these are done, leave `publish` false.
|
|
932
|
-
|
|
933
|
-
### After a release
|
|
934
|
-
|
|
935
|
-
After each release, confirm from a clean machine, replacing `VERSION`:
|
|
936
|
-
|
|
937
|
-
```sh
|
|
938
|
-
npx --yes metergraph-cli@VERSION --version --json
|
|
939
|
-
npx --yes metergraph-cli@VERSION doctor --json
|
|
940
|
-
npm view metergraph-cli@VERSION dist.attestations
|
|
941
|
-
```
|
|
942
|
-
|
|
943
|
-
Releases from the workflow should show a provenance attestation that names
|
|
944
|
-
`metergraph/cli` and the released commit. The bootstrap version will not.
|
|
945
|
-
|
|
946
|
-
## Security
|
|
947
|
-
|
|
948
|
-
Report security issues privately through
|
|
949
|
-
[GitHub private vulnerability reporting](https://github.com/metergraph/cli/security/advisories/new)
|
|
950
|
-
for `metergraph/cli`. Do not open a public issue, and do not include real credentials,
|
|
951
|
-
tokens or customer data in a report. The security policy and the CLI's security
|
|
952
|
-
properties are in `SECURITY.md` in the
|
|
953
|
-
[source repository](https://github.com/metergraph/cli); it is not shipped in the npm
|
|
954
|
-
package.
|
|
168
|
+
## Security and privacy
|
|
169
|
+
|
|
170
|
+
- **No usage telemetry.** The CLI collects no analytics about how you use it. It talks
|
|
171
|
+
only to the Metergraph origin you point it at.
|
|
172
|
+
- **Least access.** Sign in grants only the Metadata scope, `agent:metadata`. The CLI
|
|
173
|
+
never requests Debug or Replay access, never falls back to broader access, and never
|
|
174
|
+
reads retained prompts, responses or other content. It calls no model provider.
|
|
175
|
+
- **Credentials.** The sign in grant is stored per user in `~/.config/metergraph`
|
|
176
|
+
(`AppData\Roaming\Metergraph` on Windows), as owner-only files on Linux and macOS and
|
|
177
|
+
encrypted with DPAPI on Windows. The ingest key goes only into the project's private
|
|
178
|
+
`.env`, which `setup` adds to `.gitignore`. No command accepts a credential on the
|
|
179
|
+
command line, in the environment or on stdin, or prints one.
|
|
180
|
+
- **Files it writes in your project:** `.env` (from `setup`), the skill file, and
|
|
181
|
+
secret-free state under `.metergraph/` that is safe to commit. The skill installer
|
|
182
|
+
never replaces a file it did not write and has no force option.
|
|
183
|
+
- **Network.** Only `GET` requests to fixed paths for reads, no redirects followed,
|
|
184
|
+
bounded response sizes and fixed timeouts. Mistyped arguments are never echoed, since
|
|
185
|
+
they may hold a secret. `--help`, `--version` and `skill` make no network requests.
|
|
186
|
+
|
|
187
|
+
The [CLI reference](https://github.com/metergraph/cli/blob/main/docs/reference.md) states what each command does and does not do.
|
|
188
|
+
Report vulnerabilities privately through
|
|
189
|
+
[GitHub private vulnerability reporting](https://github.com/metergraph/cli/security/advisories/new),
|
|
190
|
+
not in a public issue, and leave real credentials, tokens and customer data out of the
|
|
191
|
+
report. See [SECURITY.md](https://github.com/metergraph/cli/blob/main/SECURITY.md).
|
|
192
|
+
|
|
193
|
+
## Deployments
|
|
194
|
+
|
|
195
|
+
`--url ORIGIN` selects the service; the default is `https://app.metergraph.dev`. It
|
|
196
|
+
accepts a bare `https://` origin, or `http://` on `localhost`, `127.0.0.1` or `[::1]`.
|
|
197
|
+
`setup --deployment` supports the hosted service (`managed`), the commercial
|
|
198
|
+
`customer-local` bundle, customer-owned cloud (`byoc`) and the open source server
|
|
199
|
+
(`oss`). See [self-hosting](https://www.metergraph.dev/docs/self-host/local/) and the
|
|
200
|
+
[setup reference](https://github.com/metergraph/cli/blob/main/docs/reference.md#setup).
|
|
201
|
+
|
|
202
|
+
## Links
|
|
203
|
+
|
|
204
|
+
- [Documentation](https://www.metergraph.dev/docs/)
|
|
205
|
+
- [CLI reference](https://github.com/metergraph/cli/blob/main/docs/reference.md) and [changelog](https://github.com/metergraph/cli/blob/main/CHANGELOG.md)
|
|
206
|
+
- [Issues](https://github.com/metergraph/cli/issues)
|
|
207
|
+
- [Contributing](https://github.com/metergraph/cli/blob/main/CONTRIBUTING.md) and [releasing](https://github.com/metergraph/cli/blob/main/RELEASING.md)
|
|
955
208
|
|
|
956
209
|
## License
|
|
957
210
|
|
|
958
|
-
Apache-2.0. See [LICENSE](LICENSE).
|
|
211
|
+
Apache-2.0. See [LICENSE](https://github.com/metergraph/cli/blob/main/LICENSE).
|