@getpeppr/cli 0.7.1 → 0.8.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/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ All notable changes to `@getpeppr/cli` are documented here.
4
4
 
5
5
  The CLI bundles `@getpeppr/sdk` into its published artifact (tsup `noExternal`), so `validate`, `convert` and `init` run the SDK's validators, country rules and UBL builder locally — an SDK release only reaches CLI users once the CLI is rebundled. Rebundle-only releases are listed with the SDK version they ship.
6
6
 
7
+ ## [0.8.1] — 2026-08-19
8
+
9
+ Republish of 0.8.0 with the actual code in the tarball. The 0.8.0 tarball on
10
+ the registry shipped **without `dist/`** — three files, no binary — because
11
+ this package had no `prepack`/`prepublishOnly` guards and was published from a
12
+ tree where the bundle had never been built. 0.8.0 is deprecated on the
13
+ registry; this version is identical in content and adds the guards so a
14
+ dist-less publish can never happen again (`prepack` builds, `prepublishOnly`
15
+ runs the tests, `pretest` builds first — the same arming the SDK has).
16
+
17
+ ## [0.8.0] — 2026-08-19
18
+
19
+ **`getpeppr whoami`** — show the Peppol identity of the account behind your
20
+ API key (GPR-1094). Environment, company, country, and every registered
21
+ identifier with its status. Works with any key; `--prod`, `--local`, `--key`
22
+ and `--json` behave as on `send` and `lookup`. No identity yet is an answer,
23
+ not an error — the command points you at the console page where you set one up.
24
+
25
+ Bundles SDK 4.3.0 (`peppol.identity.get()` — the API this command consumes).
26
+
7
27
  ## [0.7.1] — 2026-08-18
8
28
 
9
29
  Rebundle SDK 4.1.1. This release matters for `getpeppr validate`, which runs the
package/README.md CHANGED
@@ -159,6 +159,30 @@ getpeppr send invoice.json --prod
159
159
 
160
160
  When sending through the API, getpeppr determines the seller from the API key. A `from` party can still be present for local validation and UBL conversion, but the gateway ignores it for delivery.
161
161
 
162
+ ### `getpeppr whoami` — Show your Peppol identity
163
+
164
+ Read the Peppol identity of the account behind your API key: the environment the key operates in, your legal entity as the gateway holds it, and every Peppol identifier registered for it with its status. Works with **any** key — standard keys included. This is the fastest way to check "is my identifier registered and verified?" without opening the console.
165
+
166
+ ```bash
167
+ # Sandbox identity (default)
168
+ getpeppr whoami
169
+
170
+ # Production identity (uses your live key)
171
+ getpeppr whoami --prod
172
+
173
+ # Machine-readable
174
+ getpeppr whoami --json
175
+ ```
176
+
177
+ | Flag | Description |
178
+ |------|-------------|
179
+ | `--prod` | Use the live API key (default: sandbox) |
180
+ | `--local` | Target `http://localhost:3001` (dev server) |
181
+ | `--key <key>` | Override API key. CI use only — visible in `ps`/shell history. Prefer `GETPEPPR_API_KEY`. |
182
+ | `--json` | Output the identity as JSON |
183
+
184
+ If no legal entity exists in the environment yet, `whoami` says so and points to the console page where you create one.
185
+
162
186
  ### `getpeppr logout` — Remove stored credentials
163
187
 
