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

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,7 +1,7 @@
1
1
  # metergraph-cli
2
2
 
3
- The Metergraph command line tool. This checkout is a **development preview**, version
4
- `0.2.0-preview.0`, which has not been published.
3
+ The Metergraph command line tool. Version `0.2.0-preview.1` is published on the
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:
7
7
 
@@ -11,42 +11,41 @@ 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.1.0` for that exact preview.
14
+ The installed command is `metergraph`. Pin `metergraph-cli@0.2.0-preview.1`
15
+ when you need this exact preview rather than whichever version `next` names later.
15
16
 
16
17
  **Availability:**
17
18
 
18
- - The published package, `metergraph-cli@0.1.0`, contains only `doctor` and
19
- `skill install` / `skill update`. It has no sign in commands.
20
- - `login` and `logout`, described below, exist only in this checkout. They are an
21
- upcoming preview: run them from a checkout or a locally packed tarball (see
22
- [Development](#development)). Do not expect them from `npx metergraph-cli` until a
23
- release that includes them is announced.
24
- - They also need a Metergraph service that offers Metadata-only CLI sign in and grant
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
+ - Sign in needs a Metergraph service that offers Metadata-only CLI grants and grant
25
28
  revocation. A service without them is reported as unsupported, and the CLI never
26
29
  falls back to broader access.
27
- - The read commands `status`, `context`, `capabilities`, `usage`, `routes` and `traces`
28
- also exist only in this checkout and are part of the same unpublished upcoming preview.
29
- They need a project signed in with `login`.
30
- - `setup` also exists only in this checkout. It guides sign in and workspace choice,
31
- then requires the deployment's separate ingest bootstrap API and browser approval
32
- by a member of that workspace.
33
- - `verify` also exists only in this checkout. It checks one exact trace identity in an
34
- explicit invocation window using Metadata access. It never sends application data.
30
+ - The read commands need a project signed in with `login`. `setup` also needs the
31
+ deployment's ingest bootstrap API and browser approval by a workspace member.
32
+ `verify` checks one exact trace identity in an explicit invocation window using
33
+ Metadata access. It never sends application data.
35
34
 
36
- This checkout includes:
35
+ The preview includes:
37
36
 
38
37
  - `doctor` checks whether a Metergraph service is reachable, healthy and supported.
39
38
  - `skill install` and `skill update` copy the Metergraph agent skill bundled with the
40
39
  CLI into one coding agent's project skill directory.
41
- - `login` and `logout` (checkout only) sign a project in to one workspace through your
40
+ - `login` and `logout` sign a project in to one workspace through your
42
41
  browser with a delegated, Metadata-only grant, and sign it out again.
43
- - The read commands (checkout only) use that grant to read bounded workspace Metadata:
42
+ - The read commands use that grant to read bounded workspace Metadata:
44
43
  connection status, workspace context, capabilities, daily usage, routes and one page
45
44
  of trace metadata.
46
- - `setup` (checkout only) guides browser sign in and workspace choice, asks for
45
+ - `setup` guides browser sign in and workspace choice, asks for
47
46
  ingest-only approval, writes a private project env file, confirms delivery, and
48
47
  installs the selected client skill.
49
- - `verify` (checkout only) polls for one exact processed trace in a bounded window.
48
+ - `verify` polls for one exact processed trace in a bounded window.
50
49
  It does not infer application provenance from a Metadata match.
51
50
 
52
51
  It does not read retained content, replay traces, call model providers or send
@@ -205,7 +204,7 @@ does not match. It never downloads the skill or runs a remote script. A new skil
205
204
  revision ships only in a new CLI release; `skill update` then upgrades projects that
206
205
  hold an unchanged earlier revision.
207
206
 
208
- ### login and logout (checkout only, unreleased)
207
+ ### login and logout
209
208
 
210
209
  `login` binds a project directory to one Metergraph workspace. Your browser does the
211
210
  sign in, sign up, invitation and workspace consent on the service's own pages and
@@ -296,7 +295,7 @@ revocation request. If it does not answer `200`, local sign out still happens an
296
295
  command exits 13 with `revocation: "unconfirmed"`. A project that is not signed in
297
296
  exits 0 without any request.
298
297
 
299
- ### setup (checkout only, unreleased)
298
+ ### setup
300
299
 
301
300
  Run setup once from a project directory, choosing the coding client that will use
302
301
  the skill:
@@ -343,6 +342,11 @@ page shows the workspace and the consequence of approval. A signed-in browser
343
342
  on another workspace must switch in Metergraph and rerun; the CLI does not
344
343
  switch it automatically.
345
344
 
345
+ `METERGRAPH_INGEST_URL` is the deployment's service root. The Python SDK
346
+ appends `/v1/ingest` itself. A setup rerun with a verified
347
+ key repairs the endpoint value written by the first preview without minting a
348
+ new key.
349
+
346
350
  The env file must be a project-relative `.env`, `.env.<name>` or `<name>.env`
347
351
  (`--env-file` selects another). The writer refuses tracked files, links,
348
352
  ambiguous dotenv syntax and unsafe paths. It adds a project `.gitignore` rule
@@ -367,11 +371,11 @@ conflict leaves the delivered key in place and reports `credential_ready_skill_p
367
371
  resolve the skill file conflict and rerun setup without another approval.
