metergraph-cli 0.2.0-preview.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # metergraph-cli
2
2
 
3
- [![npm](https://img.shields.io/npm/v/metergraph-cli/next?label=npm%40next)](https://www.npmjs.com/package/metergraph-cli)
3
+ [![npm](https://img.shields.io/npm/v/metergraph-cli?label=npm)](https://www.npmjs.com/package/metergraph-cli)
4
4
  [![CI](https://github.com/metergraph/cli/actions/workflows/ci.yml/badge.svg)](https://github.com/metergraph/cli/actions/workflows/ci.yml)
5
5
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/metergraph/cli/blob/main/LICENSE)
6
6
 
@@ -13,11 +13,10 @@ token and route metadata back. Every command has stable `--json` output and exit
13
13
  so agents can run it as well as people.
14
14
 
15
15
  ```sh
16
- npx --yes metergraph-cli@next --help
16
+ npx --yes metergraph-cli --help
17
17
  ```
18
18
 
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).
19
+ Published on npm as `latest`. See the [changelog](https://github.com/metergraph/cli/blob/main/CHANGELOG.md).
21
20
 
22
21
  ## Quickstart
23
22
 
@@ -27,7 +26,7 @@ writes `METERGRAPH_APP_TOKEN` and `METERGRAPH_INGEST_URL` to a private `.env`, a
27
26
  installs the skill for your coding agent.
28
27
 
29
28
  ```sh
30
- npx --yes metergraph-cli@next setup --runtime local --client claude
29
+ npx --yes metergraph-cli setup --runtime local --client claude
31
30
  ```
32
31
 
33
32
  Use `--client codex` or `--client cursor` for the other agents.
@@ -40,20 +39,20 @@ ID or request ID and when the call started and ended.
40
39
  exact trace is visible in your workspace.
41
40
 
42
41
  ```sh
43
- npx --yes metergraph-cli@next verify --trace-id TRACE_ID \
42
+ npx --yes metergraph-cli verify --trace-id TRACE_ID \
44
43
  --since 2026-01-08T12:00:00Z --until 2026-01-08T12:00:05Z
45
44
  ```
46
45
 
47
46
  Then read what Metergraph has recorded:
48
47
 
49
48
  ```sh
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
49
+ npx --yes metergraph-cli usage --days 7 # calls, errors, cost and tokens per route per day
50
+ npx --yes metergraph-cli traces --limit 20 # one page of trace metadata
52
51
  ```
53
52
 
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.
53
+ To install it globally instead, run `npm install -g metergraph-cli`. The command is
54
+ `metergraph`. Pin an exact version, such as `metergraph-cli@0.2.0`, when a
55
+ script must not change with the `latest` tag. Previews use the `next` tag.
57
56
 
58
57
  ## Requirements
59
58
 
@@ -80,11 +79,11 @@ The CLI is built to be run by Codex, Claude Code and Cursor as well as by people
80
79
  network access:
81
80
 
82
81
  ```sh
83
- npx --yes metergraph-cli@next skill install --client codex --runtime local
82
+ npx --yes metergraph-cli skill install --client codex
84
83
  ```
85
84
 
86
85
  Python users can run `pip install metergraph-skills` and
87
- `metergraph-skills install --client codex --runtime local` instead
86
+ `metergraph-skills install --client codex` instead
88
87
  ([details](https://github.com/metergraph/cli/blob/main/python/metergraph-skills/README.md)).
89
88
  Both write the same files and recognise each other's installs.
90
89
  - **Model-swap skills.** `skills install` adds the workflow skills from
@@ -94,17 +93,26 @@ The CLI is built to be run by Codex, Claude Code and Cursor as well as by people
94
93
  the `metergraph/skills` plugin marketplace instead.
95
94
 
96
95
  ```sh
97
- npx --yes metergraph-cli@next skills install --client claude --runtime local
96
+ npx --yes metergraph-cli skills install --client claude
98
97
  ```
99
98
 
99
+ `--runtime` defaults to `local`. Pass `--runtime cloud` when the client runs in a
100
+ cloud environment with a checkout of the project.
101
+
100
102
  - **Machine output.** Every command takes `--json` and then prints exactly one JSON
101
103
  line on stdout and nothing on stderr. All results share one envelope:
102
104
  `schema_version`, `command`, `ok`, `outcome`, `exit_code`, `data` and `error`.
103
105
  Failures carry a fixed `error.reason` and, where useful, a `data.next_action` that
104
106
  tells the agent what to do next.
105
107
  - **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
+ prints the sign in URL for a person to open instead of launching a browser. With
109
+ `--json`, a step that needs approval exits within seconds with 18
110
+ `action_required` and `data.next_action` set to
111
+ `{"kind": "open_url", "url": "...", "timeout_seconds": N, "message": "..."}`, so an
112
+ agent can show the URL to the person. A background process waits for the approval
113
+ until `--timeout-ms`; afterwards, run the same command again to continue from the
114
+ saved state
115
+ ([details](https://github.com/metergraph/cli/blob/main/docs/reference.md#approval-from-an-agent)).
108
116
  - **MCP.** Workspace tools for agents are served by the hosted MCP endpoint, not by this
109
117
  CLI. See the [MCP server guide](https://www.metergraph.dev/docs/guides/mcp-server/)
110
118
  and the [agent access guide](https://www.metergraph.dev/docs/guides/agent-access/).
@@ -112,7 +120,7 @@ The CLI is built to be run by Codex, Claude Code and Cursor as well as by people
112
120
  Suggested `AGENTS.md` or `CLAUDE.md` lines for a project that uses Metergraph:
113
121
 
114
122
  ```md
115
- - Metergraph CLI: `npx --yes metergraph-cli@next <command> --json`. Check exit codes;
123
+ - Metergraph CLI: `npx --yes metergraph-cli <command> --json`. Check exit codes;
116
124
  follow `data.next_action` on failure.
117
125
  - After changing instrumented code, run the app once and confirm the trace with
118
126
  `metergraph verify` before reporting that tracing works.
@@ -141,18 +149,18 @@ writes, and full JSON examples.
141
149
 
142
150
  ## Exit codes
143
151
 
144
- Exit codes are stable. Changing one is a breaking change. Codes 10 to 17 are not in
152
+ Exit codes are stable. Changing one is a breaking change. Codes 10 to 18 are not in
145
153
  `0.1.0`.
146
154
 
147
155
  | Code | Outcome | Meaning |
148
156
  | --- | --- | --- |
149
- | 0 | `ok` | Command succeeded. `doctor` does not return this in this preview. For `skill`, the file is in place; discovery is still pending. |
157
+ | 0 | `ok` | Command succeeded. `doctor` does not return this in this release. For `skill`, the file is in place; discovery is still pending. |
150
158
  | 1 | `internal_error` | Unexpected failure inside the CLI. |
151
159
  | 2 | `invalid_input` | Unknown command or argument, or an invalid option value. No request was made. |
152
160
  | 3 | `authentication_required` | Service is reachable, healthy and supported, and requires authentication. No workspace is connected. |
153
161
  | 4 | `connection_failed` | The origin could not be reached, the connection failed, or the probe timed out. For `verify`, the deadline expired before any valid answer arrived. A trace that is still pending is exit 11, not 4. |
154
162
  | 5 | `unhealthy` | The service answered but reported that it is not healthy, or answered with a server error. |
155
- | 6 | `unsupported` | The service answered with a response, deployment profile or status this CLI does not support, or the skill client or runtime cannot use project skill files, or sign in cannot run in this environment, or `--json --no-browser` needs a browser approval it cannot show (`no_browser_requires_terminal`). Nothing was written. |
163
+ | 6 | `unsupported` | The service answered with a response, deployment profile or status this CLI does not support, or the skill client or runtime cannot use project skill files, or sign in cannot run in this environment. Nothing was written. |
156
164
  | 7 | `redirect_rejected` | The service answered with a redirect. Redirects are never followed. |
157
165
  | 8 | `conflict` | The skill target is not owned by this CLI, was modified, is unsafe, is locked or needs an explicit update, or the project is bound to a different origin or workspace. Nothing was changed. |
158
166
  | 9 | `filesystem_error` | Project or credential files could not be read or written. Partial changes were rolled back unless the message says otherwise. |
@@ -164,6 +172,7 @@ Exit codes are stable. Changing one is a breaking change. Codes 10 to 17 are not
164
172
  | 15 | `permission_denied` | The service refused this read for the signed in grant, for example for a missing scope or permission. |
165
173
  | 16 | `rate_limited` | The service asked the CLI to slow down. Nothing was retried. Try again later. |
166
174
  | 17 | `cancelled` | A read command was interrupted before it finished. Read commands never change workspace configuration or telemetry. |
175
+ | 18 | `action_required` | A person must act before the command can finish; `data.next_action` says what to do. `login` and `setup` with `--json --no-browser` exit with this as soon as an approval URL exists (`next_action.kind: "open_url"`), while a background process waits for the approval until `--timeout-ms`. After approval, run the same command again to continue. |
167
176
 
168
177
  ## Security and privacy
169
178
 
@@ -194,9 +203,11 @@ report. See [SECURITY.md](https://github.com/metergraph/cli/blob/main/SECURITY.m
194
203
 
195
204
  `--url ORIGIN` selects the service; the default is `https://app.metergraph.dev`. It
196
205
  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
206
+ `setup --deployment` routes to the hosted service (`managed`), the commercial
207
+ `customer-local` bundle, customer-owned cloud (`byoc`) or the open source server
208
+ (`oss`). The hosted service and released customer-local bundle are the release
209
+ targets; `byoc` and `oss` are not yet verified against a live deployment. See
210
+ [self-hosting](https://www.metergraph.dev/docs/self-host/local/) and the
200
211
  [setup reference](https://github.com/metergraph/cli/blob/main/docs/reference.md#setup).
201
212
 
202
213
  ## Links
@@ -36,6 +36,15 @@ package does not support. A CLI login or health probe alone does not prove
36
36
  application traffic. Verification needs an exact trace or request ID from an
37
37
  application invocation, its time window and the intended workspace.
38
38
 
39
+ `login` and `setup` need the person to approve in a browser on this machine. If the
40
+ installed CLI's help lists exit code 18 `action_required`, run them with
41
+ `--json --no-browser`. When a step needs approval, the command exits 18 within
42
+ seconds. Show `data.next_action.url` to the person and ask them to approve; a
43
+ background process waits for the approval. After they approve, run the same command
44
+ again. A new project takes three runs: sign in, ingest approval, then a final check.
45
+ An older CLI exits 6 `no_browser_requires_terminal` instead. Then the person has to
46
+ run setup in their own terminal.
47
+
39
48
  Repository identity is optional for capture but keeps traces attributable when
40
49
  several repositories share a workspace. If the installed CLI's `setup` help lists
41
50
  `--repository`, setup records `owner/name` from the git remote in a committed
@@ -68,7 +77,7 @@ For hosted Claude Desktop on an individual account, guide the human through Cust
68
77
 
69
78
  For a fresh hosted workspace, first ask whether the human already has a Metergraph account and workspace. Direct them to https://app.metergraph.dev/ and the signup, invitation or sign-in flow actually offered. The hosted UI can offer "Get a free API key" for self-service signup, but do not assume that button or a workspace invitation is available to everyone. If access is unavailable, direct them to their workspace owner or Metergraph. Confirm the workspace before creating a coding-agent key.
70
79
 
71
- For the commercial customer-local bundle, direct them to https://www.metergraph.dev/docs/self-host/local/#the-customer-local-bundle and the signed public release. They need a separate pull-only registry credential from Metergraph to pull the images; the public bundle download does not grant registry access. Verify the release before installation. Local admin credentials from the bundle's private configuration provide dashboard sign-in; create a coding-agent key in that installation's Keys page. Do not substitute hosted signup or a hosted key. Do not clone the private implementation or run development seed commands.
80
+ For the commercial customer-local bundle, direct them to https://www.metergraph.dev/docs/self-host/local/#the-customer-local-bundle and the signed public release. They need a separate pull-only registry credential from Metergraph to pull the images; the public bundle download does not grant registry access. Verify the release before installation. Local admin credentials from the bundle's private configuration provide dashboard sign-in; create a coding-agent key in that installation's Keys page. Do not substitute hosted signup or a hosted key. Do not clone the private implementation or run development seed commands. Use `metergraph-local-bundle` to stand the bundle up when that skill is installed.
72
81
 
73
82
  For customer-owned AWS, direct them to https://www.metergraph.dev/docs/self-host/aws/. An operator provisions the installation, identity and membership. There is no public signup. Ask for the installation's actual reachable address and use a coding-agent key from its Keys page. Its default internal load balancer is private, and its Agent Access surface excludes trace content and replay.
74
83
 
@@ -3,7 +3,7 @@
3
3
  "name": "metergraph",
4
4
  "file": "SKILL.md",
5
5
  "source_url": "https://www.metergraph.dev/SKILL.md",
6
- "sha256": "92f7955f6f8761a20a1886ad81f22dfcee239afddf8e9f1e761c35eb8e70f218",
7
- "size": 12980,
8
- "revision": "sha256-92f7955f6f87"
6
+ "sha256": "e7fc4eb7a22eeb16c357bede9d0a080ad3b415aaab1d9c72f164d5350d48c4b1",
7
+ "size": 13664,
8
+ "revision": "sha256-e7fc4eb7a22e"
9
9
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 1,
3
3
  "repository": "metergraph/skills",
4
- "commit": "25a62e420589cb805e3dd7d9bb3e3a35d85e2ece",
4
+ "commit": "dda4d5f130448f12a7fb2549ed264503d8b51d8d",
5
5
  "skills": [
6
6
  {
7
7
  "name": "metergraph-analyze",
@@ -33,6 +33,12 @@
33
33
  "size": 3789,
34
34
  "revision": "sha256-9ce5a2cf527e"
35
35
  },
36
+ {
37
+ "name": "metergraph-local-bundle",
38
+ "sha256": "692cd8952a8a8896ec2c06091b329a7a01912e261e87d62a9458452172a7c9b0",
39
+ "size": 12245,
40
+ "revision": "sha256-692cd8952a8a"
41
+ },
36
42
  {
37
43
  "name": "metergraph-model-swap",
38
44
  "sha256": "d77ee37f882ab1e50dc4a093e64a2c3661f4cd9bb59f184da6dccbd76fd51a18",
@@ -41,9 +47,9 @@
41
47
  },
42
48
  {
43
49
  "name": "metergraph-onboarding",
44
- "sha256": "15be9083f5b0d18cc6fe0d225f0921e2f16c5b4fb3d811c3984cef5cbc7941ba",
45
- "size": 10208,
46
- "revision": "sha256-15be9083f5b0"
50
+ "sha256": "6e6f418933f37b34437035ab3171344d43f37dee3770815b19f97efb62e5f1d1",
51
+ "size": 10267,
52
+ "revision": "sha256-6e6f418933f3"
47
53
  },
48
54
  {
49
55
  "name": "metergraph-report",
@@ -0,0 +1,283 @@
1
+ ---
2
+ name: metergraph-local-bundle
3
+ description: Verify and start the signed Metergraph customer-local bundle on a person's own machine, preserve existing local data and secrets, then hand over to metergraph setup. Use when customer-local setup reports that the bundle_started_verified prerequisite is incomplete or when installing the commercial local bundle from a public release.
4
+ ---
5
+
6
+ # Start the signed customer-local bundle
7
+
8
+ Stand up the released bundle only on the person's own machine, then run
9
+ `metergraph setup`. The CLI does not provision it, and
10
+ `--confirm-prerequisites` records confirmation rather than installing anything.
11
+
12
+ Keep registry, admin and database secrets out of chat, command arguments and
13
+ output. Never read or print `.env` values, `~/.docker/config.json`,
14
+ `docker compose config` output or container environment from `docker inspect`.
15
+ Do not configure model-provider keys.
16
+
17
+ ## Stop in remote runtimes
18
+
19
+ Before changing anything, determine where the agent executes. Check only
20
+ whether these variables exist, never their values:
21
+
22
+ ```sh
23
+ for name in SSH_CONNECTION SSH_CLIENT SSH_TTY CODESPACES GITPOD_WORKSPACE_ID CLOUD_SHELL CI; do
24
+ if printenv "$name" >/dev/null 2>&1; then printf '%s is set\n' "$name"; fi
25
+ done
26
+ ```
27
+
28
+ If any is set, or this is otherwise a hosted/cloud runtime, download, verify,
29
+ extract and start nothing. Prefer an installed `metergraph` whose `--help`
30
+ lists `setup`; otherwise state that the fallback fetches the preview CLI and
31
+ use `npx --yes metergraph-cli@next` as the prefix. Run only:
32
+
33
+ ```sh
34
+ metergraph setup --runtime cloud --deployment customer-local --url http://localhost:8080 --workspace 11111111-1111-4111-8111-111111111111 --skip-skill --json
35
+ ```
36
+
37
+ Relay its `data.next_action.kind: "run_on_customer_machine"` handoff and tell
38
+ the person to run this skill on their own machine. The placeholder workspace is
39
+ intentional: the runtime check refuses before making a request or writing a
40
+ file. When an SSH, cloud or CI marker is set, even `--runtime local` is refused
41
+ with reason `run_on_local_machine` and the same handoff. Valid runtimes are
42
+ `local`, `cloud` and `cloud-no-shell`. Never expose localhost to the remote
43
+ session.
44
+
45
+ ## Preflight and reuse
46
+
47
+ Ask for the coding client and bundle version. Default to the newest version at
48
+ https://www.metergraph.dev/docs/releases/ and record its `v`-prefixed value.
49
+ Record the application repository directory to return to for setup. Run:
50
+
51
+ ```sh
52
+ docker info >/dev/null
53
+ docker compose version
54
+ cosign version
55
+ curl -fsS http://localhost:8080/v1/deployment
56
+ lsof -nP -iTCP:8080 -sTCP:LISTEN
57
+ ```
58
+
59
+ Docker with Compose must be running and `cosign` installed. If the deployment
60
+ endpoint returns the `local` profile, reuse that stack and download nothing.
61
+ Find its bundle directory using only the Compose working-directory label:
62
+
63
+ ```sh
64
+ container_id="$(docker ps --filter publish=8080 --format '{{.ID}}' | head -n 1)"
65
+ bundle_dir="$(docker inspect --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}' "$container_id")"
66
+ (cd "$bundle_dir" && ./bin/status)
67
+ ```
68
+
69
+ Do not inspect container environment. Read the workspace ID from the
70
+ `workspace` check and skip to **Run setup**. If the directory cannot be found,
71
+ ask for the workspace ID from Settings. If nothing listens on 8080, continue.
72
+ If anything else owns it, stop and ask; never stop or reconfigure it.
73
+
74
+ Use `~/metergraph/<version>` and set
75
+ `bundle_dir="$HOME/metergraph/$version/local"`. If it contains `.env`, reuse
76
+ that existing installation: never overwrite, delete or read the file, and
77
+ never extract over `local/`. Continue at **Log in and start**. Otherwise check:
78
+
79
+ ```sh
80
+ docker volume inspect local_pgdata >/dev/null 2>&1
81
+ ```
82
+
83
+ Because the extracted directory and Compose project are `local`, an existing
84
+ volume is `local_pgdata`. If it exists without the matching `local/.env`, stop
85
+ and ask for the prior bundle/configuration. Postgres fixed its password when
86
+ the volume was created; a new `.env` can cause `password authentication failed`.
87
+ Never remove the volume, delete `.env` or use `down --volumes`.
88
+
89
+ ## Download and verify
90
+
91
+ Require a fresh, empty version directory. Before creating it, tell the person
92
+ the directory and four public files that will be written. No account is needed.
93
+ Set `version` to the recorded version and run:
94
+
95
+ ```sh
96
+ release_dir="$HOME/metergraph/$version"; mkdir -p "$release_dir"; cd "$release_dir"
97
+ archive="metergraph-customer-local-$version.tar.gz"
98
+ base="https://www.metergraph.dev/releases/$version"
99
+ curl -fsSLO "$base/$archive"
100
+ curl -fsSLO "$base/metergraph-byoc-release.json"
101
+ curl -fsSLO "$base/metergraph-byoc-release.json.sig"
102
+ curl -fsSLO "$base/metergraph-byoc-release.json.pem"
103
+ ```
104
+
105
+ Do not list, extract or run archive contents. Verify the manifest signature
106
+ with exactly this command. The issuer and identity are pinned here, not read
107
+ from the release page, so a changed download site cannot change who must have
108
+ signed the release:
109
+
110
+ ```sh
111
+ cosign verify-blob \
112
+ --certificate metergraph-byoc-release.json.pem \
113
+ --signature metergraph-byoc-release.json.sig \
114
+ --certificate-oidc-issuer https://token.actions.githubusercontent.com \
115
+ --certificate-identity-regexp '^https://github\.com/PioneerSquareLabs/metergraph-internal/\.github/workflows/byoc-release\.yml@refs/(heads/main|tags/v.+)$' \
116
+ metergraph-byoc-release.json
117
+ ```
118
+
119
+ Never loosen or drop the issuer or identity, and never add
120
+ `--insecure-ignore-tlog` or a similar bypass. If the release page publishes a
121
+ different identity, stop and tell the person rather than using it.
122
+
123
+ If cosign fails, stop: "The manifest signature did not verify. Do not extract
124
+ or run this bundle." After it succeeds, check signed fields:
125
+
126
+ ```sh
127
+ python3 - "$version" "$archive" <<'EOF'
128
+ import json, sys
129
+ version, archive = sys.argv[1:]
130
+ m = json.load(open("metergraph-byoc-release.json"))
131
+ required = ("release", "local_bundle", "local_bundle_sha256",
132
+ "local_image_digest", "local_analysis_image_digest")
133
+ missing = [key for key in required if not m.get(key)]
134
+ if missing: sys.exit("Signed manifest is missing: " + ", ".join(missing))
135
+ if m["release"] != version: sys.exit("Signed manifest release mismatch. Do not run it.")
136
+ if m["local_bundle"] != archive: sys.exit("Signed archive name mismatch. Do not run it.")
137
+ print("Signed manifest release, archive, and image digests are present.")
138
+ EOF
139
+ ```
140
+
141
+ Then run this digest check, still before any `tar` command:
142
+
143
+ ```sh
144
+ python3 - <<'EOF'
145
+ import hashlib, json, sys
146
+ manifest = json.load(open("metergraph-byoc-release.json"))
147
+ name = manifest["local_bundle"]
148
+ actual = hashlib.sha256(open(name, "rb").read()).hexdigest()
149
+ if actual != manifest["local_bundle_sha256"]:
150
+ sys.exit(f"{name} does not match the signed manifest. Do not run it.")
151
+ print(f"{name} matches the signed manifest.")
152
+ EOF
153
+ ```
154
+
155
+ Stop on failure. After both checks pass, list and validate archive paths:
156
+
157
+ ```sh
158
+ tar -tzf "$archive"
159
+ python3 - "$archive" <<'EOF'
160
+ from pathlib import PurePosixPath
161
+ import sys, tarfile
162
+ bad = []
163
+ with tarfile.open(sys.argv[1], "r:gz") as bundle:
164
+ for member in bundle.getmembers():
165
+ path = PurePosixPath(member.name)
166
+ if path.is_absolute() or not path.parts or path.parts[0] != "local" or ".." in path.parts:
167
+ bad.append(member.name)
168
+ if bad: sys.exit("Unsafe archive paths; do not extract: " + ", ".join(bad[:10]))
169
+ print("Every archive entry is beneath local/ with a safe relative path.")
170
+ EOF
171
+ ```
172
+
173
+ Only now tell the person extraction will create `local/`, then run:
174
+
175
+ ```sh
176
+ tar xzf "$archive"
177
+ mv metergraph-byoc-release.json* local/
178
+ cd local
179
+ ```
180
+
181
+ ## Configure without exposing passwords
182
+
183
+ Reuse an existing `.env` unchanged. For a new installation, state that a
184
+ mode-0600 file with random admin/database passwords and empty provider keys
185
+ will be created. Confirm `local_pgdata` did not exist, then run:
186
+
187
+ ```sh
188
+ umask 077; cp .env.example .env; chmod 600 .env
189
+ python3 - <<'EOF'
190
+ from pathlib import Path
191
+ import secrets
192
+ path = Path(".env")
193
+ keys = ("METERGRAPH_LOCAL_ADMIN_PASSWORD", "METERGRAPH_DB_PASSWORD")
194
+ values = {key: secrets.token_urlsafe(24) for key in keys}
195
+ lines, found = path.read_text().splitlines(), set()
196
+ for i, line in enumerate(lines):
197
+ for key in keys:
198
+ if line.startswith(key + "="):
199
+ lines[i], found = key + "=" + values[key], found | {key}
200
+ if found != set(keys): raise SystemExit("Password keys missing from .env.example")
201
+ path.write_text("\n".join(lines) + "\n")
202
+ print("Set random values for: " + ", ".join(keys))
203
+ EOF
204
+ ```
205
+
206
+ Do not display, source or parse `.env`. Leave `AI_GATEWAY_API_KEY`,
207
+ `FIREWORKS_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` and `GOOGLE_API_KEY`
208
+ empty.
209
+
210
+ ## Log in and start
211
+
212
+ Stop for the person to log in to the registry in their own terminal. They use
213
+ the pull-only invitation values, never chat or an agent-visible command:
214
+
215
+ ```sh
216
+ echo "$METERGRAPH_PULL_TOKEN" | docker login ghcr.io -u "$METERGRAPH_PULL_USER" --password-stdin
217
+ ```
218
+
219
+ Wait for confirmation only; never read Docker's credential file. State that
220
+ `./bin/start` will re-verify the manifest and both image digests, pull by digest,
221
+ start containers, and on a new database send one synthetic demo trace labeled
222
+ `environment=local-demo`. Then run:
223
+
224
+ ```sh
225
+ ./bin/start
226
+ ./bin/status
227
+ ```
228
+
229
+ An image-pull failure usually means login is missing or expired. On other
230
+ failure, read only the log named by `./bin/start` and `./bin/status`; never read
231
+ rendered Compose configuration or container environment. On password
232
+ authentication failure with `local_pgdata`, stop and ask for its matching
233
+ `.env`. Never delete data. Read the workspace ID and suggested command from the
234
+ status JSON's `workspace` check. The service listens only on localhost:8080.
235
+
236
+ ## Run setup
237
+
238
+ Return to the application repository. Prefer installed `metergraph` only when
239
+ its help lists `setup`; otherwise state that `npx --yes metergraph-cli@next`
240
+ fetches the preview CLI and use it as the prefix. Run with `--json`:
241
+
242
+ ```sh
243
+ metergraph setup --runtime local --deployment customer-local --url http://localhost:8080 --workspace 11111111-1111-4111-8111-111111111111 --confirm-prerequisites --client <client> --json
244
+ ```
245
+
246
+ Replace the example UUID and client only. Setup writes an ingest key to the
247
+ application `.env`, installs the skill and requests Metadata access only; never
248
+ print that file. Setup opens the deployment's browser consent page and waits;
249
+ the person signs in as the local admin and completes two approvals. If the
250
+ browser cannot open, follow the `metergraph` skill's guidance for handing the
251
+ person the approval URL. Continue until `data.status` is `ok`. For
252
+ `operator_handoff`, relay `data.next_action.kind` (`complete_prerequisite`,
253
+ `run_on_customer_machine`, `connection_guide`, or other); never report a
254
+ handoff as success.
255
+
256
+ The agent never runs a command that reads `.env`. To see the admin login without
257
+ printing it, the person runs these no-output pipelines themselves, one at a
258
+ time, in their own terminal or in Claude Code with the `!` prefix. Fill in the
259
+ absolute bundle directory for them; their shell does not have `bundle_dir`:
260
+
261
+ ```sh
262
+ sed -n 's/^METERGRAPH_LOCAL_ADMIN_EMAIL=//p' ~/metergraph/<version>/local/.env | pbcopy
263
+ sed -n 's/^METERGRAPH_LOCAL_ADMIN_PASSWORD=//p' ~/metergraph/<version>/local/.env | pbcopy
264
+ ```
265
+
266
+ They may instead use their own editor. Never use `cat`, `grep` or any other
267
+ command that prints `.env`, particularly with `!`, because `!` output lands in
268
+ the transcript. A new configuration keeps `admin@example.com` as the email.
269
+
270
+ ## Recovery and stopping
271
+
272
+ | State | Action |
273
+ | --- | --- |
274
+ | Signature, manifest, digest or path check fails | Stop; extract and run nothing |
275
+ | Port 8080 belongs to something else | Stop and ask; do not alter it |
276
+ | Registry pull is denied | Person repeats private login; never request the token |
277
+ | Existing `.env` | Reuse unchanged; never regenerate passwords |
278
+ | `local_pgdata` lacks its matching `.env` | Stop and find prior configuration; never remove it |
279
+ | Startup fails | Use status and only the named log; preserve secrets and data |
280
+ | Setup returns a handoff | Relay exact status/next action; do not report success |
281
+
282
+ To stop later, follow the bundle README's `stop`. Prefer it over `down`, which
283
+ loses raw payloads in local object storage. Never use `down --volumes`.
@@ -34,8 +34,9 @@ skill installation; a later installed version may offer `login`, `setup` and
34
34
  https://www.metergraph.dev/docs/start/first-trace/ and its manual setup steps.
35
35
  Do not infer that a server endpoint or source-only CLI command has shipped.
36
36
 
37
- For customer-local MeterGraph, use the released bundle and its included
38
- README, signed manifest and verification files. The human obtains the current
37
+ For customer-local MeterGraph, use `metergraph-local-bundle` to stand up the
38
+ released bundle, then continue here. Use its included README, signed manifest
39
+ and verification files. The human obtains the current
39
40
  pull-only registry credential; registry access is not yet universally self-serve.
40
41
  Use password-stdin and the local secret store for registry login. Verify the
41
42
  archive before running its scripts. Configure the bundle's local settings,
package/bin/metergraph.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import { handOff, handsOff } from "../src/approval-handoff.js";
2
3
  import { main } from "../src/cli.js";
3
4
 
4
5
  // A closed pipe on stdout (for example "metergraph --help | head -1") is not
5
6
  // a CLI failure.
6
7
  process.stdout.on("error", () => {});
7
8
 
8
- process.exitCode = await main(process.argv.slice(2), {
9
- stdout: process.stdout,
10
- stderr: process.stderr,
11
- });
9
+ const argv = process.argv.slice(2);
10
+ const io = { stdout: process.stdout, stderr: process.stderr };
11
+ process.exitCode = handsOff(argv) ? await handOff(argv, io) : await main(argv, io);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "metergraph-cli",
3
- "version": "0.2.0-preview.5",
4
- "description": "Preview Metergraph command line tool with a read-only connection probe, a project skill installer, Metadata-only sign in and bounded workspace Metadata reads",
3
+ "version": "0.2.0",
4
+ "description": "Metergraph command line tool with a read-only connection probe, a project skill installer, Metadata-only sign in and bounded workspace Metadata reads",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "metergraph": "bin/metergraph.js"
@@ -0,0 +1,91 @@
1
+ import childProcess from "node:child_process";
2
+ import { fileURLToPath } from "node:url";
3
+
4
+ import { parseArgs } from "./args.js";
5
+ import { envelope, toJsonLine } from "./output.js";
6
+
7
+ // login and setup with --json --no-browser may need a person to approve in a
8
+ // browser. The approval returns to a loopback listener, so something must
9
+ // keep listening after the agent has the URL. Agent shells usually show a
10
+ // command's output only after it exits, so that cannot be this process.
11
+ //
12
+ // This process runs the same command as a detached waiter and relays the
13
+ // waiter's one JSON line. When the line is action_required, it exits with it
14
+ // at once and leaves the waiter listening until approval or --timeout-ms; the
15
+ // waiter saves what the approval completes and prints nothing more. Any other
16
+ // line is the command's result, and this process waits for the waiter to
17
+ // finish. The arguments carry no credential, and the waiter's environment is
18
+ // this one plus a marker.
19
+ const BIN = fileURLToPath(new URL("../bin/metergraph.js", import.meta.url));
20
+ const WAITER = "METERGRAPH_APPROVAL_WAITER";
21
+
22
+ export function handsOff(argv, env = process.env) {
23
+ if (env[WAITER] === "1") return false;
24
+ const parsed = parseArgs(argv);
25
+ return parsed.ok && (parsed.command === "login" || parsed.command === "setup") && parsed.json && parsed.noBrowser;
26
+ }
27
+
28
+ export function handOff(argv, { stdout }) {
29
+ return new Promise((resolve) => {
30
+ let child;
31
+ try {
32
+ child = childProcess.spawn(process.execPath, [...process.execArgv, BIN, ...argv], {
33
+ env: { ...process.env, [WAITER]: "1" },
34
+ stdio: ["ignore", "pipe", "ignore"],
35
+ detached: true,
36
+ windowsHide: true,
37
+ });
38
+ } catch {
39
+ resolve(failed(argv, stdout));
40
+ return;
41
+ }
42
+ let text = "";
43
+ let settled = false;
44
+ // Ctrl+C before the line arrives cancels the waiter, which reports it.
45
+ const forward = (signal) => child.kill(signal);
46
+ const signals = ["SIGINT", "SIGTERM"];
47
+ for (const signal of signals) process.on(signal, forward);
48
+ const finish = (code) => {
49
+ if (settled) return;
50
+ settled = true;
51
+ for (const signal of signals) process.removeListener(signal, forward);
52
+ resolve(code);
53
+ };
54
+ child.once("error", () => finish(failed(argv, stdout)));
55
+ child.stdout.setEncoding("utf8");
56
+ child.stdout.on("data", (chunk) => {
57
+ if (settled) return;
58
+ text += chunk;
59
+ const end = text.indexOf("\n");
60
+ if (end === -1) return;
61
+ let line;
62
+ try { line = JSON.parse(text.slice(0, end)); } catch { line = null; }
63
+ if (line?.outcome !== "action_required") return;
64
+ stdout.write(text.slice(0, end + 1));
65
+ child.stdout.destroy();
66
+ child.unref();
67
+ finish(line.exit_code);
68
+ });
69
+ child.once("close", (code) => {
70
+ if (settled) return;
71
+ if (text.endsWith("\n") && text.indexOf("\n") === text.length - 1) {
72
+ stdout.write(text);
73
+ finish(code ?? 1);
74
+ } else {
75
+ finish(failed(argv, stdout));
76
+ }
77
+ });
78
+ });
79
+ }
80
+
81
+ function failed(argv, stdout) {
82
+ const parsed = parseArgs(argv);
83
+ const result = envelope({
84
+ command: parsed.command ?? null,
85
+ outcome: "internal_error",
86
+ reason: "unexpected_failure",
87
+ message: "The CLI failed unexpectedly. No details are shown to avoid exposing sensitive input.",
88
+ });
89
+ stdout.write(toJsonLine(result));
90
+ return result.exit_code;
91
+ }