/** * Resolve the Jira credential this process should use, normalized to ONE shape * the request layer can switch on. A Jira connection is either: * * • `{ authType: 'oauth', accessToken, cloudId }` — the 3LO connection. The * caller addresses `api.atlassian.com/ex/jira/` with a Bearer. * • `{ authType: 'token', apiToken, email, baseUrl }` — a paste-token / * self-host connection (backend `handlers/jira.js connectJiraToken`), or an * operator's `.env`. The caller addresses the instance DIRECTLY and * authenticates as HTTP Basic `email:apiToken`. * * TWO SOURCES, in this order: * * 1. The Basic-auth TRIO in the run env — `JIRA_API_TOKEN` + `JIRA_EMAIL` + * `JIRA_BASE_URL`, which `workflow-executor` injects for an * `authType:'token'` row (and the self-host docker dispatcher fills from the * operator `.env` when the table set none). All THREE are required, so a * half-set env can never shadow a working OAuth credential. This is the * `SELF_HOST_ENV` bargain the github/gitlab skills already make: complete on * its own, so no round-trip and the token never leaves the box. * 2. `resolveIntegrationToken('jira')` → the backend's `/jira/token`. It * answers `{token, cloudId}` for OAuth and `{token, instanceUrl}` for a * token row (VERIFIED on the founder's box, 2026-08-22). It carries no * email, so the token branch reads that from the env alongside. * * The shape is chosen by WHAT THE CREDENTIAL CARRIES — never by an environment * check. On cloud, an OAuth row injects no `JIRA_*` trio and answers with a * `cloudId`, so the oauth branch is taken and the request is byte-identical to * what this file built before. */ export declare function resolveJiraCredential(): Promise<{ authType: string; accessToken: any; cloudId: any; apiToken?: undefined; email?: undefined; baseUrl?: undefined; } | { authType: string; apiToken: any; email: any; baseUrl: any; accessToken?: undefined; cloudId?: undefined; }>; /** * A Jira REST request — `{ url, headers }` — as a PURE FUNCTION of the resolved * credential. The run-time twin of the backend's `handlers/jira.js jiraApiCall`, * deliberately the same shape so the two halves of one connection cannot drift. * * MEASURED against the founder's real instance (backend side, 2026-08-22): * • `api.atlassian.com/ex/jira/undefined/rest/api/3/project` + Bearer → 404 * • `https:///rest/api/3/project` + Basic b64(email:apiToken) → 200 * • `https:///rest/api/3/project` + Bearer → 403 * The third line is why the header is chosen by SHAPE and not shared: an * Atlassian API token is a Basic-auth PASSWORD, never a bearer. One header * cannot serve both. * * Both branches fail LOUD on a credential that cannot address an instance, * rather than composing `.../undefined/...` and letting Atlassian word the * error. */ export declare function jiraApiCall(cred: any, path: string, opts?: any): { url: string; headers: any; }; /** * Low-level Jira REST helper. Resolves the credential via * `resolveJiraCredential()`, builds the request with `jiraApiCall()` (which * knows the two connection shapes), retries once on transient auth errors, and * returns parsed JSON (or `{ raw }` for non-JSON bodies). * * Exported so other templates (e.g. tracker-writeback, bug-autofix, * board-runner) can issue Jira REST calls the JIRA skill's MCP tools don't * cover. Keep this the single auth chokepoint; don't re-implement credential * resolution at call sites. * * ── `opts.signal`: OPTIONAL, and it is what turns a caller's WAIT into a real * ABORT ──────────────────────────────────────────────────────────────────── * Node's global fetch has NO default timeout, and A HANG IS NOT A THROW, so an * Atlassian connection that is accepted and never answered parks whoever called * this forever. Callers that care already bound the WAIT from outside — * workflow-templates' `_shared/tracker.js jiraCall` races this promise against * a `BOARD_API_TIMEOUT_MS` deadline — but a race can only stop waiting; it * cannot close the socket, because the socket lives in here. Accepting a * `signal` is the missing half: the same deadline that ends the wait now also * ends the REQUEST. * * The signal covers the BODY read too — undici ties the response stream to the * request's signal — so a response whose headers arrive and whose body then * stalls is aborted on the same clock, which is the half a naive passthrough * would miss. * * ── AND NOW A DEFAULT UNDER IT (#1124) ───────────────────────────────────── * The passthrough shipped OPT-IN and inert, with no default timeout, on this * reasoning: "a shared library whose callers know their own budget … inventing * one for them is how a legitimately slow bulk JQL query starts failing in * production for a reason nobody asked for." * * That reasoning held while there was nowhere to put a budget that was not a * number invented in this file. It does not hold now: `lib/http-deadline.ts` * declares budgets BY WHAT IS MOVING, once, for every skill — and the slow bulk * JQL query is not a counter-example to bounding, it is one of the KINDS. A * `/search` is the far end COMPUTING, so it draws the `job` budget (5 min); * everything else here is a row read and draws `api` (30s). The knobs raise * either one without touching this file. * * What survived unchanged is the important half: EVERY CALLER TODAY PASSES * NOTHING, and a caller that does pass a signal still gets THEIR abort — their * reason, their error — because `fetchWithDeadline` COMPOSES the two rather * than replacing one with the other. * * @param {string} path Jira REST path, e.g. `/rest/api/3/issue/PROJ-1` * @param {{ method?: string, body?: any, headers?: object, signal?: AbortSignal }} [opts] * @returns {Promise} parsed JSON response body */ export declare function jiraFetch(path: any, opts?: any): Promise; export declare const jiraSkill: any;