368
372
  Success means the key was delivered and the project is ready to instrument.
369
373
  It does **not** mean application traffic has arrived. Run your application and
370
- verify one exact trace afterward. This checkout and the matching server slice
371
- are development work; neither their availability on a deployed service nor a
372
- published package has been established by these local tests.
374
+ verify one exact trace afterward. The hosted service advertises the setup
375
+ contract, but each non-hosted deployment must be checked at its own origin.
376
+ Local protocol tests do not prove browser approval or real application traffic.
373
377
 
374
- ### Read commands (checkout only, unreleased)
378
+ ### Read commands
375
379
 
376
380
  The read commands use the grant `login` saved for this project. They never open a
377
381
  browser, never sign in on their own and never request another scope. Each one:
@@ -512,8 +516,8 @@ before the failure, and `notices` lists fixed tokens such as `rows_truncated`,
512
516
 
513
517
  ## Exit codes
514
518
 
515
- Exit codes are stable. Changing one is a breaking change. Codes 10 to 17 exist only in
516
- this checkout.
519
+ Exit codes are stable. Changing one is a breaking change. Codes 10 to 17 were
520
+ added in `0.2.0-preview.0` and are unavailable in `0.1.0`.
517
521
 
518
522
  | Code | Outcome | Meaning |
519
523
  | --- | --- | --- |
@@ -618,7 +622,7 @@ A successful `skill install`:
618
622
  `receipt_invalid`, `locked`, `invalid_project`, `client_not_supported`,
619
623
  `write_failed` or `bundled_skill_invalid`.
620
624
 
621
- A successful `login` (checkout only):
625
+ A successful `login`:
622
626
 
623
627
  ```json
624
628
  {
@@ -759,7 +763,7 @@ To try a packed artifact without publishing:
759
763
 
760
764
  ```sh
761
765
  npm pack --pack-destination "$(mktemp -d)"
