metergraph-cli 0.2.0-preview.1 → 0.2.0-preview.2

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
- The Metergraph command line tool. Version `0.2.0-preview.1` is published on the
3
+ The Metergraph command line tool. Version `0.2.0-preview.2` is published on the
4
4
  `next` npm tag. The `latest` tag remains on `0.1.0`.
5
5
 
6
6
  Install the released preview channel with npm or run it directly:
@@ -11,7 +11,7 @@ npx --yes metergraph-cli@next doctor --json
11
11
  npm install -g metergraph-cli@next
12
12
  ```
13
13
 
14
- The installed command is `metergraph`. Pin `metergraph-cli@0.2.0-preview.1`
14
+ The installed command is `metergraph`. Pin `metergraph-cli@0.2.0-preview.2`
15
15
  when you need this exact preview rather than whichever version `next` names later.
16
16
 
17
17
  **Availability:**
@@ -24,6 +24,10 @@ when you need this exact preview rather than whichever version `next` names late
24
24
  `METERGRAPH_INGEST_URL` and repairs the full ingest endpoint that
25
25
  `0.2.0-preview.0` wrote, after checking the saved key. It also keeps validated,
26
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.
27
31
  - Sign in needs a Metergraph service that offers Metadata-only CLI grants and grant
28
32
  revocation. A service without them is reported as unsupported, and the CLI never
29
33
  falls back to broader access.
@@ -100,7 +104,16 @@ sends a test event.
100
104
  `--open` launches only a server-provided link carrying the exact trace and the
101
105
  verified workspace ID. The dashboard must check that ID against its signed-in
102
106
  workspace before displaying traces. Older links without a workspace remain a
103
- manual handoff; a conflicting workspace or unsafe link is refused.
107
+ manual handoff; a conflicting workspace or unsafe link is refused. The result's
108
+ `browser` field is `launcher_started`, `launcher_unavailable`, `not_requested`
109
+ (no `--open`), or `suppressed_by_json` / `suppressed_by_no_browser` when `--open`
110
+ was given but that flag returned the link instead of launching it.
111
+
112
+ If `--timeout-ms` expires after the service has answered at least once with a
113
+ pending result, `verify` exits 11 `verification_failed` with reason
114
+ `verification_timeout`: the origin is reachable and the trace is not visible yet.
115
+ Exit 4 `connection_failed` with that reason means no valid answer arrived before
116
+ the deadline.
104
117
 
105
118
  ### doctor
106
119
 
@@ -225,7 +238,7 @@ metergraph logout
225
238
  | `--config-dir DIR` | see below | Private per-user directory for the saved grant. |
226
239
  | `--timeout-ms N` | `300000` | How long to wait for the browser, 1000 to 900000. |
227
240
  | `--signup` | off | Start at the hosted sign up page, which returns to the same authorization request. Managed service only; other profiles exit 6. |
228
- | `--no-browser` | off | Print the authorization URL on stderr for you to open on this machine, then wait. Not with `--json`. |
241
+ | `--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`. |
229
242
  | `--reconnect` | off | Allow replacing a binding to a different origin or workspace. |
230
243
  | `--json` | off | Print exactly one JSON line on stdout and nothing on stderr. |
231
244
 
@@ -763,7 +776,7 @@ To try a packed artifact without publishing:
763
776
 
764
777
  ```sh
765
778
  npm pack --pack-destination "$(mktemp -d)"