164
188
  ```bash
@@ -213,7 +237,7 @@ The CLI runs three validation engines from the [@getpeppr/sdk](https://www.npmjs
213
237
  2. **Business Rules** — Peppol BIS 3.0 / EN 16931 compliance (BR-xx, BR-CO-xx, PEPPOL-xx rules)
214
238
  3. **Country Rules** — Belgium (BE), France (FR), Italy (IT), Netherlands (NL), Germany (DE)
215
239
 
216
- Scaffolding (`init`), validation, and conversion run fully offline — no API key or network connection required. Only `lookup` and `send` need a network connection.
240
+ Scaffolding (`init`), validation, and conversion run fully offline — no API key or network connection required. Only `lookup`, `send`, and `whoami` need a network connection.
217
241
 
218
242
  ## Invoice format
219
243
 
package/dist/index.js CHANGED
@@ -1161,7 +1161,7 @@ function statusFamily(status) {
1161
1161
  var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
1162
1162
 
1163
1163
  // ../sdk/dist/version.js
1164
- var SDK_VERSION = "4.1.1";
1164
+ var SDK_VERSION = "4.3.0";
1165
1165
 
1166
1166
  // ../sdk/dist/core/client.js
1167
1167
  function findHeaderCaseInsensitive(headers, name) {
@@ -1713,6 +1713,10 @@ var GetpepprAdapter = class {
1713
1713
  expiresAt: String(result.expiresAt ?? "")
1714
1714
  };
1715
1715
  }
1716
+ async getIdentity() {
1717
+ const result = await this.request("GET", "/identity");
1718
+ return parseAccountIdentity(result);
1719
+ }
1716
1720
  async listBankAccounts(options) {
1717
1721
  const params = new URLSearchParams();
1718
1722
  if (options?.limit != null)
@@ -1871,6 +1875,14 @@ function parseSendResult(body) {
1871
1875
  if (typeof rulebook === "object" && rulebook !== null && typeof rulebook.peppol === "string" && typeof rulebook.verifiedAt === "string") {
1872
1876
  sendResult.rulebook = rulebook;
1873
1877
  }
1878
+ const transmission = result.transmission;
1879
+ if (typeof transmission === "object" && transmission !== null && !Array.isArray(transmission)) {
1880
+ const mode = readOwn(transmission, "mode");
1881
+ const bytePreservation = readOwn(transmission, "bytePreservation");
1882
+ if (typeof mode === "string" && typeof bytePreservation === "string") {
1883
+ sendResult.transmission = { mode, bytePreservation };
1884
+ }
1885
+ }
1874
1886
  const submissionId = optionalWireId(result.submissionId);
1875
1887
  if (submissionId)
1876
1888
  sendResult.submissionId = submissionId;
@@ -2012,6 +2024,74 @@ function parseLegalEntity(raw) {
2012
2024
  }
2013
2025
  return le;
2014
2026
  }
2027
+ function parseAccountIdentity(raw) {
2028
+ const result = requireRecordBody(raw, "the identity request");
2029
+ const environment = readOwn(result, "environment");
2030
+ if (typeof environment !== "string" || environment.trim() === "") {
2031
+ throw new PeppolProtocolError("The getpeppr API answered the identity request without an environment. The SDK will not invent one \u2014 please report this response to support@getpeppr.dev.", "environment", boundedBody(result));
2032
+ }
2033
+ const identifiersRaw = readOwn(result, "identifiers");
2034
+ if (!Array.isArray(identifiersRaw)) {
2035
+ throw new PeppolProtocolError("The getpeppr API answered the identity request without an identifiers array. The SDK will not invent one \u2014 please report this response to support@getpeppr.dev.", "identifiers", boundedBody(result));
2036
+ }
2037
+ const identifiers = identifiersRaw.map((row) => {
2038
+ if (!isRecord(row)) {
2039
+ throw new PeppolProtocolError("The getpeppr API answered the identity request with a malformed identifier row. Please report this response to support@getpeppr.dev.", "identifiers", boundedBody(result));
2040
+ }
2041
+ const scheme = readOwn(row, "scheme");
2042
+ const value = readOwn(row, "value");
2043
+ const status = readOwn(row, "status");
2044
+ if (typeof scheme !== "string" || typeof value !== "string" || typeof status !== "string") {
2045
+ throw new PeppolProtocolError("The getpeppr API answered the identity request with a malformed identifier row. Please report this response to support@getpeppr.dev.", "identifiers", boundedBody(result));
2046
+ }
2047
+ const createdAt = readOwn(row, "createdAt");
2048
+ return {
2049
+ scheme,
2050
+ value,
2051
+ status,
2052
+ createdAt: typeof createdAt === "string" ? createdAt : null
2053
+ };
2054
+ });
2055
+ return {
2056
+ environment,
2057
+ legalEntity: parseAccountIdentityLegalEntity(readOwn(result, "legalEntity"), result),
2058
+ identifiers
2059
+ };
2060
+ }
2061
+ function parseAccountIdentityLegalEntity(raw, body) {
2062
+ if (raw === null)
2063
+ return null;
2064
+ if (!isRecord(raw)) {
2065
+ throw new PeppolProtocolError(
2066
+ "The getpeppr API answered the identity request without a valid legalEntity field. Please report this response to support@getpeppr.dev.",
2067
+ "legalEntity",
2068
+ // The FULL body, per the responseBody contract — `raw` alone serialises
2069
+ // to the string "undefined" when the field is missing (2nd-pass gate N1).
2070
+ boundedBody(body)
2071
+ );
2072
+ }
2073
+ const companyName = readOwn(raw, "companyName");
2074
+ const country = readOwn(raw, "country");
2075
+ const createdAt = readOwn(raw, "createdAt");
2076
+ return {
2077
+ companyName: typeof companyName === "string" ? companyName : null,
2078
+ country: typeof country === "string" ? country : null,
2079
+ address: parseAccountIdentityAddress(readOwn(raw, "address")),
2080
+ createdAt: typeof createdAt === "string" ? createdAt : null
2081
+ };
2082
+ }
2083
+ function parseAccountIdentityAddress(raw) {
2084
+ if (!isRecord(raw))
2085
+ return null;
2086
+ const line1 = readOwn(raw, "line1");
2087
+ const city = readOwn(raw, "city");
2088
+ const zip = readOwn(raw, "zip");
2089
+ return {
2090
+ line1: typeof line1 === "string" ? line1 : null,
2091
+ city: typeof city === "string" ? city : null,
2092
+ zip: typeof zip === "string" ? zip : null
2093
+ };
2094
+ }
2015
2095
  function parseInvoiceSummary(row) {
2016
2096
  const raw = requireRecordBody(row, "this invoice row");
2017
2097
  const rawStatus = requireWireStatus(raw.state ?? raw.status, raw, "this invoice row");
@@ -2156,6 +2236,12 @@ var Peppol = class {
2156
2236
  contacts;
2157
2237
  bankAccounts;
2158
2238
  transports;
2239
+ /**
2240
+ * Your own account's Peppol identity — works with ANY key, standard keys
2241
+ * included. `peppol.identity.get()` answers "who am I on the Peppol
2242
+ * network?" for the account behind the key making the call.
2243
+ */
2244
+ identity;
2159
2245
  /**
2160
2246
  * Sub-tenant Legal Entities — **platform accounts only**.
2161
2247
  *
@@ -2163,8 +2249,9 @@ var Peppol = class {
2163
2249
  * every call here fails with 403 `master_key_required`.
2164
2250
  *
2165
2251
  * Onboarding your OWN company is not done through this API: your legal entity
2166
- * is managed in the console, on the Peppol identity page. This surface is for
2167
- * platforms that onboard their customers as sub-tenants.
2252
+ * is managed in the console, on the Peppol identity page and READ from the
2253
+ * API with `peppol.identity.get()`, which works with any key. This surface is
2254
+ * for platforms that onboard their customers as sub-tenants.
2168
2255
  *
2169
2256
  * **Getting access:** platform mode is enabled by getpeppr on your account —
2170
2257
  * email hello@getpeppr.dev to have it switched on. Once it is, you create the
@@ -2185,6 +2272,7 @@ var Peppol = class {
2185
2272
  this.contacts = new ContactOperations(this.adapter);
2186
2273
  this.bankAccounts = new BankAccountOperations(this.adapter);
2187
2274
  this.transports = new TransportOperations(this.adapter);
2275
+ this.identity = new IdentityOperations(this.adapter);
2188
2276
  this.legalEntities = new LegalEntityOperations(this.adapter);
2189
2277
  }
2190
2278
  /**
@@ -2379,6 +2467,17 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
2379
2467
  * **If you seal your documents, hash a canonical form (C14N) rather than the
2380
2468
  * raw bytes.**
2381
2469
  *
2470
+ * The receipt says this itself, so your code need not rely on this comment:
2471
+ * a successful import carries `transmission`, whose `bytePreservation` this
2472
+ * gateway sets to `"not_guaranteed"` (GPR-1089). It is a guarantee we decline
2473
+ * to give, not a claim that your document was altered.
2474
+ *
2475
+ * ⚠️ Read the value, do not assume it. This SDK talks to whatever gateway
2476
+ * version you point it at: one predating GPR-1089 returns no `transmission`
2477
+ * at all, and the field is typed `string` so a later value reaches you rather
2478
+ * than being dropped. **Absent is not `false`** — it means the gateway did
2479
+ * not say, never that your bytes are safe.
2480
+ *
2382
2481
  * `to` is required and is never read from the document. Routing decides
2383
2482
  * delivery, the document travels as payload, and getpeppr will not guess a
2384
2483
  * destination by parsing your XML.
@@ -2772,6 +2871,34 @@ var ContactOperations = class {
2772
2871
  return paginate((offset, limit) => this.adapter.listContacts({ ...options, offset, limit }), options);
2773
2872
  }
2774
2873
  };
2874
+ var IdentityOperations = class {
2875
+ adapter;
2876
+ constructor(adapter) {
2877
+ this.adapter = adapter;
2878
+ }
2879
+ /**
2880
+ * Read the Peppol identity of your own account: the environment this key
2881
+ * operates in, your legal entity as the gateway holds it, and the Peppol
2882
+ * identifiers registered for it.
2883
+ *
2884
+ * Works with ANY API key — standard keys included; no platform mode or
2885
+ * master key required. This is the read counterpart to onboarding: your
2886
+ * legal entity is created and edited in the console (Peppol identity page)
2887
+ * or via onboarding, and this call is how you READ it from the API.
2888
+ *
2889
+ * @example
2890
+ * ```ts
2891
+ * const me = await peppol.identity.get();
2892
+ * console.log(me.environment, me.legalEntity?.companyName);
2893
+ * for (const id of me.identifiers) {
2894
+ * console.log(`${id.scheme}:${id.value} — ${id.status}`);
2895
+ * }
2896
+ * ```
2897
+ */
2898
+ async get() {
2899
+ return this.adapter.getIdentity();
2900
+ }
2901
+ };
2775
2902
  var LegalEntityOperations = class {
2776
2903
  adapter;
2777
2904
  constructor(adapter) {
@@ -2785,7 +2912,8 @@ var LegalEntityOperations = class {
2785
2912
  * https://console.getpeppr.dev/api-keys.
2786
2913
  *
2787
2914
  * Your own company's legal entity is managed in the console, on the Peppol
2788
- * identity page; this creates an entity for a customer of yours.
2915
+ * identity page (and read from the API with `peppol.identity.get()`); this
2916
+ * creates an entity for a customer of yours.
2789
2917
  *
2790
2918
  * Idempotent on `externalId`: repeated calls with the same `externalId` return
2791
2919
  * the existing entity (HTTP 200) instead of creating a duplicate. Transient 5xx
@@ -4433,14 +4561,117 @@ function registerLoginCommand(program2) {
4433
4561
  });
4434
4562
  }
4435
4563
 
4436
- // src/commands/logout.ts
4564
+ // src/commands/whoami.ts
4437
4565
  import pc8 from "picocolors";
4566
+ var API_BASE2 = "https://api.getpeppr.dev/v1";
4567
+ var LOCAL_BASE2 = "http://localhost:3001/api/v1";
4568
+ var PEPPOL_IDENTITY_URL = "https://console.getpeppr.dev/peppol-identity";
4569
+ function sanitizeTerminal(s) {
4570
+ return s.replace(/[\x00-\x1f\x7f-\x9f]/g, "");
4571
+ }
4572
+ function formatIdentity(identity) {
4573
+ const lines = [];
4574
+ lines.push(
4575
+ ` ${pc8.dim("Environment")} ${sanitizeTerminal(identity.environment)}`
4576
+ );
4577
+ const le = identity.legalEntity;
4578
+ if (!le) {
4579
+ lines.push("");
4580
+ lines.push(
4581
+ `${pc8.yellow("!")} No Peppol identity in this environment yet.`
4582
+ );
4583
+ lines.push(
4584
+ ` Create your legal entity in the console: ${PEPPOL_IDENTITY_URL}`
4585
+ );
4586
+ return lines.join("\n");
4587
+ }
4588
+ lines.unshift(
4589
+ `${pc8.green("\u2713")} ${le.companyName != null ? sanitizeTerminal(le.companyName) : pc8.dim("(no company name)")}`
4590
+ );
4591
+ if (le.country) {
4592
+ lines.push(
4593
+ ` ${pc8.dim("Country")} ${sanitizeTerminal(countryLabel(le.country))}`
4594
+ );
4595
+ }
4596
+ if (le.address) {
4597
+ const parts = [le.address.line1, le.address.zip, le.address.city].filter((p) => Boolean(p)).map(sanitizeTerminal);
4598
+ if (parts.length > 0) {
4599
+ lines.push(` ${pc8.dim("Address")} ${parts.join(", ")}`);
4600
+ }
4601
+ }
4602
+ if (le.createdAt) {
4603
+ lines.push(` ${pc8.dim("Created")} ${sanitizeTerminal(le.createdAt)}`);
4604
+ }
4605
+ lines.push("");
4606
+ if (identity.identifiers.length === 0) {
4607
+ lines.push(
4608
+ `${pc8.dim("No identifiers registered yet.")} Register one in the console: ${PEPPOL_IDENTITY_URL}`
4609
+ );
4610
+ return lines.join("\n");
4611
+ }
4612
+ const plural = identity.identifiers.length === 1 ? "identifier" : "identifiers";
4613
+ lines.push(`${identity.identifiers.length} Peppol ${plural}:`);
4614
+ const rows = identity.identifiers.map((id) => ({
4615
+ peppolId: sanitizeTerminal(`${id.scheme}:${id.value}`),
4616
+ status: sanitizeTerminal(id.status),
4617
+ createdAt: id.createdAt ? sanitizeTerminal(id.createdAt) : null
4618
+ }));
4619
+ const idW = Math.max(...rows.map((r) => r.peppolId.length)) + 3;
4620
+ const statusW = Math.max(...rows.map((r) => r.status.length)) + 3;
4621
+ for (const row of rows) {
4622
+ const line = ` ${row.peppolId.padEnd(idW)}${row.status.padEnd(statusW)}${row.createdAt ? pc8.dim(row.createdAt) : ""}`;
4623
+ lines.push(line.trimEnd());
4624
+ }
4625
+ return lines.join("\n");
4626
+ }
4627
+ function registerWhoamiCommand(program2) {
4628
+ program2.command("whoami").description("Show the Peppol identity of the account behind your API key").option("--prod", "use the live API key (default: sandbox)").option("--local", "target localhost:3001 dev server").option(
4629
+ "--key <key>",
4630
+ "override API key \u2014 for CI/scripted use only; visible in `ps` and shell history. Prefer GETPEPPR_API_KEY env var."
4631
+ ).option("--json", "output the identity as JSON").action(async (flags) => {
4632
+ let auth;
4633
+ try {
4634
+ auth = resolveApiKey({
4635
+ flagKey: flags.key,
4636
+ forceProd: Boolean(flags.prod),
4637
+ forceLocal: Boolean(flags.local)
4638
+ });
4639
+ } catch (e) {
4640
+ if (e instanceof AuthError) {
4641
+ exitWithError(e.message);
4642
+ return;
4643
+ }
4644
+ throw e;
4645
+ }
4646
+ const baseUrl = flags.local ? LOCAL_BASE2 : API_BASE2;
4647
+ const client = new Peppol({ apiKey: auth.apiKey, baseUrl });
4648
+ let identity;
4649
+ try {
4650
+ identity = await client.identity.get();
4651
+ } catch (e) {
4652
+ const msg = e instanceof Error ? e.message : String(e);
4653
+ process.stderr.write(`${pc8.red("\u2717")} ${sanitizeTerminal(msg)}
4654
+ `);
4655
+ process.exit(1);
4656
+ return;
4657
+ }
4658
+ if (flags.json) {
4659
+ console.log(JSON.stringify(identity, null, 2));
4660
+ } else {
4661
+ console.log(formatIdentity(identity));
4662
+ }
4663
+ process.exitCode = 0;
4664
+ });
4665
+ }
4666
+
4667
+ // src/commands/logout.ts
4668
+ import pc9 from "picocolors";
4438
4669
  function registerLogoutCommand(program2) {
4439
4670
  program2.command("logout").description("Remove ~/.config/getpeppr/credentials.json").action(() => {
4440
4671
  const path = getCredentialsPath();
4441
4672
  const removed = deleteCredentials();
4442
4673
  if (removed) {
4443
- process.stderr.write(`${pc8.green("\u2713")} Removed ${path}
4674
+ process.stderr.write(`${pc9.green("\u2713")} Removed ${path}
4444
4675
  `);
4445
4676
  } else {
4446
4677
  process.stderr.write(`No credentials to remove (${path})
@@ -4461,6 +4692,7 @@ registerConvertCommand(program);
4461
4692
  registerLookupCommand(program);
4462
4693
  registerSendCommand(program);
4463
4694
  registerLoginCommand(program);
4695
+ registerWhoamiCommand(program);
4464
4696
  registerLogoutCommand(program);
4465
4697
  program.parse();
4466
4698
  //# sourceMappingURL=index.js.map