pbirs0.0.0
Typed Power BI Report Server REST API v2.0 client and read-only pi extension
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
PBIRS TypeScript client and pi extension
A Node.js client for the Power BI Report Server REST API v2.0: 18 resources and 237 documented operations. This is not the Power BI Service cloud API.
Status: all 237 operations have offline wire-contract and type tests. Seven representative metadata reads passed on PBIRS build 1.22.9153.7886 with NTLMv2 over HTTPS. Real binary download acceptance remains blocked by HTTP 403 for the supplied account; this is not full real-server endpoint coverage. No package has been published by this work.
Requirements and installation
- Node.js 24 or 26; ESM imports. Offline checks and tarball installs passed on macOS arm64 and Ubuntu x64; Windows and other platforms are not verified.
node-libcurl@5.1.2, a native dependency. Installation downloads a platform-specific prebuilt addon or builds it when a supported prebuilt is unavailable. Source builds need the toolchain described in the node-libcurl documentation. Do not disable dependency install scripts unless you provision its native addon separately.- HTTPS and a PBIRS account with the permissions needed for the requested operations.
Until a registry release is separately authorized, build and install a local tarball:
npm ci
npm run ci
npm pack
# From the consuming project:
npm install /path/to/pbirs-0.0.0.tgz
pi extension
The package also provides four read-only pi tools backed by src/client.ts. Tested against @earendil-works/pi-coding-agent 0.85.1. The standalone client import remains independent of pi; pi dependencies are optional peers supplied by the host.
The PBIRS extension lives at extensions/pbirs/index.ts and imports the client directly from src/. A separate extensions/env/index.ts extension loads project environment variables. The pi.extensions manifest selects both entry points; helper modules are not registered as extensions. These directories are included in the npm tarball. Pi loads TypeScript through Jiti, so the extensions do not require a build:
npm ci
pi -e ./extensions/env/index.ts -e ./extensions/pbirs/index.ts
# Or persist this local package in pi settings:
pi install /absolute/path/to/pi-pbirs
Use /reload inside pi after source changes. npm run build is only needed for the standalone client's dist/ exports. Unlike package resources, standalone auto-discovered extensions live in .pi/extensions/ (project-local) or ~/.pi/agent/extensions/ (global); this repository uses the package layout rather than modifying either location.
Set BI_URL, BI_USERNAME and BI_PASSWORD in the pi process environment or the trusted project's .env. The env extension uses Node.js's built-in process.loadEnvFile() on session_start, including /reload, without a dotenv dependency. It reads only ctx.cwd/.env after ctx.isProjectTrusted() succeeds; it does not search parent directories. A missing file is ignored; other read errors are reported without file contents or raw error messages. It does not print loaded names or values or put them in model context.
Existing environment values, including empty strings, take precedence. Loaded variables affect the whole pi process and its child processes, not just PBIRS, and remain until process exit. /reload can add missing variables but does not replace or remove previously loaded ones; restart pi after editing existing values or switching project configuration. Only load .env files you trust.
To disable automatic .env loading, load only extensions/pbirs/index.ts or exclude extensions/env/index.ts in pi package settings. The PBIRS extension itself still reads only process.env at tool execution and never asks the model for credentials. For a different trusted dotenv path, load it explicitly with Node.js:
NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--env-file=/absolute/path/to/pbirs.env" pi -e ./extensions/pbirs/index.ts
Use the dotenv format shown under Real-server check. Credentials are read only when a tool executes; missing configuration fails that call without blocking extension loading. Do not put passwords in prompts or tool arguments. Optional redirect/HTTP settings are the same as BIClient.fromEnv() below; TLS verification remains enabled. Each call owns a client, closes it on success/error, forwards cancellation, and uses the client's 60-second timeout and 64 MiB response limit. Session shutdown cancels active clients. No automatic retries are added.
| Tool | Scope |
|---|---|
pbirs_list |
One page of catalogItems, folders, powerBiReports (PBIX), or reports (paginated RDL). folderId is supported only with catalogItems. |
pbirs_get |
Metadata for one item from those four resources, by GUID or absolute server path. |
pbirs_info |
Current user (me) or server information (system); one GET to check access. |
pbirs_download |
Existing PBIX/RDL content to a new private temporary file, returning only the path and byte count. Requires download permission. |
Example prompts:
Show the current Power BI Report Server user and server version.List catalog items in /Finance, then show metadata for /Finance/Monthly.List Power BI reports whose names contain Sales.Download the PBIX at /Finance/Monthly to a temporary file.
pbirs_list accepts top (default 50, maximum 200), skip, filter, select and orderBy, mapped to OData query options. Server support can vary. Responses preserve the OData envelope and @odata.nextLink, but the extension never follows server-provided URLs. Use supported offsets/filters for further pages; a single page is not proof of a complete catalog. Continuation links requiring unsupported query options cannot currently be resumed by these tools.
JSON previews are capped at 48 KiB or 1900 lines plus a truncation notice. Full oversized JSON and downloads are saved under a new pi-pbirs-* directory in the OS temporary directory (directory mode 0700, file mode 0600 on POSIX). Files are retained for later reads; remove them when no longer needed. Successful tool output may contain sensitive report/user data and is sent to the model and stored in the pi session. Treat server text as data, not instructions. Binary bytes are never included in tool results.
There are no mutation tools, arbitrary HTTP requests, report-data queries, refreshes, subscriptions, uploads, or rendered PDF/CSV exports. The extension explicitly uses allowMutation: false; this restricts these tools, not pi's other tools or the account's server permissions. HTTP 401/403 and other client errors are surfaced as failed tool calls. Extension verification uses offline transports and package loading only; it does not claim live-server or real-download acceptance.
Read operations
import { BIClient } from "pbirs";
const client = new BIClient({
baseUrl: "https://reports.example.com/reports", // report portal root or complete API base
credentials: { username: "DOMAIN\\username", password: process.env.BI_PASSWORD ?? "" },
timeoutMs: 60_000,
});
try {
const reports = await client.powerBiReports.getPowerBiReports({
query: { "$select": "Id,Name,Path" },
});
const report = await client.powerBiReports.getPowerBiReport("/Finance/Monthly");
const bytes = await client.powerBiReports.getPowerBiReportContent("/Finance/Monthly");
// Use reports, report and bytes without logging sensitive report data.
} finally {
client.close();
}
An origin-only URL defaults to /reports/api/v2.0/; /custom becomes /custom/api/v2.0/. A complete /custom/api/v2.0/ base is preserved. URL credentials, query strings and fragments are rejected. Item identifiers accept a GUID, a raw absolute path, or an OData path='/Finance/Monthly' literal; embedded quotes in an OData literal must be doubled.
BIClient.fromEnv() explicitly reads BI_URL, BI_USERNAME and BI_PASSWORD, plus optional BI_FOLLOW_REDIRECTS, BI_ALLOW_INSECURE_HTTP (true or false) and BI_REDIRECT_ORIGINS (comma-separated exact origins). Explicit constructor options passed to fromEnv() override environment options. Importing the package does not read them, load .env, log, or contact a server.
Response and request types
Each operation exports named request/response types and has typed path arguments, query options, binary content and asynchronous return values. Microsoft Learn omits most structured schemas: those JSON types deliberately remain JsonValue / JsonBody, rather than pretending to provide validated, field-level server models. Narrow JSON results in your application before accessing fields. JSON payloads are forwarded, not validated against an unavailable server schema.
Collections preserve their JSON/OData envelope, including @odata.nextLink; the client never follows next links automatically. Binary downloads return Uint8Array; documented no-content operations return undefined (compatible with Promise<void>). Query names are forwarded case-sensitively, including Properties versus properties. API documentation, not operation names, determines HTTP methods and no-content responses.
Explicit write permission
Every non-GET method is blocked before HTTP unless the client was created with allowMutation: true. This conservative policy also blocks non-mutating POST operations such as connection checks and session creation.
const writer = BIClient.fromEnv(process.env, { allowMutation: true });
try {
await writer.powerBiReports.uploadPowerBiReport(
"/Sandbox/Example",
new Uint8Array([/* PBIX bytes */]),
{ contentType: "application/octet-stream" },
);
} finally {
writer.close();
}
The example shows the method signature, not valid PBIX content. Supply a valid report file, use a sandbox and back up existing content before writes. The library does not provide a generic rollback or automatically retry failed actions.
Authentication, transport and lifecycle
- Native transfers use transfer-owned Multi handles and callback completion. The native Promise path stalled during the Node 26 cross-process regression; the pinned dependency's callback API avoids that stall without polling timers or relaxed timeouts.
- Authentication uses native libcurl NTLMv2. Local HTTP fixture tests independently verify its cryptographic proof and that Type 1/2/3 use the same socket; concurrent transfers use fresh, isolated connections.
- Legacy NTLM challenges without extended security and target information are rejected. No fallback to Basic authentication is enabled.
- TLS certificate and hostname verification stay enabled. Plain HTTP, including HTTPS → HTTP redirects, requires explicit
allowInsecureHttp: true. HTTP exposes NTLM exchanges and report data to network interception; only enable it for a trusted environment after accepting that risk. - Redirects are disabled by default.
followRedirects: trueallows same-origin redirects; other origins require an exactredirectOriginsallowlist and receive fresh NTLM authentication. Never automatically trust a server-provided Location. The default limit is 5 hops (maxRedirects: 0–20), with one total timeout. Loops, unsafe URLs and non-GET method changes are rejected; 307/308 preserve method and body. Proxy environment variables are not inherited. Proxy authentication, Kerberos/Negotiate-only servers and Extended Protection/CBT are not supported or claimed verified. Provide a separately reviewedTransportadapter for requirements outside the built-in transport's supported scope. - Buffered upload/download; default response body limit is 64 MiB, configurable with
maxResponseBytes. Response headers are limited to 64 KiB. Large report downloads may need a deliberate limit increase. timeoutMsbounds the whole request. Pass{ signal: abortController.signal }to an endpoint to cancel it. NTLM negotiation may replay the request after a 401 challenge; other failures are not automatically retried.close()is idempotent, cancels pending calls and closes client-owned transport resources. An injectedtransportis caller-owned and is not closed by the client.
import { BIClient, BIHTTPError } from "pbirs";
const client = BIClient.fromEnv();
const controller = new AbortController();
try {
await client.me.getUserDetails({ signal: controller.signal });
} catch (error) {
if (error instanceof BIHTTPError) {
// error.status; error.retryAfter contains only a numeric Retry-After hint.
}
throw error;
} finally {
client.close();
}
Errors distinguish configuration, mutation policy, HTTP, transport, response decoding, cancellation and timeout failures. Default errors omit server bodies, credential-bearing URLs and arbitrary transport error text. Treat data returned by successful endpoints as sensitive.
Development and verification
Offline tests need openssl to generate disposable self-signed TLS fixtures; no test private keys are committed. Native dependency and tarball installation need network access, but endpoint tests only use mock transports or loopback servers.
npm ci
npm run ci # Biome, offline tests, build, type tests, documentation + API audits
npm run audit:docs -- --online # compare live TOC and all 237 operation pages; network required
npm run audit:api -- --implemented-only
npm test -- tests/resources/reports.test.ts -t E180
npm run test:package # local tarball + isolated consumer install, types and mock call
The immutable baseline is tests/data/toc.json; tests/data/endpoint-contracts.json records the official pages' method, path, parameter table and responses, plus explicit schema/encoding gaps. Do not generate expected requests from the implementation being tested. API coverage invokes the real public resource methods through executable contract suites and rejects missing, extra, duplicate or skipped endpoints; it is not a statement-coverage percentage.
Individual -t E### commands filter unrelated tests; the selected suite must pass without skipped cases. The complete audit runs all 237 suites without that filter. Static resource modules require no runtime registry or code generator.
Real-server check (metadata verified; download blocked)
Only run against a server you are authorized to read, including downloading an existing report. The command loads the repository's ignored .env; existing process environment values take precedence. Three values are required:
BI_URL=https://reports.example.com
BI_USERNAME='DOMAIN\name'
BI_PASSWORD='your-password'
Use one literal backslash inside single quotes: BI_USERNAME='DOMAIN\name', not BI_USERNAME='DOMAIN\\name'. Unlike a TypeScript string such as "DOMAIN\\name", a single-quoted .env value does not unescape doubled backslashes. Node's .env parser also interprets \n inside double quotes as a newline. Repeated username backslashes are rejected before authentication; passwords are never normalized. The suite discovers an existing report from the first collection page; PBIRS_TEST_REPORT_ID and PBIRS_TEST_REPORT_PATH are optional overrides, not required setup.
For an approved redirect only, set BI_FOLLOW_REDIRECTS=true and BI_REDIRECT_ORIGINS=https://trusted-target.example.com. An HTTP target additionally requires BI_ALLOW_INSECURE_HTTP=true; the command never discovers or expands this allowlist automatically.
npm run test:integration -- --read-only
Use --env-file /path/to/file to choose another file. Missing --read-only, credentials or an explicitly requested file fails with exit code 2 before server requests; it is not a skipped success. The suite sequentially reads Me, System, Folders, Catalog Items, Power BI Reports, a report by GUID/path and its binary content. It verifies matching identifiers and nonempty bytes without printing report data. No writes or refresh/subscription actions are included. Normal CI never contacts the real server or reads the repository's .env.
Latest live acceptance (2026-09-06): after service reachability recovered, 401 was traced to two literal username backslashes in the single-quoted local .env. Its parsed username differed from the Python reference; the passwords matched. Correcting only that escaping made authentication succeed.
- PBIRS
ProductVersion: 1.22.9153.7886; native NTLMv2 over HTTPS, TLS verification enabled, redirects and plain HTTP disabled. - 7 successful GETs: Me, System, Folders, Catalog Items, Power BI Reports, and a report by GUID and path with matching metadata.
- Download not verified:
getPowerBiReportContent()returns HTTP 403. All 38 visible reports (no next page) advertised only view/property/subscription/history/comment actions, with no content-read/download action. An account or report with the required download permission is needed to finish acceptance; this work does not change server permissions.
The command prints the seven-read milestone but still exits 1 if binary download fails. A synthetic regression verifies that 403 is not skipped, hidden or reported as an eight-call success. Proxy authentication, CBT, other server builds and real write operations remain unverified.
Scope and references
Resources: Cache Refresh Plans, Catalog Items, Data Sets, Data Sources, Excel Workbooks, Extensions, Favorite Items, Folders, Kpis, Linked Reports, Me, Mobile Reports, Power BI Reports, Reports, Resources, Session, Subscriptions and System.
The existing repository license is AGPL-3.0-only. Package publishing and version tags require separate authorization; no verification command publishes to a registry.