766
- npx --yes --package=/path/to/metergraph-cli-0.2.0-preview.1.tgz -- metergraph --version
779
+ npx --yes --package=/path/to/metergraph-cli-0.2.0-preview.2.tgz -- metergraph --version
767
780
  ```
768
781
 
769
782
  Do not commit tarballs or other generated files.
@@ -772,7 +785,7 @@ Do not commit tarballs or other generated files.
772
785
 
773
786
  The source of truth is the public repository
774
787
  [github.com/metergraph/cli](https://github.com/metergraph/cli), licensed Apache-2.0.
775
- `0.2.0-preview.1` is published on the `next` npm tag; `latest` remains on
788
+ `0.2.0-preview.2` is published on the `next` npm tag; `latest` remains on
776
789
  `0.1.0`. The hosted service supports sign in, Metadata reads and ingest bootstrap.
777
790
  Subsequent releases must pass the checks below before publication.
778
791
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metergraph-cli",
3
- "version": "0.2.0-preview.1",
3
+ "version": "0.2.0-preview.2",
4
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",
5
5
  "type": "module",
6
6
  "bin": {
package/src/auth-login.js CHANGED
@@ -58,12 +58,13 @@ export async function runLogin(options, progress) {
58
58
  if (options.runtime !== "local") return end("unsupported", "runtime_not_supported", GUIDE);
59
59
  const remote = detectRemoteSession();
60
60
  if (remote !== null) return end("unsupported", remote, GUIDE);
61
- if (options.json && options.noBrowser) {
62
- return end("unsupported", "no_browser_requires_terminal", {
63
- kind: "run_in_terminal",
64
- message: "Run the same login command in a terminal without --json to see the sign in URL.",
65
- });
66
- }
61
+ // --json cannot show a person the --no-browser URL. A rerun with a saved,
62
+ // working grant needs no URL, so only refuse once approval is required.
63
+ const noBrowserHandoff = () => end("unsupported", "no_browser_requires_terminal", {
64
+ kind: "run_in_terminal",
65
+ message: "Run the same login command in a terminal without --json to see the sign in URL.",
66
+ });
67
+ const urlUnshowable = options.json && options.noBrowser;
67
68
 
68
69
  const trap = trapSignals();
69
70
  try {
@@ -71,6 +72,7 @@ export async function runLogin(options, progress) {
71
72
  const configDir = resolveConfigDir(options.configDir);
72
73
  const existing = readBinding(root);
73
74
  const bound = existing?.binding ?? null;
75
+ if (urlUnshowable && (bound === null || options.reconnect)) return noBrowserHandoff();
74
76
  // Setup pins a deployment model before sign in. A stale binding for a
75
77
  // different profile must never be silently reused or reconnected.
76
78
  if (options.expectedProfile && bound !== null && !options.reconnect &&
@@ -100,6 +102,7 @@ export async function runLogin(options, progress) {
100
102
  }
101
103
  if (session.outcome !== "login_required") return end(session.outcome, session.reason);
102
104
  }
105
+ if (urlUnshowable) return noBrowserHandoff();
103
106
 
104
107
  const flow = await authorize(options, ctx, trap.signal, progress);
105
108
  if (!flow.ok) return end(flow.outcome, flow.reason, flow.next ?? null);
package/src/output.js CHANGED
@@ -599,8 +599,8 @@ const AUTH_MESSAGES = {
599
599
  ci_environment:
600
600
  "This is a CI environment, where no person can approve sign in in a browser. Nothing was written.",
601
601
  no_browser_requires_terminal:
602
- "--no-browser prints the sign in URL for a person to open, which --json cannot do. " +
603
- "Run login without --json in a terminal. Nothing was done.",
602
+ "Approval is needed, and --no-browser prints its URL for a person to open, which --json cannot do. " +
603
+ "Run the same command without --json in a terminal. Nothing was done.",
604
604
  bound_to_other_origin:
605
605
  "This project is bound to a different origin. Nothing was changed. Use --reconnect to switch it.",
606
606
  bound_to_other_workspace:
package/src/setup.js CHANGED
@@ -31,7 +31,6 @@ const fail = (outcome, reason, status = "not_ready", receipt = null) => ({ outco
31
31
  // response can supply it. Every returned string is fixed and non-secret.
32
32
  export async function runSetup(options, progress = () => {}) {
33
33
  if (options.runtime !== "local" || detectRemoteSession() !== null) return fail("unsupported", "run_on_local_machine");
34
- if (options.json && options.noBrowser) return fail("unsupported", "no_browser_requires_terminal");
35
34
  const trap = trapSignals();
36
35
  let receipt = null;
37
36
  let loginVerified = false;
@@ -138,6 +137,9 @@ export async function runSetup(options, progress = () => {}) {
138
137
  return stop("conflict", "repair_requires_saved_key");
139
138
  }
140
139
 
140
+ // A verified rerun returned above without approval. From here a person
141
+ // must open an approval URL, which --json cannot show with --no-browser.
142
+ if (options.json && options.noBrowser) return stop("unsupported", "no_browser_requires_terminal");
141
143
  const setup = await discoverSetup(origin, profile, trap.signal);
142
144
  if (!setup.ok) return stop(setup.outcome, setup.reason);
143
145
 
package/src/trace-open.js CHANGED
@@ -4,7 +4,8 @@ import { normalizeUuid } from "./auth-oauth.js";
4
4
  import { parseOrigin } from "./origin.js";
5
5
 
6
6
  // Operates only on an already verified, provenance-bound receipt. JSON and
7
- // no-browser mode return its URL without starting a launcher. "opened" means
7
+ // no-browser mode return its URL without starting a launcher, and say which
8
+ // one suppressed an --open request. "opened" means
8
9
  // the OS launcher started; it does not assert a browser rendered the page.
9
10
  export async function openTrace(receipt, options = {}, launch = openBrowser) {
10
11
  if (receipt?.readiness?.metadata_available !== true || receipt?.readiness?.processed !== true ||
@@ -20,7 +21,11 @@ export async function openTrace(receipt, options = {}, launch = openBrowser) {
20
21
  if (link.workspaceBound !== true || receipt.link_workspace_bound !== true || receipt.link_status !== "available") {
21
22
  return { outcome: "unsupported", reason: "link_workspace_binding_unavailable", data: { ...receipt, browser: "not_requested" } };
22
23
  }
23
- if (options.json || options.noBrowser || !options.open) return { outcome: "ok", reason: null, data: { ...receipt, browser: "not_requested" } };
24
+ if (!options.open) return { outcome: "ok", reason: null, data: { ...receipt, browser: "not_requested" } };
25
+ if (options.json || options.noBrowser) {
26
+ const browser = options.json ? "suppressed_by_json" : "suppressed_by_no_browser";
27
+ return { outcome: "ok", reason: null, data: { ...receipt, browser } };
28
+ }
24
29
  if (options.signal?.aborted) return { outcome: "cancelled", reason: "cancelled", data: receipt };
25
30
  const launched = await launchWithinSignal(link.value, launch, options.signal);
26
31
  if (options.signal?.aborted) return { outcome: "cancelled", reason: "cancelled", data: receipt };
package/src/verify.js CHANGED
@@ -33,7 +33,10 @@ export async function runVerify(options, dependencies = {}) {
33
33
  if (holdsKnown(data, known)) return { outcome: "verification_failed", reason: "credential_in_metadata_response", data: null };
34
34
  return { outcome, reason, data: data === null ? null : { ...data, attempts } };
35
35
  };
36
- const interrupted = () => limit.timedOut() ? end("connection_failed", "verification_timeout") : end("cancelled", "cancelled");
36
+ // Once the origin has answered with a valid pending receipt, a deadline
37
+ // means the trace is not visible yet, not that the origin is unreachable.
38
+ const interrupted = () => !limit.timedOut() ? end("cancelled", "cancelled")
39
+ : end(receipt === null ? "connection_failed" : "verification_failed", "verification_timeout");
37
40
  try {
38
41
  const sessionResult = await (dependencies.session ?? verifiedSession)({ project: options.project, configDir: options.configDir, cancel: limit.signal });
39
42
  if (limit.signal.aborted) return interrupted();