762
- npx --yes --package=/path/to/metergraph-cli-0.2.0-preview.0.tgz -- metergraph --version
766
+ npx --yes --package=/path/to/metergraph-cli-0.2.0-preview.1.tgz -- metergraph --version
763
767
  ```
764
768
 
765
769
  Do not commit tarballs or other generated files.
@@ -768,10 +772,9 @@ Do not commit tarballs or other generated files.
768
772
 
769
773
  The source of truth is the public repository
770
774
  [github.com/metergraph/cli](https://github.com/metergraph/cli), licensed Apache-2.0.
771
- The first preview uses the `next` npm tag. `0.1.0` is the only published version.
772
- This checkout's `0.2.0-preview.0` is not published and must not be published until
773
- the service side of sign in and the Metadata read endpoints are released. Subsequent releases must pass the checks
774
- below before publication.
775
+ `0.2.0-preview.1` is published on the `next` npm tag; `latest` remains on
776
+ `0.1.0`. The hosted service supports sign in, Metadata reads and ingest bootstrap.
777
+ Subsequent releases must pass the checks below before publication.
775
778
 
776
779
  Releases are manual. The `Release CLI` workflow (`.github/workflows/release.yml`) runs
777
780
  only when a maintainer starts it from `main`. It does not run on tags, pushes or a
@@ -803,12 +806,12 @@ commit:
803
806
  - If `main` moves after you copy the SHA, the run fails. Start a new run with the new
804
807
  head. To release an older state, land it on `main` first.
805
808
 
806
- ### First package bootstrap
809
+ ### First package bootstrap (completed for 0.1.0)
807
810
 
808
811
  npm trusted publishing is configured on a package that already exists, so the very
809
- first version cannot come from this workflow. Creating the package is a one time,
810
- human step that a Metergraph maintainer must approve and perform. Nothing in this
811
- repository automates it, and no npm token or secret is stored here.
812
+ first version could not come from this workflow. The steps below describe the
813
+ completed bootstrap of `0.1.0`; they are not part of subsequent releases. No npm
814
+ token or secret is stored here.
812
815
 
813
816
  1. Confirm the intended npm maintainer accounts and that `metergraph-cli` is
814
817
  available. The first approved publish establishes package ownership.
@@ -837,15 +840,13 @@ Trusted publishing requires npm 11.5.1 or newer. The publish job checks this bef
837
840
  publishes. After the trusted publisher works, consider restricting the package to
838
841
  trusted publishing so that long lived tokens cannot publish it.
839
842
 
840
- ### Remaining maintainer setup
843
+ ### Release configuration
841
844
 
842
- The source repository and license are settled. Before any automated release, a
843
- maintainer still has to:
845
+ Before an automated publish, confirm the following settings are still in place:
844
846
 
845
- - complete the [first package bootstrap](#first-package-bootstrap);
846
- - configure the [trusted publisher](#trusted-publishing);
847
- - create the `npm-release` environment with required reviewers;
848
- - set the repository variable `METERGRAPH_CLI_PUBLISH_ENABLED` to `true`.
847
+ - the [trusted publisher](#trusted-publishing) matches this repository and workflow;
848
+ - the `npm-release` environment has required reviewers;
849
+ - the repository variable `METERGRAPH_CLI_PUBLISH_ENABLED` is `true`.
849
850
 
850
851
  Until all of these are done, leave `publish` false.
851
852
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metergraph-cli",
3
- "version": "0.2.0-preview.0",
3
+ "version": "0.2.0-preview.1",
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": {
@@ -1,5 +1,6 @@
1
1
  import { normalizeUuid } from "./auth-oauth.js";
2
2
  import { AGENT_CONTRACT_VERSION, MAX_CURSOR_LENGTH, METADATA_SCOPE } from "./constants.js";
3
+ import { traceLink } from "./trace-contract.js";
3
4
 
4
5
  // Validators for the service's Metadata read documents. Each one checks the
5
6
  // document against the agent access contract, the bound workspace and the
@@ -530,6 +531,8 @@ export function tracesReport(body, ctx, request, notices) {
530
531
  ) {
531
532
  throw new Invalid("verification_failed", reason);
532
533
  }
534
+ const link = traceLink(row.metergraph_links?.trace ?? null, ctx.origin, row, ctx.workspaceId);
535
+ if (!link.ok) throw new Invalid(link.outcome, link.reason);
533
536
  return {
534
537
  id: row.id,
535
538
  trace_id: row.trace_id,
@@ -546,12 +549,16 @@ export function tracesReport(body, ctx, request, notices) {
546
549
  routes: nameList(row.routes, notices, reason),
547
550
  providers: nameList(row.providers, notices, reason),
548
551
  models: nameList(row.models, notices, reason),
549
- link: null,
552
+ link: link.value,
553
+ link_workspace_bound: link.workspaceBound === true,
550
554
  };
551
555
  });
552
556
  if (!proof.complete) notices.add("evidence_incomplete");
553
557
  if (page.truncated) notices.add("more_pages");
554
- notices.add("trace_links_unavailable");
558
+ const missingLinks = traces.length === 0 || traces.some((trace) => trace.link === null);
559
+ if (missingLinks) notices.add("trace_links_unavailable");
560
+ const unboundLinks = traces.some((trace) => trace.link !== null && !trace.link_workspace_bound);
561
+ if (unboundLinks) notices.add("trace_workspace_binding_unavailable");
555
562
  return {
556
563
  provenance: origin,
557
564
  window: span,
@@ -570,7 +577,8 @@ export function tracesReport(body, ctx, request, notices) {
570
577
  next_cursor: page.next_cursor,
571
578
  complete: proof.complete && !page.truncated,
572
579
  empty: traces.length === 0,
573
- link_status: "server_link_unavailable",
580
+ link_status: missingLinks ? "server_link_unavailable" :
581
+ unboundLinks ? "workspace_binding_unavailable" : "available",
574
582
  traces,
575
583
  };
576
584
  });
@@ -13,14 +13,14 @@ export const ENV_NAMES = Object.freeze({
13
13
  });
14
14
  const NAMES = Object.values(ENV_NAMES);
15
15
 
16
- // Ingest paths the service accepts on an origin. A URL with any other path,
17
- // a query, a fragment or user information is refused.
18
- export const INGEST_PATHS = Object.freeze(["/v1/ingest"]);
16
+ // The SDK expects a service root and appends /v1/ingest itself. Accept the
17
+ // endpoint form only while reading setup files written by the first preview.
18
+ export const INGEST_PATHS = Object.freeze(["", "/v1/ingest"]);
19
19
 
20
20
  // Server-generated application tokens are RFC 3986 unreserved characters
21
21
  // only, so a value can never carry a quote, newline, comment or space.
22
22
  const TOKEN = /^[A-Za-z0-9._~-]{16,1024}$/;
23
- const URL_TEXT = /^(https?:\/\/[^/?#@\\]+)(\/[A-Za-z0-9/_.-]*)$/i;
23
+ const URL_TEXT = /^(https?:\/\/[^/?#@\\]+)(\/[A-Za-z0-9/_.-]*)?$/i;
24
24
  const SAFE_URL = /^[A-Za-z0-9.:/[\]_-]+$/;
25
25
 
26
26
  const ASSIGNMENT = /^([ \t]*)(export[ \t]+)?([A-Za-z_][A-Za-z0-9_.-]*)([ \t]*=[ \t]*)(.*)$/;
@@ -35,15 +35,15 @@ export function isAppToken(value) {
35
35
  return typeof value === "string" && TOKEN.test(value);
36
36
  }
37
37
 
38
- // Returns the normalized ingest URL (origin plus a supported path) or null.
38
+ // Returns the normalized service root or legacy ingest endpoint, or null.
39
39
  // The caller must not echo the raw input when this returns null.
40
40
  export function parseIngestUrl(raw) {
41
41
  if (typeof raw !== "string" || raw.length > 2048) return null;
42
42
  const match = URL_TEXT.exec(raw);
43
- if (match === null || !INGEST_PATHS.includes(match[2])) return null;
43
+ if (match === null || !INGEST_PATHS.includes(match[2] ?? "")) return null;
44
44
  const origin = parseOrigin(match[1]);
45
45
  if (origin === null) return null;
46
- const url = `${origin}${match[2]}`;
46
+ const url = `${origin}${match[2] ?? ""}`;
47
47
  return SAFE_URL.test(url) ? url : null;
48
48
  }
49
49
 
package/src/setup.js CHANGED
@@ -111,7 +111,9 @@ export async function runSetup(options, progress = () => {}) {
111
111
  if (previous && existing.token !== null) {
112
112
  const checked = await checkCredential(origin, existing.token, previous.value, profile, trap.signal);
113
113
  if (checked.ok) {
114
- if (existing.ingestUrl !== `${origin}/v1/ingest`) return stop("conflict", "ingest_url_mismatch");
114
+ if (existing.ingestUrl !== origin && existing.ingestUrl !== `${origin}/v1/ingest`) {
115
+ return stop("conflict", "ingest_url_mismatch");
116
+ }
115
117
  if (previous.value.key_id !== null && previous.value.key_id !== checked.keyId) {
116
118
  return stop("conflict", "setup_key_changed");
117
119
  }
@@ -121,6 +123,13 @@ export async function runSetup(options, progress = () => {}) {
121
123
  if (previous.value.phase !== "delivered" || previous.value.key_id === null) {
122
124
  writeSetupState(root, withSetupState(previous.value, { key_id: checked.keyId, phase: "delivered" }), previous);
123
125
  }
126
+ // The first preview wrote the endpoint, which the SDK appends again.
127
+ // Repair only after verifying this family's saved key, without issuing
128
+ // another key or changing the workspace binding.
129
+ if (existing.ingestUrl !== origin) {
130
+ const repaired = await commitEnv(plan, { ingestUrl: origin, signal: trap.signal });
131
+ return finishSkill(root, options, repaired.receipt.file);
132
+ }
124
133
  return finishSkill(root, options, "unchanged");
125
134
  }
126
135
  if (!options.repair && previous.value.phase !== "redeem_attempted") return stop("verification_failed", "saved_key_unverified");
@@ -188,7 +197,7 @@ export async function runSetup(options, progress = () => {}) {
188
197
  redirect_uri: listener.redirectUri, code_verifier: pkce.verifier,
189
198
  family_id: state.family_id, workspace_id: workspaceId }, profile, trap.signal);
190
199
  if (!redeemed.ok) return stop(redeemed.outcome, redeemed.reason, "delivery_pending");
191
- const written = await commitEnv(plan, { token: redeemed.token, ingestUrl: `${origin}/v1/ingest`, signal: trap.signal });
200
+ const written = await commitEnv(plan, { token: redeemed.token, ingestUrl: origin, signal: trap.signal });
192
201
  const checked = await checkCredential(origin, redeemed.token, state, profile, trap.signal);
193
202
  if (!checked.ok || checked.keyId !== redeemed.keyId) return stop("verification_failed", "issued_key_unverified", "delivery_pending");
194
203
  const ack = await acknowledge(origin, redeemed.token, state, profile, redeemed.keyId, trap.signal);