dsh-atlassian-plugin 0.3.4 → 0.4.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 +2 -0
- package/icon.svg +6 -10
- package/lib/client.js +48 -30
- package/lib/client.js.map +4 -4
- package/lib/index.js +201 -57
- package/lib/index.js.map +4 -4
- package/package.json +6 -6
package/lib/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../mcp-oauth/src/account.ts", "../../mcp-oauth/src/config.ts", "../../mcp-oauth/src/log.ts", "../../mcp-oauth/src/net.ts", "../../mcp-oauth/src/server.ts", "../../mcp-oauth/src/discovery.ts", "../../mcp-oauth/src/provider.ts", "../../mcp-oauth/src/tool-name.ts", "../../mcp-oauth/src/bridge.ts", "../../mcp-oauth/src/browser.ts", "../../mcp-oauth/src/flow.ts", "../../mcp-oauth/src/json.ts", "../../mcp-oauth/src/grant.ts", "../../mcp-oauth/src/loopback.ts", "../../mcp-oauth/src/payload.ts", "../../integration-host/src/auth.ts", "../../integration-host/src/command.ts", "../../integration-host/src/config.ts", "../../integration-host/src/correlation.ts", "../../integration-host/src/digest.ts", "../../integration-host/src/markdown.ts", "../../integration-host/src/route.ts", "../../integration-host/src/state.ts", "../src/host/store/site.ts", "../src/host/auth.ts", "../src/host/config.ts", "../src/host/presentation/extract.ts", "../src/host/correlation.ts", "../src/host/digest.ts", "../src/host/panels/panel.ts", "../src/host/surface/command.ts", "../src/host/surface/http.ts", "../src/host/presentation/gist.ts", "../src/host/tools/present.ts", "../src/host/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Which account of one provider a credential record belongs to.\n *\n * A plugin may hold several accounts of the same provider \u2014 a work GitLab and a\n * personal one, or two Atlassian sites \u2014 and the credential store addresses a\n * record as `<scope>/<id>`, so the account has to live inside the id half. The\n * default account keeps the id `mcp` that records were first written with,\n * which is what makes accounts additive: a grant stored before they existed\n * still resolves to the default one.\n */\n\nimport type { CredentialRecordEntry } from \"@deepseek-ai/dsh-credentials\";\n\n/** The account a provider uses when its plugin names none. */\nexport const DEFAULT_INSTANCE = \"default\";\n\n/** What an account name may be: one credential-key segment. */\nconst INSTANCE_PATTERN = /^[a-z][a-z0-9-]*$/;\n\n/** The record id one account owns. */\nconst idFor = (instance: string): string =>\n\tinstance === DEFAULT_INSTANCE ? \"mcp\" : `mcp-${instance}`;\n\n/** The account an id names, or undefined for an id this plugin never wrote. */\nconst instanceFor = (id: string): string | undefined => {\n\tif (id === \"mcp\") return DEFAULT_INSTANCE;\n\n\tif (!id.startsWith(\"mcp-\")) return undefined;\n\n\tconst instance = id.slice(\"mcp-\".length);\n\n\treturn INSTANCE_PATTERN.test(instance) ? instance : undefined;\n};\n\n/**\n * The credential record key of one provider account.\n * @param scope - the owning plugin's registered name.\n * @param instance - the account within it; the default one unless named.\n * @throws TypeError when either segment is not a lowercase hyphenated identifier,\n * which is the grammar the credential store admits.\n */\nexport function credentialKeyFor(\n\tscope: string,\n\tinstance: string = DEFAULT_INSTANCE,\n): string {\n\tfor (const segment of [scope, instance])\n\t\tif (!INSTANCE_PATTERN.test(segment))\n\t\t\tthrow new TypeError(\n\t\t\t\t`credential key segment \"${segment}\" must match ${String(INSTANCE_PATTERN)}`,\n\t\t\t);\n\n\treturn `${scope}/${idFor(instance)}`;\n}\n\n/**\n * The account a stored record key names.\n * @param scope - the owning plugin's registered name.\n * @param key - a record key in the store's `<scope>/<id>` grammar.\n * @returns the account, or undefined for a key another plugin owns and for an\n * id this plugin never wrote.\n */\nexport function instanceOfKey(scope: string, key: string): string | undefined {\n\tconst [keyScope, id] = key.split(\"/\");\n\n\tif (keyScope !== scope || id === undefined) return undefined;\n\n\treturn instanceFor(id);\n}\n\n/**\n * The accounts a plugin holds records for.\n * @param scope - the owning plugin's registered name.\n * @param records - every stored record, as the credential seam lists them.\n * @returns one account name per record, in the store's own order.\n */\nexport function instancesOf(\n\tscope: string,\n\trecords: readonly CredentialRecordEntry[],\n): string[] {\n\tconst instances: string[] = [];\n\n\tfor (const record of records) {\n\t\tconst instance = instanceOfKey(scope, record.key);\n\n\t\tif (instance !== undefined && !instances.includes(instance))\n\t\t\tinstances.push(instance);\n\t}\n\n\treturn instances;\n}\n", "/**\n * Bounds the library puts on its own work.\n *\n * These are transport and token-lifetime facts, not preferences: they are the\n * same for every provider, and changing one changes how a stalled server or an\n * expiring token behaves everywhere.\n */\n\n/** How long any single OAuth HTTP call may take. */\nexport const REQUEST_TIMEOUT_MS = 30_000;\n\n/** How many consecutive loopback ports to try for the redirect. */\nexport const PORT_ATTEMPTS = 20;\n\n/** How long to wait for a human to finish signing in. */\nexport const SIGN_IN_TIMEOUT_MS = 5 * 60_000;\n\n/** How long before expiry a token is refreshed. */\nexport const REFRESH_MARGIN_MS = 60_000;\n", "/**\n * Logging for the host half. The harness context carries a logger; a bare\n * context (tests, dry runs) falls back to the console so the same code runs\n * outside the harness.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\n\nexport interface Logger {\n\tinfo: (message: string, ...args: unknown[]) => void;\n\twarn: (message: string, ...args: unknown[]) => void;\n}\n\ninterface MaybeLogger {\n\tinfo?: (message: string, ...args: unknown[]) => void;\n\twarn?: (message: string, ...args: unknown[]) => void;\n}\n\n/** The context's logger when it has one, the console otherwise. */\nexport function loggerOf(ctx: Context): Logger {\n\tconst logger = (ctx as { logger?: MaybeLogger }).logger;\n\n\tif (typeof logger?.info === \"function\" && typeof logger.warn === \"function\") {\n\t\treturn {\n\t\t\tinfo: (message, ...args) => logger.info?.(message, ...args),\n\t\t\twarn: (message, ...args) => logger.warn?.(message, ...args),\n\t\t};\n\t}\n\n\treturn {\n\t\tinfo: (message, ...args) => console.info(message, ...args),\n\t\twarn: (message, ...args) => console.warn(message, ...args),\n\t};\n}\n\n/** A message for a caught value, whatever it is. */\nexport function errorText(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n", "/**\n * Thin HTTP helpers for the OAuth endpoints.\n *\n * Every call carries a deadline. OAuth discovery, registration, and token\n * exchange happen while the user waits, and an endpoint that accepts a\n * connection and then stalls must not wedge sign-in forever.\n */\n\nimport { REQUEST_TIMEOUT_MS } from \"./config\";\nimport { errorText } from \"./log\";\n\nclass Network {\n\t/** A JSON GET, or undefined when the document is absent or unreadable. */\n\tgetJSON = async (\n\t\turl: string,\n\t): Promise<Record<string, unknown> | undefined> => {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { accept: \"application/json\" },\n\t\t\t\tsignal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n\t\t\t});\n\n\t\t\tif (!response.ok) return undefined;\n\n\t\t\tconst body: unknown = await response.json();\n\n\t\t\treturn body !== null && typeof body === \"object\"\n\t\t\t\t? (body as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t};\n\n\t/** A JSON POST that must succeed; the response body is returned as JSON. */\n\tpostJSON = async (\n\t\turl: string,\n\t\tbody: unknown,\n\t\twhat: string,\n\t): Promise<Record<string, unknown>> => {\n\t\tlet response: Response;\n\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow new Error(`${what} failed: ${errorText(error)}`);\n\t\t}\n\n\t\tconst text = await response.text();\n\n\t\tif (!response.ok)\n\t\t\tthrow new Error(`${what} failed (${response.status}): ${text}`);\n\n\t\tconst parsed: unknown = JSON.parse(text);\n\n\t\tif (parsed === null || typeof parsed !== \"object\")\n\t\t\tthrow new Error(`${what} returned a non-object`);\n\n\t\treturn parsed as Record<string, unknown>;\n\t};\n\n\t/** A form POST to a token endpoint, with the same deadline discipline. */\n\tpostForm = async (\n\t\turl: string,\n\t\tfields: Record<string, string>,\n\t\twhat: string,\n\t\tauthorization?: string,\n\t): Promise<Record<string, unknown>> => {\n\t\tlet response: Response;\n\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t\t...(authorization === undefined ? {} : { authorization }),\n\t\t\t\t},\n\t\t\t\tbody: new URLSearchParams(fields),\n\t\t\t\tsignal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow new Error(`${what} failed: ${errorText(error)}`);\n\t\t}\n\n\t\tconst text = await response.text();\n\n\t\tif (!response.ok)\n\t\t\tthrow new Error(`${what} failed (${response.status}): ${text}`);\n\n\t\tconst parsed: unknown = JSON.parse(text);\n\n\t\tif (parsed === null || typeof parsed !== \"object\")\n\t\t\tthrow new Error(`${what} returned a non-object`);\n\n\t\treturn parsed as Record<string, unknown>;\n\t};\n}\n\nexport const network = new Network();\n", "/**\n * Everything the OAuth flow needs to talk to one authorization server: the\n * resolved endpoints, dynamic client registration, and the token endpoint.\n *\n * One vendor may run several generations of its MCP server, each a separate\n * OAuth resource with its own authorization server and scope vocabulary.\n * Nothing here is hard-coded to any of them: the endpoints come from discovery,\n * and the requested scopes are intersected with what the server advertises.\n */\n\nimport type { Grant } from \"./grant\";\nimport { network } from \"./net\";\n\n/** How a client authenticates at the token endpoint. */\nexport type TokenAuthMethod =\n\t| \"none\"\n\t| \"client_secret_post\"\n\t| \"client_secret_basic\";\n\n/** What dynamic client registration issued. */\nexport interface RegisteredClient {\n\treadonly clientId: string;\n\treadonly clientSecret?: string;\n\treadonly tokenAuthMethod: TokenAuthMethod;\n}\n\n/**\n * The client authentication to register with.\n *\n * A public client is preferred whenever the server allows one: PKCE already\n * binds the code to this installation, so nothing has to be stored. A server\n * that offers no `none` gets a secret instead. What it actually issued is what\n * counts afterwards, because a registration endpoint may ignore the request and\n * hand back a public client anyway.\n * @param advertised - the server's `token_endpoint_auth_methods_supported`.\n */\nexport function chooseTokenAuthMethod(\n\tadvertised: readonly string[] | undefined,\n): TokenAuthMethod {\n\tif (advertised === undefined || advertised.includes(\"none\")) return \"none\";\n\n\tif (advertised.includes(\"client_secret_post\")) return \"client_secret_post\";\n\n\tif (advertised.includes(\"client_secret_basic\")) return \"client_secret_basic\";\n\n\treturn \"none\";\n}\n\n/**\n * The client authentication a stored client uses.\n *\n * A record may carry no method \u2014 only whether it holds a secret \u2014 and then the\n * secret decides: a client with one posts it, a client without one is public.\n */\nexport function authMethodOf(\n\tclient: Pick<Grant, \"clientSecret\" | \"tokenAuthMethod\">,\n): TokenAuthMethod {\n\tif (client.tokenAuthMethod !== undefined) return client.tokenAuthMethod;\n\n\treturn client.clientSecret === undefined ? \"none\" : \"client_secret_post\";\n}\n\n/** A discovered authorization server plus the scope set chosen for it. */\nexport class OAuthServer {\n\t/** Issuer identifier, used to detect a generation switch. */\n\treadonly issuer: string;\n\t/** MCP resource URL the tokens are scoped to (RFC 8707 `resource`). */\n\treadonly resource: string;\n\treadonly authorizationEndpoint: string;\n\treadonly tokenEndpoint: string;\n\treadonly registrationEndpoint?: string;\n\treadonly revocationEndpoint?: string;\n\treadonly scopes: readonly string[];\n\t/** The space-delimited `scope` parameter. */\n\treadonly scopeString: string;\n\t/** Client authentication to ask for at registration. */\n\treadonly tokenAuthMethod: TokenAuthMethod;\n\n\tconstructor(\n\t\tparams: Pick<\n\t\t\tOAuthServer,\n\t\t\t| \"issuer\"\n\t\t\t| \"resource\"\n\t\t\t| \"authorizationEndpoint\"\n\t\t\t| \"tokenEndpoint\"\n\t\t\t| \"registrationEndpoint\"\n\t\t\t| \"revocationEndpoint\"\n\t\t\t| \"scopes\"\n\t\t\t| \"scopeString\"\n\t\t\t| \"tokenAuthMethod\"\n\t\t>,\n\t) {\n\t\tthis.issuer = params.issuer;\n\t\tthis.resource = params.resource;\n\t\tthis.authorizationEndpoint = params.authorizationEndpoint;\n\t\tthis.tokenEndpoint = params.tokenEndpoint;\n\t\tthis.registrationEndpoint = params.registrationEndpoint;\n\t\tthis.revocationEndpoint = params.revocationEndpoint;\n\t\tthis.scopes = params.scopes;\n\t\tthis.scopeString = params.scopeString;\n\t\tthis.tokenAuthMethod = params.tokenAuthMethod;\n\t}\n\n\t/** Register this installation as a client (RFC 7591). */\n\tregisterClient = async (\n\t\tredirectUri: string,\n\t\tclientName: string,\n\t): Promise<RegisteredClient> => {\n\t\tif (this.registrationEndpoint === undefined)\n\t\t\tthrow new Error(`${this.issuer} offers no dynamic client registration`);\n\n\t\tconst body = await network.postJSON(\n\t\t\tthis.registrationEndpoint,\n\t\t\t{\n\t\t\t\tclient_name: clientName,\n\t\t\t\tredirect_uris: [redirectUri],\n\t\t\t\tgrant_types: [\"authorization_code\", \"refresh_token\"],\n\t\t\t\tresponse_types: [\"code\"],\n\t\t\t\ttoken_endpoint_auth_method: this.tokenAuthMethod,\n\t\t\t\tscope: this.scopeString,\n\t\t\t},\n\t\t\t\"dynamic client registration\",\n\t\t);\n\n\t\tconst clientId = body.client_id;\n\n\t\tif (typeof clientId !== \"string\" || clientId === \"\")\n\t\t\tthrow new Error(\"client registration returned no client_id\");\n\n\t\tconst clientSecret =\n\t\t\ttypeof body.client_secret === \"string\" ? body.client_secret : undefined;\n\t\tconst tokenAuthMethod =\n\t\t\treadTokenAuthMethod(body.token_endpoint_auth_method) ??\n\t\t\t(clientSecret === undefined ? \"none\" : this.tokenAuthMethod);\n\n\t\treturn clientSecret === undefined\n\t\t\t? { clientId, tokenAuthMethod }\n\t\t\t: { clientId, clientSecret, tokenAuthMethod };\n\t};\n\n\t/** Exchange an authorization code, or a refresh token, for tokens. */\n\ttokenRequest = async (\n\t\tclient: Pick<Grant, \"clientId\" | \"clientSecret\" | \"tokenAuthMethod\">,\n\t\tbody: Record<string, string>,\n\t): Promise<Record<string, unknown>> => {\n\t\tconst { clientId, clientSecret } = client;\n\t\tconst method = authMethodOf(client);\n\t\tconst fields: Record<string, string> = {\n\t\t\t...body,\n\t\t\tresource: this.resource,\n\t\t};\n\n\t\tif (method === \"client_secret_basic\") {\n\t\t\tif (clientId === undefined || clientSecret === undefined)\n\t\t\t\tthrow new Error(\"client_secret_basic needs a client id and secret\");\n\n\t\t\treturn await network.postForm(\n\t\t\t\tthis.tokenEndpoint,\n\t\t\t\tfields,\n\t\t\t\t\"token request\",\n\t\t\t\tbasicAuth(clientId, clientSecret),\n\t\t\t);\n\t\t}\n\n\t\tif (clientId !== undefined) fields.client_id = clientId;\n\n\t\tif (method === \"client_secret_post\" && clientSecret !== undefined)\n\t\t\tfields.client_secret = clientSecret;\n\n\t\treturn await network.postForm(this.tokenEndpoint, fields, \"token request\");\n\t};\n\n\t/**\n\t * Map a token-endpoint response onto grant fields, keeping what the response\n\t * omits. Servers that rotate refresh tokens send a new one, and a response\n\t * that carries one replaces the stored value; one that does not leaves it\n\t * alone.\n\t */\n\ttoTokenFields = (\n\t\tpayload: Record<string, unknown>,\n\t\tprevious: Pick<\n\t\t\tGrant,\n\t\t\t\"accessToken\" | \"refreshToken\" | \"tokenType\" | \"scope\"\n\t\t>,\n\t): Pick<\n\t\tGrant,\n\t\t\"accessToken\" | \"refreshToken\" | \"tokenType\" | \"scope\" | \"expiresAt\"\n\t> => {\n\t\tconst expiresIn = Number(payload.expires_in);\n\n\t\treturn {\n\t\t\taccessToken:\n\t\t\t\ttypeof payload.access_token === \"string\"\n\t\t\t\t\t? payload.access_token\n\t\t\t\t\t: previous.accessToken,\n\t\t\trefreshToken:\n\t\t\t\ttypeof payload.refresh_token === \"string\"\n\t\t\t\t\t? payload.refresh_token\n\t\t\t\t\t: previous.refreshToken,\n\t\t\ttokenType:\n\t\t\t\ttypeof payload.token_type === \"string\"\n\t\t\t\t\t? payload.token_type\n\t\t\t\t\t: previous.tokenType,\n\t\t\tscope: typeof payload.scope === \"string\" ? payload.scope : previous.scope,\n\t\t\texpiresAt:\n\t\t\t\tDate.now() +\n\t\t\t\t(Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : 3600) * 1000,\n\t\t};\n\t};\n}\n\n/** The `Authorization` header a confidential client authenticates with. */\nfunction basicAuth(clientId: string, clientSecret: string): string {\n\tconst credentials = `${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`;\n\n\treturn `Basic ${Buffer.from(credentials).toString(\"base64\")}`;\n}\n\n/** One of the three methods the library understands, or undefined. */\nfunction readTokenAuthMethod(value: unknown): TokenAuthMethod | undefined {\n\treturn value === \"none\" ||\n\t\tvalue === \"client_secret_post\" ||\n\t\tvalue === \"client_secret_basic\"\n\t\t? value\n\t\t: undefined;\n}\n", "/**\n * OAuth discovery for one MCP resource.\n *\n * Servers differ in exactly the ways this walks around: some publish RFC 9728\n * protected-resource metadata naming a separate authorization server, some are\n * their own issuer, and the metadata itself may sit at the origin root or under\n * the issuer's path. So both document families are tried most-specific-first\n * and the first usable answer wins.\n *\n * The requested scopes are the provider's, narrowed to what the server\n * advertises: one profile serves a server that renamed its whole scope\n * vocabulary, which is what Atlassian's second generation did.\n */\n\nimport { network } from \"./net\";\nimport type { OAuthProviderProfile } from \"./provider\";\nimport { chooseTokenAuthMethod, OAuthServer } from \"./server\";\n\n/**\n * Resolve the authorization server for one MCP resource.\n * @param profile - the provider: its resource URL and wanted scopes.\n * @returns the discovered server, its endpoints, and the chosen scopes.\n * @throws when no authorization-server metadata can be read at all.\n */\nexport async function discover(\n\tprofile: OAuthProviderProfile,\n): Promise<OAuthServer> {\n\tconst endpoint = profile.endpoint;\n\tlet protectedResource: Record<string, unknown> | undefined;\n\tlet issuer = new URL(endpoint).origin;\n\n\tfor (const candidate of protectedResourceCandidates(endpoint)) {\n\t\tconst document = await network.getJSON(candidate);\n\t\tconst servers = stringList(document?.authorization_servers);\n\n\t\tif (document !== undefined && servers !== undefined) {\n\t\t\tprotectedResource = document;\n\t\t\tissuer = servers[0] ?? issuer;\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tlet metadata: Record<string, unknown> | undefined;\n\tfor (const candidate of authorizationServerCandidates(issuer)) {\n\t\tconst document = await network.getJSON(candidate);\n\n\t\tif (firstString(document?.token_endpoint) !== undefined) {\n\t\t\tmetadata = document;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (metadata === undefined)\n\t\tthrow new Error(`no OAuth metadata for ${endpoint} (issuer ${issuer})`);\n\n\tconst authorizationEndpoint = firstString(metadata.authorization_endpoint);\n\tconst tokenEndpoint = firstString(metadata.token_endpoint);\n\n\tif (authorizationEndpoint === undefined || tokenEndpoint === undefined)\n\t\tthrow new Error(`incomplete OAuth metadata for ${issuer}`);\n\n\t// The resource's own document is the more specific scope list when present.\n\tconst advertised =\n\t\tstringList(protectedResource?.scopes_supported) ??\n\t\tstringList(metadata.scopes_supported);\n\tconst scopes = chooseScopes(profile, advertised);\n\tconst registrationEndpoint = firstString(metadata.registration_endpoint);\n\tconst revocationEndpoint = firstString(metadata.revocation_endpoint);\n\n\treturn new OAuthServer({\n\t\tissuer: firstString(metadata.issuer) ?? issuer,\n\t\tresource: endpoint,\n\t\tauthorizationEndpoint,\n\t\ttokenEndpoint,\n\t\tscopes,\n\t\tscopeString: scopes.join(\" \"),\n\t\ttokenAuthMethod: chooseTokenAuthMethod(\n\t\t\tstringList(metadata.token_endpoint_auth_methods_supported),\n\t\t),\n\t\t...(registrationEndpoint === undefined ? {} : { registrationEndpoint }),\n\t\t...(revocationEndpoint === undefined ? {} : { revocationEndpoint }),\n\t});\n}\n\n/** Well-known protected-resource locations for a resource, most specific first. */\nfunction protectedResourceCandidates(endpoint: string): string[] {\n\tconst url = new URL(endpoint);\n\tconst path = url.pathname.replace(/\\/+$/u, \"\");\n\n\treturn [\n\t\t`${url.origin}/.well-known/oauth-protected-resource${path}`,\n\t\t`${url.origin}/.well-known/oauth-protected-resource`,\n\t];\n}\n\n/**\n * Well-known authorization-server locations for an issuer.\n *\n * RFC 8414 inserts the well-known segment before the issuer's path; some\n * servers instead append it, and OpenID Connect appends `/openid-configuration`,\n * so all of them are tried.\n */\nfunction authorizationServerCandidates(issuer: string): string[] {\n\tconst url = new URL(issuer);\n\tconst path = url.pathname.replace(/\\/+$/u, \"\");\n\tconst bare = issuer.replace(/\\/+$/u, \"\");\n\n\treturn [\n\t\t`${url.origin}/.well-known/oauth-authorization-server${path}`,\n\t\t`${bare}/.well-known/oauth-authorization-server`,\n\t\t`${url.origin}/.well-known/openid-configuration${path}`,\n\t\t`${bare}/.well-known/openid-configuration`,\n\t];\n}\n\n/**\n * Choose the scope set to request: what the provider wants, narrowed to what\n * the server advertises. A server that advertises nothing gets the provider's\n * fallback set, and `offline_access` is preserved whenever it is offered,\n * because that is what buys a refresh token.\n */\nfunction chooseScopes(\n\tprofile: OAuthProviderProfile,\n\tadvertised: readonly string[] | undefined,\n): string[] {\n\tconst wanted = profile.oauth.scopes;\n\n\tif (advertised === undefined || advertised.length === 0)\n\t\treturn [...profile.oauth.fallbackScopes];\n\n\tconst chosen = wanted.filter((scope) => advertised.includes(scope));\n\n\tif (chosen.length > 0) return chosen;\n\n\treturn advertised.includes(\"offline_access\")\n\t\t? [\"offline_access\"]\n\t\t: [...profile.oauth.fallbackScopes];\n}\n\nfunction stringList(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\n\tconst items = value.filter(\n\t\t(item): item is string => typeof item === \"string\",\n\t);\n\n\treturn items.length > 0 ? items : undefined;\n}\n\nfunction firstString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n", "/**\n * What the library needs to know about one MCP server it talks to.\n *\n * Every provider-specific fact enters through here: the library itself never\n * names a vendor, a scope vocabulary, or a credential record. A plugin builds\n * one profile per account and hands it to the pieces below.\n */\n\nimport { credentialKeyFor, DEFAULT_INSTANCE } from \"./account\";\n\n/**\n * The OAuth facts of a profile, absent for a server that needs no credentials.\n *\n * They are grouped rather than spread over the profile because they are the\n * half a local MCP server has nothing to say about.\n */\nexport interface OAuthProfile {\n\t/** First loopback port to try for the redirect. */\n\treadonly redirectPort: number;\n\t/** Scopes to ask for, most wanted first. */\n\treadonly scopes: readonly string[];\n\t/** What to ask for when the server advertises no scopes at all. */\n\treadonly fallbackScopes: readonly string[];\n\t/** Name shown on the consent screen. */\n\treadonly clientName: string;\n}\n\n/** One provider account: its identity, its server, and how it authenticates. */\nexport interface ProviderProfile {\n\t/** The owning plugin's registered name; owns the record and names log lines. */\n\treadonly scope: string;\n\t/** User-facing provider name (\"Atlassian\", \"GitLab\"). */\n\treadonly label: string;\n\t/** Account within the provider; the default one unless the plugin runs several. */\n\treadonly instance: string;\n\t/** MCP resource URL; also the RFC 8707 `resource` parameter. */\n\treadonly endpoint: string;\n\t/** Published tools live under `mcp__<namespace>__<tool>`. */\n\treadonly toolNamespace: string;\n\t/** Log the raw MCP payload of every tool result. */\n\treadonly debug: boolean;\n\t/** Absent for a server reached with no credentials at all, such as a local MCP. */\n\treadonly oauth?: OAuthProfile;\n}\n\n/** A profile whose server is reached through OAuth. */\nexport type OAuthProviderProfile = ProviderProfile & {\n\treadonly oauth: OAuthProfile;\n};\n\n/** A profile whose server is reached with no credentials at all. */\nexport type NoAuthProviderProfile = ProviderProfile & {\n\treadonly oauth?: undefined;\n};\n\n/** Whether a profile's server is reached through OAuth. */\nexport function needsOAuth(\n\tprofile: ProviderProfile,\n): profile is OAuthProviderProfile {\n\treturn profile.oauth !== undefined;\n}\n\n/** The credential record one account's tokens live in. */\nexport function credentialKeyOf(profile: OAuthProviderProfile): string {\n\treturn credentialKeyFor(profile.scope, profile.instance);\n}\n\n/** How one account names itself in log lines: `dsh-gitlab` or `dsh-gitlab/work`. */\nexport function accountLabel(profile: ProviderProfile): string {\n\treturn profile.instance === DEFAULT_INSTANCE\n\t\t? profile.scope\n\t\t: `${profile.scope}/${profile.instance}`;\n}\n", "/**\n * The harness's public tool names.\n *\n * A published MCP tool is renamed into the harness's namespace, which is capped\n * at 64 characters. Both halves of a plugin key their UI by the result \u2014 the\n * host publishes under it and the browser half claims it \u2014 so the rule lives in\n * exactly one place, bundled into each half rather than shared at runtime, and\n * the two can never disagree.\n */\n\n/** The harness caps a tool name at this many characters. */\nconst MAX_TOOL_NAME = 64;\n\n/** The prefix every published tool of one namespace carries. */\nexport function toolPrefix(namespace: string): string {\n\treturn `mcp__${namespace}__`;\n}\n\n/**\n * The public harness tool name for one server tool.\n *\n * The server's own spelling is kept whenever it fits, because that is what\n * makes a plugin's keyed tool views line up; a name that does not fit gets a\n * digest appended, so two long names can never collapse into one.\n * @param serverTool - the tool name as the server spells it.\n * @param namespace - the plugin's namespace (\"atlassian\").\n */\nexport function publicToolName(serverTool: string, namespace: string): string {\n\tconst cleaned = serverTool.replace(/[^A-Za-z0-9_-]/gu, \"_\") || \"tool\";\n\tconst full = `${toolPrefix(namespace)}${cleaned}`;\n\n\tif (full.length <= MAX_TOOL_NAME) return full;\n\n\tconst digest = digestOf(serverTool);\n\n\treturn `${full.slice(0, MAX_TOOL_NAME - digest.length - 1)}_${digest}`;\n}\n\n/**\n * A deterministic 12-hex-character digest of a string.\n *\n * Disambiguating two long tool names needs no cryptography, only a hash that\n * runs identically in Node and in the browser.\n */\nfunction digestOf(input: string): string {\n\tlet hash = 0xcbf29ce484222325n;\n\tconst prime = 0x100000001b3n;\n\tconst mask = 0xffffffffffffffffn;\n\n\tfor (let index = 0; index < input.length; index += 1) {\n\t\thash = ((hash ^ BigInt(input.charCodeAt(index))) * prime) & mask;\n\t}\n\n\treturn hash.toString(16).padStart(16, \"0\").slice(0, 12);\n}\n", "/**\n * The bridge to one remote MCP server.\n *\n * The stock MCP client takes its HTTP headers from static configuration, and an\n * OAuth access token changes over the life of the process. So the transport is\n * built here with an `authProvider` that is asked for the token before every\n * request \u2014 a refresh therefore needs no reconnect \u2014 and each discovered tool is\n * published through the harness's own MCP adapter, so results keep the canonical\n * shape.\n *\n * A profile without OAuth facts is a server that needs no credentials at all,\n * such as a local MCP server; the same transport is built without an\n * `authProvider`, and the bridge holds no record.\n *\n * The bridge owns its connection: the client, the published tools, and the\n * authorization server it discovered. A caller that wants provider-specific\n * work done at connect time passes {@link McpBridgeOptions.onConnected}.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type { ToolDefinition } from \"@deepseek-ai/dsh-tools\";\nimport type {\n\tCallToolResult,\n\tClient,\n\tTool,\n} from \"@modelcontextprotocol/client\";\nimport { discover } from \"./discovery\";\nimport type { GrantStore } from \"./grant\";\nimport { errorText, loggerOf } from \"./log\";\nimport {\n\taccountLabel,\n\tneedsOAuth,\n\ttype OAuthProviderProfile,\n\ttype ProviderProfile,\n} from \"./provider\";\nimport type { OAuthServer } from \"./server\";\nimport { publicToolName } from \"./tool-name\";\n\n/** How long one MCP tool call may take. */\nconst TOOL_CALL_TIMEOUT_MS = 60_000;\n\nexport interface McpBridgeOptions {\n\t/** The provider account whose server this bridge talks to. */\n\treadonly profile: ProviderProfile;\n\t/**\n\t * The record the access token is read and refreshed through. Required for a\n\t * profile with OAuth facts, refused without one at construction.\n\t */\n\treadonly store?: GrantStore;\n\t/** How the server sees this client; some gate capabilities on the name. */\n\treadonly clientInfo: { readonly name: string; readonly version: string };\n\t/** Called once per discovered tool, before it is registered. */\n\treadonly decorate?: (\n\t\tdefinition: ToolDefinition,\n\t\ttool: Tool,\n\t) => ToolDefinition;\n\t/** Called after a successful connect, for provider-specific learning. */\n\treadonly onConnected?: () => Promise<void>;\n}\n\nexport class McpBridge {\n\tprivate client: Client | undefined;\n\tprivate oauth: OAuthServer | undefined;\n\tprivate disposers = new Map<string, () => void>();\n\tprivate names: string[] = [];\n\tprivate ready = false;\n\tprivate failure: string | undefined;\n\n\tconstructor(private readonly options: McpBridgeOptions) {\n\t\tif (needsOAuth(options.profile) && options.store === undefined)\n\t\t\tthrow new TypeError(\n\t\t\t\t`the ${accountLabel(options.profile)} bridge needs a grant store for its OAuth profile`,\n\t\t\t);\n\t}\n\n\t/** Public names of the published tools, in discovery order. */\n\tget toolNames(): readonly string[] {\n\t\treturn this.names;\n\t}\n\n\t/** Whether the connection is currently usable. */\n\tget isConnected(): boolean {\n\t\treturn this.ready;\n\t}\n\n\t/**\n\t * Why the last connection attempt failed, when one did.\n\t *\n\t * A server that refuses the connection \u2014 a resource that needs an\n\t * administrator's switch, a scope the token lacks \u2014 says so here, so a\n\t * settings card can show the reason instead of an unexplained \"not connected\".\n\t */\n\tget lastFailure(): string | undefined {\n\t\treturn this.failure;\n\t}\n\n\t/**\n\t * Connect and publish tools, once the account is signed in if it needs to be.\n\t * @param ctx - host context, for logging.\n\t * @returns whether the bridge is usable; failures are logged, never thrown.\n\t */\n\tasync ensure(ctx: Context): Promise<boolean> {\n\t\tconst { profile, store } = this.options;\n\n\t\tif (needsOAuth(profile)) {\n\t\t\tconst grant = await store?.read();\n\n\t\t\tif (grant === undefined || !grant.isSignedIn()) return false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst oauth = needsOAuth(profile)\n\t\t\t\t? await this.authorizationServer(profile)\n\t\t\t\t: undefined;\n\n\t\t\tif (this.client === undefined) await this.connect(profile, store, oauth);\n\n\t\t\tawait this.publishTools(ctx);\n\t\t\tawait this.options.onConnected?.();\n\t\t\tthis.failure = undefined;\n\n\t\t\tloggerOf(ctx).info(\n\t\t\t\t\"[%s] %d tools available from %s\",\n\t\t\t\taccountLabel(profile),\n\t\t\t\tthis.names.length,\n\t\t\t\tprofile.endpoint,\n\t\t\t);\n\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tthis.ready = false;\n\n\t\t\tawait this.drop();\n\t\t\tthis.failure = errorText(error);\n\t\t\tloggerOf(ctx).warn(\n\t\t\t\t\"[%s] MCP bridge unavailable: %s\",\n\t\t\t\taccountLabel(profile),\n\t\t\t\tthis.failure,\n\t\t\t);\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Close the connection and withdraw every published tool. */\n\tasync drop(): Promise<void> {\n\t\tfor (const dispose of this.disposers.values()) dispose();\n\n\t\tthis.disposers = new Map();\n\t\tthis.names = [];\n\t\tthis.ready = false;\n\t\tthis.failure = undefined;\n\n\t\tconst client = this.client;\n\t\tthis.client = undefined;\n\n\t\tif (client !== undefined) await client.close().catch(() => undefined);\n\t}\n\n\t/**\n\t * Call one tool of the connected server by its own name.\n\t * @param name - the server's tool name, without any published prefix.\n\t * @param args - tool arguments.\n\t * @throws when the connection is not open.\n\t */\n\tasync callTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<CallToolResult> {\n\t\tconst client = this.client;\n\n\t\tif (client === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t`the ${this.options.profile.label} MCP connection is not open`,\n\t\t\t);\n\n\t\treturn await client.callTool(\n\t\t\t{ name, arguments: args },\n\t\t\t{ timeout: TOOL_CALL_TIMEOUT_MS },\n\t\t);\n\t}\n\n\t/** The discovered authorization server, remembered across reconnects. */\n\tprivate async authorizationServer(\n\t\tprofile: OAuthProviderProfile,\n\t): Promise<OAuthServer> {\n\t\tconst oauth = this.oauth ?? (await discover(profile));\n\t\tthis.oauth = oauth;\n\n\t\treturn oauth;\n\t}\n\n\t/** Open the MCP connection, authenticated when the profile has OAuth facts. */\n\tprivate async connect(\n\t\tprofile: ProviderProfile,\n\t\tstore: GrantStore | undefined,\n\t\toauth: OAuthServer | undefined,\n\t): Promise<void> {\n\t\tconst { Client: McpClient, StreamableHTTPClientTransport } = await import(\n\t\t\t\"@modelcontextprotocol/client\"\n\t\t);\n\n\t\tconst transport = new StreamableHTTPClientTransport(\n\t\t\tnew URL(profile.endpoint),\n\t\t\toauth === undefined || store === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tauthProvider: {\n\t\t\t\t\t\t\t// Asked for before every request, so a refreshed token needs no reconnect.\n\t\t\t\t\t\t\ttoken: async () => await store.accessToken(oauth),\n\t\t\t\t\t\t\tonUnauthorized: async () => {\n\t\t\t\t\t\t\t\tawait store.refresh(oauth);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t);\n\n\t\tconst client = new McpClient(this.options.clientInfo, { capabilities: {} });\n\t\tawait client.connect(transport);\n\n\t\tthis.client = client;\n\t\tthis.ready = true;\n\t}\n\n\t/** Publish every server tool, replacing the previous generation. */\n\tprivate async publishTools(ctx: Context): Promise<string[]> {\n\t\tconst client = this.client;\n\n\t\tif (client === undefined) throw new Error(\"the MCP connection is not open\");\n\n\t\tconst { createMcpToolDefinition } = await import(\n\t\t\t\"@deepseek-ai/dsh-mcp-client\"\n\t\t);\n\n\t\tconst listed = await client.listTools(undefined, { cacheMode: \"refresh\" });\n\t\tconst disposers = new Map<string, () => void>();\n\t\tconst names: string[] = [];\n\n\t\tfor (const tool of listed.tools) {\n\t\t\tconst name = publicToolName(\n\t\t\t\ttool.name,\n\t\t\t\tthis.options.profile.toolNamespace,\n\t\t\t);\n\t\t\tconst base = createMcpToolDefinition(ctx, {\n\t\t\t\tname,\n\t\t\t\trawName: tool.name,\n\t\t\t\tdescription: tool.description ?? `MCP tool ${tool.name}`,\n\t\t\t\tinputSchema: (tool.inputSchema ?? {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tproperties: {},\n\t\t\t\t}) as Record<string, unknown>,\n\t\t\t\t...(tool.outputSchema === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { outputSchema: tool.outputSchema }),\n\t\t\t\t...(tool.execution?.taskSupport === \"required\"\n\t\t\t\t\t? { taskRequired: true }\n\t\t\t\t\t: {}),\n\t\t\t\tcall: async (args, execution) =>\n\t\t\t\t\tawait client.callTool(\n\t\t\t\t\t\t{ name: tool.name, arguments: args },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsignal: execution.signal,\n\t\t\t\t\t\t\ttoolDefinition: tool as Tool,\n\t\t\t\t\t\t\ttimeout: TOOL_CALL_TIMEOUT_MS,\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t});\n\n\t\t\tdisposers.set(\n\t\t\t\tname,\n\t\t\t\tctx.tools.register(this.options.decorate?.(base, tool as Tool) ?? base),\n\t\t\t);\n\t\t\tnames.push(name);\n\t\t}\n\n\t\tfor (const dispose of this.disposers.values()) dispose();\n\n\t\tthis.disposers = disposers;\n\t\tthis.names = names;\n\n\t\treturn names;\n\t}\n}\n", "/**\n * Opening the user's browser. Sign-in is impossible without a human in a\n * browser, so this is best-effort: a failure costs one copy-paste from the\n * notice the flow already published.\n */\n\nimport { execFile } from \"node:child_process\";\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport { errorText, loggerOf } from \"./log\";\n\n/** Launch the platform's default browser on a URL. */\nexport function openBrowser(ctx: Context, url: string): void {\n\tconst command =\n\t\tprocess.platform === \"darwin\"\n\t\t\t? \"open\"\n\t\t\t: process.platform === \"win32\"\n\t\t\t\t? \"cmd\"\n\t\t\t\t: \"xdg-open\";\n\n\tconst args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n\n\texecFile(command, args, (error) => {\n\t\tif (error !== null)\n\t\t\tloggerOf(ctx).warn(\n\t\t\t\t\"[mcp-oauth] could not open a browser: %s\",\n\t\t\t\terrorText(error),\n\t\t\t);\n\t});\n}\n", "/**\n * The authorization-code flow with PKCE, driven on the harness seam.\n *\n * The flow owns the whole conversation: it discovers the authorization server,\n * registers this installation as a client for the redirect URI it will actually\n * use, publishes the authorize URL, waits for the loopback callback, exchanges\n * the code, and commits the grant. Committing before `run` resolves is what the\n * seam requires.\n */\n\nimport { createHash, randomBytes } from \"node:crypto\";\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type { AuthorizationSession } from \"@deepseek-ai/dsh-authorization\";\nimport { SIGN_IN_TIMEOUT_MS } from \"./config\";\nimport { discover } from \"./discovery\";\nimport { Grant, type GrantStore } from \"./grant\";\nimport { jsonSafe } from \"./json\";\nimport { LoopbackListener } from \"./loopback\";\nimport type { OAuthProviderProfile } from \"./provider\";\nimport { authMethodOf, type RegisteredClient } from \"./server\";\n\nexport class AuthorizationFlow {\n\t/**\n\t * @param profile - the provider account to sign in to.\n\t * @param store - the record the finished grant is committed through.\n\t */\n\tconstructor(\n\t\tprivate readonly profile: OAuthProviderProfile,\n\t\tprivate readonly store: GrantStore,\n\t) {}\n\n\t/**\n\t * Run one sign-in attempt to completion.\n\t * @param _ctx - host context; the flow itself only touches the store.\n\t * @param session - the seam's session: signals cancellation, publishes notices, commits.\n\t */\n\tasync run(_ctx: Context, session: AuthorizationSession): Promise<void> {\n\t\tconst endpoint = this.profile.endpoint;\n\t\tconst oauth = await discover(this.profile);\n\n\t\tconst verifier = b64url(randomBytes(32));\n\t\tconst expectedState = b64url(randomBytes(32));\n\t\tconst challenge = b64url(createHash(\"sha256\").update(verifier).digest());\n\n\t\tconst listener = await LoopbackListener.listenForCode(\n\t\t\tthis.profile.oauth.redirectPort,\n\t\t\texpectedState,\n\t\t\tthis.profile.label,\n\t\t);\n\n\t\tconst onAbort = (): void => listener.fail(new Error(\"sign-in cancelled\"));\n\t\tsession.signal.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\n\t\ttry {\n\t\t\tconst existing = await this.store.read();\n\t\t\tconst client = await this.clientFor(\n\t\t\t\toauth,\n\t\t\t\tlistener.redirectUri,\n\t\t\t\texisting,\n\t\t\t);\n\n\t\t\tconst authorize = new URL(oauth.authorizationEndpoint);\n\n\t\t\tfor (const [key, value] of Object.entries({\n\t\t\t\tresponse_type: \"code\",\n\t\t\t\tclient_id: client.clientId,\n\t\t\t\tredirect_uri: listener.redirectUri,\n\t\t\t\tscope: oauth.scopeString,\n\t\t\t\tstate: expectedState,\n\t\t\t\tcode_challenge: challenge,\n\t\t\t\tcode_challenge_method: \"S256\",\n\t\t\t\tresource: endpoint,\n\t\t\t})) {\n\t\t\t\tauthorize.searchParams.set(key, value);\n\t\t\t}\n\n\t\t\tsession.notify({\n\t\t\t\tmessage: `Approve the ${this.profile.label} sign-in in your browser.`,\n\t\t\t\turl: authorize.href,\n\t\t\t});\n\n\t\t\tconst authorizationCode = await new Promise<string>((resolve, reject) => {\n\t\t\t\ttimer = setTimeout(\n\t\t\t\t\t() => reject(new Error(\"sign-in timed out\")),\n\t\t\t\t\tSIGN_IN_TIMEOUT_MS,\n\t\t\t\t);\n\n\t\t\t\tlistener.code.then(resolve, reject);\n\t\t\t});\n\n\t\t\tsession.signal.throwIfAborted();\n\n\t\t\tconst payload = await oauth.tokenRequest(client, {\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\tcode: authorizationCode,\n\t\t\t\tredirect_uri: listener.redirectUri,\n\t\t\t\tcode_verifier: verifier,\n\t\t\t});\n\n\t\t\tconst base = {\n\t\t\t\t...(existing ?? { version: 1 as const }),\n\t\t\t\tversion: 1 as const,\n\t\t\t\tissuer: oauth.issuer,\n\t\t\t\tresource: endpoint,\n\t\t\t\tredirectUri: listener.redirectUri,\n\t\t\t\t...client,\n\t\t\t\tscope: oauth.scopeString,\n\t\t\t};\n\n\t\t\tconst payloadPair = jsonSafe({\n\t\t\t\t...base,\n\t\t\t\t...oauth.toTokenFields(payload, base),\n\t\t\t});\n\n\t\t\tif (payloadPair === undefined)\n\t\t\t\tthrow new Error(\"the grant could not be represented as JSON\");\n\n\t\t\tawait session.commit({ kind: \"grant\", payload: payloadPair });\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\n\t\t\tsession.signal.removeEventListener(\"abort\", onAbort);\n\t\t\tawait listener.close();\n\t\t}\n\t}\n\n\t/**\n\t * The client to authorize with, registering one when the stored identity\n\t * cannot be reused.\n\t *\n\t * A registration is bound to the exact redirect URIs it was issued for, and\n\t * the loopback port is not guaranteed to be free, so a stored client is\n\t * reused only when the resource and the URI both still match.\n\t */\n\tprivate async clientFor(\n\t\toauth: Awaited<ReturnType<typeof discover>>,\n\t\tredirectUri: string,\n\t\texisting: Grant | undefined,\n\t): Promise<RegisteredClient> {\n\t\tif (\n\t\t\texisting !== undefined &&\n\t\t\texisting.resource === this.profile.endpoint &&\n\t\t\texisting.redirectUri === redirectUri &&\n\t\t\ttypeof existing.clientId === \"string\"\n\t\t) {\n\t\t\tconst tokenAuthMethod = authMethodOf(existing);\n\n\t\t\treturn existing.clientSecret === undefined\n\t\t\t\t? { clientId: existing.clientId, tokenAuthMethod }\n\t\t\t\t: {\n\t\t\t\t\t\tclientId: existing.clientId,\n\t\t\t\t\t\tclientSecret: existing.clientSecret,\n\t\t\t\t\t\ttokenAuthMethod,\n\t\t\t\t\t};\n\t\t}\n\n\t\tconst registered = await oauth.registerClient(\n\t\t\tredirectUri,\n\t\t\tthis.profile.oauth.clientName,\n\t\t);\n\n\t\tawait this.store.mutate(\n\t\t\tasync (grant) =>\n\t\t\t\tnew Grant({\n\t\t\t\t\t...(grant ?? { version: 1 as const }),\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tissuer: oauth.issuer,\n\t\t\t\t\tresource: this.profile.endpoint,\n\t\t\t\t\tredirectUri,\n\t\t\t\t\t...registered,\n\t\t\t\t}),\n\t\t);\n\n\t\treturn registered;\n\t}\n}\n\n/** base64url without padding, as PKCE and `state` require. */\nfunction b64url(input: Buffer): string {\n\treturn input\n\t\t.toString(\"base64\")\n\t\t.replace(/\\+/gu, \"-\")\n\t\t.replace(/\\//gu, \"_\")\n\t\t.replace(/=+$/u, \"\");\n}\n", "/**\n * The credential store persists a record payload through JSON, and refuses a\n * value JSON cannot represent. Dynamic client registration legitimately leaves\n * fields absent (`client_secret`, `site_url`), so every payload is normalized\n * before it is written instead of leaking `undefined` into the store.\n */\n\nexport type JsonValue =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| JsonValue[]\n\t| { [key: string]: JsonValue };\n\n/**\n * Copy a value, dropping every member JSON cannot carry.\n * @param value - arbitrary record payload.\n * @returns the same shape with `undefined`, functions, and symbols removed.\n */\nexport function jsonSafe(value: unknown): JsonValue | undefined {\n\tif (Array.isArray(value)) {\n\t\tconst items: JsonValue[] = [];\n\n\t\tfor (const item of value) {\n\t\t\tconst safe = jsonSafe(item);\n\n\t\t\tif (safe !== undefined) items.push(safe);\n\t\t}\n\n\t\treturn items;\n\t}\n\n\tif (value !== null && typeof value === \"object\") {\n\t\tconst out: { [key: string]: JsonValue } = {};\n\n\t\tfor (const [key, item] of Object.entries(value)) {\n\t\t\tconst safe = jsonSafe(item);\n\n\t\t\tif (safe !== undefined) out[key] = safe;\n\t\t}\n\n\t\treturn out;\n\t}\n\n\tif (\n\t\tvalue === undefined ||\n\t\ttypeof value === \"function\" ||\n\t\ttypeof value === \"symbol\"\n\t)\n\t\treturn undefined;\n\n\tif (typeof value === \"number\" && !Number.isFinite(value)) return undefined;\n\n\treturn value as JsonValue;\n}\n", "/**\n * The durable credential record and the token lifetime logic over it.\n *\n * A record is written under the store's `<scope>/<id>` key, and its payload is\n * plain JSON: what comes back from disk is not an instance, which is why every\n * read path goes through {@link Grant.from} and callers only ever see a real\n * `Grant`.\n *\n * Tokens refresh inside `modifyRecord`, whose lock spans the network round\n * trip, so two processes can never rotate the same refresh token twice.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type { CredentialKey } from \"@deepseek-ai/dsh-credentials\";\nimport { REFRESH_MARGIN_MS } from \"./config\";\nimport { type JsonValue, jsonSafe } from \"./json\";\nimport type { OAuthServer, TokenAuthMethod } from \"./server\";\n\n/** Provider-owned facts kept beside the tokens. */\nexport type GrantExtra = Readonly<Record<string, JsonValue>>;\n\n/** A grant that definitely carries an access token. */\nexport type SignedInGrant = Grant & { accessToken: string };\n\n/** What a successful sign-in stores. */\nexport class Grant {\n\treadonly version: 1;\n\t/** Issuer the tokens came from, so a generation switch is detectable. */\n\treadonly issuer?: string;\n\t/** MCP resource the tokens are valid for (RFC 8707). */\n\treadonly resource?: string;\n\t/** Dynamically registered client identity. */\n\treadonly clientId?: string;\n\t/** Present only for a server that issues one, which a confidential client requires. */\n\treadonly clientSecret?: string;\n\t/** How this client authenticates at the token endpoint. */\n\treadonly tokenAuthMethod?: TokenAuthMethod;\n\t/** The exact redirect URI the client was registered with. */\n\treadonly redirectUri?: string;\n\t/** Application data the provider keeps with its tokens. */\n\treadonly extra?: GrantExtra;\n\treadonly accessToken?: string;\n\treadonly refreshToken?: string;\n\treadonly tokenType?: string;\n\treadonly scope?: string;\n\treadonly expiresAt?: number;\n\n\tconstructor(params: Omit<Grant, \"isSignedIn\">) {\n\t\tthis.version = params.version;\n\t\tthis.issuer = params.issuer;\n\t\tthis.resource = params.resource;\n\t\tthis.clientId = params.clientId;\n\t\tthis.clientSecret = params.clientSecret;\n\t\tthis.tokenAuthMethod = params.tokenAuthMethod;\n\t\tthis.redirectUri = params.redirectUri;\n\t\tthis.extra = params.extra;\n\t\tthis.accessToken = params.accessToken;\n\t\tthis.refreshToken = params.refreshToken;\n\t\tthis.tokenType = params.tokenType;\n\t\tthis.scope = params.scope;\n\t\tthis.expiresAt = params.expiresAt;\n\t}\n\n\t/**\n\t * Rebuild a grant from a stored payload.\n\t * @param payload - whatever the credential store returned.\n\t * @returns the grant, or undefined for a payload that is not an object.\n\t */\n\tstatic from(payload: unknown): Grant | undefined {\n\t\tif (payload === null || typeof payload !== \"object\") return undefined;\n\n\t\treturn new Grant(payload as Omit<Grant, \"isSignedIn\">);\n\t}\n\n\t/** Whether a stored grant carries a usable access token. */\n\tisSignedIn(): this is SignedInGrant {\n\t\treturn typeof this.accessToken === \"string\" && this.accessToken !== \"\";\n\t}\n}\n\n/** One provider's record in the credential store. */\nexport class GrantStore {\n\t/** The record key, as the harness authorization seam spells it. */\n\treadonly credentialKey: CredentialKey;\n\n\t/**\n\t * @param ctx - host context carrying the credential store.\n\t * @param key - the record key, in the store's `<scope>/<id>` grammar.\n\t */\n\tconstructor(\n\t\tprivate readonly ctx: Context,\n\t\tkey: string,\n\t) {\n\t\tthis.credentialKey = key as CredentialKey;\n\t}\n\n\t/** Read the stored grant, or undefined when there is none. */\n\tasync read(): Promise<Grant | undefined> {\n\t\tconst record = await this.ctx.credentials.readRecord(this.credentialKey);\n\n\t\tif (record === undefined || record.kind !== \"grant\") return undefined;\n\n\t\treturn Grant.from(record.payload);\n\t}\n\n\t/**\n\t * Read-decide-replace the grant under the store's cross-process lock.\n\t * @param mutate - receives the current grant; returning undefined leaves it alone.\n\t * @returns the stored grant after the change.\n\t */\n\tasync mutate(\n\t\tmutate: (current: Grant | undefined) => Promise<Grant | undefined>,\n\t): Promise<Grant | undefined> {\n\t\tconst record = await this.ctx.credentials.modifyRecord(\n\t\t\tthis.credentialKey,\n\t\t\tasync (current) => {\n\t\t\t\tconst previous =\n\t\t\t\t\tcurrent?.kind === \"grant\" ? Grant.from(current.payload) : undefined;\n\n\t\t\t\tconst next = await mutate(previous);\n\t\t\t\tif (next === undefined) return undefined;\n\n\t\t\t\t// The store refuses a value JSON cannot represent, and a token response\n\t\t\t\t// omits fields the previous grant carried.\n\t\t\t\tconst payload = jsonSafe(next);\n\t\t\t\tif (payload === undefined) return undefined;\n\n\t\t\t\treturn { kind: \"grant\", payload };\n\t\t\t},\n\t\t);\n\n\t\treturn record?.kind === \"grant\" ? Grant.from(record.payload) : undefined;\n\t}\n\n\t/** Merge provider facts into the record, creating it when absent. */\n\tasync remember(patch: GrantExtra): Promise<void> {\n\t\tawait this.mutate(\n\t\t\tasync (grant) =>\n\t\t\t\tnew Grant({\n\t\t\t\t\t...(grant ?? { version: 1 as const }),\n\t\t\t\t\tversion: 1,\n\t\t\t\t\textra: { ...grant?.extra, ...patch },\n\t\t\t\t}),\n\t\t);\n\t}\n\n\t/** Forget the record entirely; the issuer is not told, matching the seam. */\n\tasync clear(): Promise<void> {\n\t\tawait this.ctx.credentials.deleteRecord(this.credentialKey);\n\t}\n\n\t/**\n\t * The token an MCP transport asks for before every request, refreshed when\n\t * it is about to expire.\n\t * @param oauth - the authorization server the tokens belong to.\n\t * @returns the access token, or undefined when nobody is signed in.\n\t */\n\tasync accessToken(oauth: OAuthServer): Promise<string | undefined> {\n\t\tconst grant = await this.read();\n\n\t\tif (grant === undefined || !grant.isSignedIn()) return undefined;\n\n\t\tif (Date.now() < (grant.expiresAt ?? 0) - REFRESH_MARGIN_MS)\n\t\t\treturn grant.accessToken;\n\n\t\treturn await this.refresh(oauth);\n\t}\n\n\t/**\n\t * Exchange the refresh token for a new access token, inside the store lock.\n\t * A peer that refreshed while we waited makes this a no-op.\n\t * @param oauth - the authorization server to refresh against.\n\t * @throws when the record carries no refresh token, or nobody is signed in.\n\t */\n\tasync refresh(oauth: OAuthServer): Promise<string> {\n\t\tconst after = await this.mutate(async (grant) => {\n\t\t\tif (grant === undefined) return undefined;\n\n\t\t\tif (Date.now() < (grant.expiresAt ?? 0) - REFRESH_MARGIN_MS)\n\t\t\t\treturn undefined;\n\n\t\t\tif (typeof grant.refreshToken !== \"string\" || grant.refreshToken === \"\")\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"the stored sign-in has no refresh token; sign in again\",\n\t\t\t\t);\n\n\t\t\tconst payload = await oauth.tokenRequest(grant, {\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\trefresh_token: grant.refreshToken,\n\t\t\t});\n\n\t\t\treturn new Grant({ ...grant, ...oauth.toTokenFields(payload, grant) });\n\t\t});\n\n\t\tif (after === undefined || !after.isSignedIn())\n\t\t\tthrow new Error(\"not signed in\");\n\n\t\treturn after.accessToken;\n\t}\n}\n", "/**\n * The loopback redirect listener for the authorization-code flow.\n *\n * A desktop OAuth client cannot receive a redirect on a public URL, so it binds\n * `127.0.0.1` and hands the browser a redirect URI pointing at itself. The\n * registered client is bound to the exact URI, and the preferred port may be\n * taken, so the listener reports the port it actually got.\n */\n\nimport { timingSafeEqual } from \"node:crypto\";\nimport { createServer, type Server } from \"node:http\";\nimport { PORT_ATTEMPTS } from \"./config\";\nimport { errorText } from \"./log\";\n\nexport class LoopbackListener {\n\treadonly server: Server;\n\treadonly port: number;\n\treadonly redirectUri: string;\n\t/** Resolves with the authorization code the browser returned. */\n\treadonly code: Promise<string>;\n\n\treadonly fail: (error: Error) => void;\n\n\tprotected constructor(\n\t\tserver: Server,\n\t\tport: number,\n\t\tredirectUri: string,\n\t\tcode: Promise<string>,\n\t\tfail: (error: Error) => void,\n\t) {\n\t\tthis.server = server;\n\t\tthis.port = port;\n\t\tthis.redirectUri = redirectUri;\n\t\tthis.code = code;\n\t\tthis.fail = fail;\n\t}\n\n\t/**\n\t * Bind the first free port at or after `preferredPort` and wait for the code.\n\t * @param preferredPort - the configured port to try first.\n\t * @param expectedState - the `state` value the response must carry back.\n\t * @param label - the provider's name, for the pages the browser sees.\n\t */\n\tstatic async listenForCode(\n\t\tpreferredPort: number,\n\t\texpectedState: string,\n\t\tlabel: string,\n\t): Promise<LoopbackListener> {\n\t\tlet resolveCode: (code: string) => void = () => {};\n\t\tlet rejectCode: (error: Error) => void = () => {};\n\n\t\tconst code = new Promise<string>((resolve, reject) => {\n\t\t\tresolveCode = resolve;\n\t\t\trejectCode = reject;\n\t\t});\n\n\t\t// The caller always awaits `code`; this keeps a rejection observed even when\n\t\t// it settles before that await is attached.\n\t\tcode.catch(() => undefined);\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let offset = 0; offset < PORT_ATTEMPTS; offset += 1) {\n\t\t\tconst port = preferredPort + offset;\n\n\t\t\tconst server = createServer((request, response) => {\n\t\t\t\tconst url = new URL(request.url ?? \"/\", `http://127.0.0.1:${port}`);\n\n\t\t\t\tif (url.pathname !== \"/callback\") {\n\t\t\t\t\tresponse\n\t\t\t\t\t\t.writeHead(404, { \"content-type\": \"text/plain; charset=utf-8\" })\n\t\t\t\t\t\t.end(\"not found\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst gotState = url.searchParams.get(\"state\") ?? \"\";\n\t\t\t\tconst oauthError = url.searchParams.get(\"error\");\n\t\t\t\tconst returned = url.searchParams.get(\"code\");\n\n\t\t\t\tconst comparable =\n\t\t\t\t\tBuffer.byteLength(gotState) === Buffer.byteLength(expectedState);\n\t\t\t\tconst stateMatches =\n\t\t\t\t\tcomparable &&\n\t\t\t\t\ttimingSafeEqual(Buffer.from(gotState), Buffer.from(expectedState));\n\n\t\t\t\tif (oauthError !== null || returned === null || !stateMatches) {\n\t\t\t\t\tconst reason =\n\t\t\t\t\t\toauthError !== null\n\t\t\t\t\t\t\t? `${label} reported: ${oauthError}`\n\t\t\t\t\t\t\t: \"The response did not match this sign-in attempt.\";\n\n\t\t\t\t\tresponse\n\t\t\t\t\t\t.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" })\n\t\t\t\t\t\t.end(LoopbackListener.callbackPage(\"Authorization failed\", reason));\n\n\t\t\t\t\trejectCode(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\toauthError !== null\n\t\t\t\t\t\t\t\t? `authorization denied: ${oauthError}`\n\t\t\t\t\t\t\t\t: \"state mismatch\",\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresponse\n\t\t\t\t\t.writeHead(200, { \"content-type\": \"text/html; charset=utf-8\" })\n\t\t\t\t\t.end(\n\t\t\t\t\t\tLoopbackListener.callbackPage(\n\t\t\t\t\t\t\t`Signed in to ${label}`,\n\t\t\t\t\t\t\t\"You can close this tab and return to DeepSeek Harness.\",\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\tresolveCode(returned);\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tserver.once(\"error\", reject);\n\t\t\t\t\tserver.listen(port, \"127.0.0.1\", resolve);\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\treturn new LoopbackListener(\n\t\t\t\tserver,\n\t\t\t\tport,\n\t\t\t\t`http://127.0.0.1:${port}/callback`,\n\t\t\t\tcode,\n\t\t\t\trejectCode,\n\t\t\t);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`no free loopback port in ${preferredPort}..${preferredPort + PORT_ATTEMPTS - 1}: ${errorText(lastError)}`,\n\t\t);\n\t}\n\n\t// `close()` alone only stops accepting: it waits for existing sockets,\n\t// and a browser that used keep-alive would hold the flow open forever.\n\tclose = async () => {\n\t\tthis.server.closeAllConnections();\n\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tthis.server.close(() => resolve());\n\t\t});\n\t};\n\n\tprivate static callbackPage = (title: string, message: string): string => {\n\t\treturn (\n\t\t\t`<!doctype html><meta charset=\"utf-8\"><title>${title}</title>` +\n\t\t\t'<body style=\"font:16px/1.5 system-ui;margin:4rem auto;max-width:32rem;text-align:center\">' +\n\t\t\t`<h1 style=\"font-size:1.25rem\">${title}</h1><p>${message}</p></body>`\n\t\t);\n\t};\n}\n", "/**\n * Reading an MCP tool result.\n *\n * A server may answer with JSON inside text content blocks, with prose, or\n * with `structuredContent` \u2014 sometimes withholding the last from clients it\n * does not recognize by name. So a reader starts from the same two reads: the\n * text blocks in order, and whatever JSON those blocks carry.\n */\n\n/** The text content blocks of an MCP result, in order. */\nexport function mcpText(value: unknown): string[] {\n\tif (value === null || typeof value !== \"object\") return [];\n\n\tconst content = (value as { content?: unknown }).content;\n\n\tif (!Array.isArray(content)) return [];\n\n\tconst out: string[] = [];\n\tfor (const block of content) {\n\t\tif (block === null || typeof block !== \"object\") continue;\n\n\t\tconst { type, text } = block as { type?: unknown; text?: unknown };\n\n\t\tif (type === \"text\" && typeof text === \"string\") out.push(text);\n\t}\n\n\treturn out;\n}\n\n/**\n * The JSON payloads an MCP result carries.\n *\n * `structuredContent` comes first when the server sent it: it is the canonical\n * machine-readable answer, and the text blocks are its rendering. A block that\n * is not JSON is skipped rather than failing the read: a server may answer a\n * successful call with prose, and the caller's text fallback is then the\n * truthful answer.\n */\nexport function mcpJson(value: unknown): unknown[] {\n\tconst roots: unknown[] = [];\n\n\tif (value !== null && typeof value === \"object\") {\n\t\tconst structured = (value as { structuredContent?: unknown })\n\t\t\t.structuredContent;\n\n\t\tif (structured !== undefined) roots.push(structured);\n\t}\n\n\tfor (const text of mcpText(value)) {\n\t\tconst trimmed = text.trim();\n\n\t\tif (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) continue;\n\n\t\ttry {\n\t\t\troots.push(JSON.parse(trimmed));\n\t\t} catch {\n\t\t\t// Not JSON; nothing to walk.\n\t\t}\n\t}\n\n\treturn roots;\n}\n\n/** Whether a value is a plain object, which every payload record is. */\nexport function asRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: undefined;\n}\n\n/** The first non-empty string among a record's named fields. */\nexport function pickString(\n\tsource: unknown,\n\t...names: string[]\n): string | undefined {\n\tconst record = asRecord(source);\n\tif (record === undefined) return undefined;\n\n\tfor (const name of names) {\n\t\tconst value = record[name];\n\n\t\tif (typeof value === \"string\" && value.trim() !== \"\") return value.trim();\n\t\tif (typeof value === \"number\") return String(value);\n\t}\n\n\treturn undefined;\n}\n\n/** The first finite number among a record's named fields. */\nexport function pickNumber(\n\tsource: unknown,\n\t...names: string[]\n): number | undefined {\n\tconst record = asRecord(source);\n\tif (record === undefined) return undefined;\n\n\tfor (const name of names) {\n\t\tconst value = record[name];\n\n\t\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\n\t\tif (typeof value === \"string\" && value.trim() !== \"\") {\n\t\t\tconst parsed = Number(value);\n\n\t\t\tif (Number.isFinite(parsed)) return parsed;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n", "/**\n * Sign-in state and the operations that change it.\n *\n * One implementation serves every caller \u2014 the slash command, the settings\n * card's HTTP calls, and the automatic connect after a successful attempt \u2014 so\n * the card and the command can never drift apart. What differs between\n * integrations is only what the provider's own server names: its label, the log\n * tag, and the account facts learned after connecting.\n *\n * The OAuth machinery itself belongs to `dsh-mcp-oauth`.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport {\n\ttype AuthorizationFlow,\n\terrorText,\n\ttype GrantStore,\n\tloggerOf,\n\ttype McpBridge,\n\topenBrowser,\n} from \"dsh-mcp-oauth\";\nimport type { EndpointConfig } from \"./config\";\nimport type { AccountFacts, FactsReader } from \"./facts\";\nimport type { RuntimeState } from \"./state\";\nimport type { IntegrationStatus, StatusNotice } from \"./status\";\n\n/** What one provider adds to the generic status document. */\nexport type StatusExtras<TStatus extends IntegrationStatus, TFacts> = (\n\tbase: IntegrationStatus,\n\tfacts: TFacts | undefined,\n) => TStatus;\n\n/** Everything that is the provider's rather than the library's. */\nexport interface AuthSpec<\n\tTStatus extends IntegrationStatus,\n\tTFacts extends AccountFacts,\n> {\n\t/** The provider's name, as the user sees it. */\n\treadonly label: string;\n\t/** The log tag every message of this plugin carries, such as `[sentry]`. */\n\treadonly logTag: string;\n\t/** How this provider reads the facts its own server names. */\n\treadonly facts: FactsReader<TFacts>;\n\t/** The provider's own fields of the status document. */\n\treadonly status: StatusExtras<TStatus, TFacts>;\n}\n\n/** Everything the auth needs, wired once at apply time. */\nexport interface AuthDeps<\n\tTStatus extends IntegrationStatus,\n\tTFacts extends AccountFacts,\n> {\n\treadonly ctx: Context;\n\treadonly config: EndpointConfig;\n\treadonly state: RuntimeState<TFacts>;\n\treadonly store: GrantStore;\n\treadonly flow: AuthorizationFlow;\n\treadonly bridge: McpBridge;\n\treadonly spec: AuthSpec<TStatus, TFacts>;\n}\n\n/** The sign-in operations every caller of an integration shares. */\nexport interface IntegrationAuthFace<TStatus extends IntegrationStatus> {\n\tsignIn(): Promise<unknown>;\n\tsignOut(): Promise<void>;\n\tconnect(): Promise<boolean>;\n\tgetStatus(): Promise<TStatus>;\n\tloginMessage(status: TStatus): string;\n\tsettleNotice(ms?: number): Promise<void>;\n}\n\nexport class IntegrationAuth<\n\tTStatus extends IntegrationStatus,\n\tTFacts extends AccountFacts,\n> implements IntegrationAuthFace<TStatus>\n{\n\tconstructor(private readonly deps: AuthDeps<TStatus, TFacts>) {}\n\n\t/** Start a sign-in attempt, or join the one already running. */\n\tasync signIn(): Promise<unknown> {\n\t\tconst { ctx, state, spec } = this.deps;\n\n\t\tif (state.running !== undefined) return state.running;\n\n\t\tstate.notice = undefined;\n\t\tstate.error = undefined;\n\n\t\tconst controller = new AbortController();\n\n\t\tstate.running = (async () => {\n\t\t\ttry {\n\t\t\t\tconst outcome = await ctx.authorization.begin({\n\t\t\t\t\tkey: this.deps.store.credentialKey,\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\tinteraction: {\n\t\t\t\t\t\t// The flow publishes the authorize URL; opening it is the driver's\n\t\t\t\t\t\t// business, because a generic UI driving the same flow would render\n\t\t\t\t\t\t// the URL instead of launching a browser.\n\t\t\t\t\t\tnotify: (notice) => {\n\t\t\t\t\t\t\tstate.notice = notice satisfies StatusNotice;\n\n\t\t\t\t\t\t\tif (typeof notice.url === \"string\") openBrowser(ctx, notice.url);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tprompt: () =>\n\t\t\t\t\t\t\tPromise.reject(\n\t\t\t\t\t\t\t\tnew Error(`${spec.label} sign-in needs no prompts`),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (outcome.status === \"authorized\") {\n\t\t\t\t\tstate.notice = { message: `Signed in to ${spec.label}.` };\n\n\t\t\t\t\tawait this.connect();\n\t\t\t\t} else {\n\t\t\t\t\tstate.error = \"Sign-in was cancelled.\";\n\t\t\t\t}\n\n\t\t\t\treturn outcome;\n\t\t\t} catch (error) {\n\t\t\t\tstate.error = errorText(error);\n\t\t\t\tloggerOf(ctx).warn(\"%s sign-in failed: %s\", spec.logTag, state.error);\n\n\t\t\t\treturn { status: \"failed\" as const };\n\t\t\t} finally {\n\t\t\t\tstate.running = undefined;\n\t\t\t}\n\t\t})();\n\n\t\treturn state.running;\n\t}\n\n\t/** Forget the sign-in and withdraw every published tool. */\n\tasync signOut(): Promise<void> {\n\t\tconst { ctx, state, store, bridge } = this.deps;\n\n\t\ttry {\n\t\t\tctx.authorization.cancel(store.credentialKey);\n\t\t} catch {\n\t\t\t// Nothing in flight; cancel is not an error here.\n\t\t}\n\n\t\tawait bridge.drop();\n\t\tawait store.clear();\n\n\t\tstate.notice = undefined;\n\t\tstate.error = undefined;\n\t\tstate.facts = undefined;\n\t}\n\n\t/**\n\t * Connect, restoring the known account facts first so published tools have\n\t * them.\n\t * @returns whether the connection is usable.\n\t */\n\tasync connect(): Promise<boolean> {\n\t\tconst { state, store, bridge, ctx, spec } = this.deps;\n\n\t\tstate.facts = spec.facts.from(await store.read());\n\n\t\tconst ready = await bridge.ensure(ctx);\n\n\t\tif (ready) {\n\t\t\tstate.error = undefined;\n\t\t\tawait this.learn();\n\t\t} else {\n\t\t\t// The reason a connection failed is the provider's own words \u2014 a group\n\t\t\t// switch that is off, a scope the token lacks \u2014 and it is what the\n\t\t\t// settings card must show, rather than a bare \"not connected\".\n\t\t\tstate.error = bridge.lastFailure;\n\t\t}\n\n\t\treturn ready;\n\t}\n\n\t/** Build the status document for one caller. */\n\tasync getStatus(): Promise<TStatus> {\n\t\tconst { state, config, bridge, store, spec } = this.deps;\n\t\tconst grant = await store.read();\n\t\tconst signedIn = grant?.isSignedIn() ?? false;\n\n\t\treturn spec.status(\n\t\t\t{\n\t\t\t\tsignedIn,\n\t\t\t\tinFlight: state.running !== undefined,\n\t\t\t\texpiresAt: signedIn ? (grant?.expiresAt ?? null) : null,\n\t\t\t\tscope: signedIn ? (grant?.scope ?? null) : null,\n\t\t\t\tnotice: state.notice ?? null,\n\t\t\t\terror: state.error ?? null,\n\t\t\t\tconnected: bridge.isConnected,\n\t\t\t\ttools: [...bridge.toolNames],\n\t\t\t\tendpoint: config.endpoint,\n\t\t\t},\n\t\t\tstate.facts ?? spec.facts.from(grant),\n\t\t);\n\t}\n\n\t/** The line the command and any notice show for a sign-in in progress. */\n\tloginMessage(status: TStatus): string {\n\t\tif (status.notice?.url !== undefined)\n\t\t\treturn `${status.notice.message} ${status.notice.url}`;\n\n\t\treturn (\n\t\t\tstatus.notice?.message ??\n\t\t\t`Opening your browser to sign in to ${this.deps.spec.label}\u2026`\n\t\t);\n\t}\n\n\t/** Wait briefly so a just-started attempt can publish its authorize URL. */\n\tasync settleNotice(ms = 400): Promise<void> {\n\t\tawait new Promise((resolve) => {\n\t\t\tsetTimeout(resolve, ms);\n\t\t});\n\t}\n\n\t/** Learn the account facts once, and remember them for the next start. */\n\tprivate async learn(): Promise<void> {\n\t\tconst { ctx, state, store, bridge, spec } = this.deps;\n\t\tconst known = state.facts;\n\n\t\tif (known?.isComplete === true) return;\n\t\tif (!bridge.toolNames.some((name) => name.endsWith(spec.facts.toolName)))\n\t\t\treturn;\n\n\t\ttry {\n\t\t\tconst learned = spec.facts.parse(\n\t\t\t\tawait bridge.callTool(spec.facts.toolName, {}),\n\t\t\t);\n\n\t\t\tif (learned.isEmpty) return;\n\n\t\t\tconst merged = spec.facts.merge(known, learned);\n\n\t\t\tstate.facts = merged;\n\t\t\tawait store.remember(spec.facts.remember(merged));\n\t\t} catch (error) {\n\t\t\tloggerOf(ctx).warn(\n\t\t\t\t\"%s could not read the account facts: %s\",\n\t\t\t\tspec.logTag,\n\t\t\t\terrorText(error),\n\t\t\t);\n\t\t}\n\t}\n}\n", "/**\n * The slash command every integration offers.\n *\n * A command runs directly against the agent without creating a model message,\n * which makes it the right surface for a human-driven action like signing in.\n * It shares its implementation with the settings card, so the two can never\n * disagree about what \"signed in\" means.\n */\n\nimport { errorText } from \"dsh-mcp-oauth\";\nimport type { IntegrationAuthFace } from \"./auth\";\nimport type { IntegrationStatus } from \"./status\";\n\n/** What the command accepts after its name. */\nconst ACTIONS = [\"status\", \"login\", \"logout\", \"sync\"] as const;\n\ntype Action = (typeof ACTIONS)[number];\n\n/** The command result vocabulary the command seam renders. */\nexport interface CommandResult {\n\treadonly kind: \"success\" | \"error\";\n\treadonly text: string;\n}\n\n/** What one provider says about itself in the command's answers. */\nexport interface CommandSpec<TStatus extends IntegrationStatus> {\n\t/** The command's name, without the leading slash. */\n\treadonly name: string;\n\t/** The provider's name, as the user sees it. */\n\treadonly label: string;\n\t/**\n\t * One sentence the status answer adds after the token's expiry, such as the\n\t * account it works with.\n\t * @returns the sentence with its own full stop, or an empty string.\n\t */\n\treadonly describe?: (status: TStatus) => string;\n}\n\n/**\n * Build the handler one integration's command registers.\n * @param spec - the command's name, its provider's label, and what that provider adds.\n * @param auth - the one implementation of sign-in state.\n * @returns a handler the command seam can call.\n */\nexport const createIntegrationCommand = <TStatus extends IntegrationStatus>(\n\tspec: CommandSpec<TStatus>,\n\tauth: IntegrationAuthFace<TStatus>,\n): ((invocation: { rawInput?: unknown }) => Promise<CommandResult>) => {\n\tconst parseAction = (rawInput: unknown): Action => {\n\t\tconst first =\n\t\t\tString(rawInput ?? \"\")\n\t\t\t\t.trim()\n\t\t\t\t.split(/\\s+/u)[0] ?? \"\";\n\n\t\treturn (ACTIONS as readonly string[]).includes(first)\n\t\t\t? (first as Action)\n\t\t\t: \"status\";\n\t};\n\n\treturn async ({ rawInput }) => {\n\t\tconst action = parseAction(rawInput);\n\n\t\ttry {\n\t\t\tif (action === \"login\") {\n\t\t\t\tvoid auth.signIn();\n\t\t\t\tawait auth.settleNotice();\n\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"success\",\n\t\t\t\t\ttext: auth.loginMessage(await auth.getStatus()),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (action === \"logout\") {\n\t\t\t\tawait auth.signOut();\n\n\t\t\t\treturn { kind: \"success\", text: `Signed out of ${spec.label}.` };\n\t\t\t}\n\n\t\t\tif (action === \"sync\") {\n\t\t\t\tconst ok = await auth.connect();\n\t\t\t\tconst status = await auth.getStatus();\n\n\t\t\t\treturn ok\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tkind: \"success\",\n\t\t\t\t\t\t\ttext: `${spec.label} tools ready (${status.tools.length}).`,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tkind: \"error\",\n\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\tstatus.error ??\n\t\t\t\t\t\t\t\t`Not signed in to ${spec.label}. Run /${spec.name} login.`,\n\t\t\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst status = await auth.getStatus();\n\n\t\t\tif (!status.signedIn)\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"success\",\n\t\t\t\t\ttext: `Not signed in to ${spec.label}. Run /${spec.name} login.`,\n\t\t\t\t};\n\n\t\t\tconst until =\n\t\t\t\tstatus.expiresAt === null\n\t\t\t\t\t? \"unknown\"\n\t\t\t\t\t: new Date(status.expiresAt).toLocaleString();\n\t\t\tconst extra = spec.describe?.(status) ?? \"\";\n\t\t\tconst tail = extra === \"\" ? \"\" : ` ${extra}`;\n\n\t\t\treturn {\n\t\t\t\tkind: \"success\",\n\t\t\t\ttext: `Signed in to ${spec.label}. Token valid until ${until}.${tail} ${status.tools.length} tools loaded.`,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn { kind: \"error\", text: `${spec.label}: ${errorText(error)}` };\n\t\t}\n\t};\n};\n", "/**\n * The endpoint configuration every integration is parameterized with.\n *\n * A profile patch may carry an endpoint, a redirect port, and a debug flag;\n * this normalizes whatever arrived. Unknown or malformed fields fall back to the\n * provider's defaults rather than failing activation, because a typo in a patch\n * should not take a plugin down.\n */\n\n/** Raw shape accepted from the profile patch. */\nexport interface ConfigInput {\n\treadonly endpoint?: unknown;\n\treadonly redirectPort?: unknown;\n\treadonly debug?: unknown;\n}\n\n/** Resolved configuration: every field present and of the right type. */\nexport interface EndpointConfig {\n\treadonly endpoint: string;\n\treadonly redirectPort: number;\n\treadonly debug: boolean;\n}\n\n/** What a provider falls back to when the patch names nothing. */\nexport interface ConfigDefaults {\n\t/** The provider's own MCP resource. */\n\treadonly endpoint: string;\n\t/** The first loopback port tried for the OAuth redirect. */\n\treadonly redirectPort: number;\n}\n\n/**\n * Normalize one patch against a provider's defaults.\n * @param input - whatever the loader passed.\n * @param defaults - the provider's own resource and port.\n * @returns the configuration every library call is parameterized with.\n */\nexport function resolveEndpointConfig(\n\tinput: ConfigInput | undefined,\n\tdefaults: ConfigDefaults,\n): EndpointConfig {\n\tconst endpoint =\n\t\ttypeof input?.endpoint === \"string\" && input.endpoint !== \"\"\n\t\t\t? input.endpoint\n\t\t\t: defaults.endpoint;\n\n\tconst port = Number(input?.redirectPort);\n\n\treturn {\n\t\tendpoint,\n\t\tredirectPort:\n\t\t\tNumber.isInteger(port) && port > 0 && port < 65_536\n\t\t\t\t? port\n\t\t\t\t: defaults.redirectPort,\n\t\tdebug: input?.debug === true,\n\t};\n}\n", "/**\n * The seam a cross-provider chain is assembled through.\n *\n * One answer spans several integrations \u2014 an error names a commit, a commit\n * belongs to a merge request, a merge request names a ticket \u2014 and none of them\n * may import another's code: the browser and host bundles are separate, and a\n * plugin's own readers belong to it. So the *vocabulary* of a chain lives here,\n * in the library every integration already shares, and the correlation plugin\n * owns the registry those contributions arrive in.\n *\n * The vocabulary is deliberately about the world, not about a vendor: an error,\n * a commit, a change request, and a tracked task exist whatever the tool is\n * called. A provider says which of them it can resolve and what it knows when\n * asked; it never names another provider, and it never parses another provider's\n * identifier. Where two providers must agree \u2014 the commit a code host can look\n * up, the key a tracker recognizes \u2014 the shared shape here is that agreement,\n * and a reference carries a link alongside it so the other side can find its own\n * way to the same object.\n *\n * A source answers *about the reference it was asked about* with a fact, and\n * that fact carries the references onward. A walker therefore never needs to\n * know what a chain looks like: the links are data.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport { Service } from \"@deepseek-ai/cordis\";\n\n/** The service name the correlation plugin provides and every source registers into. */\nexport const CORRELATION_SOURCES = \"correlationSources\";\n\n/** The kinds of thing a chain is made of. */\nexport type CorrelationKind = \"error\" | \"commit\" | \"change\" | \"task\";\n\n/**\n * A pointer from one fact to another.\n *\n * Each variant carries what the *resolving* side needs to find the object\n * itself, and nothing about how that side is implemented. `label` is what the\n * card calls the unreachable hop when nobody resolves it.\n */\nexport type CorrelationRef =\n\t| { readonly kind: \"error\"; readonly id: string; readonly label?: string }\n\t| {\n\t\t\treadonly kind: \"commit\";\n\t\t\treadonly sha: string;\n\t\t\t/**\n\t\t\t * A web address anywhere in the repository the commit lives in.\n\t\t\t *\n\t\t\t * It is how a code host recognizes its own project without either\n\t\t\t * side agreeing on a naming scheme: the host reads the path it\n\t\t\t * already knows how to read, and ignores the rest.\n\t\t\t */\n\t\t\treadonly url?: string;\n\t\t\t/** The repository as the referring side names it, when it names one. */\n\t\t\treadonly repository?: string;\n\t\t\treadonly label?: string;\n\t }\n\t| {\n\t\t\treadonly kind: \"change\";\n\t\t\t/** The project path the change request lives in, as the code host spells it. */\n\t\t\treadonly repository: string;\n\t\t\t/** The project-local number, without any prefix. */\n\t\t\treadonly number: string;\n\t\t\treadonly url?: string;\n\t\t\treadonly label?: string;\n\t }\n\t| { readonly kind: \"task\"; readonly key: string; readonly label?: string };\n\n/** What one provider knows about one reference. */\nexport interface CorrelationFact {\n\t/** Stable across the chain: what two providers' answers are deduplicated by. */\n\treadonly id: string;\n\t/** The provider's name as the user sees it, such as `Sentry`. */\n\treadonly provider: string;\n\t/** Which reference shape this fact answers. */\n\treadonly kind: CorrelationKind;\n\t/** What the card calls this hop, such as `Suspect commit`. */\n\treadonly label: string;\n\t/** The hop's own line, such as `CHECKOUT-13P \u00B7 AxiosError: \u2026`. */\n\treadonly title: string;\n\treadonly url?: string;\n\t/** One supporting detail, such as `unresolved \u00B7 35 events`. */\n\treadonly meta?: string;\n\t/** What this fact points at, which this or another provider resolves next. */\n\treadonly references?: readonly CorrelationRef[];\n}\n\n/** One provider's contribution to every chain. */\nexport interface CorrelationSource {\n\t/** The provider's name as the user sees it, such as `GitLab`. */\n\treadonly provider: string;\n\t/** The kind a chain starts from when the user names this provider. */\n\treadonly entry: CorrelationKind;\n\t/**\n\t * The kinds this source can resolve.\n\t *\n\t * A reference nobody declares is reported as an unreachable hop rather than\n\t * silently dropped, so a missing integration is legible on the card.\n\t */\n\treadonly resolves: readonly CorrelationKind[];\n\t/**\n\t * Answer one reference.\n\t * @param ref - the reference to resolve; its `kind` is one this source listed.\n\t * @returns the fact for that reference, and any it can already name beside it.\n\t * @throws when the provider could not be read; the walker reports the reason\n\t * against that hop instead of failing the chain.\n\t */\n\tresolve(ref: CorrelationRef): Promise<readonly CorrelationFact[]>;\n}\n\n/** One registered source, as the walker sees it. */\nexport interface CorrelationSourceEntry {\n\treadonly provider: string;\n\treadonly entry: CorrelationKind;\n\treadonly resolves: readonly CorrelationKind[];\n}\n\n/**\n * The registry of correlation sources.\n *\n * Owned by the correlation plugin and provided as `ctx.correlationSources`. An\n * integration registers through a scoped `ctx.inject([CORRELATION_SOURCES], \u2026)`,\n * so a deployment without the correlation plugin simply never calls it and\n * nothing waits for a service that will not arrive.\n */\nexport class CorrelationSources extends Service {\n\tprivate readonly sources: CorrelationSource[] = [];\n\n\tconstructor(ctx: Context) {\n\t\tsuper(ctx, CORRELATION_SOURCES);\n\t}\n\n\t/**\n\t * Add one provider's contribution.\n\t * @param source - the kinds this provider resolves and how to resolve them.\n\t * @returns the exact disposer that withdraws them.\n\t * @throws when a provider of that name is already registered.\n\t */\n\tregister(source: CorrelationSource): () => void {\n\t\tif (this.sources.some((other) => other.provider === source.provider))\n\t\t\tthrow new Error(\n\t\t\t\t`a correlation source named \"${source.provider}\" is already registered`,\n\t\t\t);\n\n\t\tthis.sources.push(source);\n\n\t\treturn () => {\n\t\t\tconst index = this.sources.indexOf(source);\n\t\t\tif (index >= 0) this.sources.splice(index, 1);\n\t\t};\n\t}\n\n\t/** Every provider on offer, in registration order. */\n\tget providers(): readonly CorrelationSourceEntry[] {\n\t\treturn this.sources.map((source) => ({\n\t\t\tprovider: source.provider,\n\t\t\tentry: source.entry,\n\t\t\tresolves: source.resolves,\n\t\t}));\n\t}\n\n\t/**\n\t * Whether any installed integration can resolve one kind.\n\t *\n\t * The walker asks before it resolves so that \"GitLab is not installed\" and\n\t * \"GitLab found no merge request for this commit\" stay different answers.\n\t */\n\tcanResolve(kind: CorrelationKind): boolean {\n\t\treturn this.sources.some((source) => source.resolves.includes(kind));\n\t}\n\n\t/**\n\t * The kind a chain starts from when the user names one provider.\n\t * @param provider - a provider's name as the user sees it.\n\t * @returns that provider's entry kind, or undefined when it is not installed.\n\t */\n\tentryOf(provider: string): CorrelationKind | undefined {\n\t\treturn this.sources.find((source) => source.provider === provider)?.entry;\n\t}\n\n\t/**\n\t * Ask whichever providers resolve one reference.\n\t *\n\t * A kind may have more than one owner \u2014 two code hosts both answer for\n\t * commits \u2014 so each is asked in turn and the first that recognizes the\n\t * reference answers. One that does not returns nothing rather than failing.\n\t * @param ref - the reference to resolve.\n\t * @throws when no registered provider resolves that kind.\n\t */\n\tasync resolve(ref: CorrelationRef): Promise<readonly CorrelationFact[]> {\n\t\tconst owners = this.sources.filter((source) =>\n\t\t\tsource.resolves.includes(ref.kind),\n\t\t);\n\n\t\tif (owners.length === 0)\n\t\t\tthrow new Error(\n\t\t\t\t`no correlation source resolves \"${ref.kind}\" references`,\n\t\t\t);\n\n\t\tfor (const owner of owners) {\n\t\t\tconst facts = await owner.resolve(ref);\n\n\t\t\tif (facts.length > 0) return facts;\n\t\t}\n\n\t\treturn [];\n\t}\n}\n\ndeclare module \"@deepseek-ai/cordis\" {\n\tinterface Context {\n\t\t/** The correlation registry; present only while the correlation plugin is loaded. */\n\t\tcorrelationSources: CorrelationSources;\n\t}\n}\n", "/**\n * The seam a digest is assembled through.\n *\n * A digest spans several integrations, and none of them may import another's\n * code: the browser and host bundles are separate, and a plugin's own readers\n * belong to it. So the *shape* of a contribution lives here, in the library\n * every integration already shares, and the digest plugin owns the registry\n * those contributions arrive in. An integration registers what it can report;\n * the digest plugin decides when to ask and how to lay the answers out.\n *\n * A source is one provider's promise to answer one block. The block's title and\n * its default switch travel with it, because the provider is the only party\n * that knows what it is offering \u2014 the digest card is drawn from whatever the\n * installed sources declare, never from a list this library keeps.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport { Service } from \"@deepseek-ai/cordis\";\n\n/** The service name the digest plugin provides and every source registers into. */\nexport const DIGEST_SOURCES = \"digestSources\";\n\n/**\n * One row a block contributes.\n *\n * Deliberately small: a digest is read at a glance, so a row carries a line, a\n * link, and one supporting detail. A provider that wants to say more says it in\n * `meta`; the card gives no row more room than that.\n */\nexport interface DigestItem {\n\t/** Stable within its block; what keys the row. */\n\treadonly id: string;\n\t/** The row's own line, such as `DEV-6477 \u00B7 Fix the parser`. */\n\treadonly title: string;\n\treadonly url?: string;\n\t/** One supporting detail, such as `In review \u00B7 due today`. */\n\treadonly meta?: string;\n}\n\n/** One block of the digest, as the provider that owns it declares it. */\nexport interface DigestBlockSpec {\n\t/** Stable across restarts: what a saved switch is stored under. */\n\treadonly id: string;\n\t/** The block's heading on the card, such as `Jira \u00B7 In progress`. */\n\treadonly title: string;\n\t/** The switch a user who has never touched it gets. */\n\treadonly defaultOn: boolean;\n}\n\n/** What one load is allowed to assume about its window. */\nexport interface DigestLoadOptions {\n\t/** How far back a block that reports \"recent\" work looks. */\n\treadonly since: Date;\n\t/** The most rows the card will show from this block. */\n\treadonly limit: number;\n}\n\n/** One provider's contribution to every digest. */\nexport interface DigestSource {\n\t/** The provider's name as the user sees it, such as `GitLab`. */\n\treadonly provider: string;\n\treadonly blocks: readonly DigestBlockSpec[];\n\t/**\n\t * Answer one block.\n\t * @param blockId - one of this source's own block ids.\n\t * @param options - the window and the row cap the digest is assembling under.\n\t * @returns the rows to show; an empty list is a truthful \"nothing to report\".\n\t * @throws when the provider could not be read; the digest reports the reason\n\t * against that block instead of failing whole.\n\t */\n\tload(\n\t\tblockId: string,\n\t\toptions: DigestLoadOptions,\n\t): Promise<readonly DigestItem[]>;\n}\n\n/** What one registered source offers, with the provider that owns it. */\nexport interface DigestBlockEntry extends DigestBlockSpec {\n\treadonly provider: string;\n}\n\n/**\n * The registry of digest sources.\n *\n * Owned by the digest plugin and provided as `ctx.digestSources`. An\n * integration registers through a scoped `ctx.inject([DIGEST_SOURCES], \u2026)`, so\n * a deployment without the digest plugin simply never calls it and nothing\n * waits for a service that will not arrive.\n */\nexport class DigestSources extends Service {\n\tprivate readonly sources: DigestSource[] = [];\n\n\tconstructor(ctx: Context) {\n\t\tsuper(ctx, DIGEST_SOURCES);\n\t}\n\n\t/**\n\t * Add one provider's contribution.\n\t * @param source - the blocks this provider offers and how to load them.\n\t * @returns the exact disposer that withdraws them.\n\t * @throws when the provider declares a block id another provider already owns.\n\t */\n\tregister(source: DigestSource): () => void {\n\t\tconst clash = source.blocks.find((block) =>\n\t\t\tthis.sources.some((other) =>\n\t\t\t\tother.blocks.some((taken) => taken.id === block.id),\n\t\t\t),\n\t\t);\n\n\t\tif (clash !== undefined)\n\t\t\tthrow new Error(\n\t\t\t\t`digest block \"${clash.id}\" is already registered by another provider`,\n\t\t\t);\n\n\t\tthis.sources.push(source);\n\n\t\treturn () => {\n\t\t\tconst index = this.sources.indexOf(source);\n\t\t\tif (index >= 0) this.sources.splice(index, 1);\n\t\t};\n\t}\n\n\t/** Every block on offer, in registration order, with its owning provider. */\n\tget blocks(): readonly DigestBlockEntry[] {\n\t\treturn this.sources.flatMap((source) =>\n\t\t\tsource.blocks.map((block) => ({ ...block, provider: source.provider })),\n\t\t);\n\t}\n\n\t/** The providers that have registered, in registration order. */\n\tget providers(): readonly string[] {\n\t\treturn this.sources.map((source) => source.provider);\n\t}\n\n\t/**\n\t * Ask the provider that owns one block.\n\t * @param blockId - a block id from {@link blocks}.\n\t * @param options - the window and the row cap.\n\t * @throws when no registered provider offers that block.\n\t */\n\tasync load(\n\t\tblockId: string,\n\t\toptions: DigestLoadOptions,\n\t): Promise<readonly DigestItem[]> {\n\t\tfor (const source of this.sources) {\n\t\t\tif (source.blocks.some((block) => block.id === blockId))\n\t\t\t\treturn await source.load(blockId, options);\n\t\t}\n\n\t\tthrow new Error(`no digest source offers the block \"${blockId}\"`);\n\t}\n}\n\ndeclare module \"@deepseek-ai/cordis\" {\n\tinterface Context {\n\t\t/** The digest registry; present only while the digest plugin is loaded. */\n\t\tdigestSources: DigestSources;\n\t}\n}\n", "/**\n * What a provider's markdown means once an integration reads it.\n *\n * Jira, Confluence and GitLab all store markdown whose HTML comments are\n * invisible where the text was written: a comment is a note to whoever edits the\n * document, not part of what it says. This harness renders that markdown in a\n * panel and hands the same string to the model, so the note is dropped where the\n * document is read rather than hidden where it is drawn \u2014 an integration's\n * reader is the one place both surfaces go through.\n *\n * Only what the document says outside its code fences is touched: an example of\n * a comment is content, and so is the whitespace inside the example. An\n * unterminated comment swallows the rest of its document, which is what an HTML\n * comment means anyway.\n */\n\n/** A line that opens or closes a fenced code block. */\nconst FENCE = /^\\s{0,3}(?:```|~~~)/u;\n\n/** What a comment starts and ends with. */\nconst OPEN = \"<!--\";\nconst CLOSE = \"-->\";\n\n/** One line, with the comments in it removed and its spacing tidied. */\ninterface Uncommented {\n\treadonly text: string;\n\t/** Whether this line held a comment, which is what its spacing depends on. */\n\treadonly touched: boolean;\n}\n\n/**\n * Remove the comments from the lines outside code fences.\n * @param lines - the document's lines.\n * @returns each line with its comments gone, and whether it held any.\n */\nconst uncomment = (lines: readonly string[]): Uncommented[] => {\n\tconst kept: Uncommented[] = [];\n\tlet inFence = false;\n\tlet inComment = false;\n\n\tfor (const line of lines) {\n\t\tif (!inComment && FENCE.test(line)) {\n\t\t\tinFence = !inFence;\n\t\t\tkept.push({ text: line, touched: false });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (inFence) {\n\t\t\tkept.push({ text: line, touched: false });\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet rest = line;\n\t\tlet text = \"\";\n\t\tlet touched = false;\n\n\t\twhile (rest !== \"\") {\n\t\t\tif (inComment) {\n\t\t\t\tconst end = rest.indexOf(CLOSE);\n\n\t\t\t\t// An unterminated comment runs to the end of the document.\n\t\t\t\tif (end === -1) {\n\t\t\t\t\trest = \"\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tinComment = false;\n\t\t\t\trest = rest.slice(end + CLOSE.length);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst start = rest.indexOf(OPEN);\n\n\t\t\tif (start === -1) {\n\t\t\t\ttext += rest;\n\t\t\t\trest = \"\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\ttext += rest.slice(0, start);\n\t\t\ttouched = true;\n\t\t\tinComment = true;\n\t\t\trest = rest.slice(start + OPEN.length);\n\t\t}\n\n\t\tkept.push({ text, touched });\n\t}\n\n\treturn kept;\n};\n\n/**\n * Remove the HTML comments from one markdown document.\n * @param markdown - the document as the provider stores it.\n * @returns the document without its comments; the same string when it has none.\n */\nexport const stripHtmlComments = (markdown: string): string => {\n\tif (!markdown.includes(OPEN)) return markdown;\n\n\tconst kept: string[] = [];\n\tlet inFence = false;\n\t/** Whether the line just kept was blank, so a run of them stays one line. */\n\tlet blank = false;\n\n\tfor (const line of uncomment(markdown.split(\"\\n\"))) {\n\t\tif (FENCE.test(line.text)) {\n\t\t\tinFence = !inFence;\n\t\t\tblank = false;\n\t\t\tkept.push(line.text);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (inFence) {\n\t\t\tkept.push(line.text);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// A comment that stood as its own paragraph leaves the empty line that\n\t\t// separated it from the next one; markdown reads a run of blank lines as\n\t\t// one, so the run collapses rather than the document gaining a gap.\n\t\tif (line.text.trim() === \"\") {\n\t\t\tif (kept.length === 0 || blank) continue;\n\n\t\t\tblank = true;\n\t\t\tkept.push(line.text);\n\t\t\tcontinue;\n\t\t}\n\n\t\tblank = false;\n\n\t\t// Where a page renders \"one two\", the source reads \"one <!-- x --> two\" and\n\t\t// the removal would leave the two spaces both sides brought. Only a line\n\t\t// that lost a comment is touched, and only between words: two trailing\n\t\t// spaces are markdown's own way of writing a line break.\n\t\tkept.push(\n\t\t\tline.touched ? line.text.replace(/[ \\t]{2,}(?=\\S)/gu, \" \") : line.text,\n\t\t);\n\t}\n\n\t// A blank line at the end of a document is not part of what it says, and the\n\t// one a trailing comment left is the reader's, not the author's.\n\twhile (kept.length > 0 && kept[kept.length - 1]?.trim() === \"\") kept.pop();\n\n\treturn kept.join(\"\\n\");\n};\n", "/**\n * The localhost API a browser half calls.\n *\n * A settings card and a side panel live in the page, so they need a channel to\n * the host. A named route on the harness web server is that channel: it needs no\n * generated remote namespace, it is structured JSON in both directions, and it\n * is bound to loopback exactly like the rest of the web server.\n *\n * The four paths every integration answers \u2014 status, login, logout, sync \u2014 are\n * this module's; a provider adds its own reads and writes after them, and the\n * first handler that claims a request ends it.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport { errorText } from \"dsh-mcp-oauth\";\nimport type { IntegrationAuthFace } from \"./auth\";\nimport type { IntegrationStatus } from \"./status\";\n\n/** Longest write body any integration's route accepts, in bytes. */\nconst MAX_BODY_BYTES = 8 * 1024;\n\n/** Mount a route the web server can serve. */\nexport interface RouteHost {\n\tregister(route: {\n\t\tkind: \"exact\" | \"prefix\";\n\t\tpath: string;\n\t\thandler: (request: IncomingMessage, response: ServerResponse) => void;\n\t}): () => void;\n}\n\n/** One integration's route, as the shared mount takes it. */\nexport interface MountSpec {\n\t/** The route prefix this plugin owns, such as `/dsh-sentry`. */\n\treadonly prefix: string;\n\t/** The handler every request under that prefix reaches. */\n\treadonly handler: (\n\t\trequest: IncomingMessage,\n\t\tresponse: ServerResponse,\n\t) => Promise<void>;\n}\n\n/**\n * Mount one integration's route on the harness web server.\n *\n * The registration waits for the web server rather than reading it once: the\n * loader mounts rows as their own dependencies resolve, so a plugin that merely\n * asks can run before the listener's fiber is active \u2014 and then its card never\n * reaches the host, which looks like a broken integration rather than a race. A\n * composition with no web server registers nothing, and the tools and the\n * command keep working.\n * @param ctx - the plugin's host context.\n * @param spec - the prefix this plugin owns and the handler under it.\n */\nexport function mountIntegrationRoute(ctx: Context, spec: MountSpec): void {\n\tctx.inject([\"webServer\"], (scoped) => {\n\t\tconst webServer = (scoped as { get: (name: string) => unknown }).get(\n\t\t\t\"webServer\",\n\t\t) as RouteHost;\n\n\t\tscoped.effect(\n\t\t\t() =>\n\t\t\t\twebServer.register({\n\t\t\t\t\tkind: \"prefix\",\n\t\t\t\t\tpath: spec.prefix,\n\t\t\t\t\thandler: spec.handler,\n\t\t\t\t}),\n\t\t\t`${spec.prefix}: route`,\n\t\t);\n\t});\n}\n\n/** One request's parsed JSON body, and the fields it must carry. */\nexport interface RouteBody {\n\t/** The parsed object, or undefined when the request carried no body. */\n\treadonly fields: Record<string, unknown> | undefined;\n\t/**\n\t * One required string field.\n\t * @throws a bad-request failure, which the route answers with 400.\n\t */\n\trequired(name: string): string;\n}\n\n/** What one provider path handler reads and answers with. */\nexport interface RouteContext {\n\treadonly url: URL;\n\treadonly method: string;\n\t/** Read the request's JSON body; the read happens once. */\n\tread(): Promise<RouteBody>;\n\t/** Answer with JSON. */\n\tsend(status: number, body: unknown): void;\n\t/**\n\t * Answer with bytes and their media type.\n\t *\n\t * A panel that draws a picture needs the bytes themselves: an image cannot\n\t * travel inside the JSON document that describes it.\n\t */\n\tsendBytes(status: number, contentType: string, body: Uint8Array): void;\n\t/** Answer a read, translating a failure behind it into a bad gateway. */\n\tsendData(load: () => Promise<unknown>): Promise<void>;\n}\n\n/**\n * One provider path.\n * @returns whether this handler claimed the request.\n */\nexport type RouteHandler = (route: RouteContext) => Promise<boolean> | boolean;\n\n/** Everything the route answers with, wired once at apply time. */\nexport interface RouteSpec<TStatus extends IntegrationStatus> {\n\t/** The route prefix this plugin owns, such as `/dsh-sentry`. */\n\treadonly prefix: string;\n\treadonly auth: IntegrationAuthFace<TStatus>;\n\t/** Provider paths, tried after the shared four, in order. */\n\treadonly routes?: readonly RouteHandler[];\n}\n\n/**\n * A request this route cannot act on, as opposed to a failure behind it.\n *\n * A provider path throws one of these for a caller's mistake, and the route\n * answers it with 400 rather than as a failure of the integration behind it.\n */\nexport class BadRequest extends Error {}\n\n/** One body, read once and narrowed to the object shape every route expects. */\nfunction bodyOf(value: Record<string, unknown> | undefined): RouteBody {\n\treturn {\n\t\tfields: value,\n\t\trequired: (name) => {\n\t\t\tconst field = value?.[name];\n\n\t\t\tif (typeof field !== \"string\" || field === \"\")\n\t\t\t\tthrow new BadRequest(`the request needs a \"${name}\"`);\n\n\t\t\treturn field;\n\t\t},\n\t};\n}\n\n/**\n * Read one small JSON request body.\n * @param request - the incoming request.\n * @returns the parsed body, or undefined when it is not a JSON object.\n */\nasync function readJson(\n\trequest: IncomingMessage,\n): Promise<Record<string, unknown> | undefined> {\n\tconst chunks: Buffer[] = [];\n\tlet size = 0;\n\n\tfor await (const chunk of request) {\n\t\tconst buffer = chunk as Buffer;\n\t\tsize += buffer.length;\n\n\t\tif (size > MAX_BODY_BYTES)\n\t\t\tthrow new Error(\"request body is larger than this route accepts\");\n\t\tchunks.push(buffer);\n\t}\n\n\tif (chunks.length === 0) return undefined;\n\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n\n\t\treturn parsed !== null &&\n\t\t\ttypeof parsed === \"object\" &&\n\t\t\t!Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Build the request handler for one integration.\n * @param spec - the route prefix, the sign-in face, and the provider's own paths.\n */\nexport function createIntegrationRoute<TStatus extends IntegrationStatus>(\n\tspec: RouteSpec<TStatus>,\n): (request: IncomingMessage, response: ServerResponse) => Promise<void> {\n\tconst { prefix, auth } = spec;\n\n\treturn async (request, response) => {\n\t\tconst url = new URL(request.url ?? \"/\", \"http://127.0.0.1\");\n\t\tconst method = request.method ?? \"GET\";\n\n\t\tconst send = (status: number, body: unknown): void => {\n\t\t\tresponse.writeHead(status, {\n\t\t\t\t\"content-type\": \"application/json; charset=utf-8\",\n\t\t\t\t\"cache-control\": \"no-store\",\n\t\t\t});\n\n\t\t\tresponse.end(JSON.stringify(body));\n\t\t};\n\n\t\tconst sendBytes = (\n\t\t\tstatus: number,\n\t\t\tcontentType: string,\n\t\t\tbody: Uint8Array,\n\t\t): void => {\n\t\t\tresponse.writeHead(status, {\n\t\t\t\t\"content-type\": contentType,\n\t\t\t\t\"content-length\": body.byteLength,\n\t\t\t\t\"cache-control\": \"no-store\",\n\t\t\t});\n\n\t\t\tresponse.end(body);\n\t\t};\n\n\t\t/** Answer a read the settings card or the side panel asked for. */\n\t\tconst sendData = async (load: () => Promise<unknown>): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tsend(200, await load());\n\t\t\t} catch (error) {\n\t\t\t\tsend(502, { error: errorText(error) });\n\t\t\t}\n\t\t};\n\n\t\tlet body: Promise<RouteBody> | undefined;\n\n\t\tconst context: RouteContext = {\n\t\t\turl,\n\t\t\tmethod,\n\t\t\tread: () => (body ??= readJson(request).then(bodyOf)),\n\t\t\tsend,\n\t\t\tsendBytes,\n\t\t\tsendData,\n\t\t};\n\n\t\ttry {\n\t\t\tif (url.pathname === `${prefix}/status` && method === \"GET\") {\n\t\t\t\tsend(200, await auth.getStatus());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (url.pathname === `${prefix}/login` && method === \"POST\") {\n\t\t\t\t// Start and answer immediately: the user finishes in a browser, and the\n\t\t\t\t// card follows `inFlight` until the attempt settles.\n\t\t\t\tvoid auth.signIn();\n\t\t\t\tawait auth.settleNotice(300);\n\n\t\t\t\tsend(202, await auth.getStatus());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (url.pathname === `${prefix}/logout` && method === \"POST\") {\n\t\t\t\tawait auth.signOut();\n\n\t\t\t\tsend(200, await auth.getStatus());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (url.pathname === `${prefix}/sync` && method === \"POST\") {\n\t\t\t\tawait auth.connect();\n\n\t\t\t\tsend(200, await auth.getStatus());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (const handler of spec.routes ?? []) {\n\t\t\t\tif (await handler(context)) return;\n\t\t\t}\n\n\t\t\tsend(404, { error: \"not-found\", path: url.pathname });\n\t\t} catch (error) {\n\t\t\tsend(error instanceof BadRequest ? 400 : 500, {\n\t\t\t\terror: errorText(error),\n\t\t\t});\n\t\t}\n\t};\n}\n", "/**\n * Process-local state of one integration connection.\n *\n * Everything durable \u2014 the tokens, and the account facts learned from the\n * account \u2014 lives in the credential record. This is only what a running attempt\n * and the settings card need between calls, so a restart loses nothing that\n * matters.\n */\n\nimport type { AuthorizationNotice } from \"@deepseek-ai/dsh-authorization\";\nimport type { AccountFacts } from \"./facts\";\n\n/**\n * Everything one host half keeps between calls.\n *\n * `TFacts` is what this integration learned from its own server, and a server\n * that names no account \u2014 a local MCP, which answers whoever is on the machine \u2014\n * leaves it at its default and keeps the rest.\n */\nexport class RuntimeState<TFacts extends AccountFacts = AccountFacts> {\n\t/** The last notice a running attempt published, so a card can show the URL. */\n\tnotice: AuthorizationNotice | undefined;\n\t/** The last failure, cleared when a new attempt starts. */\n\terror: string | undefined;\n\t/** Settles when the in-flight attempt finishes. */\n\trunning: Promise<unknown> | undefined;\n\t/** The account facts this connection works with, once known. */\n\tfacts: TFacts | undefined;\n\n\tconstructor() {\n\t\tthis.notice = undefined;\n\t\tthis.error = undefined;\n\t\tthis.running = undefined;\n\t\tthis.facts = undefined;\n\t}\n}\n", "/**\n * The Atlassian site this installation works with.\n *\n * Atlassian withholds `webUrl` from clients it does not recognize by name, so\n * the account's own list of sites is what gives a card a link and every Jira\n * and Confluence tool call the cloud id it requires. The facts are read once,\n * remembered in the credential record, and restored on the next start.\n */\n\nimport type { FactsReader } from \"dsh-integration-host\";\nimport { asRecord, type Grant, mcpText, pickString } from \"dsh-mcp-oauth\";\n\n/** The tool whose answer names the account's site. */\nexport const SITE_TOOL = \"getAccessibleAtlassianResources\";\n\nexport class SiteFacts {\n\treadonly siteUrl?: string;\n\treadonly cloudId?: string;\n\n\tconstructor(params: { siteUrl?: string; cloudId?: string }) {\n\t\tthis.siteUrl = params.siteUrl;\n\t\tthis.cloudId = params.cloudId;\n\t}\n\n\t/** What the stored grant already knows, if anything. */\n\tstatic from(grant: Grant | undefined): SiteFacts {\n\t\tconst extra = grant?.extra;\n\n\t\treturn new SiteFacts({\n\t\t\t...(typeof extra?.siteUrl === \"string\" ? { siteUrl: extra.siteUrl } : {}),\n\t\t\t...(typeof extra?.cloudId === \"string\" ? { cloudId: extra.cloudId } : {}),\n\t\t});\n\t}\n\n\t/**\n\t * Read the first site of an account's own list.\n\t * @param value - the result of `getAccessibleAtlassianResources`.\n\t * @returns the facts it names, or empty facts when it names none.\n\t */\n\tstatic parse(value: unknown): SiteFacts {\n\t\tfor (const text of mcpText(value)) {\n\t\t\tconst sites = parseJson(text);\n\t\t\tconst record = asRecord(Array.isArray(sites) ? sites[0] : undefined);\n\n\t\t\tif (record === undefined) continue;\n\n\t\t\tconst url = pickString(record, \"url\");\n\t\t\tconst id = pickString(record, \"id\");\n\n\t\t\tif (url === undefined && id === undefined) continue;\n\n\t\t\treturn new SiteFacts({\n\t\t\t\t...(url === undefined ? {} : { siteUrl: url.replace(/\\/+$/u, \"\") }),\n\t\t\t\t...(id === undefined ? {} : { cloudId: id }),\n\t\t\t});\n\t\t}\n\n\t\treturn new SiteFacts({});\n\t}\n\n\t/** Whether neither the site nor the cloud id is known. */\n\tget isEmpty(): boolean {\n\t\treturn this.siteUrl === undefined && this.cloudId === undefined;\n\t}\n\n\t/** Whether both the site and the cloud id are known, so nothing is left to learn. */\n\tget isComplete(): boolean {\n\t\treturn this.siteUrl !== undefined && this.cloudId !== undefined;\n\t}\n}\n\n/** How Atlassian's own site list is read, merged, and remembered. */\nexport const siteFacts: FactsReader<SiteFacts> = {\n\ttoolName: SITE_TOOL,\n\tfrom: (grant) => SiteFacts.from(grant),\n\tparse: (value) => SiteFacts.parse(value),\n\tmerge: (known, learned) =>\n\t\tnew SiteFacts({\n\t\t\tsiteUrl: learned.siteUrl ?? known?.siteUrl,\n\t\t\tcloudId: learned.cloudId ?? known?.cloudId,\n\t\t}),\n\tremember: (facts) => ({\n\t\t...(facts.siteUrl === undefined ? {} : { siteUrl: facts.siteUrl }),\n\t\t...(facts.cloudId === undefined ? {} : { cloudId: facts.cloudId }),\n\t}),\n};\n\nfunction parseJson(text: string): unknown {\n\ttry {\n\t\treturn JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n", "/**\n * What is Atlassian's rather than the library's about signing in.\n *\n * The sign-in operations themselves belong to `dsh-integration-host`, which\n * serves the `/atlassian` command, the settings card's HTTP calls, and the\n * automatic connect after a successful attempt from one implementation. All\n * Atlassian supplies is its own name, the log tag, how its site list is read,\n * and the field it adds to the shared status document.\n */\n\nimport type { AuthSpec } from \"dsh-integration-host\";\nimport type { AtlassianStatus } from \"../shared/status\";\nimport { type SiteFacts, siteFacts } from \"./store/site\";\n\n/** The provider's own half of the shared sign-in implementation. */\nexport const atlassianAuthSpec: AuthSpec<AtlassianStatus, SiteFacts> = {\n\tlabel: \"Atlassian\",\n\tlogTag: \"[atlassian]\",\n\tfacts: siteFacts,\n\tstatus: (base, facts) => ({\n\t\t...base,\n\t\tsiteUrl: facts?.siteUrl ?? null,\n\t}),\n};\n", "/**\n * Atlassian configuration, and the provider profile built from it.\n *\n * The scope lists live here because they are Atlassian's, not the library's:\n * the two server generations use different vocabularies, and the library\n * narrows whichever list it is given to what the server actually advertises.\n */\n\nimport {\n\ttype ConfigInput,\n\ttype EndpointConfig,\n\tresolveEndpointConfig,\n} from \"dsh-integration-host\";\nimport { DEFAULT_INSTANCE, type OAuthProviderProfile } from \"dsh-mcp-oauth\";\n\n/** Atlassian's v1 MCP resource; the default because it needs no extra setup. */\nexport const DEFAULT_ENDPOINT = \"https://mcp.atlassian.com/v1/mcp\";\n\n/** First loopback port tried for the OAuth redirect. */\nexport const DEFAULT_REDIRECT_PORT = 8143;\n\n/** The name this plugin is registered under; it owns the credential record. */\nexport const PLUGIN_NAME = \"dsh-atlassian\";\n\n/** Scopes wanted across both generations; whatever is not advertised is dropped. */\nconst DESIRED_SCOPES = [\n\t\"offline_access\",\n\t\"read:jira-work\",\n\t\"write:jira-work\",\n\t\"read:jira-user\",\n\t\"read:page:confluence\",\n\t\"write:page:confluence\",\n\t\"search:confluence\",\n\t\"read:space:confluence\",\n\t\"read:jira:agent-interface\",\n\t\"write:jira:agent-interface\",\n\t\"search:jira:agent-interface\",\n\t\"read:confluence:agent-interface\",\n\t\"write:confluence:agent-interface\",\n\t\"search:confluence:agent-interface\",\n];\n\n/** What a v1 authorization server understands when it advertises no scopes. */\nconst FALLBACK_SCOPES = [\n\t\"offline_access\",\n\t\"read:jira-work\",\n\t\"write:jira-work\",\n\t\"read:jira-user\",\n\t\"read:page:confluence\",\n\t\"write:page:confluence\",\n\t\"search:confluence\",\n\t\"read:space:confluence\",\n];\n\nexport type { ConfigInput };\n\n/**\n * Normalize whatever the loader passed against Atlassian's own defaults.\n * @param input - `endpoint`, `redirectPort`, and `debug` from the bundle patch.\n */\nexport const resolveConfig = (\n\tinput: ConfigInput | undefined,\n): EndpointConfig => {\n\treturn resolveEndpointConfig(input, {\n\t\tendpoint: DEFAULT_ENDPOINT,\n\t\tredirectPort: DEFAULT_REDIRECT_PORT,\n\t});\n};\n\n/** The profile every library call of this plugin is parameterized with. */\nexport const atlassianProfile = (\n\tconfig: EndpointConfig,\n): OAuthProviderProfile => {\n\treturn {\n\t\tscope: PLUGIN_NAME,\n\t\tlabel: \"Atlassian\",\n\t\tinstance: DEFAULT_INSTANCE,\n\t\tendpoint: config.endpoint,\n\t\ttoolNamespace: \"atlassian\",\n\t\tdebug: config.debug,\n\t\toauth: {\n\t\t\tredirectPort: config.redirectPort,\n\t\t\tscopes: DESIRED_SCOPES,\n\t\t\tfallbackScopes: FALLBACK_SCOPES,\n\t\t\tclientName: \"DeepSeek Harness\",\n\t\t},\n\t};\n};\n", "/**\n * Turn one Atlassian MCP tool result into the card data the Web Client draws.\n *\n * Atlassian returns JSON payloads inside text content blocks and withholds\n * `structuredContent` (including `webUrl`) from clients it does not recognize by\n * name, so this walks whatever JSON it can find and recognizes Jira issues and\n * Confluence pages by their own fields. Anything unrecognized survives as a\n * bounded text excerpt, because the server's own words are always truthful.\n */\n\nimport { stripHtmlComments } from \"dsh-integration-host\";\nimport { mcpJson, mcpText } from \"dsh-mcp-oauth\";\nimport type {\n\tAtlassianMeta,\n\tIssueCard,\n\tMetaKind,\n\tPageCard,\n} from \"../../shared/meta\";\n\nexport interface ExtractOptions {\n\t/** The server's own tool name. */\n\treadonly tool: string;\n\t/** The arguments the model supplied. */\n\treadonly args: unknown;\n\t/** The canonical MCP value the tool returned. */\n\treadonly value: unknown;\n\t/** Atlassian site base URL, when the plugin has learned it. */\n\treadonly siteUrl: string | undefined;\n\t/**\n\t * Also carry each item's long text.\n\t *\n\t * Off by default: the recorded card is a summary, and a description belongs\n\t * in the session log only after it has been compressed. A caller that asks\n\t * for it reads `rawText` and projects the card again without it.\n\t */\n\treadonly bodies?: boolean;\n}\n\n/** Minutes in the eight-hour day Jira estimates are quoted in. */\nconst MINUTES_PER_DAY = 480;\n\n/**\n * Render a duration in seconds the way Jira quotes estimates.\n * @param seconds - a raw `timeoriginalestimate`, or the equivalent.\n * @returns a compact \"3d 4h\" style string, or undefined for nothing usable.\n */\nexport function formatDuration(seconds: number): string | undefined {\n\tif (!Number.isFinite(seconds) || seconds <= 0) return undefined;\n\n\tconst total = Math.round(seconds / 60);\n\tif (total < 1) return \"<1m\";\n\n\tconst days = Math.floor(total / MINUTES_PER_DAY);\n\tconst hours = Math.floor((total - days * MINUTES_PER_DAY) / 60);\n\tconst minutes = total - days * MINUTES_PER_DAY - hours * 60;\n\n\t// Minutes are dropped once the estimate reaches a day: \"3d 4h 12m\" claims a\n\t// precision nobody plans with.\n\tconst parts = [\n\t\tdays === 0 ? \"\" : `${days}d`,\n\t\thours === 0 ? \"\" : `${hours}h`,\n\t\tminutes === 0 || days > 0 ? \"\" : `${minutes}m`,\n\t];\n\n\treturn parts.filter(Boolean).join(\" \");\n}\n\nclass Extractor {\n\t/** Jira issue keys: an uppercase project key, a dash, and a number. */\n\tprivate readonly ISSUE_KEY = /\\b[A-Z][A-Z0-9_]{0,9}-\\d+\\b/u;\n\n\t/** Bound the work a pathological payload can cause. */\n\tprivate readonly MAX_OBJECTS = 3000;\n\tprivate readonly MAX_STRINGS = 800;\n\tprivate readonly MAX_DEPTH = 7;\n\tprivate readonly MAX_CARDS = 25;\n\tprivate readonly MAX_TEXT = 4000;\n\t/** Longest description or body kept for a summary pass or a panel. */\n\tprivate readonly MAX_BODY = 8000;\n\n\t/** Node types that end a line, so a flattened document keeps its shape. */\n\tprivate readonly BLOCK_TYPES = new Set([\n\t\t\"paragraph\",\n\t\t\"heading\",\n\t\t\"blockquote\",\n\t\t\"codeBlock\",\n\t\t\"listItem\",\n\t\t\"tableRow\",\n\t\t\"panel\",\n\t\t\"rule\",\n\t]);\n\n\t/**\n\t * Extract card data from one tool result.\n\t * @param options - the tool name, its arguments, and the returned value.\n\t * @returns the meta the client renders; `text` is always populated.\n\t */\n\textract = (options: ExtractOptions): AtlassianMeta => {\n\t\tconst texts = mcpText(options.value);\n\t\tconst joined = texts.join(\"\\n\").trim();\n\n\t\tconst roots: unknown[] = [];\n\t\tconst structured = this.asRecord(options.value)?.structuredContent;\n\n\t\tif (structured !== undefined) roots.push(structured);\n\n\t\troots.push(...mcpJson(options.value));\n\n\t\tconst objects: Record<string, unknown>[] = [];\n\t\tconst strings: string[] = [];\n\n\t\tfor (const root of roots) {\n\t\t\tthis.collectObjects(root, objects, 0);\n\t\t\tthis.collectStrings(root, strings, 0);\n\t\t}\n\n\t\t// Links often live outside the JSON payload: markdown rows, plain prose.\n\t\tthis.collectStrings(joined, strings, 0);\n\n\t\tconst issues = this.issueCards(\n\t\t\tobjects,\n\t\t\tstrings,\n\t\t\toptions.siteUrl,\n\t\t\toptions.bodies === true,\n\t\t);\n\t\tconst pages =\n\t\t\tissues.length > 0\n\t\t\t\t? []\n\t\t\t\t: this.pageCards(\n\t\t\t\t\t\tobjects,\n\t\t\t\t\t\tstrings,\n\t\t\t\t\t\toptions.siteUrl,\n\t\t\t\t\t\toptions.bodies === true,\n\t\t\t\t\t);\n\t\tconst query = this.pick(\n\t\t\toptions.args,\n\t\t\t\"jql\",\n\t\t\t\"cql\",\n\t\t\t\"query\",\n\t\t\t\"searchString\",\n\t\t\t\"issueIdOrKey\",\n\t\t\t\"pageId\",\n\t\t\t\"cloudId\",\n\t\t);\n\n\t\tconst kind: MetaKind =\n\t\t\tissues.length > 0\n\t\t\t\t? \"jira-issues\"\n\t\t\t\t: pages.length > 0\n\t\t\t\t\t? \"confluence-pages\"\n\t\t\t\t\t: \"text\";\n\n\t\t// A total only means something next to items it counts.\n\t\tconst total = kind === \"text\" ? undefined : this.totalOf(roots);\n\n\t\treturn {\n\t\t\tkind,\n\t\t\ttool: options.tool,\n\t\t\t...(query === undefined ? {} : { query }),\n\t\t\t...(total === undefined ? {} : { total }),\n\t\t\tissues,\n\t\t\tpages,\n\t\t\ttext: joined.slice(0, this.MAX_TEXT),\n\t\t};\n\t};\n\n\t/**\n\t * Flatten one rich field to the plain text a caller can use.\n\t *\n\t * The side panel reads a description or a comment body through this: the same\n\t * markdown-or-ADF handling the cards use, with the same bound.\n\t * @param value - a markdown string, an ADF tree, or anything else.\n\t * @returns the text, or undefined when there is none.\n\t */\n\ttext = (value: unknown): string | undefined => this.plainText(value);\n\n\tprivate collectStrings = (\n\t\tnode: unknown,\n\t\tout: string[],\n\t\tdepth: number,\n\t): void => {\n\t\tif (\n\t\t\tdepth > this.MAX_DEPTH ||\n\t\t\tnode === null ||\n\t\t\tnode === undefined ||\n\t\t\tout.length >= this.MAX_STRINGS\n\t\t)\n\t\t\treturn;\n\n\t\tif (typeof node === \"string\") {\n\t\t\tout.push(node);\n\t\t\treturn;\n\t\t}\n\n\t\tif (Array.isArray(node)) {\n\t\t\tfor (const item of node) this.collectStrings(item, out, depth + 1);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof node === \"object\") {\n\t\t\tfor (const item of Object.values(node))\n\t\t\t\tthis.collectStrings(item, out, depth + 1);\n\t\t}\n\t};\n\n\tprivate collectObjects = (\n\t\tnode: unknown,\n\t\tout: Record<string, unknown>[],\n\t\tdepth: number,\n\t): void => {\n\t\tif (\n\t\t\tdepth > this.MAX_DEPTH ||\n\t\t\tnode === null ||\n\t\t\tnode === undefined ||\n\t\t\tout.length >= this.MAX_OBJECTS\n\t\t)\n\t\t\treturn;\n\n\t\tif (Array.isArray(node)) {\n\t\t\tfor (const item of node) this.collectObjects(item, out, depth + 1);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof node !== \"object\") return;\n\t\tout.push(node as Record<string, unknown>);\n\n\t\tfor (const item of Object.values(node))\n\t\t\tthis.collectObjects(item, out, depth + 1);\n\t};\n\n\t/** The first non-empty string among the named fields. */\n\tprivate pick = (source: unknown, ...names: string[]): string | undefined => {\n\t\tif (source === null || source === undefined || typeof source !== \"object\")\n\t\t\treturn undefined;\n\n\t\tconst record = source as Record<string, unknown>;\n\n\t\tfor (const name of names) {\n\t\t\tconst value = record[name];\n\n\t\t\tif (typeof value === \"string\" && value.trim() !== \"\") return value.trim();\n\t\t\tif (typeof value === \"number\") return String(value);\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\t/** The first finite number among the named fields. */\n\tprivate numberOf = (\n\t\tsource: unknown,\n\t\t...names: string[]\n\t): number | undefined => {\n\t\tif (source === null || source === undefined || typeof source !== \"object\")\n\t\t\treturn undefined;\n\n\t\tconst record = source as Record<string, unknown>;\n\n\t\tfor (const name of names) {\n\t\t\tconst value = record[name];\n\n\t\t\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\n\t\t\t// A JSON payload may quote a count; a string that is not a number is\n\t\t\t// not a count.\n\t\t\tif (typeof value === \"string\" && value.trim() !== \"\") {\n\t\t\t\tconst parsed = Number(value);\n\n\t\t\t\tif (Number.isFinite(parsed)) return parsed;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\t/**\n\t * How many items the server says matched, when a root record reports it.\n\t *\n\t * Only the roots are read, never the hundreds of nested objects: `total` is\n\t * a page-level fact, and a nested one belongs to something else.\n\t */\n\tprivate totalOf = (roots: readonly unknown[]): number | undefined => {\n\t\tlet total: number | undefined;\n\n\t\tconst raise = (candidate: number | undefined): void => {\n\t\t\tif (candidate === undefined) return;\n\t\t\ttotal = total === undefined ? candidate : Math.max(total, candidate);\n\t\t};\n\n\t\tfor (const root of roots) {\n\t\t\tif (Array.isArray(root)) {\n\t\t\t\traise(root.length);\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\traise(\n\t\t\t\tthis.numberOf(this.asRecord(root), \"total\", \"totalCount\", \"totalSize\"),\n\t\t\t);\n\t\t}\n\n\t\treturn total;\n\t};\n\n\t/** A duration estimate, in Jira's own words when it gave them. */\n\tprivate estimateOf = (\n\t\tfields: Record<string, unknown>,\n\t): string | undefined => {\n\t\tconst tracking =\n\t\t\tthis.asRecord(fields.timetracking) ?? this.asRecord(fields.timeTracking);\n\n\t\tconst stated = this.pick(tracking, \"originalEstimate\", \"remainingEstimate\");\n\n\t\tif (stated !== undefined) return stated;\n\n\t\tconst seconds = this.numberOf(\n\t\t\ttracking,\n\t\t\t\"originalEstimateSeconds\",\n\t\t\t\"remainingEstimateSeconds\",\n\t\t);\n\n\t\tif (seconds !== undefined) return formatDuration(seconds);\n\n\t\treturn formatDuration(\n\t\t\tthis.numberOf(\n\t\t\t\tfields,\n\t\t\t\t\"timeoriginalestimate\",\n\t\t\t\t\"aggregateoriginalestimate\",\n\t\t\t\t\"timeestimate\",\n\t\t\t\t\"aggregatetimeestimate\",\n\t\t\t) ?? 0,\n\t\t);\n\t};\n\n\t/**\n\t * The plain text of a rich field.\n\t *\n\t * A description arrives as markdown text from one server generation and as\n\t * an Atlassian Document Format tree from the other, so this reads both: text\n\t * nodes in order, with a break between the blocks that carry one.\n\t */\n\tprivate plainText = (value: unknown): string | undefined => {\n\t\t// A markdown field is where a provider's own notes-to-editors hide; the ADF\n\t\t// branch below carries text nodes, which have no comments to remove.\n\t\tif (typeof value === \"string\") return this.tidy(stripHtmlComments(value));\n\n\t\tconst text = this.tidy(this.richText(value, 0));\n\n\t\treturn text === \"\" ? undefined : text;\n\t};\n\n\t/**\n\t * The plain text of a search snippet.\n\t *\n\t * A Confluence search result carries an `excerpt` that is not a document but\n\t * a fragment of rendered HTML, entities and all.\n\t */\n\tprivate snippetText = (value: unknown): string | undefined => {\n\t\tif (typeof value !== \"string\") return this.plainText(value);\n\n\t\tconst stripped = value\n\t\t\t.replace(/<[^>]*>/gu, \" \")\n\t\t\t.replace(/</gu, \"<\")\n\t\t\t.replace(/>/gu, \">\")\n\t\t\t.replace(/"/gu, '\"')\n\t\t\t.replace(/'/gu, \"'\")\n\t\t\t.replace(/ /gu, \" \")\n\t\t\t.replace(/&/gu, \"&\");\n\n\t\treturn this.plainText(stripped);\n\t};\n\n\tprivate richText = (node: unknown, depth: number): string => {\n\t\tif (depth > this.MAX_DEPTH || node === null || node === undefined)\n\t\t\treturn \"\";\n\n\t\tif (typeof node === \"string\") return node;\n\t\tif (typeof node === \"number\" || typeof node === \"boolean\")\n\t\t\treturn ` ${String(node)} `;\n\n\t\tif (Array.isArray(node))\n\t\t\treturn node.map((item) => this.richText(item, depth + 1)).join(\"\");\n\n\t\tif (typeof node !== \"object\") return \"\";\n\n\t\tconst record = node as Record<string, unknown>;\n\n\t\tconst body =\n\t\t\ttypeof record.text === \"string\"\n\t\t\t\t? record.text\n\t\t\t\t: record.content === undefined\n\t\t\t\t\t? \"\"\n\t\t\t\t\t: this.richText(record.content, depth + 1);\n\n\t\treturn this.BLOCK_TYPES.has(String(record.type)) ? `${body}\\n\\n` : body;\n\t};\n\n\t/** Collapse the whitespace a markdown or ADF body is full of. */\n\tprivate tidy = (value: string): string => {\n\t\treturn value\n\t\t\t.replace(/\\r\\n?/gu, \"\\n\")\n\t\t\t.replace(/[^\\S\\n]+/gu, \" \")\n\t\t\t.replace(/\\n{3,}/gu, \"\\n\\n\")\n\t\t\t.trim()\n\t\t\t.slice(0, this.MAX_BODY);\n\t};\n\n\tprivate asRecord = (value: unknown): Record<string, unknown> | undefined => {\n\t\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n\t\t\t? (value as Record<string, unknown>)\n\t\t\t: undefined;\n\t};\n\n\tprivate compact = <T extends object>(value: T): T => {\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value).filter(\n\t\t\t\t([, item]) => item !== undefined && item !== \"\",\n\t\t\t),\n\t\t) as T;\n\t};\n\n\t/** The first absolute `/browse/<key>` URL in the payload, else the site's own. */\n\tprivate issueUrl = (\n\t\tstrings: readonly string[],\n\t\tkey: string,\n\t\tsiteUrl: string | undefined,\n\t): string | undefined => {\n\t\tfor (const text of strings) {\n\t\t\tif (!text.includes(\"/browse/\") || !text.includes(key)) continue;\n\n\t\t\tconst match = text.match(\n\t\t\t\t/https?:\\/\\/[^\\s\"')<>]*\\/browse\\/[A-Z][A-Z0-9_]{0,9}-\\d+/u,\n\t\t\t);\n\n\t\t\tif (match !== null) return match[0];\n\t\t}\n\n\t\treturn siteUrl === undefined ? undefined : `${siteUrl}/browse/${key}`;\n\t};\n\n\t/**\n\t * Resolve a possibly relative Atlassian path against the site's own root.\n\t *\n\t * A Confluence search answers with `\"/spaces/KEY/pages/123/Title\"`, which is\n\t * relative to the wiki (`https://site.atlassian.net/wiki`), while a `webui`\n\t * link that already starts with `/wiki` is relative to the site itself. The\n\t * payload's own absolute URLs say which site that is; the learned site is\n\t * the fallback when the payload never spells one out.\n\t */\n\tprivate absoluteUrl = (\n\t\traw: string | undefined,\n\t\tstrings: readonly string[],\n\t\tsiteUrl: string | undefined,\n\t): string | undefined => {\n\t\tif (raw === undefined) return undefined;\n\n\t\tif (raw.startsWith(\"http\")) return raw;\n\t\tif (!raw.startsWith(\"/\")) return undefined;\n\n\t\tconst site = this.siteBase(strings) ?? siteUrl;\n\t\tif (site === undefined) return undefined;\n\n\t\treturn `${raw.startsWith(\"/wiki/\") ? site : `${site}/wiki`}${raw}`;\n\t};\n\n\t/** The first `https://<site>.atlassian.net` the payload spells out. */\n\tprivate siteBase = (strings: readonly string[]): string | undefined => {\n\t\tfor (const text of strings) {\n\t\t\tconst match = text.match(/https?:\\/\\/[^\\s\"')<>]*atlassian\\.net/u);\n\n\t\t\tif (match !== null) return match[0];\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\tprivate issueCards = (\n\t\tobjects: readonly Record<string, unknown>[],\n\t\tstrings: readonly string[],\n\t\tsiteUrl: string | undefined,\n\t\tbodies: boolean,\n\t): IssueCard[] => {\n\t\tconst issues = new Map<string, IssueCard>();\n\n\t\tfor (const node of objects) {\n\t\t\tconst key = this.pick(node, \"key\");\n\n\t\t\tif (key === undefined || !this.ISSUE_KEY.test(key) || issues.has(key))\n\t\t\t\tcontinue;\n\n\t\t\t// A Jira REST issue nests its fields; an Atlassian MCP payload may not.\n\t\t\tconst fields = this.asRecord(node.fields) ?? node;\n\t\t\tconst rawText = bodies ? this.plainText(fields.description) : undefined;\n\n\t\t\tissues.set(\n\t\t\t\tkey,\n\t\t\t\tthis.compact({\n\t\t\t\t\tkey,\n\t\t\t\t\tsummary: this.pick(fields, \"summary\", \"name\", \"title\"),\n\t\t\t\t\tstatus:\n\t\t\t\t\t\tthis.pick(this.asRecord(fields.status) ?? fields.status, \"name\") ??\n\t\t\t\t\t\tthis.pick(fields, \"status\"),\n\t\t\t\t\tassignee:\n\t\t\t\t\t\tthis.pick(\n\t\t\t\t\t\t\tthis.asRecord(fields.assignee) ?? fields.assignee,\n\t\t\t\t\t\t\t\"displayName\",\n\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t) ?? this.pick(fields, \"assignee\"),\n\t\t\t\t\tpriority:\n\t\t\t\t\t\tthis.pick(\n\t\t\t\t\t\t\tthis.asRecord(fields.priority) ?? fields.priority,\n\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t) ?? this.pick(fields, \"priority\"),\n\t\t\t\t\ttype:\n\t\t\t\t\t\tthis.pick(\n\t\t\t\t\t\t\tthis.asRecord(fields.issuetype) ?? fields.issuetype,\n\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t) ?? this.pick(fields, \"issuetype\"),\n\t\t\t\t\testimate: this.estimateOf(fields),\n\t\t\t\t\turl: this.issueUrl(strings, key, siteUrl),\n\t\t\t\t\trawText,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (issues.size >= this.MAX_CARDS) break;\n\t\t}\n\n\t\treturn [...issues.values()];\n\t};\n\n\tprivate pageCards = (\n\t\tobjects: readonly Record<string, unknown>[],\n\t\tstrings: readonly string[],\n\t\tsiteUrl: string | undefined,\n\t\tbodies: boolean,\n\t): PageCard[] => {\n\t\tconst pages = new Map<string, PageCard>();\n\n\t\tfor (const node of objects) {\n\t\t\tconst title = this.pick(node, \"title\");\n\n\t\t\tif (title === undefined) continue;\n\n\t\t\tconst id = this.pick(node, \"id\");\n\t\t\t// `_links.webui` is the canonical page path; `_links.base` is the\n\t\t\t// site's own root, which is not where a title should lead.\n\t\t\tconst raw =\n\t\t\t\tthis.pick(node, \"url\") ??\n\t\t\t\tthis.pick(this.asRecord(node._links), \"webui\");\n\n\t\t\tif (id === undefined && raw === undefined) continue;\n\n\t\t\tconst identity = raw ?? id ?? title;\n\n\t\t\tif (pages.has(identity)) continue;\n\n\t\t\tconst url =\n\t\t\t\tthis.absoluteUrl(raw, strings, siteUrl) ??\n\t\t\t\t(id !== undefined && siteUrl !== undefined\n\t\t\t\t\t? `${siteUrl}/wiki/pages/viewpage.action?pageId=${id}`\n\t\t\t\t\t: undefined);\n\n\t\t\t// A full page carries its body; a search result carries only an\n\t\t\t// HTML-ish snippet. Either is enough for the one sentence shown.\n\t\t\tconst rawText = !bodies\n\t\t\t\t? undefined\n\t\t\t\t: node.body === undefined\n\t\t\t\t\t? this.snippetText(node.excerpt)\n\t\t\t\t\t: this.plainText(node.body);\n\n\t\t\tpages.set(\n\t\t\t\tidentity,\n\t\t\t\tthis.compact({\n\t\t\t\t\tid,\n\t\t\t\t\ttitle,\n\t\t\t\t\turl,\n\t\t\t\t\tspace: this.pick(\n\t\t\t\t\t\tthis.asRecord(node.space) ?? node.space,\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t),\n\t\t\t\t\trawText,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (pages.size >= this.MAX_CARDS) break;\n\t\t}\n\n\t\treturn [...pages.values()];\n\t};\n}\n\nexport const extractor = new Extractor();\n", "/**\n * What Atlassian contributes to a chain.\n *\n * One entry kind, `task`: asked about a key, it answers with the Jira issue the\n * key names \u2014 its summary, its status, and whom it is with. It is the end of a\n * chain as written today, because a tracker issue does not itself point at code;\n * if it ever does, its own links become references and this source grows a kind\n * to answer for them.\n *\n * The source registers itself with the correlation registry rather than being\n * imported by it; a deployment without the correlation plugin never calls this.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport {\n\tCORRELATION_SOURCES,\n\ttype CorrelationFact,\n\ttype CorrelationRef,\n\ttype CorrelationSource,\n\ttype RuntimeState,\n} from \"dsh-integration-host\";\nimport { asRecord, type McpBridge, pickString } from \"dsh-mcp-oauth\";\nimport { extractor } from \"./presentation/extract\";\nimport type { SiteFacts } from \"./store/site\";\n\n/** The tool that reads one issue. */\nconst ISSUE_TOOL = \"getJiraIssue\";\n\n/** The fields a chain hop shows, and no more. */\nconst ISSUE_FIELDS = [\"summary\", \"status\", \"assignee\", \"issuetype\", \"priority\"];\n\n/** Everything the source reads through, wired once at apply time. */\nexport interface CorrelationDeps {\n\treadonly bridge: McpBridge;\n\treadonly state: RuntimeState<SiteFacts>;\n}\n\n/** The named value of a Jira field, which may arrive as an object or a string. */\nconst nameOf = (value: unknown): string | undefined =>\n\ttypeof value === \"string\" && value !== \"\"\n\t\t? value\n\t\t: pickString(asRecord(value) ?? {}, \"displayName\", \"name\", \"value\");\n\n/** What one issue answers with. */\nconst factOf = (\n\tkey: string,\n\tvalue: unknown,\n\tsiteUrl?: string,\n): CorrelationFact => {\n\tconst record = asRecord(value) ?? {};\n\tconst fields = asRecord(record.fields) ?? record;\n\tconst identity = pickString(record, \"key\") ?? key;\n\tconst summary = extractor.text(pickString(fields, \"summary\"));\n\tconst parts = [\n\t\tnameOf(fields.status),\n\t\tnameOf(fields.issuetype),\n\t\tnameOf(fields.priority),\n\t\tnameOf(fields.assignee),\n\t].filter((part): part is string => part !== undefined && part !== \"\");\n\n\treturn {\n\t\tid: `jira:${identity}`,\n\t\tprovider: \"Atlassian\",\n\t\tkind: \"task\",\n\t\tlabel: \"Jira issue\",\n\t\ttitle: summary === undefined ? identity : `${identity} \u00B7 ${summary}`,\n\t\t...(siteUrl === undefined ? {} : { url: `${siteUrl}/browse/${identity}` }),\n\t\t...(parts.length === 0 ? {} : { meta: parts.join(\" \u00B7 \") }),\n\t};\n};\n\n/** The Atlassian contribution. */\nexport const atlassianCorrelationSource = (\n\tdeps: CorrelationDeps,\n): CorrelationSource => ({\n\tprovider: \"Atlassian\",\n\tentry: \"task\",\n\tresolves: [\"task\"],\n\tresolve: async (ref: CorrelationRef) => {\n\t\tif (ref.kind !== \"task\") return [];\n\n\t\tconst cloudId = deps.state.facts?.cloudId;\n\n\t\tif (cloudId === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t\"no Atlassian site known yet; sign in again or run /atlassian sync\",\n\t\t\t);\n\n\t\tconst value = await deps.bridge.callTool(ISSUE_TOOL, {\n\t\t\tcloudId,\n\t\t\tissueIdOrKey: ref.key,\n\t\t\tfields: ISSUE_FIELDS,\n\t\t\tresponseContentFormat: \"markdown\",\n\t\t});\n\n\t\treturn [factOf(ref.key, value, deps.state.facts?.siteUrl)];\n\t},\n});\n\n/**\n * Offer Atlassian's chain contribution to the correlation plugin, if it is installed.\n * @param ctx - the plugin's host context.\n * @param deps - the MCP bridge and the site facts.\n */\nexport const registerCorrelationSource = (\n\tctx: Context,\n\tdeps: CorrelationDeps,\n): void => {\n\tctx.inject([CORRELATION_SOURCES], (scoped) => {\n\t\tscoped.effect(\n\t\t\t() =>\n\t\t\t\tscoped.correlationSources.register(atlassianCorrelationSource(deps)),\n\t\t\t\"atlassian: correlation source\",\n\t\t);\n\t});\n};\n", "/**\n * What Atlassian contributes to a digest.\n *\n * The two Jira blocks are the ones a morning digest is for: work already in\n * flight (including anything overdue), and work assigned but not started. Both\n * are asked for in JQL \u2014 the site's own vocabulary \u2014 and read through the same\n * extractor the `atlassian_present` tool uses, so the digest and the card can\n * never disagree about what a Jira issue looks like.\n *\n * The source is registered through the digest registry rather than imported by\n * it: a deployment without the digest plugin never calls this, and no plugin\n * reaches into another's code.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport {\n\tDIGEST_SOURCES,\n\ttype DigestItem,\n\ttype DigestLoadOptions,\n\ttype DigestSource,\n\ttype RuntimeState,\n} from \"dsh-integration-host\";\nimport type { McpBridge } from \"dsh-mcp-oauth\";\nimport type { IssueCard } from \"../shared/meta\";\nimport { extractor } from \"./presentation/extract\";\nimport type { SiteFacts } from \"./store/site\";\n\n/** The Jira tool the two blocks read through. */\nconst SEARCH_TOOL = \"searchJiraIssuesUsingJql\";\n\n/** The fields a digest row needs, and nothing a summary pass would want. */\nconst ISSUE_FIELDS = [\n\t\"summary\",\n\t\"status\",\n\t\"assignee\",\n\t\"priority\",\n\t\"issuetype\",\n\t\"duedate\",\n];\n\n/**\n * The query behind each block.\n *\n * `statusCategory != Done` keeps both blocks to open work; the active block adds\n * anything whose due date has passed, because an overdue task is work in flight\n * whatever its status column says.\n */\nconst JQL: Readonly<Record<string, string>> = {\n\t\"jira-active\":\n\t\t'assignee = currentUser() AND statusCategory != Done AND (statusCategory = \"In Progress\" OR duedate <= now()) ORDER BY duedate ASC',\n\t\"jira-queued\":\n\t\t'assignee = currentUser() AND statusCategory = \"To Do\" ORDER BY created DESC',\n};\n\n/** Everything the source reads through, wired once at apply time. */\nexport interface DigestDeps {\n\treadonly bridge: McpBridge;\n\treadonly state: RuntimeState<SiteFacts>;\n}\n\n/** One issue, as a digest row. */\nconst toItem = (issue: IssueCard): DigestItem => {\n\tconst meta = [issue.status, issue.priority].filter(Boolean).join(\" \u00B7 \");\n\n\treturn {\n\t\tid: issue.key,\n\t\ttitle: [issue.key, issue.summary].filter(Boolean).join(\" \u00B7 \"),\n\t\t...(issue.url === undefined ? {} : { url: issue.url }),\n\t\t...(meta === \"\" ? {} : { meta }),\n\t};\n};\n\n/** The Atlassian contribution. */\nexport const atlassianDigestSource = (deps: DigestDeps): DigestSource => ({\n\tprovider: \"Atlassian\",\n\tblocks: [\n\t\t{\n\t\t\tid: \"jira-active\",\n\t\t\ttitle: \"Jira \u00B7 In progress and overdue\",\n\t\t\tdefaultOn: true,\n\t\t},\n\t\t{\n\t\t\tid: \"jira-queued\",\n\t\t\ttitle: \"Jira \u00B7 Assigned, not started\",\n\t\t\tdefaultOn: true,\n\t\t},\n\t],\n\tload: async (\n\t\tblockId: string,\n\t\toptions: DigestLoadOptions,\n\t): Promise<readonly DigestItem[]> => {\n\t\tconst jql = JQL[blockId];\n\n\t\tif (jql === undefined) throw new Error(`no Jira block named \"${blockId}\"`);\n\n\t\tconst cloudId = deps.state.facts?.cloudId;\n\n\t\tif (cloudId === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t\"the Atlassian site is not known yet; run /atlassian sync\",\n\t\t\t);\n\n\t\tconst value = await deps.bridge.callTool(SEARCH_TOOL, {\n\t\t\tcloudId,\n\t\t\tjql,\n\t\t\tfields: ISSUE_FIELDS,\n\t\t\tmaxResults: options.limit,\n\t\t});\n\n\t\treturn extractor\n\t\t\t.extract({\n\t\t\t\ttool: SEARCH_TOOL,\n\t\t\t\targs: {},\n\t\t\t\tvalue,\n\t\t\t\tsiteUrl: deps.state.facts?.siteUrl,\n\t\t\t})\n\t\t\t.issues.slice(0, options.limit)\n\t\t\t.map(toItem);\n\t},\n});\n\n/**\n * Offer Atlassian's blocks to the digest plugin, if it is installed.\n *\n * A scoped `inject` is what makes the digest plugin optional: the registration\n * waits for the service and is withdrawn with it, and a deployment that never\n * loads the digest plugin simply leaves this fiber pending.\n * @param ctx - the plugin's host context.\n * @param deps - the MCP bridge and the site facts.\n */\nexport const registerDigestSource = (ctx: Context, deps: DigestDeps): void => {\n\tctx.inject([DIGEST_SOURCES], (scoped) => {\n\t\tscoped.effect(\n\t\t\t() => scoped.digestSources.register(atlassianDigestSource(deps)),\n\t\t\t\"atlassian: digest source\",\n\t\t);\n\t});\n};\n", "/**\n * Loading what the side panel shows.\n *\n * The panel is the second reader of the MCP connection: where a card wants a\n * summary, the panel wants the whole issue \u2014 description, comments, the\n * transitions the workflow offers right now \u2014 and it is the only place this\n * plugin writes to Atlassian at all (a status transition, an assignee).\n *\n * Every read is best-effort around the edges. A transition list that fails to\n * load, or a comment section the server declines, leaves the rest of the\n * document intact: a panel missing one section still answers the question the\n * user clicked on. Writes are the opposite \u2014 a write that fails is reported,\n * because silence there would look like success.\n */\n\nimport type { RuntimeState } from \"dsh-integration-host\";\nimport type { McpBridge } from \"dsh-mcp-oauth\";\nimport { asRecord, mcpJson, pickNumber, pickString } from \"dsh-mcp-oauth\";\nimport type {\n\tIssuePanel,\n\tPagePanel,\n\tPanelComment,\n\tPanelPerson,\n\tPanelTransition,\n} from \"../../shared/panel\";\nimport { extractor, formatDuration } from \"../presentation/extract\";\nimport type { SiteFacts } from \"../store/site\";\n\n/** How many comments to show on one panel. */\nconst COMMENT_LIMIT = 25;\n\n/** How many people one lookup answers with. */\nconst PEOPLE_LIMIT = 8;\n\nexport interface PanelDeps {\n\treadonly bridge: McpBridge;\n\treadonly state: RuntimeState<SiteFacts>;\n}\n\nexport class PanelApi {\n\tconstructor(private readonly deps: PanelDeps) {}\n\n\t/** The issue fields the panel renders. */\n\tprivate readonly ISSUE_FIELDS = [\n\t\t\"summary\",\n\t\t\"description\",\n\t\t\"status\",\n\t\t\"issuetype\",\n\t\t\"priority\",\n\t\t\"assignee\",\n\t\t\"reporter\",\n\t\t\"created\",\n\t\t\"updated\",\n\t\t\"duedate\",\n\t\t\"labels\",\n\t\t\"resolution\",\n\t\t\"timetracking\",\n\t\t\"timeoriginalestimate\",\n\t];\n\n\t/** Everything one issue panel shows. */\n\tissue = async (key: string): Promise<IssuePanel> => {\n\t\tconst cloudId = this.cloudId();\n\n\t\t// The transitions are a second call, and one that may legitimately fail\n\t\t// (a workflow the token cannot read): the issue is what matters.\n\t\tconst [detail, transitions] = await Promise.all([\n\t\t\tthis.deps.bridge.callTool(\"getJiraIssue\", {\n\t\t\t\tcloudId,\n\t\t\t\tissueIdOrKey: key,\n\t\t\t\tfields: [...this.ISSUE_FIELDS, \"comment\"],\n\t\t\t\tresponseContentFormat: \"markdown\",\n\t\t\t}),\n\t\t\tthis.deps.bridge\n\t\t\t\t.callTool(\"getTransitionsForJiraIssue\", {\n\t\t\t\t\tcloudId,\n\t\t\t\t\tissueIdOrKey: key,\n\t\t\t\t})\n\t\t\t\t.then((value) => this.transitionsOf(value))\n\t\t\t\t.catch(() => [] as PanelTransition[]),\n\t\t]);\n\n\t\tconst record = this.issueRecord(detail);\n\t\tif (record === undefined)\n\t\t\tthrow new Error(`Atlassian returned no issue for ${key}`);\n\n\t\tconst fields = asRecord(record.fields) ?? record;\n\t\tconst identity = pickString(record, \"key\") ?? key;\n\t\tconst summary = extractor.text(pickString(fields, \"summary\"));\n\n\t\treturn {\n\t\t\tkind: \"issue\",\n\t\t\tkey: identity,\n\t\t\t...(summary === undefined ? {} : { summary }),\n\t\t\ttype: pickString(asRecord(fields.issuetype) ?? fields.issuetype, \"name\"),\n\t\t\tstatus: pickString(asRecord(fields.status) ?? fields.status, \"name\"),\n\t\t\tpriority: pickString(\n\t\t\t\tasRecord(fields.priority) ?? fields.priority,\n\t\t\t\t\"name\",\n\t\t\t),\n\t\t\tassignee: this.personOf(fields.assignee),\n\t\t\treporter: pickString(\n\t\t\t\tasRecord(fields.reporter) ?? fields.reporter,\n\t\t\t\t\"displayName\",\n\t\t\t\t\"name\",\n\t\t\t),\n\t\t\testimate: this.estimateOf(fields),\n\t\t\tdue: pickString(fields, \"duedate\"),\n\t\t\tlabels: Array.isArray(fields.labels)\n\t\t\t\t? fields.labels.filter(\n\t\t\t\t\t\t(label): label is string => typeof label === \"string\",\n\t\t\t\t\t)\n\t\t\t\t: [],\n\t\t\tcreated: pickString(fields, \"created\"),\n\t\t\tupdated: pickString(fields, \"updated\"),\n\t\t\tdescription: extractor.text(fields.description),\n\t\t\turl:\n\t\t\t\tthis.deps.state.facts?.siteUrl === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: `${this.deps.state.facts?.siteUrl}/browse/${identity}`,\n\t\t\tcomments: this.commentsOf(fields.comment),\n\t\t\ttransitions,\n\t\t};\n\t};\n\n\t/** Everything one page panel shows. */\n\tpage = async (id: string): Promise<PagePanel> => {\n\t\tconst cloudId = this.cloudId();\n\n\t\tconst [detail, comments] = await Promise.all([\n\t\t\tthis.deps.bridge.callTool(\"getConfluencePage\", {\n\t\t\t\tcloudId,\n\t\t\t\tpageId: id,\n\t\t\t\tcontentFormat: \"markdown\",\n\t\t\t}),\n\t\t\tthis.deps.bridge\n\t\t\t\t.callTool(\"getConfluencePageFooterComments\", {\n\t\t\t\t\tcloudId,\n\t\t\t\t\tpageId: id,\n\t\t\t\t\tlimit: COMMENT_LIMIT,\n\t\t\t\t})\n\t\t\t\t.catch(() => undefined),\n\t\t]);\n\n\t\tconst record = this.firstRecord(detail);\n\t\tif (record === undefined)\n\t\t\tthrow new Error(`Atlassian returned no page for ${id}`);\n\n\t\tconst title = pickString(record, \"title\") ?? id;\n\t\tconst webui = pickString(asRecord(record._links), \"webui\");\n\n\t\treturn {\n\t\t\tkind: \"page\",\n\t\t\tid: pickString(record, \"id\") ?? id,\n\t\t\ttitle,\n\t\t\tspace: pickString(asRecord(record.space) ?? record.space, \"name\", \"key\"),\n\t\t\turl:\n\t\t\t\twebui === undefined\n\t\t\t\t\t? this.deps.state.facts?.siteUrl === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: `${this.deps.state.facts?.siteUrl}/wiki/pages/viewpage.action?pageId=${id}`\n\t\t\t\t\t: this.wikiUrl(webui, this.deps.state.facts?.siteUrl),\n\t\t\tbody: extractor.text(record.body),\n\t\t\tcomments: this.commentsOf(asRecord(comments)?.results),\n\t\t};\n\t};\n\n\t/** People the site matches to one search string. */\n\tpeople = async (query: string): Promise<PanelPerson[]> => {\n\t\tconst value = await this.deps.bridge.callTool(\"lookupJiraAccountId\", {\n\t\t\tcloudId: this.cloudId(),\n\t\t\tsearchString: query,\n\t\t\tshowAvatar: false,\n\t\t});\n\n\t\tconst users = this.userList(value);\n\n\t\treturn users\n\t\t\t.map((user) => ({\n\t\t\t\tname: pickString(user, \"displayName\") ?? \"\",\n\t\t\t\taccountId: pickString(user, \"accountId\") ?? \"\",\n\t\t\t}))\n\t\t\t.filter((person) => person.name !== \"\" && person.accountId !== \"\")\n\t\t\t.slice(0, PEOPLE_LIMIT);\n\t};\n\n\t/**\n\t * The signed-in user, as the site names them.\n\t *\n\t * `atlassianUserInfo` answers about the account the connection belongs to, in\n\t * a shape of its own; the first record that carries an account id is the\n\t * answer. A site that names none leaves the panel without a self-assignment\n\t * rather than offering one Jira would refuse.\n\t * @returns the reader, or undefined when the answer names no account.\n\t */\n\tme = async (): Promise<PanelPerson | undefined> => {\n\t\tconst value = await this.deps.bridge.callTool(\"atlassianUserInfo\", {});\n\n\t\treturn this.personIn(value);\n\t};\n\n\t/** Move one issue through a workflow transition. */\n\ttransition = async (key: string, transitionId: string): Promise<void> => {\n\t\tawait this.deps.bridge.callTool(\"transitionJiraIssue\", {\n\t\t\tcloudId: this.cloudId(),\n\t\t\tissueIdOrKey: key,\n\t\t\ttransition: { id: transitionId },\n\t\t});\n\t};\n\n\t/** Set or clear one issue's assignee. */\n\tassign = async (key: string, accountId: string | null): Promise<void> => {\n\t\tawait this.deps.bridge.callTool(\"editJiraIssue\", {\n\t\t\tcloudId: this.cloudId(),\n\t\t\tissueIdOrKey: key,\n\t\t\tfields: { assignee: accountId === null ? null : { accountId } },\n\t\t});\n\t};\n\n\t/** The connected site's cloud id, or a message the user can act on. */\n\tprivate cloudId = (): string => {\n\t\tif (this.deps.state.facts?.cloudId === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t\"no Atlassian site known yet \u2014 sign in from Settings, then try again\",\n\t\t\t);\n\n\t\treturn this.deps.state.facts?.cloudId;\n\t};\n\n\t/** Jira's own estimate words, or a duration rendered from raw seconds. */\n\tprivate estimateOf = (\n\t\tfields: Record<string, unknown>,\n\t): string | undefined => {\n\t\tconst tracking = asRecord(fields.timetracking);\n\t\tconst stated = pickString(\n\t\t\ttracking,\n\t\t\t\"originalEstimate\",\n\t\t\t\"remainingEstimate\",\n\t\t);\n\n\t\tif (stated !== undefined) return stated;\n\n\t\tconst seconds = pickNumber(\n\t\t\ttracking,\n\t\t\t\"originalEstimateSeconds\",\n\t\t\t\"remainingEstimateSeconds\",\n\t\t);\n\n\t\tif (seconds !== undefined) return formatDuration(seconds);\n\n\t\treturn formatDuration(\n\t\t\tpickNumber(fields, \"timeoriginalestimate\", \"timeestimate\") ?? 0,\n\t\t);\n\t};\n\n\tprivate personOf = (value: unknown): PanelPerson | undefined => {\n\t\tconst record = asRecord(value);\n\t\tconst accountId = pickString(record, \"accountId\");\n\t\tconst name = pickString(record, \"displayName\", \"name\");\n\n\t\tif (accountId === undefined || name === undefined) return undefined;\n\n\t\treturn { accountId, name };\n\t};\n\n\t/** The Jira issue inside one `getJiraIssue` result. */\n\tprivate issueRecord = (value: unknown): Record<string, unknown> | undefined =>\n\t\tthis.firstRecord(value, (record) => asRecord(record.fields) !== undefined);\n\n\t/** The first JSON record in a result, or the first one a predicate accepts. */\n\tprivate firstRecord = (\n\t\tvalue: unknown,\n\t\taccept: (record: Record<string, unknown>) => boolean = () => true,\n\t): Record<string, unknown> | undefined => {\n\t\tfor (const root of mcpJson(value)) {\n\t\t\tconst record = asRecord(root);\n\t\t\tif (record !== undefined && accept(record)) return record;\n\n\t\t\tif (!Array.isArray(root)) continue;\n\n\t\t\tfor (const item of root) {\n\t\t\t\tconst candidate = asRecord(item);\n\t\t\t\tif (candidate !== undefined && accept(candidate)) return candidate;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\t/** The comment list one issue or page carries. */\n\tprivate commentsOf = (value: unknown): PanelComment[] => {\n\t\tconst list = Array.isArray(value)\n\t\t\t? value\n\t\t\t: Array.isArray(asRecord(value)?.comments)\n\t\t\t\t? (asRecord(value)?.comments as unknown[])\n\t\t\t\t: [];\n\n\t\tconst comments: PanelComment[] = [];\n\n\t\tfor (const item of list) {\n\t\t\tconst record = asRecord(item);\n\t\t\tif (record === undefined) continue;\n\n\t\t\tconst body = extractor.text(record.body);\n\t\t\tif (body === undefined) continue;\n\n\t\t\tconst author = pickString(\n\t\t\t\tasRecord(record.author) ?? record.author,\n\t\t\t\t\"displayName\",\n\t\t\t\t\"publicName\",\n\t\t\t\t\"name\",\n\t\t\t);\n\n\t\t\tcomments.push({\n\t\t\t\tbody,\n\t\t\t\t...(author === undefined ? {} : { author }),\n\t\t\t\t...(pickString(record, \"created\") === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { created: pickString(record, \"created\") }),\n\t\t\t});\n\n\t\t\tif (comments.length >= COMMENT_LIMIT) break;\n\t\t}\n\n\t\treturn comments;\n\t};\n\n\t/** The transitions a workflow currently offers. */\n\tprivate transitionsOf = (value: unknown): PanelTransition[] => {\n\t\tconst record = this.firstRecord(value);\n\t\tconst list = Array.isArray(record?.transitions) ? record.transitions : [];\n\n\t\tconst transitions: PanelTransition[] = [];\n\n\t\tfor (const item of list) {\n\t\t\tconst candidate = asRecord(item);\n\t\t\tif (candidate === undefined) continue;\n\n\t\t\tconst id = pickString(candidate, \"id\");\n\t\t\tconst name = pickString(candidate, \"name\");\n\t\t\tif (id === undefined || name === undefined) continue;\n\n\t\t\tconst to = asRecord(candidate.to);\n\n\t\t\ttransitions.push({\n\t\t\t\tid,\n\t\t\t\tname,\n\t\t\t\t...(pickString(to, \"name\") === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { to: pickString(to, \"name\") }),\n\t\t\t\t// Jira omits `isAvailable` when the transition is offered.\n\t\t\t\tavailable: candidate.isAvailable !== false,\n\t\t\t\tneedsFields: candidate.hasScreen === true,\n\t\t\t});\n\t\t}\n\n\t\treturn transitions;\n\t};\n\n\t/** The user list inside one `lookupJiraAccountId` result. */\n\tprivate userList = (value: unknown): Record<string, unknown>[] => {\n\t\tfor (const root of mcpJson(value)) {\n\t\t\tconst users = asRecord(asRecord(asRecord(root)?.data)?.users);\n\t\t\tconst list = users?.users;\n\n\t\t\tif (Array.isArray(list))\n\t\t\t\treturn list\n\t\t\t\t\t.map((item) => asRecord(item))\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(item): item is Record<string, unknown> => item !== undefined,\n\t\t\t\t\t);\n\t\t}\n\n\t\treturn [];\n\t};\n\n\t/** The first person named anywhere in one tool result. */\n\tprivate personIn = (value: unknown): PanelPerson | undefined => {\n\t\tfor (const root of mcpJson(value)) {\n\t\t\tconst person =\n\t\t\t\tthis.personOf(root) ?? this.personOf(asRecord(asRecord(root)?.data));\n\n\t\t\tif (person !== undefined) return person;\n\t\t}\n\n\t\treturn this.personOf(this.firstRecord(value));\n\t};\n\n\t/** A page's own path, resolved against the site's wiki. */\n\tprivate wikiUrl = (\n\t\traw: string,\n\t\tsiteUrl: string | undefined,\n\t): string | undefined => {\n\t\tif (raw.startsWith(\"http\")) return raw;\n\t\tif (!raw.startsWith(\"/\") || siteUrl === undefined) return undefined;\n\n\t\treturn `${raw.startsWith(\"/wiki/\") ? siteUrl : `${siteUrl}/wiki`}${raw}`;\n\t};\n}\n", "/**\n * The `/atlassian` slash command.\n *\n * A command runs directly against the agent without creating a model message,\n * which makes it the right surface for a human-driven action like signing in.\n * The command itself belongs to `dsh-integration-host`, which shares it with the\n * settings card so the two can never disagree about what \"signed in\" means.\n */\n\nimport {\n\ttype CommandResult,\n\tcreateIntegrationCommand,\n\ttype IntegrationAuthFace,\n} from \"dsh-integration-host\";\nimport type { AtlassianStatus } from \"../../shared/status\";\n\n/** The command's name, without the leading slash. */\nexport const COMMAND_NAME = \"atlassian\";\n\n/**\n * Build the handler the command registers.\n * @param auth - the one implementation of sign-in state.\n */\nexport const createCommandHandler = (\n\tauth: IntegrationAuthFace<AtlassianStatus>,\n): ((invocation: { rawInput?: unknown }) => Promise<CommandResult>) => {\n\treturn createIntegrationCommand<AtlassianStatus>(\n\t\t{ name: COMMAND_NAME, label: \"Atlassian\" },\n\t\tauth,\n\t);\n};\n", "/**\n * The localhost API the browser half calls.\n *\n * The settings card and the side panel live in the page, so they need a channel\n * to the host. A named route on the harness web server is that channel, and the\n * four paths every integration answers \u2014 status, login, logout, sync \u2014 come\n * from `dsh-integration-host`. What is left here is what is Atlassian's: the\n * reads and writes its side panel makes.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport {\n\tBadRequest,\n\tcreateIntegrationRoute,\n\ttype IntegrationAuthFace,\n\ttype RouteHandler,\n} from \"dsh-integration-host\";\nimport type { AtlassianStatus } from \"../../shared/status\";\nimport type { PanelApi } from \"../panels/panel\";\n\n/** The route prefix this plugin owns. */\nexport const ROUTE_PREFIX = \"/dsh-atlassian\";\n\n/** Everything the route answers with, wired once at apply time. */\nexport interface ApiDeps {\n\treadonly auth: IntegrationAuthFace<AtlassianStatus>;\n\treadonly panel: PanelApi;\n}\n\n/** The side panel's own paths, tried after the four shared ones. */\nconst panelRoutes = (panel: PanelApi): readonly RouteHandler[] => [\n\tasync (route) => {\n\t\tif (\n\t\t\troute.url.pathname !== `${ROUTE_PREFIX}/issue` ||\n\t\t\troute.method !== \"GET\"\n\t\t)\n\t\t\treturn false;\n\n\t\tconst key = route.url.searchParams.get(\"key\") ?? \"\";\n\n\t\tawait route.sendData(() => panel.issue(key));\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (route.url.pathname !== `${ROUTE_PREFIX}/page` || route.method !== \"GET\")\n\t\t\treturn false;\n\n\t\tconst id = route.url.searchParams.get(\"id\") ?? \"\";\n\n\t\tawait route.sendData(() => panel.page(id));\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (\n\t\t\troute.url.pathname !== `${ROUTE_PREFIX}/people` ||\n\t\t\troute.method !== \"GET\"\n\t\t)\n\t\t\treturn false;\n\n\t\tconst query = route.url.searchParams.get(\"q\") ?? \"\";\n\n\t\tawait route.sendData(() => panel.people(query));\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (route.url.pathname !== `${ROUTE_PREFIX}/me` || route.method !== \"GET\")\n\t\t\treturn false;\n\n\t\tawait route.sendData(async () => ({\n\t\t\tperson: (await panel.me()) ?? null,\n\t\t}));\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (\n\t\t\troute.url.pathname !== `${ROUTE_PREFIX}/issue/transition` ||\n\t\t\troute.method !== \"POST\"\n\t\t)\n\t\t\treturn false;\n\n\t\t// The request is read and validated before any work starts, so a caller's\n\t\t// mistake is answered as one instead of as a bad gateway.\n\t\tconst body = await route.read();\n\t\tconst key = body.required(\"key\");\n\t\tconst transitionId = body.required(\"transitionId\");\n\n\t\tawait route.sendData(async () => {\n\t\t\tawait panel.transition(key, transitionId);\n\n\t\t\treturn await panel.issue(key);\n\t\t});\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (\n\t\t\troute.url.pathname !== `${ROUTE_PREFIX}/issue/assignee` ||\n\t\t\troute.method !== \"POST\"\n\t\t)\n\t\t\treturn false;\n\n\t\tconst body = await route.read();\n\t\tconst key = body.required(\"key\");\n\t\tconst accountId = body.fields?.accountId;\n\n\t\tif (accountId !== null && typeof accountId !== \"string\")\n\t\t\tthrow new BadRequest('the request needs an \"accountId\" or null');\n\n\t\tawait route.sendData(async () => {\n\t\t\tawait panel.assign(key, accountId);\n\n\t\t\treturn await panel.issue(key);\n\t\t});\n\n\t\treturn true;\n\t},\n];\n\n/**\n * Build the request handler.\n * @param deps - the plugin's collaborators.\n */\nexport const createApiHandler = (\n\tdeps: ApiDeps,\n): ((request: IncomingMessage, response: ServerResponse) => Promise<void>) => {\n\treturn createIntegrationRoute({\n\t\tprefix: ROUTE_PREFIX,\n\t\tauth: deps.auth,\n\t\troutes: panelRoutes(deps.panel),\n\t});\n};\n", "/**\n * Compress the long text behind a card into the one line it can show.\n *\n * A Jira description is a page of context and a Confluence body is more, so the\n * card carries neither: it carries one sentence per item, written by the same\n * model the conversation runs on. That keeps the card informative without\n * turning it into a wall of text, and it costs one auxiliary call per card.\n *\n * Everything here is best-effort. No model route, no `llm` service, a slow or\n * failing provider \u2014 each of those drops the sentences and leaves the rest of\n * the card intact, because a card without a summary is still a useful card.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type {} from \"@deepseek-ai/dsh-llm\";\nimport type { ToolExecution } from \"@deepseek-ai/dsh-tools\";\n\n/** One item to compress, as the card already knows it. */\nexport interface GistTarget {\n\t/** The caller's own identity for the item; echoed back, never shown. */\n\treadonly id: string;\n\t/** The item's own heading: an issue summary or a page title. */\n\treadonly title: string;\n\t/** The long text to compress. */\n\treadonly body: string;\n}\n\n/** One sentence, tied back to the item it describes. */\nexport interface Gist {\n\treadonly id: string;\n\treadonly text: string;\n}\n\n/** The provider and model an auxiliary call should use. */\nexport interface GistRoute {\n\treadonly provider: string;\n\treadonly model: string;\n}\n\nexport interface GistOptions {\n\t/** The turn's own route, or undefined when it could not be resolved. */\n\treadonly route: GistRoute | undefined;\n\t/** The tool call's signal; cancelling the call cancels the summary. */\n\treadonly signal: AbortSignal | undefined;\n}\n\n/** Longest body sent for one item. */\nconst MAX_BODY_CHARS = 900;\n\n/** Longest prompt built in total; beyond this, bodies are dropped, titles stay. */\nconst MAX_PROMPT_CHARS = 12_000;\n\n/** Longest sentence kept, so one runaway answer cannot stretch the card. */\nconst MAX_GIST_CHARS = 240;\n\n/** A card is not worth waiting longer than this for. */\nconst TIMEOUT_MS = 20_000;\n\nconst SYSTEM_PROMPT = [\n\t\"You write the one-line summary a card shows for each numbered item below.\",\n\t\"Each item is a Jira issue or a Confluence page; its text may be in any language.\",\n\t\"Answer with exactly one line per item, formatted `<number>: <sentence>`.\",\n\t\"Say what the item actually asks for or contains, in the item's own language.\",\n\t\"At most 25 words. No markdown, no quotes, no bullet characters, no preamble, no closing remarks.\",\n].join(\" \");\n\n/**\n * The provider and model the current turn runs on.\n *\n * The logged request header is the route the harness actually used; the agent's\n * own options are the fallback for a session whose header is not readable yet.\n * @param exec - the running tool call.\n * @returns the route, or undefined when neither source names a complete one.\n */\nexport function modelRoute(exec: ToolExecution): GistRoute | undefined {\n\tconst config = exec.agent?.session.requestHeader()?.config;\n\tconst provider = config?.provider ?? exec.agent?.options.provider;\n\tconst model = config?.model ?? exec.agent?.options.model;\n\n\tif (provider === undefined || model === undefined) return undefined;\n\tif (provider === \"\" || model === \"\") return undefined;\n\n\treturn { provider, model };\n}\n\n/**\n * Read the numbered sentences back out of one answer.\n *\n * Models decorate: a code fence, a \"Here you go:\", a bullet before the number.\n * Only lines that start with an in-range number are read, and the first line\n * for a number wins, so decoration is ignored rather than corrupting a gist.\n * @param text - the model's whole answer.\n * @param count - how many items were sent.\n * @returns one entry per item, in order; entries the model skipped are undefined.\n */\nexport function parseGists(\n\ttext: string,\n\tcount: number,\n): (string | undefined)[] {\n\tconst gists: (string | undefined)[] = new Array(count).fill(undefined);\n\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst match = line\n\t\t\t.trim()\n\t\t\t.replace(/^[-*\u2022]\\s*/u, \"\")\n\t\t\t.match(/^(\\d{1,3})\\s*[).:\\-\u2013\u2014]\\s*(.+)$/u);\n\n\t\tif (match === null) continue;\n\n\t\tconst index = Number(match[1]) - 1;\n\t\tconst sentence = match[2]\n\t\t\t?.replace(/^[\"'\u00AB\u201C]+|[\"'\u00BB\u201D]+$/gu, \"\")\n\t\t\t.replace(/\\s+/gu, \" \")\n\t\t\t.trim();\n\n\t\tif (index < 0 || index >= count) continue;\n\t\tif (gists[index] !== undefined) continue;\n\t\tif (sentence === undefined || sentence === \"\") continue;\n\n\t\tgists[index] = sentence.slice(0, MAX_GIST_CHARS);\n\t}\n\n\treturn gists;\n}\n\n/**\n * Write one sentence about each item that has long text.\n * @param ctx - host context; the LLM service is read from it when present.\n * @param targets - the items to compress.\n * @param options - the turn's route and the running call's signal.\n * @returns the sentences, or undefined when the pass did not produce any.\n */\nexport async function summarizeGists(\n\tctx: Context,\n\ttargets: readonly GistTarget[],\n\toptions: GistOptions,\n): Promise<readonly Gist[] | undefined> {\n\tif (targets.length === 0) return undefined;\n\n\tconst route = options.route;\n\tif (route === undefined) return undefined;\n\n\t// Read without an injection requirement: a deployment without a model must\n\t// still get its cards, just without the sentences.\n\tconst llm = ctx.get(\"llm\");\n\tif (llm === undefined) return undefined;\n\n\tconst prompt = buildPrompt(targets);\n\tif (prompt === \"\") return undefined;\n\n\tconst signal =\n\t\toptions.signal === undefined\n\t\t\t? AbortSignal.timeout(TIMEOUT_MS)\n\t\t\t: AbortSignal.any([options.signal, AbortSignal.timeout(TIMEOUT_MS)]);\n\n\tlet answer = \"\";\n\n\ttry {\n\t\tconst stream = llm.stream({\n\t\t\tprovider: route.provider,\n\t\t\tmodel: route.model,\n\t\t\tsystem: SYSTEM_PROMPT,\n\t\t\tmessages: [{ role: \"user\", content: [{ type: \"text\", text: prompt }] }],\n\t\t\ttemperature: 0,\n\t\t\tsignal,\n\t\t});\n\n\t\tfor await (const chunk of stream) {\n\t\t\tif (chunk.type === \"text-delta\") answer += chunk.text;\n\t\t}\n\t} catch {\n\t\t// A failed or cancelled summary is not a failed card.\n\t\treturn undefined;\n\t}\n\n\tconst sentences = parseGists(answer, targets.length);\n\tconst gists: Gist[] = [];\n\n\ttargets.forEach((target, index) => {\n\t\tconst sentence = sentences[index];\n\n\t\tif (sentence !== undefined) gists.push({ id: target.id, text: sentence });\n\t});\n\n\treturn gists.length === 0 ? undefined : gists;\n}\n\n/** The numbered items, bounded so one card cannot become a book. */\nfunction buildPrompt(targets: readonly GistTarget[]): string {\n\tconst parts: string[] = [];\n\tlet used = 0;\n\n\ttargets.forEach((target, index) => {\n\t\tconst heading = `${index + 1}. ${target.title}`;\n\t\tconst room = MAX_PROMPT_CHARS - used - heading.length;\n\n\t\t// Over budget: keep the item's place in the numbering but drop its body,\n\t\t// so the sentences that do come back stay attached to the right item.\n\t\tconst body =\n\t\t\troom <= 0 ? \"\" : target.body.slice(0, Math.min(room, MAX_BODY_CHARS));\n\n\t\tused += heading.length + body.length;\n\t\tparts.push(body === \"\" ? heading : `${heading}\\n${body}`);\n\t});\n\n\treturn parts.join(\"\\n\\n\");\n}\n", "/**\n * `atlassian_present` \u2014 the tool behind a card in the conversation.\n *\n * It resolves what to show (by key, by JQL, by page id, or by CQL), records one\n * durable presentation for the turn, and answers the model with a text summary.\n * The card is presentation only: the model reads the text result, and the record\n * is what the browser half renders under the closing prose.\n *\n * The tool resolves the cloud id itself when the plugin has learned it, so the\n * model does not have to thread that identifier through every call.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type { ToolDefinition, ToolExecution } from \"@deepseek-ai/dsh-tools\";\nimport type { RuntimeState } from \"dsh-integration-host\";\nimport type { JsonValue, McpBridge } from \"dsh-mcp-oauth\";\nimport type { AtlassianMeta, IssueCard, PageCard } from \"../../shared/meta\";\nimport type {\n\tAtlassianPresentation,\n\tPresentationItem,\n\tPresentationLayout,\n\tPresentationLayoutInput,\n} from \"../../shared/presentation\";\nimport { extractor } from \"../presentation/extract\";\nimport { type Gist, modelRoute, summarizeGists } from \"../presentation/gist\";\nimport type { SiteFacts } from \"../store/site\";\n\n/** What the tool needs from the plugin to resolve and record one card. */\nexport interface PresentDeps {\n\treadonly ctx: Context;\n\treadonly bridge: McpBridge;\n\treadonly state: RuntimeState<SiteFacts>;\n}\n\n/** One tool call's text answer, before it becomes blocks. */\ninterface PresentOutcome {\n\treadonly title: string | undefined;\n\treadonly layout: PresentationLayout;\n\treadonly items: readonly PresentationItem[];\n\t/** The server's match count, when it exceeds what the card shows. */\n\treadonly total: number | undefined;\n\t/** Requests that could not be resolved, by key or id. */\n\treadonly failed: readonly string[];\n}\n\n/** What one resolution pass found: card items and the count behind them. */\ninterface Found<Item> {\n\treadonly items: readonly Item[];\n\t/** The server's own match count, when it reported one. */\n\treadonly total: number | undefined;\n}\n\n/** The arguments the model may send, after validation. */\ninterface PresentArgs {\n\treadonly title: string | undefined;\n\treadonly layout: PresentationLayoutInput | undefined;\n\treadonly cloudId: string | undefined;\n\treadonly issues: readonly string[];\n\treadonly pages: readonly string[];\n\treadonly jql: string | undefined;\n\treadonly cql: string | undefined;\n}\n\nfunction stringList(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter(\n\t\t(item): item is string => typeof item === \"string\" && item !== \"\",\n\t);\n}\n\nfunction optionalString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\nclass PresentTool {\n\t/** The wire name the agent calls. */\n\treadonly TOOL_NAME = \"atlassian_present\";\n\n\t/** Never put more than this many items on one card. */\n\tprivate readonly MAX_ITEMS = 25;\n\n\t/** Fields that make a card useful without making the call enormous. */\n\tprivate readonly ISSUE_FIELDS = [\n\t\t\"summary\",\n\t\t\"status\",\n\t\t\"assignee\",\n\t\t\"priority\",\n\t\t\"issuetype\",\n\t\t\"timetracking\",\n\t\t\"timeoriginalestimate\",\n\t\t// Read to write one sentence per issue, then dropped again: the recorded\n\t\t// card carries the sentence, never the description.\n\t\t\"description\",\n\t];\n\n\t/**\n\t * Build the tool definition for one plugin instance.\n\t * @param ctx - host context; the model service is read from it for summaries.\n\t * @param state - runtime state holding the MCP connection and cloud id.\n\t */\n\tcreate = (deps: PresentDeps): ToolDefinition => ({\n\t\tname: this.TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Show Jira issues and/or Confluence pages to the user as a card under your answer. \" +\n\t\t\t\"Use this whenever the user asks to see work \u2014 a daily plan, a shortlist, a single issue, \" +\n\t\t\t\"or search results \u2014 instead of only describing it in prose. Resolve the content by issue \" +\n\t\t\t\"keys, by page ids, by JQL, or by CQL. Each row carries a link to the item, its status, \" +\n\t\t\t\"assignee, priority, type and estimate, plus one sentence summarizing its description, \" +\n\t\t\t\"so the user can act without opening the issue. Call it once per card, then write your \" +\n\t\t\t\"answer; do not repeat the card's contents as a list in the text.\",\n\t\tparameters: {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttitle: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'Optional heading for the card, such as \"\u041F\u043B\u0430\u043D \u043D\u0430 \u0434\u0435\u043D\u044C\". Omit for an untitled card.',\n\t\t\t\t},\n\t\t\t\tlayout: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tenum: [\"auto\", \"single\", \"compact\", \"list\"],\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"auto (default) picks by item count: one item as a full card, two to four as \" +\n\t\t\t\t\t\t\"compact rows, more as a dense list. A card longer than four rows scrolls and \" +\n\t\t\t\t\t\t\"shows how many items matched. Force one when the content calls for it.\",\n\t\t\t\t},\n\t\t\t\tissues: {\n\t\t\t\t\ttype: \"array\",\n\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\tdescription: 'Issue keys to show, for example [\"DEV-6477\", \"DQ-66\"].',\n\t\t\t\t},\n\t\t\t\tpages: {\n\t\t\t\t\ttype: \"array\",\n\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\tdescription: \"Confluence page ids to show.\",\n\t\t\t\t},\n\t\t\t\tjql: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"A JQL query whose results become the card, for example \" +\n\t\t\t\t\t\t'\"assignee = currentUser() AND status != Done ORDER BY priority DESC\".',\n\t\t\t\t},\n\t\t\t\tcql: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescription: \"A CQL query whose Confluence results become the card.\",\n\t\t\t\t},\n\t\t\t\tcloudId: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Atlassian cloud id. Omit unless the user has several sites: the plugin remembers it.\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\toutput: {\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tshown: { type: \"integer\" },\n\t\t\t\t\tsummary: { type: \"string\" },\n\t\t\t\t\tfailed: { type: \"array\", items: { type: \"string\" } },\n\t\t\t\t\t// The card payload, carried to the browser as `result.meta`.\n\t\t\t\t\t// Left open on purpose: it is presentation data, and the browser\n\t\t\t\t\t// half validates it before rendering.\n\t\t\t\t\tpresentation: { type: \"object\", additionalProperties: true },\n\t\t\t\t},\n\t\t\t\trequired: [\"shown\", \"summary\", \"failed\", \"presentation\"],\n\t\t\t},\n\t\t\trender: (_args, value) => [\n\t\t\t\t{ type: \"text\", text: (value as { summary: string }).summary },\n\t\t\t],\n\t\t\t// The browser half reads this back as `result.meta` and renders the\n\t\t\t// card. It never reaches the model: the text above is what the model\n\t\t\t// reads, and this is presentation only.\n\t\t\tpresentationMeta: (_args, value) =>\n\t\t\t\t(value as { presentation: JsonValue }).presentation,\n\t\t},\n\t\texecute: async (rawArgs, exec) => {\n\t\t\tconst args = this.parseArgs(rawArgs);\n\t\t\tconst outcome = await this.resolve(deps, args, exec);\n\n\t\t\tconst viewed = this.describe(outcome, args);\n\t\t\tconst summary =\n\t\t\t\toutcome.failed.length === 0\n\t\t\t\t\t? viewed\n\t\t\t\t\t: `${viewed}\\nCould not resolve: ${outcome.failed.join(\", \")}`;\n\n\t\t\tconst presentation: AtlassianPresentation = {\n\t\t\t\tcallId: String(exec.callId),\n\t\t\t\t...(outcome.title === undefined ? {} : { title: outcome.title }),\n\t\t\t\tlayout: outcome.layout,\n\t\t\t\t...(outcome.total === undefined ? {} : { total: outcome.total }),\n\t\t\t\titems: outcome.items,\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tshown: outcome.items.length,\n\t\t\t\tsummary,\n\t\t\t\tfailed: [...outcome.failed],\n\t\t\t\tpresentation,\n\t\t\t};\n\t\t},\n\t});\n\n\t/** Validate the model's arguments; unknown keys are ignored. */\n\tprivate parseArgs(rawArgs: unknown): PresentArgs {\n\t\tconst args =\n\t\t\trawArgs !== null && typeof rawArgs === \"object\"\n\t\t\t\t? (rawArgs as Record<string, unknown>)\n\t\t\t\t: {};\n\n\t\tconst layout = optionalString(args.layout);\n\n\t\treturn {\n\t\t\ttitle: optionalString(args.title),\n\t\t\tlayout:\n\t\t\t\tlayout === \"auto\" ||\n\t\t\t\tlayout === \"single\" ||\n\t\t\t\tlayout === \"compact\" ||\n\t\t\t\tlayout === \"list\"\n\t\t\t\t\t? layout\n\t\t\t\t\t: undefined,\n\t\t\tcloudId: optionalString(args.cloudId),\n\t\t\tissues: stringList(args.issues),\n\t\t\tpages: stringList(args.pages),\n\t\t\tjql: optionalString(args.jql),\n\t\t\tcql: optionalString(args.cql),\n\t\t};\n\t}\n\n\t/** Resolve every requested source into card items. */\n\tprivate resolve = async (\n\t\tdeps: PresentDeps,\n\t\targs: PresentArgs,\n\t\texec: ToolExecution,\n\t): Promise<PresentOutcome> => {\n\t\tconst cloudId = args.cloudId ?? deps.state.facts?.cloudId;\n\n\t\tif (cloudId === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t\"no Atlassian site known yet; run a Jira or Confluence search first, or sign in again\",\n\t\t\t);\n\n\t\tconst items: PresentationItem[] = [];\n\t\tconst failed: string[] = [];\n\t\tlet matched = 0;\n\n\t\tconst push = (candidate: readonly PresentationItem[]): void => {\n\t\t\tfor (const item of candidate) {\n\t\t\t\tif (items.length >= this.MAX_ITEMS) return;\n\t\t\t\titems.push(item);\n\t\t\t}\n\t\t};\n\n\t\tfor (const key of args.issues) {\n\t\t\ttry {\n\t\t\t\tconst value = await deps.bridge.callTool(\"getJiraIssue\", {\n\t\t\t\t\tcloudId,\n\t\t\t\t\tissueIdOrKey: key,\n\t\t\t\t\tfields: this.ISSUE_FIELDS,\n\t\t\t\t});\n\n\t\t\t\tconst found = this.issuesOf(value, deps.state.facts?.siteUrl, key);\n\t\t\t\tpush(found.items);\n\t\t\t\tmatched += found.total ?? found.items.length;\n\t\t\t} catch {\n\t\t\t\tfailed.push(key);\n\t\t\t}\n\t\t}\n\n\t\tif (args.jql !== undefined) {\n\t\t\tconst value = await deps.bridge.callTool(\"searchJiraIssuesUsingJql\", {\n\t\t\t\tcloudId,\n\t\t\t\tjql: args.jql,\n\t\t\t\tfields: this.ISSUE_FIELDS,\n\t\t\t\tmaxResults: this.MAX_ITEMS,\n\t\t\t});\n\n\t\t\tconst found = this.issuesOf(value, deps.state.facts?.siteUrl);\n\t\t\tpush(found.items);\n\t\t\tmatched += found.total ?? found.items.length;\n\t\t}\n\n\t\tfor (const id of args.pages) {\n\t\t\ttry {\n\t\t\t\tconst value = await deps.bridge.callTool(\"getConfluencePage\", {\n\t\t\t\t\tcloudId,\n\t\t\t\t\tpageId: id,\n\t\t\t\t});\n\n\t\t\t\tconst found = this.pagesOf(value, deps.state.facts?.siteUrl, id);\n\t\t\t\tpush(found.items);\n\t\t\t\tmatched += found.total ?? found.items.length;\n\t\t\t} catch {\n\t\t\t\tfailed.push(id);\n\t\t\t}\n\t\t}\n\n\t\tif (args.cql !== undefined) {\n\t\t\tconst value = await deps.bridge.callTool(\"searchConfluenceUsingCql\", {\n\t\t\t\tcloudId,\n\t\t\t\tcql: args.cql,\n\t\t\t\tlimit: this.MAX_ITEMS,\n\t\t\t});\n\n\t\t\tconst found = this.pagesOf(value, deps.state.facts?.siteUrl);\n\t\t\tpush(found.items);\n\t\t\tmatched += found.total ?? found.items.length;\n\t\t}\n\n\t\tif (items.length === 0 && failed.length === 0)\n\t\t\tthrow new Error(\"nothing to show: pass issues, pages, jql, or cql\");\n\n\t\t// One sentence per item, written by the conversation's own model. The\n\t\t// card is projected again afterwards, so no raw description survives.\n\t\tconst gists = await this.summarize(deps.ctx, items, exec);\n\t\tconst projected = items.map((item, index) =>\n\t\t\titem.kind === \"issue\"\n\t\t\t\t? {\n\t\t\t\t\t\tkind: \"issue\" as const,\n\t\t\t\t\t\tissue: this.issueWire(item.issue, gists.get(String(index))),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tkind: \"page\" as const,\n\t\t\t\t\t\tpage: this.pageWire(item.page, gists.get(String(index))),\n\t\t\t\t\t},\n\t\t);\n\n\t\treturn {\n\t\t\ttitle: args.title,\n\t\t\tlayout: this.resolveLayout(args.layout, projected.length),\n\t\t\titems: projected,\n\t\t\ttotal: matched > projected.length ? matched : undefined,\n\t\t\tfailed,\n\t\t};\n\t};\n\n\t/**\n\t * Compress the long text behind each item into one sentence.\n\t *\n\t * A card is worth waiting a moment for, not a minute: the pass is bounded by\n\t * its own timeout and any failure simply leaves the items without summaries.\n\t */\n\tprivate summarize = async (\n\t\tctx: Context,\n\t\titems: readonly PresentationItem[],\n\t\texec: ToolExecution,\n\t): Promise<ReadonlyMap<string, string>> => {\n\t\tconst targets = items.flatMap((item, index) => {\n\t\t\tconst title =\n\t\t\t\titem.kind === \"issue\"\n\t\t\t\t\t? (item.issue.summary ?? item.issue.key)\n\t\t\t\t\t: item.page.title;\n\t\t\tconst body =\n\t\t\t\t(item.kind === \"issue\" ? item.issue.rawText : item.page.rawText) ?? \"\";\n\n\t\t\treturn body === \"\" ? [] : [{ id: String(index), title, body }];\n\t\t});\n\n\t\tif (targets.length === 0) return new Map();\n\n\t\tconst gists: readonly Gist[] | undefined = await summarizeGists(\n\t\t\tctx,\n\t\t\ttargets,\n\t\t\t{ route: modelRoute(exec), signal: exec.signal },\n\t\t);\n\n\t\treturn new Map((gists ?? []).map((gist) => [gist.id, gist.text]));\n\t};\n\n\t/**\n\t * The recorded shape of one issue.\n\t *\n\t * Built field by field rather than spread: `rawText` is the description the\n\t * compression pass read, and the card must not carry a word of it.\n\t */\n\tprivate issueWire = (\n\t\tissue: IssueCard,\n\t\tgist: string | undefined,\n\t): IssueCard => ({\n\t\tkey: issue.key,\n\t\t...(issue.summary === undefined ? {} : { summary: issue.summary }),\n\t\t...(issue.status === undefined ? {} : { status: issue.status }),\n\t\t...(issue.assignee === undefined ? {} : { assignee: issue.assignee }),\n\t\t...(issue.priority === undefined ? {} : { priority: issue.priority }),\n\t\t...(issue.type === undefined ? {} : { type: issue.type }),\n\t\t...(issue.estimate === undefined ? {} : { estimate: issue.estimate }),\n\t\t...(gist === undefined ? {} : { description: gist }),\n\t\t...(issue.url === undefined ? {} : { url: issue.url }),\n\t});\n\n\t/** The recorded shape of one page; see {@link issueWire}. */\n\tprivate pageWire = (page: PageCard, gist: string | undefined): PageCard => ({\n\t\ttitle: page.title,\n\t\t...(page.id === undefined ? {} : { id: page.id }),\n\t\t...(page.url === undefined ? {} : { url: page.url }),\n\t\t...(page.space === undefined ? {} : { space: page.space }),\n\t\t...(gist === undefined ? {} : { excerpt: gist }),\n\t});\n\n\t/** Normalize one Jira result; `fallbackKey` keeps a single-issue fetch labelled. */\n\tprivate issuesOf(\n\t\tvalue: unknown,\n\t\tsiteUrl: string | undefined,\n\t\tfallbackKey?: string,\n\t): Found<PresentationItem> {\n\t\tconst meta: AtlassianMeta = extractor.extract({\n\t\t\ttool: \"getJiraIssue\",\n\t\t\targs: {},\n\t\t\tvalue,\n\t\t\tsiteUrl,\n\t\t\t// The description is read here and compressed before it is recorded.\n\t\t\tbodies: true,\n\t\t});\n\n\t\tconst issues = meta.issues as readonly IssueCard[];\n\n\t\tif (issues.length > 0)\n\t\t\treturn {\n\t\t\t\titems: issues.map((issue) => ({ kind: \"issue\", issue })),\n\t\t\t\ttotal: meta.total,\n\t\t\t};\n\n\t\treturn {\n\t\t\titems:\n\t\t\t\tfallbackKey === undefined\n\t\t\t\t\t? []\n\t\t\t\t\t: [{ kind: \"issue\", issue: { key: fallbackKey } }],\n\t\t\ttotal: meta.total,\n\t\t};\n\t}\n\n\t/** Normalize one Confluence result; `fallbackId` keeps a single fetch labelled. */\n\tprivate pagesOf(\n\t\tvalue: unknown,\n\t\tsiteUrl: string | undefined,\n\t\tfallbackId?: string,\n\t): Found<PresentationItem> {\n\t\tconst meta: AtlassianMeta = extractor.extract({\n\t\t\ttool: \"getConfluencePage\",\n\t\t\targs: {},\n\t\t\tvalue,\n\t\t\tsiteUrl,\n\t\t\tbodies: true,\n\t\t});\n\n\t\tconst pages = meta.pages as readonly PageCard[];\n\n\t\tif (pages.length > 0)\n\t\t\treturn {\n\t\t\t\titems: pages.map((page) => ({ kind: \"page\", page })),\n\t\t\t\ttotal: meta.total,\n\t\t\t};\n\n\t\treturn {\n\t\t\titems:\n\t\t\t\tfallbackId === undefined\n\t\t\t\t\t? []\n\t\t\t\t\t: [{ kind: \"page\", page: { id: fallbackId, title: fallbackId } }],\n\t\t\ttotal: meta.total,\n\t\t};\n\t}\n\n\t/** Pick the layout the caller asked for, or infer one from the item count. */\n\tprivate resolveLayout(\n\t\tinput: PresentationLayoutInput | undefined,\n\t\tcount: number,\n\t): PresentationLayout {\n\t\tif (input === \"single\" || input === \"compact\" || input === \"list\")\n\t\t\treturn input;\n\t\tif (count <= 1) return \"single\";\n\t\tif (count <= 4) return \"compact\";\n\n\t\treturn \"list\";\n\t}\n\n\t/** The text the model reads: what the card shows, in order. */\n\tprivate describe(outcome: PresentOutcome, args: PresentArgs): string {\n\t\tconst issues = outcome.items.filter((item) => item.kind === \"issue\");\n\t\tconst pages = outcome.items.filter((item) => item.kind === \"page\");\n\n\t\tconst counts = [\n\t\t\tissues.length > 0\n\t\t\t\t? `${issues.length} issue${issues.length === 1 ? \"\" : \"s\"}`\n\t\t\t\t: \"\",\n\t\t\tpages.length > 0\n\t\t\t\t? `${pages.length} page${pages.length === 1 ? \"\" : \"s\"}`\n\t\t\t\t: \"\",\n\t\t].filter(Boolean);\n\n\t\tconst heading =\n\t\t\toutcome.title === undefined\n\t\t\t\t? \"Shown as a card\"\n\t\t\t\t: `Shown as a card titled \"${outcome.title}\"`;\n\n\t\tconst lines = [`${heading}: ${counts.join(\" and \")}.`];\n\n\t\tfor (const item of outcome.items) {\n\t\t\tif (item.kind === \"issue\") {\n\t\t\t\tconst parts = [\n\t\t\t\t\titem.issue.key,\n\t\t\t\t\titem.issue.summary,\n\t\t\t\t\titem.issue.status,\n\t\t\t\t\titem.issue.assignee,\n\t\t\t\t\titem.issue.estimate === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: `estimate ${item.issue.estimate}`,\n\t\t\t\t\titem.issue.url,\n\t\t\t\t].filter(\n\t\t\t\t\t(part): part is string => typeof part === \"string\" && part !== \"\",\n\t\t\t\t);\n\n\t\t\t\tlines.push(`- ${parts.join(\" \u00B7 \")}`);\n\t\t\t} else {\n\t\t\t\tconst parts = [item.page.title, item.page.space, item.page.url].filter(\n\t\t\t\t\t(part): part is string => typeof part === \"string\" && part !== \"\",\n\t\t\t\t);\n\n\t\t\t\tlines.push(`- ${parts.join(\" \u00B7 \")}`);\n\t\t\t}\n\t\t}\n\n\t\tif (args.jql !== undefined) lines.push(`JQL: ${args.jql}`);\n\t\tif (args.cql !== undefined) lines.push(`CQL: ${args.cql}`);\n\n\t\treturn lines.join(\"\\n\");\n\t}\n}\n\nexport const presentTool = new PresentTool();\n", "/**\n * dsh-atlassian \u2014 Jira and Confluence for DeepSeek Harness.\n *\n * The plugin is a bundle: this host half owns what is Atlassian's \u2014 the site\n * facts, the card extractor, the panel data, and the `/atlassian` command's own\n * wording \u2014 while `dsh-mcp-oauth` owns the OAuth flow and the bridge to\n * Atlassian's remote MCP server, and `dsh-integration-host` owns the shell every\n * integration shares. `lib/client.js` contributes the settings card, the result\n * cards, and the side panel to the Web Client.\n *\n * This module is the composition root: it builds one of each collaborator and\n * hands them to the surfaces that need them.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\n// Each of these brings a service declaration into this program: `ctx.tools`,\n// `ctx.credentials`, `ctx.authorization`, and `ctx.commands` are declared by\n// the packages that own them, not by cordis itself.\nimport type {} from \"@deepseek-ai/dsh-commands\";\nimport type {} from \"@deepseek-ai/dsh-tools\";\nimport {\n\tIntegrationAuth,\n\ttype IntegrationAuthFace,\n\tmountIntegrationRoute,\n\tRuntimeState,\n} from \"dsh-integration-host\";\nimport {\n\tAuthorizationFlow,\n\tcredentialKeyOf,\n\tGrantStore,\n\tloggerOf,\n\tMcpBridge,\n} from \"dsh-mcp-oauth\";\nimport type { AtlassianStatus } from \"../shared/status\";\nimport { atlassianAuthSpec } from \"./auth\";\nimport {\n\tatlassianProfile,\n\ttype ConfigInput,\n\tPLUGIN_NAME,\n\tresolveConfig,\n} from \"./config\";\nimport { registerCorrelationSource } from \"./correlation\";\nimport { registerDigestSource } from \"./digest\";\nimport { PanelApi } from \"./panels/panel\";\nimport { extractor } from \"./presentation/extract\";\nimport type { SiteFacts } from \"./store/site\";\nimport { COMMAND_NAME, createCommandHandler } from \"./surface/command\";\nimport { createApiHandler, ROUTE_PREFIX } from \"./surface/http\";\nimport { presentTool } from \"./tools/present\";\n\nexport const name = PLUGIN_NAME;\n\n/** Services this plugin needs before it can activate. */\nexport const inject = [\"tools\", \"credentials\", \"commands\", \"authorization\"];\n\n/** The seam's method id for the browser-based flow. */\nconst OAUTH_METHOD = \"oauth\";\n\n/** How Atlassian's server sees this client; it gates payload detail on the name. */\nconst CLIENT_INFO = { name: \"dsh-atlassian\", version: \"0.3.0\" };\n\n/**\n * @param ctx - host context carrying tools, credentials, commands, and authorization.\n * @param configInput - `endpoint`, `redirectPort`, and `debug` from the bundle patch.\n */\nexport function apply(ctx: Context, configInput?: ConfigInput): void {\n\tconst config = resolveConfig(configInput);\n\tconst profile = atlassianProfile(config);\n\tconst state = new RuntimeState<SiteFacts>();\n\tconst logger = loggerOf(ctx);\n\n\tconst store = new GrantStore(ctx, credentialKeyOf(profile));\n\tconst flow = new AuthorizationFlow(profile, store);\n\tconst bridge = new McpBridge({\n\t\tprofile,\n\t\tstore,\n\t\tclientInfo: CLIENT_INFO,\n\t\t// The durable UI channel: the Web Client reads this back as `result.meta`.\n\t\tdecorate: (definition, tool) => ({\n\t\t\t...definition,\n\t\t\toutput: {\n\t\t\t\t...definition.output,\n\t\t\t\tpresentationMeta: (args, value) =>\n\t\t\t\t\textractor.extract({\n\t\t\t\t\t\ttool: tool.name,\n\t\t\t\t\t\targs,\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tsiteUrl: state.facts?.siteUrl,\n\t\t\t\t\t}),\n\t\t\t},\n\t\t}),\n\t});\n\tconst auth: IntegrationAuthFace<AtlassianStatus> = new IntegrationAuth<\n\t\tAtlassianStatus,\n\t\tSiteFacts\n\t>({ ctx, config, state, store, flow, bridge, spec: atlassianAuthSpec });\n\tconst panel = new PanelApi({ bridge, state });\n\n\t// The digest plugin, when it is installed, composes this plugin's Jira blocks\n\t// into its card; nothing is read until a digest actually asks.\n\tregisterDigestSource(ctx, { bridge, state });\n\n\t// The correlation plugin, when it is installed, asks this plugin about the\n\t// ticket a chain reaches; nothing is read until it asks.\n\tregisterCorrelationSource(ctx, { bridge, state });\n\n\t// Registering the flow tells the harness this credential can be authorized,\n\t// and routes each attempt's notices to whoever started it.\n\tconst disposeFlow = ctx.authorization.registerFlow({\n\t\tkey: store.credentialKey,\n\t\tlabel: \"Atlassian\",\n\t\tmethods: [{ id: OAUTH_METHOD, label: \"Sign in with Atlassian\" }],\n\t\trun: async (session) => {\n\t\t\tawait flow.run(ctx, session);\n\t\t},\n\t});\n\n\t// The command registration belongs to this plugin's Cordis fiber, so it is\n\t// withdrawn with the plugin; only the resources that outlive a plain\n\t// registration need an explicit disposer.\n\tctx.commands.register({\n\t\tname: COMMAND_NAME,\n\t\tdescription: \"Jira and Confluence: status | login | logout | sync\",\n\t\tinput: { hint: \"status | login | logout | sync\" },\n\t\thandler: createCommandHandler(auth),\n\t});\n\n\t// The presentation tool is registered beside the MCP tools it resolves\n\t// through: it shows what they return as a card under the agent's answer.\n\tctx.tools.register(presentTool.create({ ctx, bridge, state }));\n\n\t// The card's channel to the host: a named route is how a plugin serves the\n\t// page without a generated remote namespace.\n\tmountIntegrationRoute(ctx, {\n\t\tprefix: ROUTE_PREFIX,\n\t\thandler: createApiHandler({ auth, panel }),\n\t});\n\n\tctx.effect(\n\t\t() => () => {\n\t\t\tdisposeFlow();\n\t\t\t// Closing the MCP connection also withdraws every published Atlassian tool.\n\t\t\treturn bridge.drop();\n\t\t},\n\t\t\"atlassian: flow and MCP bridge\",\n\t);\n\n\t// A signed-in user gets tools without a restart. A fresh install stays dormant\n\t// until someone runs `/atlassian login` or uses the settings card.\n\tvoid auth.connect().then((ready) => {\n\t\tif (!ready) {\n\t\t\tlogger.info(\n\t\t\t\t\"[atlassian] not signed in yet \u2014 use the Atlassian settings card or /atlassian login\",\n\t\t\t);\n\t\t}\n\t});\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["name", "name", "name", "status", "Service", "name", "body", "name", "ISSUE_FIELDS", "name", "stringList"]
|
|
3
|
+
"sources": ["../../mcp-oauth/src/account.ts", "../../mcp-oauth/src/config.ts", "../../mcp-oauth/src/log.ts", "../../mcp-oauth/src/net.ts", "../../mcp-oauth/src/server.ts", "../../mcp-oauth/src/discovery.ts", "../../mcp-oauth/src/provider.ts", "../../mcp-oauth/src/tool-name.ts", "../../mcp-oauth/src/bridge.ts", "../../mcp-oauth/src/browser.ts", "../../mcp-oauth/src/flow.ts", "../../mcp-oauth/src/json.ts", "../../mcp-oauth/src/grant.ts", "../../mcp-oauth/src/loopback.ts", "../../mcp-oauth/src/payload.ts", "../../integration-host/src/auth.ts", "../../integration-host/src/command.ts", "../../integration-host/src/config.ts", "../../integration-host/src/correlation.ts", "../../integration-host/src/digest.ts", "../../integration-host/src/markdown.ts", "../../integration-host/src/status.ts", "../../integration-host/src/route.ts", "../../integration-host/src/state.ts", "../src/host/store/site.ts", "../src/host/auth.ts", "../src/host/config.ts", "../src/host/presentation/extract.ts", "../src/host/correlation.ts", "../src/host/digest.ts", "../src/host/panels/panel.ts", "../src/host/surface/command.ts", "../src/host/surface/http.ts", "../src/host/presentation/gist.ts", "../src/host/tools/present.ts", "../src/host/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Which account of one provider a credential record belongs to.\n *\n * A plugin may hold several accounts of the same provider \u2014 a work GitLab and a\n * personal one, or two Atlassian sites \u2014 and the credential store addresses a\n * record as `<scope>/<id>`, so the account has to live inside the id half. The\n * default account keeps the id `mcp` that records were first written with,\n * which is what makes accounts additive: a grant stored before they existed\n * still resolves to the default one.\n */\n\nimport type { CredentialRecordEntry } from \"@deepseek-ai/dsh-credentials\";\n\n/** The account a provider uses when its plugin names none. */\nexport const DEFAULT_INSTANCE = \"default\";\n\n/** What an account name may be: one credential-key segment. */\nconst INSTANCE_PATTERN = /^[a-z][a-z0-9-]*$/;\n\n/** The record id one account owns. */\nconst idFor = (instance: string): string =>\n\tinstance === DEFAULT_INSTANCE ? \"mcp\" : `mcp-${instance}`;\n\n/** The account an id names, or undefined for an id this plugin never wrote. */\nconst instanceFor = (id: string): string | undefined => {\n\tif (id === \"mcp\") return DEFAULT_INSTANCE;\n\n\tif (!id.startsWith(\"mcp-\")) return undefined;\n\n\tconst instance = id.slice(\"mcp-\".length);\n\n\treturn INSTANCE_PATTERN.test(instance) ? instance : undefined;\n};\n\n/**\n * The credential record key of one provider account.\n * @param scope - the owning plugin's registered name.\n * @param instance - the account within it; the default one unless named.\n * @throws TypeError when either segment is not a lowercase hyphenated identifier,\n * which is the grammar the credential store admits.\n */\nexport function credentialKeyFor(\n\tscope: string,\n\tinstance: string = DEFAULT_INSTANCE,\n): string {\n\tfor (const segment of [scope, instance])\n\t\tif (!INSTANCE_PATTERN.test(segment))\n\t\t\tthrow new TypeError(\n\t\t\t\t`credential key segment \"${segment}\" must match ${String(INSTANCE_PATTERN)}`,\n\t\t\t);\n\n\treturn `${scope}/${idFor(instance)}`;\n}\n\n/**\n * The account a stored record key names.\n * @param scope - the owning plugin's registered name.\n * @param key - a record key in the store's `<scope>/<id>` grammar.\n * @returns the account, or undefined for a key another plugin owns and for an\n * id this plugin never wrote.\n */\nexport function instanceOfKey(scope: string, key: string): string | undefined {\n\tconst [keyScope, id] = key.split(\"/\");\n\n\tif (keyScope !== scope || id === undefined) return undefined;\n\n\treturn instanceFor(id);\n}\n\n/**\n * The accounts a plugin holds records for.\n * @param scope - the owning plugin's registered name.\n * @param records - every stored record, as the credential seam lists them.\n * @returns one account name per record, in the store's own order.\n */\nexport function instancesOf(\n\tscope: string,\n\trecords: readonly CredentialRecordEntry[],\n): string[] {\n\tconst instances: string[] = [];\n\n\tfor (const record of records) {\n\t\tconst instance = instanceOfKey(scope, record.key);\n\n\t\tif (instance !== undefined && !instances.includes(instance))\n\t\t\tinstances.push(instance);\n\t}\n\n\treturn instances;\n}\n", "/**\n * Bounds the library puts on its own work.\n *\n * These are transport and token-lifetime facts, not preferences: they are the\n * same for every provider, and changing one changes how a stalled server or an\n * expiring token behaves everywhere.\n */\n\n/** How long any single OAuth HTTP call may take. */\nexport const REQUEST_TIMEOUT_MS = 30_000;\n\n/** How many consecutive loopback ports to try for the redirect. */\nexport const PORT_ATTEMPTS = 20;\n\n/** How long to wait for a human to finish signing in. */\nexport const SIGN_IN_TIMEOUT_MS = 5 * 60_000;\n\n/** How long before expiry a token is refreshed. */\nexport const REFRESH_MARGIN_MS = 60_000;\n", "/**\n * Logging for the host half. The harness context carries a logger; a bare\n * context (tests, dry runs) falls back to the console so the same code runs\n * outside the harness.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\n\nexport interface Logger {\n\tinfo: (message: string, ...args: unknown[]) => void;\n\twarn: (message: string, ...args: unknown[]) => void;\n}\n\ninterface MaybeLogger {\n\tinfo?: (message: string, ...args: unknown[]) => void;\n\twarn?: (message: string, ...args: unknown[]) => void;\n}\n\n/** The context's logger when it has one, the console otherwise. */\nexport function loggerOf(ctx: Context): Logger {\n\tconst logger = (ctx as { logger?: MaybeLogger }).logger;\n\n\tif (typeof logger?.info === \"function\" && typeof logger.warn === \"function\") {\n\t\treturn {\n\t\t\tinfo: (message, ...args) => logger.info?.(message, ...args),\n\t\t\twarn: (message, ...args) => logger.warn?.(message, ...args),\n\t\t};\n\t}\n\n\treturn {\n\t\tinfo: (message, ...args) => console.info(message, ...args),\n\t\twarn: (message, ...args) => console.warn(message, ...args),\n\t};\n}\n\n/** A message for a caught value, whatever it is. */\nexport function errorText(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n", "/**\n * Thin HTTP helpers for the OAuth endpoints.\n *\n * Every call carries a deadline. OAuth discovery, registration, and token\n * exchange happen while the user waits, and an endpoint that accepts a\n * connection and then stalls must not wedge sign-in forever.\n */\n\nimport { REQUEST_TIMEOUT_MS } from \"./config\";\nimport { errorText } from \"./log\";\n\nclass Network {\n\t/** A JSON GET, or undefined when the document is absent or unreadable. */\n\tgetJSON = async (\n\t\turl: string,\n\t): Promise<Record<string, unknown> | undefined> => {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\theaders: { accept: \"application/json\" },\n\t\t\t\tsignal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n\t\t\t});\n\n\t\t\tif (!response.ok) return undefined;\n\n\t\t\tconst body: unknown = await response.json();\n\n\t\t\treturn body !== null && typeof body === \"object\"\n\t\t\t\t? (body as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t};\n\n\t/** A JSON POST that must succeed; the response body is returned as JSON. */\n\tpostJSON = async (\n\t\turl: string,\n\t\tbody: unknown,\n\t\twhat: string,\n\t): Promise<Record<string, unknown>> => {\n\t\tlet response: Response;\n\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(body),\n\t\t\t\tsignal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow new Error(`${what} failed: ${errorText(error)}`);\n\t\t}\n\n\t\tconst text = await response.text();\n\n\t\tif (!response.ok)\n\t\t\tthrow new Error(`${what} failed (${response.status}): ${text}`);\n\n\t\tconst parsed: unknown = JSON.parse(text);\n\n\t\tif (parsed === null || typeof parsed !== \"object\")\n\t\t\tthrow new Error(`${what} returned a non-object`);\n\n\t\treturn parsed as Record<string, unknown>;\n\t};\n\n\t/** A form POST to a token endpoint, with the same deadline discipline. */\n\tpostForm = async (\n\t\turl: string,\n\t\tfields: Record<string, string>,\n\t\twhat: string,\n\t\tauthorization?: string,\n\t): Promise<Record<string, unknown>> => {\n\t\tlet response: Response;\n\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"content-type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t\t...(authorization === undefined ? {} : { authorization }),\n\t\t\t\t},\n\t\t\t\tbody: new URLSearchParams(fields),\n\t\t\t\tsignal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow new Error(`${what} failed: ${errorText(error)}`);\n\t\t}\n\n\t\tconst text = await response.text();\n\n\t\tif (!response.ok)\n\t\t\tthrow new Error(`${what} failed (${response.status}): ${text}`);\n\n\t\tconst parsed: unknown = JSON.parse(text);\n\n\t\tif (parsed === null || typeof parsed !== \"object\")\n\t\t\tthrow new Error(`${what} returned a non-object`);\n\n\t\treturn parsed as Record<string, unknown>;\n\t};\n}\n\nexport const network = new Network();\n", "/**\n * Everything the OAuth flow needs to talk to one authorization server: the\n * resolved endpoints, dynamic client registration, and the token endpoint.\n *\n * One vendor may run several generations of its MCP server, each a separate\n * OAuth resource with its own authorization server and scope vocabulary.\n * Nothing here is hard-coded to any of them: the endpoints come from discovery,\n * and the requested scopes are intersected with what the server advertises.\n */\n\nimport type { Grant } from \"./grant\";\nimport { network } from \"./net\";\n\n/** How a client authenticates at the token endpoint. */\nexport type TokenAuthMethod =\n\t| \"none\"\n\t| \"client_secret_post\"\n\t| \"client_secret_basic\";\n\n/** What dynamic client registration issued. */\nexport interface RegisteredClient {\n\treadonly clientId: string;\n\treadonly clientSecret?: string;\n\treadonly tokenAuthMethod: TokenAuthMethod;\n}\n\n/**\n * The client authentication to register with.\n *\n * A public client is preferred whenever the server allows one: PKCE already\n * binds the code to this installation, so nothing has to be stored. A server\n * that offers no `none` gets a secret instead. What it actually issued is what\n * counts afterwards, because a registration endpoint may ignore the request and\n * hand back a public client anyway.\n * @param advertised - the server's `token_endpoint_auth_methods_supported`.\n */\nexport function chooseTokenAuthMethod(\n\tadvertised: readonly string[] | undefined,\n): TokenAuthMethod {\n\tif (advertised === undefined || advertised.includes(\"none\")) return \"none\";\n\n\tif (advertised.includes(\"client_secret_post\")) return \"client_secret_post\";\n\n\tif (advertised.includes(\"client_secret_basic\")) return \"client_secret_basic\";\n\n\treturn \"none\";\n}\n\n/**\n * The client authentication a stored client uses.\n *\n * A record may carry no method \u2014 only whether it holds a secret \u2014 and then the\n * secret decides: a client with one posts it, a client without one is public.\n */\nexport function authMethodOf(\n\tclient: Pick<Grant, \"clientSecret\" | \"tokenAuthMethod\">,\n): TokenAuthMethod {\n\tif (client.tokenAuthMethod !== undefined) return client.tokenAuthMethod;\n\n\treturn client.clientSecret === undefined ? \"none\" : \"client_secret_post\";\n}\n\n/** A discovered authorization server plus the scope set chosen for it. */\nexport class OAuthServer {\n\t/** Issuer identifier, used to detect a generation switch. */\n\treadonly issuer: string;\n\t/** MCP resource URL the tokens are scoped to (RFC 8707 `resource`). */\n\treadonly resource: string;\n\treadonly authorizationEndpoint: string;\n\treadonly tokenEndpoint: string;\n\treadonly registrationEndpoint?: string;\n\treadonly revocationEndpoint?: string;\n\treadonly scopes: readonly string[];\n\t/** The space-delimited `scope` parameter. */\n\treadonly scopeString: string;\n\t/** Client authentication to ask for at registration. */\n\treadonly tokenAuthMethod: TokenAuthMethod;\n\n\tconstructor(\n\t\tparams: Pick<\n\t\t\tOAuthServer,\n\t\t\t| \"issuer\"\n\t\t\t| \"resource\"\n\t\t\t| \"authorizationEndpoint\"\n\t\t\t| \"tokenEndpoint\"\n\t\t\t| \"registrationEndpoint\"\n\t\t\t| \"revocationEndpoint\"\n\t\t\t| \"scopes\"\n\t\t\t| \"scopeString\"\n\t\t\t| \"tokenAuthMethod\"\n\t\t>,\n\t) {\n\t\tthis.issuer = params.issuer;\n\t\tthis.resource = params.resource;\n\t\tthis.authorizationEndpoint = params.authorizationEndpoint;\n\t\tthis.tokenEndpoint = params.tokenEndpoint;\n\t\tthis.registrationEndpoint = params.registrationEndpoint;\n\t\tthis.revocationEndpoint = params.revocationEndpoint;\n\t\tthis.scopes = params.scopes;\n\t\tthis.scopeString = params.scopeString;\n\t\tthis.tokenAuthMethod = params.tokenAuthMethod;\n\t}\n\n\t/** Register this installation as a client (RFC 7591). */\n\tregisterClient = async (\n\t\tredirectUri: string,\n\t\tclientName: string,\n\t): Promise<RegisteredClient> => {\n\t\tif (this.registrationEndpoint === undefined)\n\t\t\tthrow new Error(`${this.issuer} offers no dynamic client registration`);\n\n\t\tconst body = await network.postJSON(\n\t\t\tthis.registrationEndpoint,\n\t\t\t{\n\t\t\t\tclient_name: clientName,\n\t\t\t\tredirect_uris: [redirectUri],\n\t\t\t\tgrant_types: [\"authorization_code\", \"refresh_token\"],\n\t\t\t\tresponse_types: [\"code\"],\n\t\t\t\ttoken_endpoint_auth_method: this.tokenAuthMethod,\n\t\t\t\tscope: this.scopeString,\n\t\t\t},\n\t\t\t\"dynamic client registration\",\n\t\t);\n\n\t\tconst clientId = body.client_id;\n\n\t\tif (typeof clientId !== \"string\" || clientId === \"\")\n\t\t\tthrow new Error(\"client registration returned no client_id\");\n\n\t\tconst clientSecret =\n\t\t\ttypeof body.client_secret === \"string\" ? body.client_secret : undefined;\n\t\tconst tokenAuthMethod =\n\t\t\treadTokenAuthMethod(body.token_endpoint_auth_method) ??\n\t\t\t(clientSecret === undefined ? \"none\" : this.tokenAuthMethod);\n\n\t\treturn clientSecret === undefined\n\t\t\t? { clientId, tokenAuthMethod }\n\t\t\t: { clientId, clientSecret, tokenAuthMethod };\n\t};\n\n\t/** Exchange an authorization code, or a refresh token, for tokens. */\n\ttokenRequest = async (\n\t\tclient: Pick<Grant, \"clientId\" | \"clientSecret\" | \"tokenAuthMethod\">,\n\t\tbody: Record<string, string>,\n\t): Promise<Record<string, unknown>> => {\n\t\tconst { clientId, clientSecret } = client;\n\t\tconst method = authMethodOf(client);\n\t\tconst fields: Record<string, string> = {\n\t\t\t...body,\n\t\t\tresource: this.resource,\n\t\t};\n\n\t\tif (method === \"client_secret_basic\") {\n\t\t\tif (clientId === undefined || clientSecret === undefined)\n\t\t\t\tthrow new Error(\"client_secret_basic needs a client id and secret\");\n\n\t\t\treturn await network.postForm(\n\t\t\t\tthis.tokenEndpoint,\n\t\t\t\tfields,\n\t\t\t\t\"token request\",\n\t\t\t\tbasicAuth(clientId, clientSecret),\n\t\t\t);\n\t\t}\n\n\t\tif (clientId !== undefined) fields.client_id = clientId;\n\n\t\tif (method === \"client_secret_post\" && clientSecret !== undefined)\n\t\t\tfields.client_secret = clientSecret;\n\n\t\treturn await network.postForm(this.tokenEndpoint, fields, \"token request\");\n\t};\n\n\t/**\n\t * Map a token-endpoint response onto grant fields, keeping what the response\n\t * omits. Servers that rotate refresh tokens send a new one, and a response\n\t * that carries one replaces the stored value; one that does not leaves it\n\t * alone.\n\t */\n\ttoTokenFields = (\n\t\tpayload: Record<string, unknown>,\n\t\tprevious: Pick<\n\t\t\tGrant,\n\t\t\t\"accessToken\" | \"refreshToken\" | \"tokenType\" | \"scope\"\n\t\t>,\n\t): Pick<\n\t\tGrant,\n\t\t\"accessToken\" | \"refreshToken\" | \"tokenType\" | \"scope\" | \"expiresAt\"\n\t> => {\n\t\tconst expiresIn = Number(payload.expires_in);\n\t\tconst accessToken = payload.access_token;\n\t\tconst refreshToken = payload.refresh_token;\n\t\tconst tokenType = payload.token_type;\n\n\t\treturn {\n\t\t\taccessToken:\n\t\t\t\ttypeof accessToken === \"string\" && accessToken !== \"\"\n\t\t\t\t\t? accessToken\n\t\t\t\t\t: previous.accessToken,\n\t\t\trefreshToken:\n\t\t\t\ttypeof refreshToken === \"string\" && refreshToken !== \"\"\n\t\t\t\t\t? refreshToken\n\t\t\t\t\t: previous.refreshToken,\n\t\t\ttokenType:\n\t\t\t\ttypeof tokenType === \"string\" && tokenType !== \"\"\n\t\t\t\t\t? tokenType\n\t\t\t\t\t: previous.tokenType,\n\t\t\tscope: typeof payload.scope === \"string\" ? payload.scope : previous.scope,\n\t\t\texpiresAt:\n\t\t\t\tDate.now() +\n\t\t\t\t(Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : 3600) * 1000,\n\t\t};\n\t};\n}\n\n/** The `Authorization` header a confidential client authenticates with. */\nfunction basicAuth(clientId: string, clientSecret: string): string {\n\tconst credentials = `${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`;\n\n\treturn `Basic ${Buffer.from(credentials).toString(\"base64\")}`;\n}\n\n/** One of the three methods the library understands, or undefined. */\nfunction readTokenAuthMethod(value: unknown): TokenAuthMethod | undefined {\n\treturn value === \"none\" ||\n\t\tvalue === \"client_secret_post\" ||\n\t\tvalue === \"client_secret_basic\"\n\t\t? value\n\t\t: undefined;\n}\n", "/**\n * OAuth discovery for one MCP resource.\n *\n * Servers differ in exactly the ways this walks around: some publish RFC 9728\n * protected-resource metadata naming a separate authorization server, some are\n * their own issuer, and the metadata itself may sit at the origin root or under\n * the issuer's path. So both document families are tried most-specific-first\n * and the first usable answer wins.\n *\n * The requested scopes are the provider's, narrowed to what the server\n * advertises: one profile serves a server that renamed its whole scope\n * vocabulary, which is what Atlassian's second generation did.\n */\n\nimport { network } from \"./net\";\nimport type { OAuthProviderProfile } from \"./provider\";\nimport { chooseTokenAuthMethod, OAuthServer } from \"./server\";\n\n/**\n * Resolve the authorization server for one MCP resource.\n * @param profile - the provider: its resource URL and wanted scopes.\n * @returns the discovered server, its endpoints, and the chosen scopes.\n * @throws when no authorization-server metadata can be read at all.\n */\nexport async function discover(\n\tprofile: OAuthProviderProfile,\n): Promise<OAuthServer> {\n\tconst endpoint = profile.endpoint;\n\tlet protectedResource: Record<string, unknown> | undefined;\n\tlet issuer = new URL(endpoint).origin;\n\n\tfor (const candidate of protectedResourceCandidates(endpoint)) {\n\t\tconst document = await network.getJSON(candidate);\n\t\tconst servers = stringList(document?.authorization_servers);\n\n\t\tif (document !== undefined && servers !== undefined) {\n\t\t\tprotectedResource = document;\n\t\t\tissuer = servers[0] ?? issuer;\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tlet metadata: Record<string, unknown> | undefined;\n\tfor (const candidate of authorizationServerCandidates(issuer)) {\n\t\tconst document = await network.getJSON(candidate);\n\n\t\tif (firstString(document?.token_endpoint) !== undefined) {\n\t\t\tmetadata = document;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (metadata === undefined)\n\t\tthrow new Error(`no OAuth metadata for ${endpoint} (issuer ${issuer})`);\n\n\tconst authorizationEndpoint = secureEndpoint(\n\t\tfirstString(metadata.authorization_endpoint),\n\t\t\"the authorization endpoint\",\n\t);\n\tconst tokenEndpoint = secureEndpoint(\n\t\tfirstString(metadata.token_endpoint),\n\t\t\"the token endpoint\",\n\t);\n\n\tif (authorizationEndpoint === undefined || tokenEndpoint === undefined)\n\t\tthrow new Error(`incomplete OAuth metadata for ${issuer}`);\n\n\t// The resource's own document is the more specific scope list when present.\n\tconst advertised =\n\t\tstringList(protectedResource?.scopes_supported) ??\n\t\tstringList(metadata.scopes_supported);\n\tconst scopes = chooseScopes(profile, advertised);\n\tconst registrationEndpoint = secureEndpoint(\n\t\tfirstString(metadata.registration_endpoint),\n\t\t\"the registration endpoint\",\n\t);\n\tconst revocationEndpoint = secureEndpoint(\n\t\tfirstString(metadata.revocation_endpoint),\n\t\t\"the revocation endpoint\",\n\t);\n\n\treturn new OAuthServer({\n\t\tissuer:\n\t\t\tsecureEndpoint(firstString(metadata.issuer) ?? issuer, \"the issuer\") ??\n\t\t\tissuer,\n\t\tresource: endpoint,\n\t\tauthorizationEndpoint,\n\t\ttokenEndpoint,\n\t\tscopes,\n\t\tscopeString: scopes.join(\" \"),\n\t\ttokenAuthMethod: chooseTokenAuthMethod(\n\t\t\tstringList(metadata.token_endpoint_auth_methods_supported),\n\t\t),\n\t\t...(registrationEndpoint === undefined ? {} : { registrationEndpoint }),\n\t\t...(revocationEndpoint === undefined ? {} : { revocationEndpoint }),\n\t});\n}\n\n/** Well-known protected-resource locations for a resource, most specific first. */\nfunction protectedResourceCandidates(endpoint: string): string[] {\n\tconst url = new URL(endpoint);\n\tconst path = url.pathname.replace(/\\/+$/u, \"\");\n\n\treturn [\n\t\t`${url.origin}/.well-known/oauth-protected-resource${path}`,\n\t\t`${url.origin}/.well-known/oauth-protected-resource`,\n\t];\n}\n\n/**\n * Well-known authorization-server locations for an issuer.\n *\n * RFC 8414 inserts the well-known segment before the issuer's path; some\n * servers instead append it, and OpenID Connect appends `/openid-configuration`,\n * so all of them are tried.\n */\nfunction authorizationServerCandidates(issuer: string): string[] {\n\tconst url = new URL(issuer);\n\tconst path = url.pathname.replace(/\\/+$/u, \"\");\n\tconst bare = issuer.replace(/\\/+$/u, \"\");\n\n\treturn [\n\t\t`${url.origin}/.well-known/oauth-authorization-server${path}`,\n\t\t`${bare}/.well-known/oauth-authorization-server`,\n\t\t`${url.origin}/.well-known/openid-configuration${path}`,\n\t\t`${bare}/.well-known/openid-configuration`,\n\t];\n}\n\n/**\n * Choose the scope set to request: what the provider wants, narrowed to what\n * the server advertises. A server that advertises nothing gets the provider's\n * fallback set, and `offline_access` is preserved whenever it is offered,\n * because that is what buys a refresh token.\n */\nfunction chooseScopes(\n\tprofile: OAuthProviderProfile,\n\tadvertised: readonly string[] | undefined,\n): string[] {\n\tconst wanted = profile.oauth.scopes;\n\n\tif (advertised === undefined || advertised.length === 0)\n\t\treturn [...profile.oauth.fallbackScopes];\n\n\tconst chosen = wanted.filter((scope) => advertised.includes(scope));\n\n\tif (chosen.length > 0) return chosen;\n\n\treturn advertised.includes(\"offline_access\")\n\t\t? [\"offline_access\"]\n\t\t: [...profile.oauth.fallbackScopes];\n}\n\n/**\n * One endpoint from OAuth metadata, refused unless it can be trusted with a token.\n *\n * RFC 8414 and 9728 require TLS. A loopback server is the one exception an MCP\n * deployment actually uses, and it never leaves the machine. A `javascript:`\n * authorization endpoint would otherwise reach the settings card's link, and an\n * `http:` token endpoint would send the refresh token in clear text.\n * @param value - the endpoint as the server published it, when it published one.\n * @param what - the field's name, for the refusal.\n * @throws when the value is not an absolute http(s) URL.\n */\nfunction secureEndpoint(\n\tvalue: string | undefined,\n\twhat: string,\n): string | undefined {\n\tif (value === undefined) return undefined;\n\n\tlet url: URL;\n\n\ttry {\n\t\turl = new URL(value);\n\t} catch {\n\t\tthrow new Error(`${what} is not a URL`);\n\t}\n\n\tconst loopback =\n\t\turl.hostname === \"127.0.0.1\" ||\n\t\turl.hostname === \"localhost\" ||\n\t\turl.hostname === \"[::1]\";\n\n\tif (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && loopback))\n\t\tthrow new Error(`${what} must use https`);\n\n\treturn value;\n}\n\nfunction stringList(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\n\tconst items = value.filter(\n\t\t(item): item is string => typeof item === \"string\",\n\t);\n\n\treturn items.length > 0 ? items : undefined;\n}\n\nfunction firstString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n", "/**\n * What the library needs to know about one MCP server it talks to.\n *\n * Every provider-specific fact enters through here: the library itself never\n * names a vendor, a scope vocabulary, or a credential record. A plugin builds\n * one profile per account and hands it to the pieces below.\n */\n\nimport { credentialKeyFor, DEFAULT_INSTANCE } from \"./account\";\n\n/**\n * The OAuth facts of a profile, absent for a server that needs no credentials.\n *\n * They are grouped rather than spread over the profile because they are the\n * half a local MCP server has nothing to say about.\n */\nexport interface OAuthProfile {\n\t/** First loopback port to try for the redirect. */\n\treadonly redirectPort: number;\n\t/** Scopes to ask for, most wanted first. */\n\treadonly scopes: readonly string[];\n\t/** What to ask for when the server advertises no scopes at all. */\n\treadonly fallbackScopes: readonly string[];\n\t/** Name shown on the consent screen. */\n\treadonly clientName: string;\n}\n\n/** One provider account: its identity, its server, and how it authenticates. */\nexport interface ProviderProfile {\n\t/** The owning plugin's registered name; owns the record and names log lines. */\n\treadonly scope: string;\n\t/** User-facing provider name (\"Atlassian\", \"GitLab\"). */\n\treadonly label: string;\n\t/** Account within the provider; the default one unless the plugin runs several. */\n\treadonly instance: string;\n\t/** MCP resource URL; also the RFC 8707 `resource` parameter. */\n\treadonly endpoint: string;\n\t/** Published tools live under `mcp__<namespace>__<tool>`. */\n\treadonly toolNamespace: string;\n\t/** Log the raw MCP payload of every tool result. */\n\treadonly debug: boolean;\n\t/** Absent for a server reached with no credentials at all, such as a local MCP. */\n\treadonly oauth?: OAuthProfile;\n}\n\n/** A profile whose server is reached through OAuth. */\nexport type OAuthProviderProfile = ProviderProfile & {\n\treadonly oauth: OAuthProfile;\n};\n\n/** A profile whose server is reached with no credentials at all. */\nexport type NoAuthProviderProfile = ProviderProfile & {\n\treadonly oauth?: undefined;\n};\n\n/** Whether a profile's server is reached through OAuth. */\nexport function needsOAuth(\n\tprofile: ProviderProfile,\n): profile is OAuthProviderProfile {\n\treturn profile.oauth !== undefined;\n}\n\n/** The credential record one account's tokens live in. */\nexport function credentialKeyOf(profile: OAuthProviderProfile): string {\n\treturn credentialKeyFor(profile.scope, profile.instance);\n}\n\n/** How one account names itself in log lines: `dsh-gitlab` or `dsh-gitlab/work`. */\nexport function accountLabel(profile: ProviderProfile): string {\n\treturn profile.instance === DEFAULT_INSTANCE\n\t\t? profile.scope\n\t\t: `${profile.scope}/${profile.instance}`;\n}\n", "/**\n * The harness's public tool names.\n *\n * A published MCP tool is renamed into the harness's namespace, which is capped\n * at 64 characters. Both halves of a plugin key their UI by the result \u2014 the\n * host publishes under it and the browser half claims it \u2014 so the rule lives in\n * exactly one place, bundled into each half rather than shared at runtime, and\n * the two can never disagree.\n */\n\n/** The harness caps a tool name at this many characters. */\nconst MAX_TOOL_NAME = 64;\n\n/** The prefix every published tool of one namespace carries. */\nexport function toolPrefix(namespace: string): string {\n\treturn `mcp__${namespace}__`;\n}\n\n/**\n * The public harness tool name for one server tool.\n *\n * The server's own spelling is kept whenever it fits, because that is what\n * makes a plugin's keyed tool views line up; a name that does not fit gets a\n * digest appended, so two long names can never collapse into one.\n * @param serverTool - the tool name as the server spells it.\n * @param namespace - the plugin's namespace (\"atlassian\").\n */\nexport function publicToolName(serverTool: string, namespace: string): string {\n\tconst cleaned = serverTool.replace(/[^A-Za-z0-9_-]/gu, \"_\") || \"tool\";\n\tconst full = `${toolPrefix(namespace)}${cleaned}`;\n\n\tif (full.length <= MAX_TOOL_NAME) return full;\n\n\tconst digest = digestOf(serverTool);\n\n\treturn `${full.slice(0, MAX_TOOL_NAME - digest.length - 1)}_${digest}`;\n}\n\n/**\n * A deterministic 12-hex-character digest of a string.\n *\n * Disambiguating two long tool names needs no cryptography, only a hash that\n * runs identically in Node and in the browser.\n */\nfunction digestOf(input: string): string {\n\tlet hash = 0xcbf29ce484222325n;\n\tconst prime = 0x100000001b3n;\n\tconst mask = 0xffffffffffffffffn;\n\n\tfor (let index = 0; index < input.length; index += 1) {\n\t\thash = ((hash ^ BigInt(input.charCodeAt(index))) * prime) & mask;\n\t}\n\n\treturn hash.toString(16).padStart(16, \"0\").slice(0, 12);\n}\n", "/**\n * The bridge to one remote MCP server.\n *\n * The stock MCP client takes its HTTP headers from static configuration, and an\n * OAuth access token changes over the life of the process. So the transport is\n * built here with an `authProvider` that is asked for the token before every\n * request \u2014 a refresh therefore needs no reconnect \u2014 and each discovered tool is\n * published through the harness's own MCP adapter, so results keep the canonical\n * shape.\n *\n * A profile without OAuth facts is a server that needs no credentials at all,\n * such as a local MCP server; the same transport is built without an\n * `authProvider`, and the bridge holds no record.\n *\n * The bridge owns its connection: the client, the published tools, and the\n * authorization server it discovered. A caller that wants provider-specific\n * work done at connect time passes {@link McpBridgeOptions.onConnected}.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type { ToolDefinition } from \"@deepseek-ai/dsh-tools\";\nimport type {\n\tCallToolResult,\n\tClient,\n\tTool,\n} from \"@modelcontextprotocol/client\";\nimport { discover } from \"./discovery\";\nimport type { GrantStore } from \"./grant\";\nimport { errorText, loggerOf } from \"./log\";\nimport {\n\taccountLabel,\n\tneedsOAuth,\n\ttype OAuthProviderProfile,\n\ttype ProviderProfile,\n} from \"./provider\";\nimport type { OAuthServer } from \"./server\";\nimport { publicToolName } from \"./tool-name\";\n\n/** How long one MCP tool call may take. */\nconst TOOL_CALL_TIMEOUT_MS = 60_000;\n\nexport interface McpBridgeOptions {\n\t/** The provider account whose server this bridge talks to. */\n\treadonly profile: ProviderProfile;\n\t/**\n\t * The record the access token is read and refreshed through. Required for a\n\t * profile with OAuth facts, refused without one at construction.\n\t */\n\treadonly store?: GrantStore;\n\t/** How the server sees this client; some gate capabilities on the name. */\n\treadonly clientInfo: { readonly name: string; readonly version: string };\n\t/** Called once per discovered tool, before it is registered. */\n\treadonly decorate?: (\n\t\tdefinition: ToolDefinition,\n\t\ttool: Tool,\n\t) => ToolDefinition;\n\t/** Called after a successful connect, for provider-specific learning. */\n\treadonly onConnected?: () => Promise<void>;\n}\n\nexport class McpBridge {\n\tprivate client: Client | undefined;\n\tprivate oauth: OAuthServer | undefined;\n\tprivate disposers = new Map<string, () => void>();\n\tprivate names: string[] = [];\n\tprivate ready = false;\n\tprivate failure: string | undefined;\n\tprivate pending: Promise<boolean> | undefined;\n\tprivate logger: ReturnType<typeof loggerOf> | undefined;\n\n\tconstructor(private readonly options: McpBridgeOptions) {\n\t\tif (needsOAuth(options.profile) && options.store === undefined)\n\t\t\tthrow new TypeError(\n\t\t\t\t`the ${accountLabel(options.profile)} bridge needs a grant store for its OAuth profile`,\n\t\t\t);\n\t}\n\n\t/** Public names of the published tools, in discovery order. */\n\tget toolNames(): readonly string[] {\n\t\treturn this.names;\n\t}\n\n\t/** Whether the connection is currently usable. */\n\tget isConnected(): boolean {\n\t\treturn this.ready;\n\t}\n\n\t/**\n\t * Why the last connection attempt failed, when one did.\n\t *\n\t * A server that refuses the connection \u2014 a resource that needs an\n\t * administrator's switch, a scope the token lacks \u2014 says so here, so a\n\t * settings card can show the reason instead of an unexplained \"not connected\".\n\t */\n\tget lastFailure(): string | undefined {\n\t\treturn this.failure;\n\t}\n\n\t/**\n\t * Connect and publish tools, once the account is signed in if it needs to be.\n\t * @param ctx - host context, for logging.\n\t * @returns whether the bridge is usable; failures are logged, never thrown.\n\t */\n\tasync ensure(ctx: Context): Promise<boolean> {\n\t\tthis.pending ??= this.connectOnce(ctx).finally(() => {\n\t\t\tthis.pending = undefined;\n\t\t});\n\n\t\treturn await this.pending;\n\t}\n\n\t/** One connection attempt, shared by every caller that arrives meanwhile. */\n\tprivate async connectOnce(ctx: Context): Promise<boolean> {\n\t\tconst { profile, store } = this.options;\n\n\t\tthis.logger = loggerOf(ctx);\n\n\t\tif (needsOAuth(profile)) {\n\t\t\tconst grant = await store?.read();\n\n\t\t\tif (grant === undefined || !grant.isSignedIn()) return false;\n\t\t}\n\n\t\ttry {\n\t\t\tconst oauth = needsOAuth(profile)\n\t\t\t\t? await this.authorizationServer(profile)\n\t\t\t\t: undefined;\n\n\t\t\tif (this.client === undefined) await this.connect(profile, store, oauth);\n\n\t\t\tawait this.publishTools(ctx);\n\t\t\tawait this.options.onConnected?.();\n\t\t\tthis.failure = undefined;\n\n\t\t\tloggerOf(ctx).info(\n\t\t\t\t\"[%s] %d tools available from %s\",\n\t\t\t\taccountLabel(profile),\n\t\t\t\tthis.names.length,\n\t\t\t\tprofile.endpoint,\n\t\t\t);\n\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tthis.ready = false;\n\n\t\t\tawait this.drop();\n\t\t\tthis.failure = errorText(error);\n\t\t\tloggerOf(ctx).warn(\n\t\t\t\t\"[%s] MCP bridge unavailable: %s\",\n\t\t\t\taccountLabel(profile),\n\t\t\t\tthis.failure,\n\t\t\t);\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Close the connection and withdraw every published tool. */\n\tasync drop(): Promise<void> {\n\t\tfor (const dispose of this.disposers.values()) dispose();\n\n\t\tthis.disposers = new Map();\n\t\tthis.names = [];\n\t\tthis.ready = false;\n\t\tthis.failure = undefined;\n\n\t\tconst client = this.client;\n\t\tthis.client = undefined;\n\n\t\tif (client !== undefined) await client.close().catch(() => undefined);\n\t}\n\n\t/**\n\t * Call one tool of the connected server by its own name.\n\t * @param name - the server's tool name, without any published prefix.\n\t * @param args - tool arguments.\n\t * @throws when the connection is not open.\n\t */\n\tasync callTool(\n\t\tname: string,\n\t\targs: Record<string, unknown>,\n\t): Promise<CallToolResult> {\n\t\tconst client = this.client;\n\n\t\tif (client === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t`the ${this.options.profile.label} MCP connection is not open`,\n\t\t\t);\n\n\t\tconst result = await client.callTool(\n\t\t\t{ name, arguments: args },\n\t\t\t{ timeout: TOOL_CALL_TIMEOUT_MS },\n\t\t);\n\n\t\tif (this.options.profile.debug)\n\t\t\tthis.logger?.info(\n\t\t\t\t\"[%s] %s answered %s\",\n\t\t\t\taccountLabel(this.options.profile),\n\t\t\t\tname,\n\t\t\t\tJSON.stringify(result),\n\t\t\t);\n\n\t\treturn result;\n\t}\n\n\t/** The discovered authorization server, remembered across reconnects. */\n\tprivate async authorizationServer(\n\t\tprofile: OAuthProviderProfile,\n\t): Promise<OAuthServer> {\n\t\tconst oauth = this.oauth ?? (await discover(profile));\n\t\tthis.oauth = oauth;\n\n\t\treturn oauth;\n\t}\n\n\t/** Open the MCP connection, authenticated when the profile has OAuth facts. */\n\tprivate async connect(\n\t\tprofile: ProviderProfile,\n\t\tstore: GrantStore | undefined,\n\t\toauth: OAuthServer | undefined,\n\t): Promise<void> {\n\t\tconst { Client: McpClient, StreamableHTTPClientTransport } = await import(\n\t\t\t\"@modelcontextprotocol/client\"\n\t\t);\n\n\t\tconst transport = new StreamableHTTPClientTransport(\n\t\t\tnew URL(profile.endpoint),\n\t\t\toauth === undefined || store === undefined\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tauthProvider: {\n\t\t\t\t\t\t\t// Asked for before every request, so a refreshed token needs no reconnect.\n\t\t\t\t\t\t\ttoken: async () => await store.accessToken(oauth),\n\t\t\t\t\t\t\tonUnauthorized: async () => {\n\t\t\t\t\t\t\t\tawait store.refresh(oauth);\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t);\n\n\t\tconst client = new McpClient(this.options.clientInfo, { capabilities: {} });\n\t\tawait client.connect(transport);\n\n\t\tthis.client = client;\n\t\tthis.ready = true;\n\t}\n\n\t/** Publish every server tool, replacing the previous generation. */\n\tprivate async publishTools(ctx: Context): Promise<string[]> {\n\t\tconst client = this.client;\n\n\t\tif (client === undefined) throw new Error(\"the MCP connection is not open\");\n\n\t\tconst { createMcpToolDefinition } = await import(\n\t\t\t\"@deepseek-ai/dsh-mcp-client\"\n\t\t);\n\n\t\tconst listed = await client.listTools(undefined, { cacheMode: \"refresh\" });\n\t\tconst disposers = new Map<string, () => void>();\n\t\tconst names: string[] = [];\n\n\t\t// A republish happens on every Sync/Re-check while the client stays open.\n\t\t// The old names have to leave the registry before the new generation is\n\t\t// registered, or the duplicate name throws and the healthy bridge is\n\t\t// dropped by the caller's catch.\n\t\tconst previous = this.disposers;\n\n\t\tthis.disposers = disposers;\n\t\tthis.names = [];\n\n\t\tfor (const dispose of previous.values()) dispose();\n\n\t\ttry {\n\t\t\tfor (const tool of listed.tools) {\n\t\t\t\tconst name = publicToolName(\n\t\t\t\t\ttool.name,\n\t\t\t\t\tthis.options.profile.toolNamespace,\n\t\t\t\t);\n\t\t\t\tconst base = createMcpToolDefinition(ctx, {\n\t\t\t\t\tname,\n\t\t\t\t\trawName: tool.name,\n\t\t\t\t\tdescription: tool.description ?? `MCP tool ${tool.name}`,\n\t\t\t\t\tinputSchema: (tool.inputSchema ?? {\n\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\tproperties: {},\n\t\t\t\t\t}) as Record<string, unknown>,\n\t\t\t\t\t...(tool.outputSchema === undefined\n\t\t\t\t\t\t? {}\n\t\t\t\t\t\t: { outputSchema: tool.outputSchema }),\n\t\t\t\t\t...(tool.execution?.taskSupport === \"required\"\n\t\t\t\t\t\t? { taskRequired: true }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\tcall: async (args, execution) =>\n\t\t\t\t\t\tawait client.callTool(\n\t\t\t\t\t\t\t{ name: tool.name, arguments: args },\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tsignal: execution.signal,\n\t\t\t\t\t\t\t\ttoolDefinition: tool as Tool,\n\t\t\t\t\t\t\t\ttimeout: TOOL_CALL_TIMEOUT_MS,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t),\n\t\t\t\t});\n\n\t\t\t\tdisposers.set(\n\t\t\t\t\tname,\n\t\t\t\t\tctx.tools.register(\n\t\t\t\t\t\tthis.options.decorate?.(base, tool as Tool) ?? base,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tnames.push(name);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tfor (const dispose of disposers.values()) dispose();\n\n\t\t\tthis.disposers = new Map();\n\t\t\tthis.names = [];\n\n\t\t\tthrow error;\n\t\t}\n\n\t\tthis.names = names;\n\n\t\treturn names;\n\t}\n}\n", "/**\n * Opening the user's browser. Sign-in is impossible without a human in a\n * browser, so this is best-effort: a failure costs one copy-paste from the\n * notice the flow already published.\n */\n\nimport { execFile } from \"node:child_process\";\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport { errorText, loggerOf } from \"./log\";\n\n/** Launch the platform's default browser on a URL. */\nexport function openBrowser(ctx: Context, url: string): void {\n\tconst command =\n\t\tprocess.platform === \"darwin\"\n\t\t\t? \"open\"\n\t\t\t: process.platform === \"win32\"\n\t\t\t\t? \"rundll32\"\n\t\t\t\t: \"xdg-open\";\n\n\t// `cmd /c start` treats an unquoted `&` in the authorize URL as a command\n\t// separator, and Node's argv quoting does not add quotes without a space.\n\t// rundll32 opens the URL without a shell in between.\n\tconst args =\n\t\tprocess.platform === \"win32\" ? [\"url.dll,FileProtocolHandler\", url] : [url];\n\n\texecFile(command, args, (error) => {\n\t\tif (error !== null)\n\t\t\tloggerOf(ctx).warn(\n\t\t\t\t\"[mcp-oauth] could not open a browser: %s\",\n\t\t\t\terrorText(error),\n\t\t\t);\n\t});\n}\n", "/**\n * The authorization-code flow with PKCE, driven on the harness seam.\n *\n * The flow owns the whole conversation: it discovers the authorization server,\n * registers this installation as a client for the redirect URI it will actually\n * use, publishes the authorize URL, waits for the loopback callback, exchanges\n * the code, and commits the grant. Committing before `run` resolves is what the\n * seam requires.\n */\n\nimport { createHash, randomBytes } from \"node:crypto\";\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type { AuthorizationSession } from \"@deepseek-ai/dsh-authorization\";\nimport { SIGN_IN_TIMEOUT_MS } from \"./config\";\nimport { discover } from \"./discovery\";\nimport { Grant, type GrantStore } from \"./grant\";\nimport { jsonSafe } from \"./json\";\nimport { LoopbackListener } from \"./loopback\";\nimport type { OAuthProviderProfile } from \"./provider\";\nimport { authMethodOf, type RegisteredClient } from \"./server\";\n\nexport class AuthorizationFlow {\n\t/**\n\t * @param profile - the provider account to sign in to.\n\t * @param store - the record the finished grant is committed through.\n\t */\n\tconstructor(\n\t\tprivate readonly profile: OAuthProviderProfile,\n\t\tprivate readonly store: GrantStore,\n\t) {}\n\n\t/**\n\t * Run one sign-in attempt to completion.\n\t * @param _ctx - host context; the flow itself only touches the store.\n\t * @param session - the seam's session: signals cancellation, publishes notices, commits.\n\t */\n\tasync run(_ctx: Context, session: AuthorizationSession): Promise<void> {\n\t\tconst endpoint = this.profile.endpoint;\n\t\tconst oauth = await discover(this.profile);\n\n\t\tconst verifier = b64url(randomBytes(32));\n\t\tconst expectedState = b64url(randomBytes(32));\n\t\tconst challenge = b64url(createHash(\"sha256\").update(verifier).digest());\n\n\t\tconst listener = await LoopbackListener.listenForCode(\n\t\t\tthis.profile.oauth.redirectPort,\n\t\t\texpectedState,\n\t\t\tthis.profile.label,\n\t\t);\n\n\t\tconst onAbort = (): void => listener.fail(new Error(\"sign-in cancelled\"));\n\t\tsession.signal.addEventListener(\"abort\", onAbort, { once: true });\n\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\n\t\ttry {\n\t\t\tconst existing = await this.store.read();\n\t\t\tconst client = await this.clientFor(\n\t\t\t\toauth,\n\t\t\t\tlistener.redirectUri,\n\t\t\t\texisting,\n\t\t\t);\n\n\t\t\tconst authorize = new URL(oauth.authorizationEndpoint);\n\n\t\t\tfor (const [key, value] of Object.entries({\n\t\t\t\tresponse_type: \"code\",\n\t\t\t\tclient_id: client.clientId,\n\t\t\t\tredirect_uri: listener.redirectUri,\n\t\t\t\tscope: oauth.scopeString,\n\t\t\t\tstate: expectedState,\n\t\t\t\tcode_challenge: challenge,\n\t\t\t\tcode_challenge_method: \"S256\",\n\t\t\t\tresource: endpoint,\n\t\t\t})) {\n\t\t\t\tauthorize.searchParams.set(key, value);\n\t\t\t}\n\n\t\t\tsession.notify({\n\t\t\t\tmessage: `Approve the ${this.profile.label} sign-in in your browser.`,\n\t\t\t\turl: authorize.href,\n\t\t\t});\n\n\t\t\tconst authorizationCode = await new Promise<string>((resolve, reject) => {\n\t\t\t\ttimer = setTimeout(\n\t\t\t\t\t() => reject(new Error(\"sign-in timed out\")),\n\t\t\t\t\tSIGN_IN_TIMEOUT_MS,\n\t\t\t\t);\n\n\t\t\t\tlistener.code.then(resolve, reject);\n\t\t\t});\n\n\t\t\tsession.signal.throwIfAborted();\n\n\t\t\tconst payload = await oauth.tokenRequest(client, {\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\tcode: authorizationCode,\n\t\t\t\tredirect_uri: listener.redirectUri,\n\t\t\t\tcode_verifier: verifier,\n\t\t\t});\n\n\t\t\t// A commit is what makes the seam call the attempt authorized. Without\n\t\t\t// an access token it would store a grant that can never connect.\n\t\t\tif (\n\t\t\t\ttypeof payload.access_token !== \"string\" ||\n\t\t\t\tpayload.access_token === \"\"\n\t\t\t)\n\t\t\t\tthrow new Error(\"the token endpoint returned no access token\");\n\n\t\t\tconst base = {\n\t\t\t\t...(existing ?? { version: 1 as const }),\n\t\t\t\tversion: 1 as const,\n\t\t\t\tissuer: oauth.issuer,\n\t\t\t\tresource: endpoint,\n\t\t\t\tredirectUri: listener.redirectUri,\n\t\t\t\t...client,\n\t\t\t\tscope: oauth.scopeString,\n\t\t\t};\n\n\t\t\tconst payloadPair = jsonSafe({\n\t\t\t\t...base,\n\t\t\t\t...oauth.toTokenFields(payload, base),\n\t\t\t});\n\n\t\t\tif (payloadPair === undefined)\n\t\t\t\tthrow new Error(\"the grant could not be represented as JSON\");\n\n\t\t\tawait session.commit({ kind: \"grant\", payload: payloadPair });\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\n\t\t\tsession.signal.removeEventListener(\"abort\", onAbort);\n\t\t\tawait listener.close();\n\t\t}\n\t}\n\n\t/**\n\t * The client to authorize with, registering one when the stored identity\n\t * cannot be reused.\n\t *\n\t * A registration is bound to the exact redirect URIs it was issued for, and\n\t * the loopback port is not guaranteed to be free, so a stored client is\n\t * reused only when the resource and the URI both still match.\n\t */\n\tprivate async clientFor(\n\t\toauth: Awaited<ReturnType<typeof discover>>,\n\t\tredirectUri: string,\n\t\texisting: Grant | undefined,\n\t): Promise<RegisteredClient> {\n\t\tif (\n\t\t\texisting !== undefined &&\n\t\t\texisting.issuer === oauth.issuer &&\n\t\t\texisting.resource === this.profile.endpoint &&\n\t\t\texisting.redirectUri === redirectUri &&\n\t\t\ttypeof existing.clientId === \"string\"\n\t\t) {\n\t\t\tconst tokenAuthMethod = authMethodOf(existing);\n\n\t\t\treturn existing.clientSecret === undefined\n\t\t\t\t? { clientId: existing.clientId, tokenAuthMethod }\n\t\t\t\t: {\n\t\t\t\t\t\tclientId: existing.clientId,\n\t\t\t\t\t\tclientSecret: existing.clientSecret,\n\t\t\t\t\t\ttokenAuthMethod,\n\t\t\t\t\t};\n\t\t}\n\n\t\tconst registered = await oauth.registerClient(\n\t\t\tredirectUri,\n\t\t\tthis.profile.oauth.clientName,\n\t\t);\n\n\t\tawait this.store.mutate(\n\t\t\tasync (grant) =>\n\t\t\t\tnew Grant({\n\t\t\t\t\t...(grant ?? { version: 1 as const }),\n\t\t\t\t\tversion: 1,\n\t\t\t\t\tissuer: oauth.issuer,\n\t\t\t\t\tresource: this.profile.endpoint,\n\t\t\t\t\tredirectUri,\n\t\t\t\t\t...registered,\n\t\t\t\t}),\n\t\t);\n\n\t\treturn registered;\n\t}\n}\n\n/** base64url without padding, as PKCE and `state` require. */\nfunction b64url(input: Buffer): string {\n\treturn input\n\t\t.toString(\"base64\")\n\t\t.replace(/\\+/gu, \"-\")\n\t\t.replace(/\\//gu, \"_\")\n\t\t.replace(/=+$/u, \"\");\n}\n", "/**\n * The credential store persists a record payload through JSON, and refuses a\n * value JSON cannot represent. Dynamic client registration legitimately leaves\n * fields absent (`client_secret`, `site_url`), so every payload is normalized\n * before it is written instead of leaking `undefined` into the store.\n */\n\nexport type JsonValue =\n\t| string\n\t| number\n\t| boolean\n\t| null\n\t| JsonValue[]\n\t| { [key: string]: JsonValue };\n\n/**\n * Copy a value, dropping every member JSON cannot carry.\n * @param value - arbitrary record payload.\n * @returns the same shape with `undefined`, functions, and symbols removed.\n */\nexport function jsonSafe(value: unknown): JsonValue | undefined {\n\tif (Array.isArray(value)) {\n\t\tconst items: JsonValue[] = [];\n\n\t\tfor (const item of value) {\n\t\t\tconst safe = jsonSafe(item);\n\n\t\t\tif (safe !== undefined) items.push(safe);\n\t\t}\n\n\t\treturn items;\n\t}\n\n\tif (value !== null && typeof value === \"object\") {\n\t\tconst out: { [key: string]: JsonValue } = {};\n\n\t\tfor (const [key, item] of Object.entries(value)) {\n\t\t\tconst safe = jsonSafe(item);\n\n\t\t\tif (safe !== undefined) out[key] = safe;\n\t\t}\n\n\t\treturn out;\n\t}\n\n\tif (\n\t\tvalue === undefined ||\n\t\ttypeof value === \"function\" ||\n\t\ttypeof value === \"symbol\"\n\t)\n\t\treturn undefined;\n\n\tif (typeof value === \"number\" && !Number.isFinite(value)) return undefined;\n\n\treturn value as JsonValue;\n}\n", "/**\n * The durable credential record and the token lifetime logic over it.\n *\n * A record is written under the store's `<scope>/<id>` key, and its payload is\n * plain JSON: what comes back from disk is not an instance, which is why every\n * read path goes through {@link Grant.from} and callers only ever see a real\n * `Grant`.\n *\n * Tokens refresh inside `modifyRecord`, whose lock spans the network round\n * trip, so two processes can never rotate the same refresh token twice.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type { CredentialKey } from \"@deepseek-ai/dsh-credentials\";\nimport { REFRESH_MARGIN_MS } from \"./config\";\nimport { type JsonValue, jsonSafe } from \"./json\";\nimport type { OAuthServer, TokenAuthMethod } from \"./server\";\n\n/** Provider-owned facts kept beside the tokens. */\nexport type GrantExtra = Readonly<Record<string, JsonValue>>;\n\n/** A grant that definitely carries an access token. */\nexport type SignedInGrant = Grant & { accessToken: string };\n\n/** What a successful sign-in stores. */\nexport class Grant {\n\treadonly version: 1;\n\t/** Issuer the tokens came from, so a generation switch is detectable. */\n\treadonly issuer?: string;\n\t/** MCP resource the tokens are valid for (RFC 8707). */\n\treadonly resource?: string;\n\t/** Dynamically registered client identity. */\n\treadonly clientId?: string;\n\t/** Present only for a server that issues one, which a confidential client requires. */\n\treadonly clientSecret?: string;\n\t/** How this client authenticates at the token endpoint. */\n\treadonly tokenAuthMethod?: TokenAuthMethod;\n\t/** The exact redirect URI the client was registered with. */\n\treadonly redirectUri?: string;\n\t/** Application data the provider keeps with its tokens. */\n\treadonly extra?: GrantExtra;\n\treadonly accessToken?: string;\n\treadonly refreshToken?: string;\n\treadonly tokenType?: string;\n\treadonly scope?: string;\n\treadonly expiresAt?: number;\n\n\tconstructor(params: Omit<Grant, \"isSignedIn\">) {\n\t\tthis.version = params.version;\n\t\tthis.issuer = params.issuer;\n\t\tthis.resource = params.resource;\n\t\tthis.clientId = params.clientId;\n\t\tthis.clientSecret = params.clientSecret;\n\t\tthis.tokenAuthMethod = params.tokenAuthMethod;\n\t\tthis.redirectUri = params.redirectUri;\n\t\tthis.extra = params.extra;\n\t\tthis.accessToken = params.accessToken;\n\t\tthis.refreshToken = params.refreshToken;\n\t\tthis.tokenType = params.tokenType;\n\t\tthis.scope = params.scope;\n\t\tthis.expiresAt = params.expiresAt;\n\t}\n\n\t/**\n\t * Rebuild a grant from a stored payload.\n\t * @param payload - whatever the credential store returned.\n\t * @returns the grant, or undefined for a payload that is not an object.\n\t */\n\tstatic from(payload: unknown): Grant | undefined {\n\t\tif (payload === null || typeof payload !== \"object\") return undefined;\n\n\t\treturn new Grant(payload as Omit<Grant, \"isSignedIn\">);\n\t}\n\n\t/** Whether a stored grant carries a usable access token. */\n\tisSignedIn(): this is SignedInGrant {\n\t\treturn typeof this.accessToken === \"string\" && this.accessToken !== \"\";\n\t}\n}\n\n/** One provider's record in the credential store. */\nexport class GrantStore {\n\t/** The record key, as the harness authorization seam spells it. */\n\treadonly credentialKey: CredentialKey;\n\n\t/**\n\t * @param ctx - host context carrying the credential store.\n\t * @param key - the record key, in the store's `<scope>/<id>` grammar.\n\t */\n\tconstructor(\n\t\tprivate readonly ctx: Context,\n\t\tkey: string,\n\t) {\n\t\tthis.credentialKey = key as CredentialKey;\n\t}\n\n\t/** Read the stored grant, or undefined when there is none. */\n\tasync read(): Promise<Grant | undefined> {\n\t\tconst record = await this.ctx.credentials.readRecord(this.credentialKey);\n\n\t\tif (record === undefined || record.kind !== \"grant\") return undefined;\n\n\t\treturn Grant.from(record.payload);\n\t}\n\n\t/**\n\t * Read-decide-replace the grant under the store's cross-process lock.\n\t * @param mutate - receives the current grant; returning undefined leaves it alone.\n\t * @returns the stored grant after the change.\n\t */\n\tasync mutate(\n\t\tmutate: (current: Grant | undefined) => Promise<Grant | undefined>,\n\t): Promise<Grant | undefined> {\n\t\tconst record = await this.ctx.credentials.modifyRecord(\n\t\t\tthis.credentialKey,\n\t\t\tasync (current) => {\n\t\t\t\tconst previous =\n\t\t\t\t\tcurrent?.kind === \"grant\" ? Grant.from(current.payload) : undefined;\n\n\t\t\t\tconst next = await mutate(previous);\n\t\t\t\tif (next === undefined) return undefined;\n\n\t\t\t\t// The store refuses a value JSON cannot represent, and a token response\n\t\t\t\t// omits fields the previous grant carried.\n\t\t\t\tconst payload = jsonSafe(next);\n\t\t\t\tif (payload === undefined) return undefined;\n\n\t\t\t\treturn { kind: \"grant\", payload };\n\t\t\t},\n\t\t);\n\n\t\treturn record?.kind === \"grant\" ? Grant.from(record.payload) : undefined;\n\t}\n\n\t/** Merge provider facts into the record, creating it when absent. */\n\tasync remember(patch: GrantExtra): Promise<void> {\n\t\tawait this.mutate(\n\t\t\tasync (grant) =>\n\t\t\t\tnew Grant({\n\t\t\t\t\t...(grant ?? { version: 1 as const }),\n\t\t\t\t\tversion: 1,\n\t\t\t\t\textra: { ...grant?.extra, ...patch },\n\t\t\t\t}),\n\t\t);\n\t}\n\n\t/** Forget the record entirely; the issuer is not told, matching the seam. */\n\tasync clear(): Promise<void> {\n\t\tawait this.ctx.credentials.deleteRecord(this.credentialKey);\n\t}\n\n\t/**\n\t * The token an MCP transport asks for before every request, refreshed when\n\t * it is about to expire.\n\t * @param oauth - the authorization server the tokens belong to.\n\t * @returns the access token, or undefined when nobody is signed in.\n\t */\n\tasync accessToken(oauth: OAuthServer): Promise<string | undefined> {\n\t\tconst grant = await this.read();\n\n\t\tif (grant === undefined || !grant.isSignedIn()) return undefined;\n\n\t\tif (Date.now() < (grant.expiresAt ?? 0) - REFRESH_MARGIN_MS)\n\t\t\treturn grant.accessToken;\n\n\t\treturn await this.refresh(oauth);\n\t}\n\n\t/**\n\t * Exchange the refresh token for a new access token, inside the store lock.\n\t * A peer that refreshed while we waited makes this a no-op.\n\t * @param oauth - the authorization server to refresh against.\n\t * @throws when the record carries no refresh token, or nobody is signed in.\n\t */\n\tasync refresh(oauth: OAuthServer): Promise<string> {\n\t\tconst after = await this.mutate(async (grant) => {\n\t\t\tif (grant === undefined) return undefined;\n\n\t\t\tif (Date.now() < (grant.expiresAt ?? 0) - REFRESH_MARGIN_MS)\n\t\t\t\treturn undefined;\n\n\t\t\tif (typeof grant.refreshToken !== \"string\" || grant.refreshToken === \"\")\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"the stored sign-in has no refresh token; sign in again\",\n\t\t\t\t);\n\n\t\t\tconst payload = await oauth.tokenRequest(grant, {\n\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\trefresh_token: grant.refreshToken,\n\t\t\t});\n\n\t\t\treturn new Grant({ ...grant, ...oauth.toTokenFields(payload, grant) });\n\t\t});\n\n\t\tif (after === undefined || !after.isSignedIn())\n\t\t\tthrow new Error(\"not signed in\");\n\n\t\treturn after.accessToken;\n\t}\n}\n", "/**\n * The loopback redirect listener for the authorization-code flow.\n *\n * A desktop OAuth client cannot receive a redirect on a public URL, so it binds\n * `127.0.0.1` and hands the browser a redirect URI pointing at itself. The\n * registered client is bound to the exact URI, and the preferred port may be\n * taken, so the listener reports the port it actually got.\n */\n\nimport { timingSafeEqual } from \"node:crypto\";\nimport { createServer, type Server } from \"node:http\";\nimport { PORT_ATTEMPTS } from \"./config\";\nimport { errorText } from \"./log\";\n\n/** The five characters a callback page interpolates, as HTML entities. */\nconst HTML_ENTITIES: Readonly<Record<string, string>> = {\n\t\"&\": \"&\",\n\t\"<\": \"<\",\n\t\">\": \">\",\n\t'\"': \""\",\n\t\"'\": \"'\",\n};\n\n/** One string as text inside an HTML page. */\nconst escapeHtml = (value: string): string =>\n\tvalue.replace(\n\t\t/[&<>\"']/gu,\n\t\t(character) => HTML_ENTITIES[character] ?? character,\n\t);\n\nexport class LoopbackListener {\n\treadonly server: Server;\n\treadonly port: number;\n\treadonly redirectUri: string;\n\t/** Resolves with the authorization code the browser returned. */\n\treadonly code: Promise<string>;\n\n\treadonly fail: (error: Error) => void;\n\n\tprotected constructor(\n\t\tserver: Server,\n\t\tport: number,\n\t\tredirectUri: string,\n\t\tcode: Promise<string>,\n\t\tfail: (error: Error) => void,\n\t) {\n\t\tthis.server = server;\n\t\tthis.port = port;\n\t\tthis.redirectUri = redirectUri;\n\t\tthis.code = code;\n\t\tthis.fail = fail;\n\t}\n\n\t/**\n\t * Bind the first free port at or after `preferredPort` and wait for the code.\n\t * @param preferredPort - the configured port to try first.\n\t * @param expectedState - the `state` value the response must carry back.\n\t * @param label - the provider's name, for the pages the browser sees.\n\t */\n\tstatic async listenForCode(\n\t\tpreferredPort: number,\n\t\texpectedState: string,\n\t\tlabel: string,\n\t): Promise<LoopbackListener> {\n\t\tlet resolveCode: (code: string) => void = () => {};\n\t\tlet rejectCode: (error: Error) => void = () => {};\n\n\t\tconst code = new Promise<string>((resolve, reject) => {\n\t\t\tresolveCode = resolve;\n\t\t\trejectCode = reject;\n\t\t});\n\n\t\t// The caller always awaits `code`; this keeps a rejection observed even when\n\t\t// it settles before that await is attached.\n\t\tcode.catch(() => undefined);\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let offset = 0; offset < PORT_ATTEMPTS; offset += 1) {\n\t\t\tconst port = preferredPort + offset;\n\n\t\t\tconst server = createServer((request, response) => {\n\t\t\t\tconst url = new URL(request.url ?? \"/\", `http://127.0.0.1:${port}`);\n\n\t\t\t\tif (url.pathname !== \"/callback\") {\n\t\t\t\t\tresponse\n\t\t\t\t\t\t.writeHead(404, { \"content-type\": \"text/plain; charset=utf-8\" })\n\t\t\t\t\t\t.end(\"not found\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst gotState = url.searchParams.get(\"state\") ?? \"\";\n\t\t\t\tconst oauthError = url.searchParams.get(\"error\");\n\t\t\t\tconst returned = url.searchParams.get(\"code\");\n\n\t\t\t\tconst comparable =\n\t\t\t\t\tBuffer.byteLength(gotState) === Buffer.byteLength(expectedState);\n\t\t\t\tconst stateMatches =\n\t\t\t\t\tcomparable &&\n\t\t\t\t\ttimingSafeEqual(Buffer.from(gotState), Buffer.from(expectedState));\n\n\t\t\t\tif (oauthError !== null || returned === null || !stateMatches) {\n\t\t\t\t\tconst reason =\n\t\t\t\t\t\toauthError !== null\n\t\t\t\t\t\t\t? `${label} reported: ${oauthError}`\n\t\t\t\t\t\t\t: \"The response did not match this sign-in attempt.\";\n\n\t\t\t\t\tresponse\n\t\t\t\t\t\t.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" })\n\t\t\t\t\t\t.end(LoopbackListener.callbackPage(\"Authorization failed\", reason));\n\n\t\t\t\t\trejectCode(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\toauthError !== null\n\t\t\t\t\t\t\t\t? `authorization denied: ${oauthError}`\n\t\t\t\t\t\t\t\t: \"state mismatch\",\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresponse\n\t\t\t\t\t.writeHead(200, { \"content-type\": \"text/html; charset=utf-8\" })\n\t\t\t\t\t.end(\n\t\t\t\t\t\tLoopbackListener.callbackPage(\n\t\t\t\t\t\t\t`Signed in to ${label}`,\n\t\t\t\t\t\t\t\"You can close this tab and return to DeepSeek Harness.\",\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\tresolveCode(returned);\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\t\tserver.once(\"error\", reject);\n\t\t\t\t\tserver.listen(port, \"127.0.0.1\", resolve);\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\treturn new LoopbackListener(\n\t\t\t\tserver,\n\t\t\t\tport,\n\t\t\t\t`http://127.0.0.1:${port}/callback`,\n\t\t\t\tcode,\n\t\t\t\trejectCode,\n\t\t\t);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`no free loopback port in ${preferredPort}..${preferredPort + PORT_ATTEMPTS - 1}: ${errorText(lastError)}`,\n\t\t);\n\t}\n\n\t// `close()` alone only stops accepting: it waits for existing sockets,\n\t// and a browser that used keep-alive would hold the flow open forever.\n\tclose = async () => {\n\t\tthis.server.closeAllConnections();\n\n\t\tawait new Promise<void>((resolve) => {\n\t\t\tthis.server.close(() => resolve());\n\t\t});\n\t};\n\n\tprivate static callbackPage = (title: string, message: string): string => {\n\t\tconst safeTitle = escapeHtml(title);\n\t\tconst safeMessage = escapeHtml(message);\n\n\t\treturn (\n\t\t\t`<!doctype html><meta charset=\"utf-8\"><title>${safeTitle}</title>` +\n\t\t\t'<body style=\"font:16px/1.5 system-ui;margin:4rem auto;max-width:32rem;text-align:center\">' +\n\t\t\t`<h1 style=\"font-size:1.25rem\">${safeTitle}</h1><p>${safeMessage}</p></body>`\n\t\t);\n\t};\n}\n", "/**\n * Reading an MCP tool result.\n *\n * A server may answer with JSON inside text content blocks, with prose, or\n * with `structuredContent` \u2014 sometimes withholding the last from clients it\n * does not recognize by name. So a reader starts from the same two reads: the\n * text blocks in order, and whatever JSON those blocks carry.\n */\n\n/** The text content blocks of an MCP result, in order. */\nexport function mcpText(value: unknown): string[] {\n\tif (value === null || typeof value !== \"object\") return [];\n\n\tconst content = (value as { content?: unknown }).content;\n\n\tif (!Array.isArray(content)) return [];\n\n\tconst out: string[] = [];\n\tfor (const block of content) {\n\t\tif (block === null || typeof block !== \"object\") continue;\n\n\t\tconst { type, text } = block as { type?: unknown; text?: unknown };\n\n\t\tif (type === \"text\" && typeof text === \"string\") out.push(text);\n\t}\n\n\treturn out;\n}\n\n/**\n * The JSON payloads an MCP result carries.\n *\n * `structuredContent` comes first when the server sent it: it is the canonical\n * machine-readable answer, and the text blocks are its rendering. A block that\n * is not JSON is skipped rather than failing the read: a server may answer a\n * successful call with prose, and the caller's text fallback is then the\n * truthful answer.\n */\nexport function mcpJson(value: unknown): unknown[] {\n\tconst roots: unknown[] = [];\n\n\tif (value !== null && typeof value === \"object\") {\n\t\tconst structured = (value as { structuredContent?: unknown })\n\t\t\t.structuredContent;\n\n\t\tif (structured !== undefined) roots.push(structured);\n\t}\n\n\tfor (const text of mcpText(value)) {\n\t\tconst trimmed = text.trim();\n\n\t\tif (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) continue;\n\n\t\ttry {\n\t\t\troots.push(JSON.parse(trimmed));\n\t\t} catch {\n\t\t\t// Not JSON; nothing to walk.\n\t\t}\n\t}\n\n\treturn roots;\n}\n\n/** Whether a value is a plain object, which every payload record is. */\nexport function asRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: undefined;\n}\n\n/** The first non-empty string among a record's named fields. */\nexport function pickString(\n\tsource: unknown,\n\t...names: string[]\n): string | undefined {\n\tconst record = asRecord(source);\n\tif (record === undefined) return undefined;\n\n\tfor (const name of names) {\n\t\tconst value = record[name];\n\n\t\tif (typeof value === \"string\" && value.trim() !== \"\") return value.trim();\n\t\tif (typeof value === \"number\") return String(value);\n\t}\n\n\treturn undefined;\n}\n\n/** The first finite number among a record's named fields. */\nexport function pickNumber(\n\tsource: unknown,\n\t...names: string[]\n): number | undefined {\n\tconst record = asRecord(source);\n\tif (record === undefined) return undefined;\n\n\tfor (const name of names) {\n\t\tconst value = record[name];\n\n\t\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\n\t\tif (typeof value === \"string\" && value.trim() !== \"\") {\n\t\t\tconst parsed = Number(value);\n\n\t\t\tif (Number.isFinite(parsed)) return parsed;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n", "/**\n * Sign-in state and the operations that change it.\n *\n * One implementation serves every caller \u2014 the slash command, the settings\n * card's HTTP calls, and the automatic connect after a successful attempt \u2014 so\n * the card and the command can never drift apart. What differs between\n * integrations is only what the provider's own server names: its label, the log\n * tag, and the account facts learned after connecting.\n *\n * The OAuth machinery itself belongs to `dsh-mcp-oauth`.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport {\n\ttype AuthorizationFlow,\n\terrorText,\n\ttype GrantStore,\n\tloggerOf,\n\ttype McpBridge,\n\topenBrowser,\n} from \"dsh-mcp-oauth\";\nimport type { EndpointConfig } from \"./config\";\nimport type { AccountFacts, FactsReader } from \"./facts\";\nimport type { RuntimeState } from \"./state\";\nimport type { IntegrationStatus, StatusNotice } from \"./status\";\n\n/** What one provider adds to the generic status document. */\nexport type StatusExtras<TStatus extends IntegrationStatus, TFacts> = (\n\tbase: IntegrationStatus,\n\tfacts: TFacts | undefined,\n) => TStatus;\n\n/** Everything that is the provider's rather than the library's. */\nexport interface AuthSpec<\n\tTStatus extends IntegrationStatus,\n\tTFacts extends AccountFacts,\n> {\n\t/** The provider's name, as the user sees it. */\n\treadonly label: string;\n\t/** The log tag every message of this plugin carries, such as `[sentry]`. */\n\treadonly logTag: string;\n\t/** How this provider reads the facts its own server names. */\n\treadonly facts: FactsReader<TFacts>;\n\t/** The provider's own fields of the status document. */\n\treadonly status: StatusExtras<TStatus, TFacts>;\n}\n\n/** Everything the auth needs, wired once at apply time. */\nexport interface AuthDeps<\n\tTStatus extends IntegrationStatus,\n\tTFacts extends AccountFacts,\n> {\n\treadonly ctx: Context;\n\treadonly config: EndpointConfig;\n\treadonly state: RuntimeState<TFacts>;\n\treadonly store: GrantStore;\n\treadonly flow: AuthorizationFlow;\n\treadonly bridge: McpBridge;\n\treadonly spec: AuthSpec<TStatus, TFacts>;\n}\n\n/** The sign-in operations every caller of an integration shares. */\nexport interface IntegrationAuthFace<TStatus extends IntegrationStatus> {\n\tsignIn(): Promise<unknown>;\n\tsignOut(): Promise<void>;\n\tconnect(): Promise<boolean>;\n\tgetStatus(): Promise<TStatus>;\n\tloginMessage(status: TStatus): string;\n\tsettleNotice(ms?: number): Promise<void>;\n}\n\nexport class IntegrationAuth<\n\tTStatus extends IntegrationStatus,\n\tTFacts extends AccountFacts,\n> implements IntegrationAuthFace<TStatus>\n{\n\tconstructor(private readonly deps: AuthDeps<TStatus, TFacts>) {}\n\n\t/** Start a sign-in attempt, or join the one already running. */\n\tasync signIn(): Promise<unknown> {\n\t\tconst { ctx, state, spec } = this.deps;\n\n\t\tif (state.running !== undefined) return state.running;\n\n\t\tstate.notice = undefined;\n\t\tstate.error = undefined;\n\n\t\tconst controller = new AbortController();\n\n\t\tstate.running = (async () => {\n\t\t\ttry {\n\t\t\t\tconst outcome = await ctx.authorization.begin({\n\t\t\t\t\tkey: this.deps.store.credentialKey,\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\tinteraction: {\n\t\t\t\t\t\t// The flow publishes the authorize URL; opening it is the driver's\n\t\t\t\t\t\t// business, because a generic UI driving the same flow would render\n\t\t\t\t\t\t// the URL instead of launching a browser.\n\t\t\t\t\t\tnotify: (notice) => {\n\t\t\t\t\t\t\tstate.notice = notice satisfies StatusNotice;\n\n\t\t\t\t\t\t\tif (typeof notice.url === \"string\") openBrowser(ctx, notice.url);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tprompt: () =>\n\t\t\t\t\t\t\tPromise.reject(\n\t\t\t\t\t\t\t\tnew Error(`${spec.label} sign-in needs no prompts`),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (outcome.status === \"authorized\") {\n\t\t\t\t\tstate.notice = { message: `Signed in to ${spec.label}.` };\n\n\t\t\t\t\tawait this.connect();\n\t\t\t\t} else {\n\t\t\t\t\tstate.error = \"Sign-in was cancelled.\";\n\t\t\t\t}\n\n\t\t\t\treturn outcome;\n\t\t\t} catch (error) {\n\t\t\t\tstate.error = errorText(error);\n\t\t\t\tloggerOf(ctx).warn(\"%s sign-in failed: %s\", spec.logTag, state.error);\n\n\t\t\t\treturn { status: \"failed\" as const };\n\t\t\t} finally {\n\t\t\t\tstate.running = undefined;\n\t\t\t}\n\t\t})();\n\n\t\treturn state.running;\n\t}\n\n\t/** Forget the sign-in and withdraw every published tool. */\n\tasync signOut(): Promise<void> {\n\t\tconst { ctx, state, store, bridge } = this.deps;\n\n\t\ttry {\n\t\t\tctx.authorization.cancel(store.credentialKey);\n\t\t} catch {\n\t\t\t// Nothing in flight; cancel is not an error here.\n\t\t}\n\n\t\tawait bridge.drop();\n\t\tawait store.clear();\n\n\t\tstate.notice = undefined;\n\t\tstate.error = undefined;\n\t\tstate.facts = undefined;\n\t}\n\n\t/**\n\t * Connect, restoring the known account facts first so published tools have\n\t * them.\n\t * @returns whether the connection is usable.\n\t */\n\tasync connect(): Promise<boolean> {\n\t\tconst { state, store, bridge, ctx, spec } = this.deps;\n\n\t\tstate.facts = spec.facts.from(await store.read());\n\n\t\tconst ready = await bridge.ensure(ctx);\n\n\t\tif (ready) {\n\t\t\tstate.error = undefined;\n\t\t\tawait this.learn();\n\t\t} else {\n\t\t\t// The reason a connection failed is the provider's own words \u2014 a group\n\t\t\t// switch that is off, a scope the token lacks \u2014 and it is what the\n\t\t\t// settings card must show, rather than a bare \"not connected\".\n\t\t\tstate.error = bridge.lastFailure;\n\t\t}\n\n\t\treturn ready;\n\t}\n\n\t/** Build the status document for one caller. */\n\tasync getStatus(): Promise<TStatus> {\n\t\tconst { state, config, bridge, store, spec } = this.deps;\n\t\tconst grant = await store.read();\n\t\tconst signedIn = grant?.isSignedIn() ?? false;\n\n\t\treturn spec.status(\n\t\t\t{\n\t\t\t\tsignedIn,\n\t\t\t\tinFlight: state.running !== undefined,\n\t\t\t\texpiresAt: signedIn ? (grant?.expiresAt ?? null) : null,\n\t\t\t\tscope: signedIn ? (grant?.scope ?? null) : null,\n\t\t\t\tnotice: state.notice ?? null,\n\t\t\t\terror: state.error ?? null,\n\t\t\t\tconnected: bridge.isConnected,\n\t\t\t\ttools: [...bridge.toolNames],\n\t\t\t\tendpoint: config.endpoint,\n\t\t\t},\n\t\t\tstate.facts ?? spec.facts.from(grant),\n\t\t);\n\t}\n\n\t/** The line the command and any notice show for a sign-in in progress. */\n\tloginMessage(status: TStatus): string {\n\t\tif (status.notice?.url !== undefined)\n\t\t\treturn `${status.notice.message} ${status.notice.url}`;\n\n\t\treturn (\n\t\t\tstatus.notice?.message ??\n\t\t\t`Opening your browser to sign in to ${this.deps.spec.label}\u2026`\n\t\t);\n\t}\n\n\t/** Wait briefly so a just-started attempt can publish its authorize URL. */\n\tasync settleNotice(ms = 400): Promise<void> {\n\t\tawait new Promise((resolve) => {\n\t\t\tsetTimeout(resolve, ms);\n\t\t});\n\t}\n\n\t/** Learn the account facts once, and remember them for the next start. */\n\tprivate async learn(): Promise<void> {\n\t\tconst { ctx, state, store, bridge, spec } = this.deps;\n\t\tconst known = state.facts;\n\n\t\tif (known?.isComplete === true) return;\n\t\tif (!bridge.toolNames.some((name) => name.endsWith(spec.facts.toolName)))\n\t\t\treturn;\n\n\t\ttry {\n\t\t\tconst learned = spec.facts.parse(\n\t\t\t\tawait bridge.callTool(spec.facts.toolName, {}),\n\t\t\t);\n\n\t\t\tif (learned.isEmpty) return;\n\n\t\t\tconst merged = spec.facts.merge(known, learned);\n\n\t\t\tstate.facts = merged;\n\t\t\tawait store.remember(spec.facts.remember(merged));\n\t\t} catch (error) {\n\t\t\tloggerOf(ctx).warn(\n\t\t\t\t\"%s could not read the account facts: %s\",\n\t\t\t\tspec.logTag,\n\t\t\t\terrorText(error),\n\t\t\t);\n\t\t}\n\t}\n}\n", "/**\n * The slash command every integration offers.\n *\n * A command runs directly against the agent without creating a model message,\n * which makes it the right surface for a human-driven action like signing in.\n * It shares its implementation with the settings card, so the two can never\n * disagree about what \"signed in\" means.\n */\n\nimport { errorText } from \"dsh-mcp-oauth\";\nimport type { IntegrationAuthFace } from \"./auth\";\nimport type { IntegrationStatus } from \"./status\";\n\n/** What the command accepts after its name. */\nconst ACTIONS = [\"status\", \"login\", \"logout\", \"sync\"] as const;\n\ntype Action = (typeof ACTIONS)[number];\n\n/** The command result vocabulary the command seam renders. */\nexport interface CommandResult {\n\treadonly kind: \"success\" | \"error\";\n\treadonly text: string;\n}\n\n/** What one provider says about itself in the command's answers. */\nexport interface CommandSpec<TStatus extends IntegrationStatus> {\n\t/** The command's name, without the leading slash. */\n\treadonly name: string;\n\t/** The provider's name, as the user sees it. */\n\treadonly label: string;\n\t/**\n\t * One sentence the status answer adds after the token's expiry, such as the\n\t * account it works with.\n\t * @returns the sentence with its own full stop, or an empty string.\n\t */\n\treadonly describe?: (status: TStatus) => string;\n}\n\n/**\n * Build the handler one integration's command registers.\n * @param spec - the command's name, its provider's label, and what that provider adds.\n * @param auth - the one implementation of sign-in state.\n * @returns a handler the command seam can call.\n */\nexport const createIntegrationCommand = <TStatus extends IntegrationStatus>(\n\tspec: CommandSpec<TStatus>,\n\tauth: IntegrationAuthFace<TStatus>,\n): ((invocation: { rawInput?: unknown }) => Promise<CommandResult>) => {\n\tconst parseAction = (rawInput: unknown): Action => {\n\t\tconst first =\n\t\t\tString(rawInput ?? \"\")\n\t\t\t\t.trim()\n\t\t\t\t.split(/\\s+/u)[0] ?? \"\";\n\n\t\treturn (ACTIONS as readonly string[]).includes(first)\n\t\t\t? (first as Action)\n\t\t\t: \"status\";\n\t};\n\n\treturn async ({ rawInput }) => {\n\t\tconst action = parseAction(rawInput);\n\n\t\ttry {\n\t\t\tif (action === \"login\") {\n\t\t\t\tvoid auth.signIn();\n\t\t\t\tawait auth.settleNotice();\n\n\t\t\t\tconst status = await auth.getStatus();\n\n\t\t\t\treturn status.error === null\n\t\t\t\t\t? { kind: \"success\", text: auth.loginMessage(status) }\n\t\t\t\t\t: { kind: \"error\", text: status.error };\n\t\t\t}\n\n\t\t\tif (action === \"logout\") {\n\t\t\t\tawait auth.signOut();\n\n\t\t\t\treturn { kind: \"success\", text: `Signed out of ${spec.label}.` };\n\t\t\t}\n\n\t\t\tif (action === \"sync\") {\n\t\t\t\tconst ok = await auth.connect();\n\t\t\t\tconst status = await auth.getStatus();\n\n\t\t\t\treturn ok\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tkind: \"success\",\n\t\t\t\t\t\t\ttext: `${spec.label} tools ready (${status.tools.length}).`,\n\t\t\t\t\t\t}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tkind: \"error\",\n\t\t\t\t\t\t\ttext:\n\t\t\t\t\t\t\t\tstatus.error ??\n\t\t\t\t\t\t\t\t`Not signed in to ${spec.label}. Run /${spec.name} login.`,\n\t\t\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst status = await auth.getStatus();\n\n\t\t\tif (status.error !== null) return { kind: \"error\", text: status.error };\n\n\t\t\tif (!status.signedIn)\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"success\",\n\t\t\t\t\ttext: `Not signed in to ${spec.label}. Run /${spec.name} login.`,\n\t\t\t\t};\n\n\t\t\tif (!status.connected)\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"success\",\n\t\t\t\t\ttext: `${spec.label} is signed in but not connected. Run /${spec.name} sync.`,\n\t\t\t\t};\n\n\t\t\tconst until =\n\t\t\t\tstatus.expiresAt === null\n\t\t\t\t\t? \"unknown\"\n\t\t\t\t\t: new Date(status.expiresAt).toLocaleString();\n\t\t\tconst extra = spec.describe?.(status) ?? \"\";\n\t\t\tconst tail = extra === \"\" ? \"\" : ` ${extra}`;\n\n\t\t\treturn {\n\t\t\t\tkind: \"success\",\n\t\t\t\ttext: `Signed in to ${spec.label}. Token valid until ${until}.${tail} ${status.tools.length} tools loaded.`,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn { kind: \"error\", text: `${spec.label}: ${errorText(error)}` };\n\t\t}\n\t};\n};\n", "/**\n * The endpoint configuration every integration is parameterized with.\n *\n * A profile patch may carry an endpoint, a redirect port, and a debug flag;\n * this normalizes whatever arrived. Unknown or malformed fields fall back to the\n * provider's defaults rather than failing activation, because a typo in a patch\n * should not take a plugin down.\n */\n\n/** Raw shape accepted from the profile patch. */\nexport interface ConfigInput {\n\treadonly endpoint?: unknown;\n\treadonly redirectPort?: unknown;\n\treadonly debug?: unknown;\n}\n\n/** Resolved configuration: every field present and of the right type. */\nexport interface EndpointConfig {\n\treadonly endpoint: string;\n\treadonly redirectPort: number;\n\treadonly debug: boolean;\n}\n\n/** What a provider falls back to when the patch names nothing. */\nexport interface ConfigDefaults {\n\t/** The provider's own MCP resource. */\n\treadonly endpoint: string;\n\t/** The first loopback port tried for the OAuth redirect. */\n\treadonly redirectPort: number;\n}\n\n/**\n * Normalize one patch against a provider's defaults.\n * @param input - whatever the loader passed.\n * @param defaults - the provider's own resource and port.\n * @returns the configuration every library call is parameterized with.\n */\nexport function resolveEndpointConfig(\n\tinput: ConfigInput | undefined,\n\tdefaults: ConfigDefaults,\n): EndpointConfig {\n\tconst endpoint =\n\t\ttypeof input?.endpoint === \"string\" && input.endpoint !== \"\"\n\t\t\t? input.endpoint\n\t\t\t: defaults.endpoint;\n\n\tconst port = Number(input?.redirectPort);\n\n\treturn {\n\t\tendpoint,\n\t\tredirectPort:\n\t\t\tNumber.isInteger(port) && port > 0 && port < 65_536\n\t\t\t\t? port\n\t\t\t\t: defaults.redirectPort,\n\t\tdebug: input?.debug === true,\n\t};\n}\n", "/**\n * The seam a cross-provider chain is assembled through.\n *\n * One answer spans several integrations \u2014 an error names a commit, a commit\n * belongs to a merge request, a merge request names a ticket \u2014 and none of them\n * may import another's code: the browser and host bundles are separate, and a\n * plugin's own readers belong to it. So the *vocabulary* of a chain lives here,\n * in the library every integration already shares, and the correlation plugin\n * owns the registry those contributions arrive in.\n *\n * The vocabulary is deliberately about the world, not about a vendor: an error,\n * a commit, a change request, and a tracked task exist whatever the tool is\n * called. A provider says which of them it can resolve and what it knows when\n * asked; it never names another provider, and it never parses another provider's\n * identifier. Where two providers must agree \u2014 the commit a code host can look\n * up, the key a tracker recognizes \u2014 the shared shape here is that agreement,\n * and a reference carries a link alongside it so the other side can find its own\n * way to the same object.\n *\n * A source answers *about the reference it was asked about* with a fact, and\n * that fact carries the references onward. A walker therefore never needs to\n * know what a chain looks like: the links are data.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport { Service } from \"@deepseek-ai/cordis\";\n\n/** The service name the correlation plugin provides and every source registers into. */\nexport const CORRELATION_SOURCES = \"correlationSources\";\n\n/** The kinds of thing a chain is made of. */\nexport type CorrelationKind = \"error\" | \"commit\" | \"change\" | \"task\";\n\n/**\n * A pointer from one fact to another.\n *\n * Each variant carries what the *resolving* side needs to find the object\n * itself, and nothing about how that side is implemented. `label` is what the\n * card calls the unreachable hop when nobody resolves it.\n */\nexport type CorrelationRef =\n\t| { readonly kind: \"error\"; readonly id: string; readonly label?: string }\n\t| {\n\t\t\treadonly kind: \"commit\";\n\t\t\treadonly sha: string;\n\t\t\t/**\n\t\t\t * A web address anywhere in the repository the commit lives in.\n\t\t\t *\n\t\t\t * It is how a code host recognizes its own project without either\n\t\t\t * side agreeing on a naming scheme: the host reads the path it\n\t\t\t * already knows how to read, and ignores the rest.\n\t\t\t */\n\t\t\treadonly url?: string;\n\t\t\t/** The repository as the referring side names it, when it names one. */\n\t\t\treadonly repository?: string;\n\t\t\treadonly label?: string;\n\t }\n\t| {\n\t\t\treadonly kind: \"change\";\n\t\t\t/** The project path the change request lives in, as the code host spells it. */\n\t\t\treadonly repository: string;\n\t\t\t/** The project-local number, without any prefix. */\n\t\t\treadonly number: string;\n\t\t\treadonly url?: string;\n\t\t\treadonly label?: string;\n\t }\n\t| { readonly kind: \"task\"; readonly key: string; readonly label?: string };\n\n/** What one provider knows about one reference. */\nexport interface CorrelationFact {\n\t/** Stable across the chain: what two providers' answers are deduplicated by. */\n\treadonly id: string;\n\t/** The provider's name as the user sees it, such as `Sentry`. */\n\treadonly provider: string;\n\t/** Which reference shape this fact answers. */\n\treadonly kind: CorrelationKind;\n\t/** What the card calls this hop, such as `Suspect commit`. */\n\treadonly label: string;\n\t/** The hop's own line, such as `CHECKOUT-13P \u00B7 AxiosError: \u2026`. */\n\treadonly title: string;\n\treadonly url?: string;\n\t/** One supporting detail, such as `unresolved \u00B7 35 events`. */\n\treadonly meta?: string;\n\t/** What this fact points at, which this or another provider resolves next. */\n\treadonly references?: readonly CorrelationRef[];\n}\n\n/** One provider's contribution to every chain. */\nexport interface CorrelationSource {\n\t/** The provider's name as the user sees it, such as `GitLab`. */\n\treadonly provider: string;\n\t/** The kind a chain starts from when the user names this provider. */\n\treadonly entry: CorrelationKind;\n\t/**\n\t * The kinds this source can resolve.\n\t *\n\t * A reference nobody declares is reported as an unreachable hop rather than\n\t * silently dropped, so a missing integration is legible on the card.\n\t */\n\treadonly resolves: readonly CorrelationKind[];\n\t/**\n\t * Answer one reference.\n\t * @param ref - the reference to resolve; its `kind` is one this source listed.\n\t * @returns the fact for that reference, and any it can already name beside it.\n\t * @throws when the provider could not be read; the walker reports the reason\n\t * against that hop instead of failing the chain.\n\t */\n\tresolve(ref: CorrelationRef): Promise<readonly CorrelationFact[]>;\n}\n\n/** One registered source, as the walker sees it. */\nexport interface CorrelationSourceEntry {\n\treadonly provider: string;\n\treadonly entry: CorrelationKind;\n\treadonly resolves: readonly CorrelationKind[];\n}\n\n/**\n * The registry of correlation sources.\n *\n * Owned by the correlation plugin and provided as `ctx.correlationSources`. An\n * integration registers through a scoped `ctx.inject([CORRELATION_SOURCES], \u2026)`,\n * so a deployment without the correlation plugin simply never calls it and\n * nothing waits for a service that will not arrive.\n */\nexport class CorrelationSources extends Service {\n\tprivate readonly sources: CorrelationSource[] = [];\n\n\tconstructor(ctx: Context) {\n\t\tsuper(ctx, CORRELATION_SOURCES);\n\t}\n\n\t/**\n\t * Add one provider's contribution.\n\t * @param source - the kinds this provider resolves and how to resolve them.\n\t * @returns the exact disposer that withdraws them.\n\t * @throws when a provider of that name is already registered.\n\t */\n\tregister(source: CorrelationSource): () => void {\n\t\tif (this.sources.some((other) => other.provider === source.provider))\n\t\t\tthrow new Error(\n\t\t\t\t`a correlation source named \"${source.provider}\" is already registered`,\n\t\t\t);\n\n\t\tthis.sources.push(source);\n\n\t\treturn () => {\n\t\t\tconst index = this.sources.indexOf(source);\n\t\t\tif (index >= 0) this.sources.splice(index, 1);\n\t\t};\n\t}\n\n\t/** Every provider on offer, in registration order. */\n\tget providers(): readonly CorrelationSourceEntry[] {\n\t\treturn this.sources.map((source) => ({\n\t\t\tprovider: source.provider,\n\t\t\tentry: source.entry,\n\t\t\tresolves: source.resolves,\n\t\t}));\n\t}\n\n\t/**\n\t * Whether any installed integration can resolve one kind.\n\t *\n\t * The walker asks before it resolves so that \"GitLab is not installed\" and\n\t * \"GitLab found no merge request for this commit\" stay different answers.\n\t */\n\tcanResolve(kind: CorrelationKind): boolean {\n\t\treturn this.sources.some((source) => source.resolves.includes(kind));\n\t}\n\n\t/**\n\t * The kind a chain starts from when the user names one provider.\n\t * @param provider - a provider's name as the user sees it.\n\t * @returns that provider's entry kind, or undefined when it is not installed.\n\t */\n\tentryOf(provider: string): CorrelationKind | undefined {\n\t\treturn this.sources.find((source) => source.provider === provider)?.entry;\n\t}\n\n\t/**\n\t * Ask whichever providers resolve one reference.\n\t *\n\t * A kind may have more than one owner \u2014 two code hosts both answer for\n\t * commits \u2014 so each is asked in turn and the first that recognizes the\n\t * reference answers. One that does not returns nothing rather than failing.\n\t * @param ref - the reference to resolve.\n\t * @throws when no registered provider resolves that kind.\n\t */\n\tasync resolve(ref: CorrelationRef): Promise<readonly CorrelationFact[]> {\n\t\tconst owners = this.sources.filter((source) =>\n\t\t\tsource.resolves.includes(ref.kind),\n\t\t);\n\n\t\tif (owners.length === 0)\n\t\t\tthrow new Error(\n\t\t\t\t`no correlation source resolves \"${ref.kind}\" references`,\n\t\t\t);\n\n\t\tfor (const owner of owners) {\n\t\t\tconst facts = await owner.resolve(ref);\n\n\t\t\tif (facts.length > 0) return facts;\n\t\t}\n\n\t\treturn [];\n\t}\n}\n\ndeclare module \"@deepseek-ai/cordis\" {\n\tinterface Context {\n\t\t/** The correlation registry; present only while the correlation plugin is loaded. */\n\t\tcorrelationSources: CorrelationSources;\n\t}\n}\n", "/**\n * The seam a digest is assembled through.\n *\n * A digest spans several integrations, and none of them may import another's\n * code: the browser and host bundles are separate, and a plugin's own readers\n * belong to it. So the *shape* of a contribution lives here, in the library\n * every integration already shares, and the digest plugin owns the registry\n * those contributions arrive in. An integration registers what it can report;\n * the digest plugin decides when to ask and how to lay the answers out.\n *\n * A source is one provider's promise to answer one block. The block's title and\n * its default switch travel with it, because the provider is the only party\n * that knows what it is offering \u2014 the digest card is drawn from whatever the\n * installed sources declare, never from a list this library keeps.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport { Service } from \"@deepseek-ai/cordis\";\n\n/** The service name the digest plugin provides and every source registers into. */\nexport const DIGEST_SOURCES = \"digestSources\";\n\n/**\n * One row a block contributes.\n *\n * Deliberately small: a digest is read at a glance, so a row carries a line, a\n * link, and one supporting detail. A provider that wants to say more says it in\n * `meta`; the card gives no row more room than that.\n */\nexport interface DigestItem {\n\t/** Stable within its block; what keys the row. */\n\treadonly id: string;\n\t/** The row's own line, such as `DEV-6477 \u00B7 Fix the parser`. */\n\treadonly title: string;\n\treadonly url?: string;\n\t/** One supporting detail, such as `In review \u00B7 due today`. */\n\treadonly meta?: string;\n}\n\n/** One block of the digest, as the provider that owns it declares it. */\nexport interface DigestBlockSpec {\n\t/** Stable across restarts: what a saved switch is stored under. */\n\treadonly id: string;\n\t/** The block's heading on the card, such as `Jira \u00B7 In progress`. */\n\treadonly title: string;\n\t/** The switch a user who has never touched it gets. */\n\treadonly defaultOn: boolean;\n}\n\n/** What one load is allowed to assume about its window. */\nexport interface DigestLoadOptions {\n\t/** How far back a block that reports \"recent\" work looks. */\n\treadonly since: Date;\n\t/** The most rows the card will show from this block. */\n\treadonly limit: number;\n}\n\n/** One provider's contribution to every digest. */\nexport interface DigestSource {\n\t/** The provider's name as the user sees it, such as `GitLab`. */\n\treadonly provider: string;\n\treadonly blocks: readonly DigestBlockSpec[];\n\t/**\n\t * Answer one block.\n\t * @param blockId - one of this source's own block ids.\n\t * @param options - the window and the row cap the digest is assembling under.\n\t * @returns the rows to show; an empty list is a truthful \"nothing to report\".\n\t * @throws when the provider could not be read; the digest reports the reason\n\t * against that block instead of failing whole.\n\t */\n\tload(\n\t\tblockId: string,\n\t\toptions: DigestLoadOptions,\n\t): Promise<readonly DigestItem[]>;\n}\n\n/** What one registered source offers, with the provider that owns it. */\nexport interface DigestBlockEntry extends DigestBlockSpec {\n\treadonly provider: string;\n}\n\n/**\n * The registry of digest sources.\n *\n * Owned by the digest plugin and provided as `ctx.digestSources`. An\n * integration registers through a scoped `ctx.inject([DIGEST_SOURCES], \u2026)`, so\n * a deployment without the digest plugin simply never calls it and nothing\n * waits for a service that will not arrive.\n */\nexport class DigestSources extends Service {\n\tprivate readonly sources: DigestSource[] = [];\n\n\tconstructor(ctx: Context) {\n\t\tsuper(ctx, DIGEST_SOURCES);\n\t}\n\n\t/**\n\t * Add one provider's contribution.\n\t * @param source - the blocks this provider offers and how to load them.\n\t * @returns the exact disposer that withdraws them.\n\t * @throws when the provider declares a block id another provider already owns.\n\t */\n\tregister(source: DigestSource): () => void {\n\t\tconst clash = source.blocks.find((block) =>\n\t\t\tthis.sources.some((other) =>\n\t\t\t\tother.blocks.some((taken) => taken.id === block.id),\n\t\t\t),\n\t\t);\n\n\t\tif (clash !== undefined)\n\t\t\tthrow new Error(\n\t\t\t\t`digest block \"${clash.id}\" is already registered by another provider`,\n\t\t\t);\n\n\t\tthis.sources.push(source);\n\n\t\treturn () => {\n\t\t\tconst index = this.sources.indexOf(source);\n\t\t\tif (index >= 0) this.sources.splice(index, 1);\n\t\t};\n\t}\n\n\t/** Every block on offer, in registration order, with its owning provider. */\n\tget blocks(): readonly DigestBlockEntry[] {\n\t\treturn this.sources.flatMap((source) =>\n\t\t\tsource.blocks.map((block) => ({ ...block, provider: source.provider })),\n\t\t);\n\t}\n\n\t/** The providers that have registered, in registration order. */\n\tget providers(): readonly string[] {\n\t\treturn this.sources.map((source) => source.provider);\n\t}\n\n\t/**\n\t * Ask the provider that owns one block.\n\t * @param blockId - a block id from {@link blocks}.\n\t * @param options - the window and the row cap.\n\t * @throws when no registered provider offers that block.\n\t */\n\tasync load(\n\t\tblockId: string,\n\t\toptions: DigestLoadOptions,\n\t): Promise<readonly DigestItem[]> {\n\t\tfor (const source of this.sources) {\n\t\t\tif (source.blocks.some((block) => block.id === blockId))\n\t\t\t\treturn await source.load(blockId, options);\n\t\t}\n\n\t\tthrow new Error(`no digest source offers the block \"${blockId}\"`);\n\t}\n}\n\ndeclare module \"@deepseek-ai/cordis\" {\n\tinterface Context {\n\t\t/** The digest registry; present only while the digest plugin is loaded. */\n\t\tdigestSources: DigestSources;\n\t}\n}\n", "/**\n * What a provider's markdown means once an integration reads it.\n *\n * Jira, Confluence and GitLab all store markdown whose HTML comments are\n * invisible where the text was written: a comment is a note to whoever edits the\n * document, not part of what it says. This harness renders that markdown in a\n * panel and hands the same string to the model, so the note is dropped where the\n * document is read rather than hidden where it is drawn \u2014 an integration's\n * reader is the one place both surfaces go through.\n *\n * Only what the document says outside its code fences is touched: an example of\n * a comment is content, and so is the whitespace inside the example. An\n * unterminated comment swallows the rest of its document, which is what an HTML\n * comment means anyway.\n */\n\n/** A line that opens or closes a fenced code block. */\nconst FENCE = /^\\s{0,3}(?:```|~~~)/u;\n\n/** What a comment starts and ends with. */\nconst OPEN = \"<!--\";\nconst CLOSE = \"-->\";\n\n/** One line, with the comments in it removed and its spacing tidied. */\ninterface Uncommented {\n\treadonly text: string;\n\t/** Whether this line held a comment, which is what its spacing depends on. */\n\treadonly touched: boolean;\n}\n\n/**\n * Remove the comments from the lines outside code fences.\n * @param lines - the document's lines.\n * @returns each line with its comments gone, and whether it held any.\n */\nconst uncomment = (lines: readonly string[]): Uncommented[] => {\n\tconst kept: Uncommented[] = [];\n\tlet inFence = false;\n\tlet inComment = false;\n\n\tfor (const line of lines) {\n\t\tif (!inComment && FENCE.test(line)) {\n\t\t\tinFence = !inFence;\n\t\t\tkept.push({ text: line, touched: false });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (inFence) {\n\t\t\tkept.push({ text: line, touched: false });\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet rest = line;\n\t\tlet text = \"\";\n\t\tlet touched = false;\n\n\t\twhile (rest !== \"\") {\n\t\t\tif (inComment) {\n\t\t\t\tconst end = rest.indexOf(CLOSE);\n\n\t\t\t\t// An unterminated comment runs to the end of the document.\n\t\t\t\tif (end === -1) {\n\t\t\t\t\trest = \"\";\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tinComment = false;\n\t\t\t\trest = rest.slice(end + CLOSE.length);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst start = rest.indexOf(OPEN);\n\n\t\t\tif (start === -1) {\n\t\t\t\ttext += rest;\n\t\t\t\trest = \"\";\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\ttext += rest.slice(0, start);\n\t\t\ttouched = true;\n\t\t\tinComment = true;\n\t\t\trest = rest.slice(start + OPEN.length);\n\t\t}\n\n\t\tkept.push({ text, touched });\n\t}\n\n\treturn kept;\n};\n\n/**\n * Remove the HTML comments from one markdown document.\n * @param markdown - the document as the provider stores it.\n * @returns the document without its comments; the same string when it has none.\n */\nexport const stripHtmlComments = (markdown: string): string => {\n\tif (!markdown.includes(OPEN)) return markdown;\n\n\tconst kept: string[] = [];\n\tlet inFence = false;\n\t/** Whether the line just kept was blank, so a run of them stays one line. */\n\tlet blank = false;\n\n\tfor (const line of uncomment(markdown.split(\"\\n\"))) {\n\t\tif (FENCE.test(line.text)) {\n\t\t\tinFence = !inFence;\n\t\t\tblank = false;\n\t\t\tkept.push(line.text);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (inFence) {\n\t\t\tkept.push(line.text);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// A comment that stood as its own paragraph leaves the empty line that\n\t\t// separated it from the next one; markdown reads a run of blank lines as\n\t\t// one, so the run collapses rather than the document gaining a gap.\n\t\tif (line.text.trim() === \"\") {\n\t\t\tif (kept.length === 0 || blank) continue;\n\n\t\t\tblank = true;\n\t\t\tkept.push(line.text);\n\t\t\tcontinue;\n\t\t}\n\n\t\tblank = false;\n\n\t\t// Where a page renders \"one two\", the source reads \"one <!-- x --> two\" and\n\t\t// the removal would leave the two spaces both sides brought. Only a line\n\t\t// that lost a comment is touched, and only between words: two trailing\n\t\t// spaces are markdown's own way of writing a line break.\n\t\tkept.push(\n\t\t\tline.touched ? line.text.replace(/[ \\t]{2,}(?=\\S)/gu, \" \") : line.text,\n\t\t);\n\t}\n\n\t// A blank line at the end of a document is not part of what it says, and the\n\t// one a trailing comment left is the reader's, not the author's.\n\twhile (kept.length > 0 && kept[kept.length - 1]?.trim() === \"\") kept.pop();\n\n\treturn kept.join(\"\\n\");\n};\n", "/**\n * The status document every integration serves and renders.\n *\n * This is one half of the two-halves wire contract: the host builds it from the\n * credential record and the open MCP connection, and the settings card reads it\n * from the plugin's own route. A provider's own status extends this shape with\n * whatever its server names \u2014 Sentry an organization, Atlassian a site \u2014 and the\n * shared settings card draws only the part declared here.\n */\n\n/**\n * The header every request from the harness page carries.\n *\n * A browser form cannot set a custom header without a CORS preflight, and these\n * routes never answer one; the header is therefore the boundary between the\n * page and a cross-origin request, not an authentication token. Local processes\n * can still forge it \u2014 that debt belongs to the harness, not to one plugin.\n */\nexport const CLIENT_REQUEST_HEADER = \"x-dsh-client\";\n\n/** The one value the client header must carry. */\nexport const CLIENT_REQUEST_VALUE = \"1\";\n\n/** A one-way message to the user, mirroring the authorization seam's notice. */\nexport interface StatusNotice {\n\treadonly message: string;\n\t/** The page the user must open, when the message has one. */\n\treadonly url?: string;\n}\n\n/** Everything every settings card needs, whatever the integration. */\nexport interface IntegrationStatus {\n\t/** Whether a usable access token is stored. */\n\treadonly signedIn: boolean;\n\t/** Whether a sign-in attempt is running right now. */\n\treadonly inFlight: boolean;\n\t/** Epoch milliseconds at which the access token expires. */\n\treadonly expiresAt: number | null;\n\t/** The scope string the grant was issued with. */\n\treadonly scope: string | null;\n\t/** The last thing a running attempt told the user. */\n\treadonly notice: StatusNotice | null;\n\t/** The last failure, cleared when a new attempt starts. */\n\treadonly error: string | null;\n\t/** Whether the MCP connection is currently usable. */\n\treadonly connected: boolean;\n\t/** Public names of the published tools. */\n\treadonly tools: readonly string[];\n\t/** The MCP resource the plugin is configured to use. */\n\treadonly endpoint: string;\n}\n", "/**\n * The localhost API a browser half calls.\n *\n * A settings card and a side panel live in the page, so they need a channel to\n * the host. A named route on the harness web server is that channel: it needs no\n * generated remote namespace, it is structured JSON in both directions, and it\n * is bound to loopback exactly like the rest of the web server.\n *\n * The four paths every integration answers \u2014 status, login, logout, sync \u2014 are\n * this module's; a provider adds its own reads and writes after them, and the\n * first handler that claims a request ends it.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport { errorText } from \"dsh-mcp-oauth\";\nimport type { IntegrationAuthFace } from \"./auth\";\nimport {\n\tCLIENT_REQUEST_HEADER,\n\tCLIENT_REQUEST_VALUE,\n\ttype IntegrationStatus,\n} from \"./status\";\n\n/** Longest write body any integration's route accepts, in bytes. */\nconst MAX_BODY_BYTES = 8 * 1024;\n\n/** Mount a route the web server can serve. */\nexport interface RouteHost {\n\tregister(route: {\n\t\tkind: \"exact\" | \"prefix\";\n\t\tpath: string;\n\t\thandler: (request: IncomingMessage, response: ServerResponse) => void;\n\t}): () => void;\n}\n\n/** One integration's route, as the shared mount takes it. */\nexport interface MountSpec {\n\t/** The route prefix this plugin owns, such as `/dsh-sentry`. */\n\treadonly prefix: string;\n\t/** The handler every request under that prefix reaches. */\n\treadonly handler: (\n\t\trequest: IncomingMessage,\n\t\tresponse: ServerResponse,\n\t) => Promise<void>;\n}\n\n/**\n * Mount one integration's route on the harness web server.\n *\n * The registration waits for the web server rather than reading it once: the\n * loader mounts rows as their own dependencies resolve, so a plugin that merely\n * asks can run before the listener's fiber is active \u2014 and then its card never\n * reaches the host, which looks like a broken integration rather than a race. A\n * composition with no web server registers nothing, and the tools and the\n * command keep working.\n * @param ctx - the plugin's host context.\n * @param spec - the prefix this plugin owns and the handler under it.\n */\nexport function mountIntegrationRoute(ctx: Context, spec: MountSpec): void {\n\tctx.inject([\"webServer\"], (scoped) => {\n\t\tconst webServer = (scoped as { get: (name: string) => unknown }).get(\n\t\t\t\"webServer\",\n\t\t) as RouteHost;\n\n\t\tscoped.effect(\n\t\t\t() =>\n\t\t\t\twebServer.register({\n\t\t\t\t\tkind: \"prefix\",\n\t\t\t\t\tpath: spec.prefix,\n\t\t\t\t\thandler: spec.handler,\n\t\t\t\t}),\n\t\t\t`${spec.prefix}: route`,\n\t\t);\n\t});\n}\n\n/** One request's parsed JSON body, and the fields it must carry. */\nexport interface RouteBody {\n\t/** The parsed object, or undefined when the request carried no body. */\n\treadonly fields: Record<string, unknown> | undefined;\n\t/**\n\t * One required string field.\n\t * @throws a bad-request failure, which the route answers with 400.\n\t */\n\trequired(name: string): string;\n}\n\n/** What one provider path handler reads and answers with. */\nexport interface RouteContext {\n\treadonly url: URL;\n\treadonly method: string;\n\t/** Read the request's JSON body; the read happens once. */\n\tread(): Promise<RouteBody>;\n\t/** Answer with JSON. */\n\tsend(status: number, body: unknown): void;\n\t/**\n\t * Answer with bytes and their media type.\n\t *\n\t * A panel that draws a picture needs the bytes themselves: an image cannot\n\t * travel inside the JSON document that describes it.\n\t */\n\tsendBytes(status: number, contentType: string, body: Uint8Array): void;\n\t/** Answer a read, translating a failure behind it into a bad gateway. */\n\tsendData(load: () => Promise<unknown>): Promise<void>;\n}\n\n/**\n * One provider path.\n * @returns whether this handler claimed the request.\n */\nexport type RouteHandler = (route: RouteContext) => Promise<boolean> | boolean;\n\n/** Everything the route answers with, wired once at apply time. */\nexport interface RouteSpec<TStatus extends IntegrationStatus> {\n\t/** The route prefix this plugin owns, such as `/dsh-sentry`. */\n\treadonly prefix: string;\n\treadonly auth: IntegrationAuthFace<TStatus>;\n\t/** Provider paths, tried after the shared four, in order. */\n\treadonly routes?: readonly RouteHandler[];\n}\n\n/**\n * A request this route cannot act on, as opposed to a failure behind it.\n *\n * A provider path throws one of these for a caller's mistake, and the route\n * answers it with 400 rather than as a failure of the integration behind it.\n */\nexport class BadRequest extends Error {}\n\n/** One body, read once and narrowed to the object shape every route expects. */\nfunction bodyOf(value: Record<string, unknown> | undefined): RouteBody {\n\treturn {\n\t\tfields: value,\n\t\trequired: (name) => {\n\t\t\tconst field = value?.[name];\n\n\t\t\tif (typeof field !== \"string\" || field === \"\")\n\t\t\t\tthrow new BadRequest(`the request needs a \"${name}\"`);\n\n\t\t\treturn field;\n\t\t},\n\t};\n}\n\n/**\n * Read one small JSON request body.\n * @param request - the incoming request.\n * @returns the parsed body, or undefined when it is not a JSON object.\n */\nasync function readJson(\n\trequest: IncomingMessage,\n): Promise<Record<string, unknown> | undefined> {\n\tconst chunks: Buffer[] = [];\n\tlet size = 0;\n\n\tfor await (const chunk of request) {\n\t\tconst buffer = chunk as Buffer;\n\t\tsize += buffer.length;\n\n\t\tif (size > MAX_BODY_BYTES)\n\t\t\tthrow new BadRequest(\"request body is larger than this route accepts\");\n\t\tchunks.push(buffer);\n\t}\n\n\tif (chunks.length === 0) return undefined;\n\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(Buffer.concat(chunks).toString(\"utf8\"));\n\n\t\treturn parsed !== null &&\n\t\t\ttypeof parsed === \"object\" &&\n\t\t\t!Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** Methods that do not change anything and need no client tag. */\nconst READ_METHODS = new Set([\"GET\", \"HEAD\"]);\n\n/** The URL form of an `Origin` that a browser attaches to a web request. */\nfunction webOrigin(value: string): URL | undefined {\n\ttry {\n\t\tconst url = new URL(value);\n\n\t\treturn url.protocol === \"http:\" || url.protocol === \"https:\"\n\t\t\t? url\n\t\t\t: undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Why one request may not reach an integration's write path.\n *\n * A browser page that is not the harness cannot send the client header without\n * a successful cross-origin preflight, and these routes answer no preflight.\n * The origin check is the second lock: an ordinary cross-origin fetch carries\n * its origin, and a same-site page is the only one allowed through. A Page\n * served from the Desktop's own scheme sends no HTTP origin; the header already\n * separates it from a web page.\n * @param request - the incoming request.\n * @returns the refusal for the user, or undefined when the request may proceed.\n */\nexport function clientRequestRefusal(\n\trequest: IncomingMessage,\n): string | undefined {\n\tconst method = request.method ?? \"GET\";\n\n\tif (READ_METHODS.has(method)) return undefined;\n\n\tconst headers = request.headers ?? {};\n\n\tif (headers[CLIENT_REQUEST_HEADER] !== CLIENT_REQUEST_VALUE)\n\t\treturn \"the request did not come from the harness page\";\n\n\tconst contentType = headers[\"content-type\"];\n\n\tif (\n\t\ttypeof contentType !== \"string\" ||\n\t\t!contentType.toLowerCase().startsWith(\"application/json\")\n\t)\n\t\treturn \"an integration write must carry a JSON body\";\n\n\tconst origin = headers.origin;\n\tconst host = headers.host;\n\tconst originUrl = typeof origin === \"string\" ? webOrigin(origin) : undefined;\n\n\tif (\n\t\toriginUrl !== undefined &&\n\t\ttypeof host === \"string\" &&\n\t\toriginUrl.host !== host\n\t)\n\t\treturn \"the request came from another origin\";\n\n\tconst site = headers[\"sec-fetch-site\"];\n\n\tif (\n\t\toriginUrl !== undefined &&\n\t\ttypeof site === \"string\" &&\n\t\tsite.toLowerCase() === \"cross-site\"\n\t)\n\t\treturn \"cross-site requests are refused\";\n\n\treturn undefined;\n}\n\n/**\n * Build the request handler for one integration.\n * @param spec - the route prefix, the sign-in face, and the provider's own paths.\n */\nexport function createIntegrationRoute<TStatus extends IntegrationStatus>(\n\tspec: RouteSpec<TStatus>,\n): (request: IncomingMessage, response: ServerResponse) => Promise<void> {\n\tconst { prefix, auth } = spec;\n\n\treturn async (request, response) => {\n\t\tconst url = new URL(request.url ?? \"/\", \"http://127.0.0.1\");\n\t\tconst method = request.method ?? \"GET\";\n\n\t\tconst send = (status: number, body: unknown): void => {\n\t\t\tresponse.writeHead(status, {\n\t\t\t\t\"content-type\": \"application/json; charset=utf-8\",\n\t\t\t\t\"cache-control\": \"no-store\",\n\t\t\t});\n\n\t\t\tresponse.end(JSON.stringify(body));\n\t\t};\n\n\t\tconst sendBytes = (\n\t\t\tstatus: number,\n\t\t\tcontentType: string,\n\t\t\tbody: Uint8Array,\n\t\t): void => {\n\t\t\tresponse.writeHead(status, {\n\t\t\t\t\"content-type\": contentType,\n\t\t\t\t\"content-length\": body.byteLength,\n\t\t\t\t\"cache-control\": \"no-store\",\n\t\t\t});\n\n\t\t\tresponse.end(body);\n\t\t};\n\n\t\t/** Answer a read the settings card or the side panel asked for. */\n\t\tconst sendData = async (load: () => Promise<unknown>): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tsend(200, await load());\n\t\t\t} catch (error) {\n\t\t\t\tsend(502, { error: errorText(error) });\n\t\t\t}\n\t\t};\n\n\t\tconst refusal = clientRequestRefusal(request);\n\n\t\tif (refusal !== undefined) {\n\t\t\tsend(403, { error: refusal });\n\t\t\treturn;\n\t\t}\n\n\t\tlet body: Promise<RouteBody> | undefined;\n\n\t\tconst context: RouteContext = {\n\t\t\turl,\n\t\t\tmethod,\n\t\t\tread: () => (body ??= readJson(request).then(bodyOf)),\n\t\t\tsend,\n\t\t\tsendBytes,\n\t\t\tsendData,\n\t\t};\n\n\t\ttry {\n\t\t\tif (url.pathname === `${prefix}/status` && method === \"GET\") {\n\t\t\t\tsend(200, await auth.getStatus());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (url.pathname === `${prefix}/login` && method === \"POST\") {\n\t\t\t\t// Start and answer immediately: the user finishes in a browser, and the\n\t\t\t\t// card follows `inFlight` until the attempt settles.\n\t\t\t\tvoid auth.signIn();\n\t\t\t\tawait auth.settleNotice(300);\n\n\t\t\t\tsend(202, await auth.getStatus());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (url.pathname === `${prefix}/logout` && method === \"POST\") {\n\t\t\t\tawait auth.signOut();\n\n\t\t\t\tsend(200, await auth.getStatus());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (url.pathname === `${prefix}/sync` && method === \"POST\") {\n\t\t\t\tawait auth.connect();\n\n\t\t\t\tsend(200, await auth.getStatus());\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (const handler of spec.routes ?? []) {\n\t\t\t\tif (await handler(context)) return;\n\t\t\t}\n\n\t\t\tsend(404, { error: \"not-found\", path: url.pathname });\n\t\t} catch (error) {\n\t\t\tsend(error instanceof BadRequest ? 400 : 500, {\n\t\t\t\terror: errorText(error),\n\t\t\t});\n\t\t}\n\t};\n}\n", "/**\n * Process-local state of one integration connection.\n *\n * Everything durable \u2014 the tokens, and the account facts learned from the\n * account \u2014 lives in the credential record. This is only what a running attempt\n * and the settings card need between calls, so a restart loses nothing that\n * matters.\n */\n\nimport type { AuthorizationNotice } from \"@deepseek-ai/dsh-authorization\";\nimport type { AccountFacts } from \"./facts\";\n\n/**\n * Everything one host half keeps between calls.\n *\n * `TFacts` is what this integration learned from its own server, and a server\n * that names no account \u2014 a local MCP, which answers whoever is on the machine \u2014\n * leaves it at its default and keeps the rest.\n */\nexport class RuntimeState<TFacts extends AccountFacts = AccountFacts> {\n\t/** The last notice a running attempt published, so a card can show the URL. */\n\tnotice: AuthorizationNotice | undefined;\n\t/** The last failure, cleared when a new attempt starts. */\n\terror: string | undefined;\n\t/** Settles when the in-flight attempt finishes. */\n\trunning: Promise<unknown> | undefined;\n\t/** The account facts this connection works with, once known. */\n\tfacts: TFacts | undefined;\n\n\tconstructor() {\n\t\tthis.notice = undefined;\n\t\tthis.error = undefined;\n\t\tthis.running = undefined;\n\t\tthis.facts = undefined;\n\t}\n}\n", "/**\n * The Atlassian site this installation works with.\n *\n * Atlassian withholds `webUrl` from clients it does not recognize by name, so\n * the account's own list of sites is what gives a card a link and every Jira\n * and Confluence tool call the cloud id it requires. The facts are read once,\n * remembered in the credential record, and restored on the next start.\n */\n\nimport type { FactsReader } from \"dsh-integration-host\";\nimport { asRecord, type Grant, mcpText, pickString } from \"dsh-mcp-oauth\";\n\n/** The tool whose answer names the account's site. */\nexport const SITE_TOOL = \"getAccessibleAtlassianResources\";\n\nexport class SiteFacts {\n\treadonly siteUrl?: string;\n\treadonly cloudId?: string;\n\n\tconstructor(params: { siteUrl?: string; cloudId?: string }) {\n\t\tthis.siteUrl = params.siteUrl;\n\t\tthis.cloudId = params.cloudId;\n\t}\n\n\t/** What the stored grant already knows, if anything. */\n\tstatic from(grant: Grant | undefined): SiteFacts {\n\t\tconst extra = grant?.extra;\n\n\t\treturn new SiteFacts({\n\t\t\t...(typeof extra?.siteUrl === \"string\" ? { siteUrl: extra.siteUrl } : {}),\n\t\t\t...(typeof extra?.cloudId === \"string\" ? { cloudId: extra.cloudId } : {}),\n\t\t});\n\t}\n\n\t/**\n\t * Read the first site of an account's own list.\n\t * @param value - the result of `getAccessibleAtlassianResources`.\n\t * @returns the facts it names, or empty facts when it names none.\n\t */\n\tstatic parse(value: unknown): SiteFacts {\n\t\tfor (const text of mcpText(value)) {\n\t\t\tconst sites = parseJson(text);\n\t\t\tconst record = asRecord(Array.isArray(sites) ? sites[0] : undefined);\n\n\t\t\tif (record === undefined) continue;\n\n\t\t\tconst url = pickString(record, \"url\");\n\t\t\tconst id = pickString(record, \"id\");\n\n\t\t\tif (url === undefined && id === undefined) continue;\n\n\t\t\treturn new SiteFacts({\n\t\t\t\t...(url === undefined ? {} : { siteUrl: url.replace(/\\/+$/u, \"\") }),\n\t\t\t\t...(id === undefined ? {} : { cloudId: id }),\n\t\t\t});\n\t\t}\n\n\t\treturn new SiteFacts({});\n\t}\n\n\t/** Whether neither the site nor the cloud id is known. */\n\tget isEmpty(): boolean {\n\t\treturn this.siteUrl === undefined && this.cloudId === undefined;\n\t}\n\n\t/** Whether both the site and the cloud id are known, so nothing is left to learn. */\n\tget isComplete(): boolean {\n\t\treturn this.siteUrl !== undefined && this.cloudId !== undefined;\n\t}\n}\n\n/** How Atlassian's own site list is read, merged, and remembered. */\nexport const siteFacts: FactsReader<SiteFacts> = {\n\ttoolName: SITE_TOOL,\n\tfrom: (grant) => SiteFacts.from(grant),\n\tparse: (value) => SiteFacts.parse(value),\n\tmerge: (known, learned) =>\n\t\tnew SiteFacts({\n\t\t\tsiteUrl: learned.siteUrl ?? known?.siteUrl,\n\t\t\tcloudId: learned.cloudId ?? known?.cloudId,\n\t\t}),\n\tremember: (facts) => ({\n\t\t...(facts.siteUrl === undefined ? {} : { siteUrl: facts.siteUrl }),\n\t\t...(facts.cloudId === undefined ? {} : { cloudId: facts.cloudId }),\n\t}),\n};\n\nfunction parseJson(text: string): unknown {\n\ttry {\n\t\treturn JSON.parse(text);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n", "/**\n * What is Atlassian's rather than the library's about signing in.\n *\n * The sign-in operations themselves belong to `dsh-integration-host`, which\n * serves the `/atlassian` command, the settings card's HTTP calls, and the\n * automatic connect after a successful attempt from one implementation. All\n * Atlassian supplies is its own name, the log tag, how its site list is read,\n * and the field it adds to the shared status document.\n */\n\nimport type { AuthSpec } from \"dsh-integration-host\";\nimport type { AtlassianStatus } from \"../shared/status\";\nimport { type SiteFacts, siteFacts } from \"./store/site\";\n\n/** The provider's own half of the shared sign-in implementation. */\nexport const atlassianAuthSpec: AuthSpec<AtlassianStatus, SiteFacts> = {\n\tlabel: \"Atlassian\",\n\tlogTag: \"[atlassian]\",\n\tfacts: siteFacts,\n\tstatus: (base, facts) => ({\n\t\t...base,\n\t\tsiteUrl: facts?.siteUrl ?? null,\n\t}),\n};\n", "/**\n * Atlassian configuration, and the provider profile built from it.\n *\n * The scope lists live here because they are Atlassian's, not the library's:\n * the two server generations use different vocabularies, and the library\n * narrows whichever list it is given to what the server actually advertises.\n */\n\nimport {\n\ttype ConfigInput,\n\ttype EndpointConfig,\n\tresolveEndpointConfig,\n} from \"dsh-integration-host\";\nimport { DEFAULT_INSTANCE, type OAuthProviderProfile } from \"dsh-mcp-oauth\";\n\n/** Atlassian's v1 MCP resource; the default because it needs no extra setup. */\nexport const DEFAULT_ENDPOINT = \"https://mcp.atlassian.com/v1/mcp\";\n\n/** First loopback port tried for the OAuth redirect. */\nexport const DEFAULT_REDIRECT_PORT = 8143;\n\n/** The name this plugin is registered under; it owns the credential record. */\nexport const PLUGIN_NAME = \"dsh-atlassian\";\n\n/** Scopes wanted across both generations; whatever is not advertised is dropped. */\nconst DESIRED_SCOPES = [\n\t\"offline_access\",\n\t\"read:jira-work\",\n\t\"write:jira-work\",\n\t\"read:jira-user\",\n\t\"read:page:confluence\",\n\t\"write:page:confluence\",\n\t\"search:confluence\",\n\t\"read:space:confluence\",\n\t\"read:jira:agent-interface\",\n\t\"write:jira:agent-interface\",\n\t\"search:jira:agent-interface\",\n\t\"read:confluence:agent-interface\",\n\t\"write:confluence:agent-interface\",\n\t\"search:confluence:agent-interface\",\n];\n\n/** What a v1 authorization server understands when it advertises no scopes. */\nconst FALLBACK_SCOPES = [\n\t\"offline_access\",\n\t\"read:jira-work\",\n\t\"write:jira-work\",\n\t\"read:jira-user\",\n\t\"read:page:confluence\",\n\t\"write:page:confluence\",\n\t\"search:confluence\",\n\t\"read:space:confluence\",\n];\n\nexport type { ConfigInput };\n\n/**\n * Normalize whatever the loader passed against Atlassian's own defaults.\n * @param input - `endpoint`, `redirectPort`, and `debug` from the bundle patch.\n */\nexport const resolveConfig = (\n\tinput: ConfigInput | undefined,\n): EndpointConfig => {\n\treturn resolveEndpointConfig(input, {\n\t\tendpoint: DEFAULT_ENDPOINT,\n\t\tredirectPort: DEFAULT_REDIRECT_PORT,\n\t});\n};\n\n/** The profile every library call of this plugin is parameterized with. */\nexport const atlassianProfile = (\n\tconfig: EndpointConfig,\n): OAuthProviderProfile => {\n\treturn {\n\t\tscope: PLUGIN_NAME,\n\t\tlabel: \"Atlassian\",\n\t\tinstance: DEFAULT_INSTANCE,\n\t\tendpoint: config.endpoint,\n\t\ttoolNamespace: \"atlassian\",\n\t\tdebug: config.debug,\n\t\toauth: {\n\t\t\tredirectPort: config.redirectPort,\n\t\t\tscopes: DESIRED_SCOPES,\n\t\t\tfallbackScopes: FALLBACK_SCOPES,\n\t\t\tclientName: \"DeepSeek Harness\",\n\t\t},\n\t};\n};\n", "/**\n * Turn one Atlassian MCP tool result into the card data the Web Client draws.\n *\n * Atlassian returns JSON payloads inside text content blocks and withholds\n * `structuredContent` (including `webUrl`) from clients it does not recognize by\n * name, so this walks whatever JSON it can find and recognizes Jira issues and\n * Confluence pages by their own fields. Anything unrecognized survives as a\n * bounded text excerpt, because the server's own words are always truthful.\n */\n\nimport { stripHtmlComments } from \"dsh-integration-host\";\nimport { mcpJson, mcpText } from \"dsh-mcp-oauth\";\nimport type {\n\tAtlassianMeta,\n\tIssueCard,\n\tMetaKind,\n\tPageCard,\n} from \"../../shared/meta\";\n\nexport interface ExtractOptions {\n\t/** The server's own tool name. */\n\treadonly tool: string;\n\t/** The arguments the model supplied. */\n\treadonly args: unknown;\n\t/** The canonical MCP value the tool returned. */\n\treadonly value: unknown;\n\t/** Atlassian site base URL, when the plugin has learned it. */\n\treadonly siteUrl: string | undefined;\n\t/**\n\t * Also carry each item's long text.\n\t *\n\t * Off by default: the recorded card is a summary, and a description belongs\n\t * in the session log only after it has been compressed. A caller that asks\n\t * for it reads `rawText` and projects the card again without it.\n\t */\n\treadonly bodies?: boolean;\n}\n\n/** Minutes in the eight-hour day Jira estimates are quoted in. */\nconst MINUTES_PER_DAY = 480;\n\n/**\n * Render a duration in seconds the way Jira quotes estimates.\n * @param seconds - a raw `timeoriginalestimate`, or the equivalent.\n * @returns a compact \"3d 4h\" style string, or undefined for nothing usable.\n */\nexport function formatDuration(seconds: number): string | undefined {\n\tif (!Number.isFinite(seconds) || seconds <= 0) return undefined;\n\n\tconst total = Math.round(seconds / 60);\n\tif (total < 1) return \"<1m\";\n\n\tconst days = Math.floor(total / MINUTES_PER_DAY);\n\tconst hours = Math.floor((total - days * MINUTES_PER_DAY) / 60);\n\tconst minutes = total - days * MINUTES_PER_DAY - hours * 60;\n\n\t// Minutes are dropped once the estimate reaches a day: \"3d 4h 12m\" claims a\n\t// precision nobody plans with.\n\tconst parts = [\n\t\tdays === 0 ? \"\" : `${days}d`,\n\t\thours === 0 ? \"\" : `${hours}h`,\n\t\tminutes === 0 || days > 0 ? \"\" : `${minutes}m`,\n\t];\n\n\treturn parts.filter(Boolean).join(\" \");\n}\n\nclass Extractor {\n\t/** Jira issue keys: an uppercase project key, a dash, and a number. */\n\tprivate readonly ISSUE_KEY = /\\b[A-Z][A-Z0-9_]{0,9}-\\d+\\b/u;\n\n\t/** Bound the work a pathological payload can cause. */\n\tprivate readonly MAX_OBJECTS = 3000;\n\tprivate readonly MAX_STRINGS = 800;\n\tprivate readonly MAX_DEPTH = 7;\n\tprivate readonly MAX_CARDS = 25;\n\tprivate readonly MAX_TEXT = 4000;\n\t/** Longest description or body kept for a summary pass or a panel. */\n\tprivate readonly MAX_BODY = 8000;\n\n\t/** Node types that end a line, so a flattened document keeps its shape. */\n\tprivate readonly BLOCK_TYPES = new Set([\n\t\t\"paragraph\",\n\t\t\"heading\",\n\t\t\"blockquote\",\n\t\t\"codeBlock\",\n\t\t\"listItem\",\n\t\t\"tableRow\",\n\t\t\"panel\",\n\t\t\"rule\",\n\t]);\n\n\t/**\n\t * Extract card data from one tool result.\n\t * @param options - the tool name, its arguments, and the returned value.\n\t * @returns the meta the client renders; `text` is always populated.\n\t */\n\textract = (options: ExtractOptions): AtlassianMeta => {\n\t\tconst texts = mcpText(options.value);\n\t\tconst joined = texts.join(\"\\n\").trim();\n\n\t\tconst roots: unknown[] = [];\n\t\tconst structured = this.asRecord(options.value)?.structuredContent;\n\n\t\tif (structured !== undefined) roots.push(structured);\n\n\t\troots.push(...mcpJson(options.value));\n\n\t\tconst objects: Record<string, unknown>[] = [];\n\t\tconst strings: string[] = [];\n\n\t\tfor (const root of roots) {\n\t\t\tthis.collectObjects(root, objects, 0);\n\t\t\tthis.collectStrings(root, strings, 0);\n\t\t}\n\n\t\t// Links often live outside the JSON payload: markdown rows, plain prose.\n\t\tthis.collectStrings(joined, strings, 0);\n\n\t\tconst issues = this.issueCards(\n\t\t\tobjects,\n\t\t\tstrings,\n\t\t\toptions.siteUrl,\n\t\t\toptions.bodies === true,\n\t\t);\n\t\tconst pages =\n\t\t\tissues.length > 0\n\t\t\t\t? []\n\t\t\t\t: this.pageCards(\n\t\t\t\t\t\tobjects,\n\t\t\t\t\t\tstrings,\n\t\t\t\t\t\toptions.siteUrl,\n\t\t\t\t\t\toptions.bodies === true,\n\t\t\t\t\t);\n\t\tconst query = this.pick(\n\t\t\toptions.args,\n\t\t\t\"jql\",\n\t\t\t\"cql\",\n\t\t\t\"query\",\n\t\t\t\"searchString\",\n\t\t\t\"issueIdOrKey\",\n\t\t\t\"pageId\",\n\t\t\t\"cloudId\",\n\t\t);\n\n\t\tconst kind: MetaKind =\n\t\t\tissues.length > 0\n\t\t\t\t? \"jira-issues\"\n\t\t\t\t: pages.length > 0\n\t\t\t\t\t? \"confluence-pages\"\n\t\t\t\t\t: \"text\";\n\n\t\t// A total only means something next to items it counts.\n\t\tconst total = kind === \"text\" ? undefined : this.totalOf(roots);\n\n\t\treturn {\n\t\t\tkind,\n\t\t\ttool: options.tool,\n\t\t\t...(query === undefined ? {} : { query }),\n\t\t\t...(total === undefined ? {} : { total }),\n\t\t\tissues,\n\t\t\tpages,\n\t\t\ttext: joined.slice(0, this.MAX_TEXT),\n\t\t};\n\t};\n\n\t/**\n\t * Flatten one rich field to the plain text a caller can use.\n\t *\n\t * The side panel reads a description or a comment body through this: the same\n\t * markdown-or-ADF handling the cards use, with the same bound.\n\t * @param value - a markdown string, an ADF tree, or anything else.\n\t * @returns the text, or undefined when there is none.\n\t */\n\ttext = (value: unknown): string | undefined => this.plainText(value);\n\n\tprivate collectStrings = (\n\t\tnode: unknown,\n\t\tout: string[],\n\t\tdepth: number,\n\t): void => {\n\t\tif (\n\t\t\tdepth > this.MAX_DEPTH ||\n\t\t\tnode === null ||\n\t\t\tnode === undefined ||\n\t\t\tout.length >= this.MAX_STRINGS\n\t\t)\n\t\t\treturn;\n\n\t\tif (typeof node === \"string\") {\n\t\t\tout.push(node);\n\t\t\treturn;\n\t\t}\n\n\t\tif (Array.isArray(node)) {\n\t\t\tfor (const item of node) this.collectStrings(item, out, depth + 1);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof node === \"object\") {\n\t\t\tfor (const item of Object.values(node))\n\t\t\t\tthis.collectStrings(item, out, depth + 1);\n\t\t}\n\t};\n\n\tprivate collectObjects = (\n\t\tnode: unknown,\n\t\tout: Record<string, unknown>[],\n\t\tdepth: number,\n\t): void => {\n\t\tif (\n\t\t\tdepth > this.MAX_DEPTH ||\n\t\t\tnode === null ||\n\t\t\tnode === undefined ||\n\t\t\tout.length >= this.MAX_OBJECTS\n\t\t)\n\t\t\treturn;\n\n\t\tif (Array.isArray(node)) {\n\t\t\tfor (const item of node) this.collectObjects(item, out, depth + 1);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof node !== \"object\") return;\n\t\tout.push(node as Record<string, unknown>);\n\n\t\tfor (const item of Object.values(node))\n\t\t\tthis.collectObjects(item, out, depth + 1);\n\t};\n\n\t/** The first non-empty string among the named fields. */\n\tprivate pick = (source: unknown, ...names: string[]): string | undefined => {\n\t\tif (source === null || source === undefined || typeof source !== \"object\")\n\t\t\treturn undefined;\n\n\t\tconst record = source as Record<string, unknown>;\n\n\t\tfor (const name of names) {\n\t\t\tconst value = record[name];\n\n\t\t\tif (typeof value === \"string\" && value.trim() !== \"\") return value.trim();\n\t\t\tif (typeof value === \"number\") return String(value);\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\t/** The first finite number among the named fields. */\n\tprivate numberOf = (\n\t\tsource: unknown,\n\t\t...names: string[]\n\t): number | undefined => {\n\t\tif (source === null || source === undefined || typeof source !== \"object\")\n\t\t\treturn undefined;\n\n\t\tconst record = source as Record<string, unknown>;\n\n\t\tfor (const name of names) {\n\t\t\tconst value = record[name];\n\n\t\t\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\n\t\t\t// A JSON payload may quote a count; a string that is not a number is\n\t\t\t// not a count.\n\t\t\tif (typeof value === \"string\" && value.trim() !== \"\") {\n\t\t\t\tconst parsed = Number(value);\n\n\t\t\t\tif (Number.isFinite(parsed)) return parsed;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\t/**\n\t * How many items the server says matched, when a root record reports it.\n\t *\n\t * Only the roots are read, never the hundreds of nested objects: `total` is\n\t * a page-level fact, and a nested one belongs to something else.\n\t */\n\tprivate totalOf = (roots: readonly unknown[]): number | undefined => {\n\t\tlet total: number | undefined;\n\n\t\tconst raise = (candidate: number | undefined): void => {\n\t\t\tif (candidate === undefined) return;\n\t\t\ttotal = total === undefined ? candidate : Math.max(total, candidate);\n\t\t};\n\n\t\tfor (const root of roots) {\n\t\t\tif (Array.isArray(root)) {\n\t\t\t\traise(root.length);\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\traise(\n\t\t\t\tthis.numberOf(this.asRecord(root), \"total\", \"totalCount\", \"totalSize\"),\n\t\t\t);\n\t\t}\n\n\t\treturn total;\n\t};\n\n\t/** A duration estimate, in Jira's own words when it gave them. */\n\tprivate estimateOf = (\n\t\tfields: Record<string, unknown>,\n\t): string | undefined => {\n\t\tconst tracking =\n\t\t\tthis.asRecord(fields.timetracking) ?? this.asRecord(fields.timeTracking);\n\n\t\tconst stated = this.pick(tracking, \"originalEstimate\", \"remainingEstimate\");\n\n\t\tif (stated !== undefined) return stated;\n\n\t\tconst seconds = this.numberOf(\n\t\t\ttracking,\n\t\t\t\"originalEstimateSeconds\",\n\t\t\t\"remainingEstimateSeconds\",\n\t\t);\n\n\t\tif (seconds !== undefined) return formatDuration(seconds);\n\n\t\treturn formatDuration(\n\t\t\tthis.numberOf(\n\t\t\t\tfields,\n\t\t\t\t\"timeoriginalestimate\",\n\t\t\t\t\"aggregateoriginalestimate\",\n\t\t\t\t\"timeestimate\",\n\t\t\t\t\"aggregatetimeestimate\",\n\t\t\t) ?? 0,\n\t\t);\n\t};\n\n\t/**\n\t * The plain text of a rich field.\n\t *\n\t * A description arrives as markdown text from one server generation and as\n\t * an Atlassian Document Format tree from the other, so this reads both: text\n\t * nodes in order, with a break between the blocks that carry one.\n\t */\n\tprivate plainText = (value: unknown): string | undefined => {\n\t\t// A markdown field is where a provider's own notes-to-editors hide; the ADF\n\t\t// branch below carries text nodes, which have no comments to remove.\n\t\tif (typeof value === \"string\") return this.tidy(stripHtmlComments(value));\n\n\t\tconst text = this.tidy(this.richText(value, 0));\n\n\t\treturn text === \"\" ? undefined : text;\n\t};\n\n\t/**\n\t * The plain text of a search snippet.\n\t *\n\t * A Confluence search result carries an `excerpt` that is not a document but\n\t * a fragment of rendered HTML, entities and all.\n\t */\n\tprivate snippetText = (value: unknown): string | undefined => {\n\t\tif (typeof value !== \"string\") return this.plainText(value);\n\n\t\tconst stripped = value\n\t\t\t.replace(/<[^>]*>/gu, \" \")\n\t\t\t.replace(/</gu, \"<\")\n\t\t\t.replace(/>/gu, \">\")\n\t\t\t.replace(/"/gu, '\"')\n\t\t\t.replace(/'/gu, \"'\")\n\t\t\t.replace(/ /gu, \" \")\n\t\t\t.replace(/&/gu, \"&\");\n\n\t\treturn this.plainText(stripped);\n\t};\n\n\tprivate richText = (node: unknown, depth: number): string => {\n\t\tif (depth > this.MAX_DEPTH || node === null || node === undefined)\n\t\t\treturn \"\";\n\n\t\tif (typeof node === \"string\") return node;\n\t\tif (typeof node === \"number\" || typeof node === \"boolean\")\n\t\t\treturn ` ${String(node)} `;\n\n\t\tif (Array.isArray(node))\n\t\t\treturn node.map((item) => this.richText(item, depth + 1)).join(\"\");\n\n\t\tif (typeof node !== \"object\") return \"\";\n\n\t\tconst record = node as Record<string, unknown>;\n\n\t\tconst body =\n\t\t\ttypeof record.text === \"string\"\n\t\t\t\t? record.text\n\t\t\t\t: record.content === undefined\n\t\t\t\t\t? \"\"\n\t\t\t\t\t: this.richText(record.content, depth + 1);\n\n\t\treturn this.BLOCK_TYPES.has(String(record.type)) ? `${body}\\n\\n` : body;\n\t};\n\n\t/** Collapse the whitespace a markdown or ADF body is full of. */\n\tprivate tidy = (value: string): string => {\n\t\tconst fences: string[] = [];\n\t\tconst protectedText = value.replace(/(```|~~~)[\\s\\S]*?\\1/gu, (block) => {\n\t\t\tfences.push(block);\n\n\t\t\treturn `\\uE000${fences.length - 1}\\uE000`;\n\t\t});\n\n\t\tconst tidied = protectedText\n\t\t\t.replace(/\\r\\n?/gu, \"\\n\")\n\t\t\t.replace(/[^\\S\\n]+/gu, \" \")\n\t\t\t.replace(/\\n{3,}/gu, \"\\n\\n\")\n\t\t\t.trim()\n\t\t\t.slice(0, this.MAX_BODY);\n\n\t\treturn tidied.replace(\n\t\t\t/\\uE000(\\d+)\\uE000/gu,\n\t\t\t(_placeholder, index: string) => fences[Number(index)] ?? \"\",\n\t\t);\n\t};\n\n\tprivate asRecord = (value: unknown): Record<string, unknown> | undefined => {\n\t\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n\t\t\t? (value as Record<string, unknown>)\n\t\t\t: undefined;\n\t};\n\n\tprivate compact = <T extends object>(value: T): T => {\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(value).filter(\n\t\t\t\t([, item]) => item !== undefined && item !== \"\",\n\t\t\t),\n\t\t) as T;\n\t};\n\n\t/** The first absolute `/browse/<key>` URL in the payload, else the site's own. */\n\tprivate issueUrl = (\n\t\tstrings: readonly string[],\n\t\tkey: string,\n\t\tsiteUrl: string | undefined,\n\t): string | undefined => {\n\t\tfor (const text of strings) {\n\t\t\tif (!text.includes(\"/browse/\") || !text.includes(key)) continue;\n\n\t\t\tconst match = text.match(\n\t\t\t\t/https?:\\/\\/[^\\s\"')<>]*\\/browse\\/[A-Z][A-Z0-9_]{0,9}-\\d+/u,\n\t\t\t);\n\n\t\t\tif (match !== null) return match[0];\n\t\t}\n\n\t\treturn siteUrl === undefined ? undefined : `${siteUrl}/browse/${key}`;\n\t};\n\n\t/**\n\t * Resolve a possibly relative Atlassian path against the site's own root.\n\t *\n\t * A Confluence search answers with `\"/spaces/KEY/pages/123/Title\"`, which is\n\t * relative to the wiki (`https://site.atlassian.net/wiki`), while a `webui`\n\t * link that already starts with `/wiki` is relative to the site itself. The\n\t * payload's own absolute URLs say which site that is; the learned site is\n\t * the fallback when the payload never spells one out.\n\t */\n\tprivate absoluteUrl = (\n\t\traw: string | undefined,\n\t\tstrings: readonly string[],\n\t\tsiteUrl: string | undefined,\n\t): string | undefined => {\n\t\tif (raw === undefined) return undefined;\n\n\t\tif (raw.startsWith(\"http\")) return raw;\n\t\tif (!raw.startsWith(\"/\")) return undefined;\n\n\t\tconst site = this.siteBase(strings) ?? siteUrl;\n\t\tif (site === undefined) return undefined;\n\n\t\treturn `${raw.startsWith(\"/wiki/\") ? site : `${site}/wiki`}${raw}`;\n\t};\n\n\t/** The first `https://<site>.atlassian.net` the payload spells out. */\n\tprivate siteBase = (strings: readonly string[]): string | undefined => {\n\t\tfor (const text of strings) {\n\t\t\tconst match = text.match(/https?:\\/\\/[^\\s\"')<>]*atlassian\\.net/u);\n\n\t\t\tif (match !== null) return match[0];\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\tprivate issueCards = (\n\t\tobjects: readonly Record<string, unknown>[],\n\t\tstrings: readonly string[],\n\t\tsiteUrl: string | undefined,\n\t\tbodies: boolean,\n\t): IssueCard[] => {\n\t\tconst issues = new Map<string, IssueCard>();\n\n\t\tfor (const node of objects) {\n\t\t\tconst key = this.pick(node, \"key\");\n\n\t\t\tif (key === undefined || !this.ISSUE_KEY.test(key) || issues.has(key))\n\t\t\t\tcontinue;\n\n\t\t\t// A Jira REST issue nests its fields; an Atlassian MCP payload may not.\n\t\t\tconst fields = this.asRecord(node.fields) ?? node;\n\t\t\tconst rawText = bodies ? this.plainText(fields.description) : undefined;\n\n\t\t\tissues.set(\n\t\t\t\tkey,\n\t\t\t\tthis.compact({\n\t\t\t\t\tkey,\n\t\t\t\t\tsummary: this.pick(fields, \"summary\", \"name\", \"title\"),\n\t\t\t\t\tstatus:\n\t\t\t\t\t\tthis.pick(this.asRecord(fields.status) ?? fields.status, \"name\") ??\n\t\t\t\t\t\tthis.pick(fields, \"status\"),\n\t\t\t\t\tassignee:\n\t\t\t\t\t\tthis.pick(\n\t\t\t\t\t\t\tthis.asRecord(fields.assignee) ?? fields.assignee,\n\t\t\t\t\t\t\t\"displayName\",\n\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t) ?? this.pick(fields, \"assignee\"),\n\t\t\t\t\tpriority:\n\t\t\t\t\t\tthis.pick(\n\t\t\t\t\t\t\tthis.asRecord(fields.priority) ?? fields.priority,\n\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t) ?? this.pick(fields, \"priority\"),\n\t\t\t\t\ttype:\n\t\t\t\t\t\tthis.pick(\n\t\t\t\t\t\t\tthis.asRecord(fields.issuetype) ?? fields.issuetype,\n\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t) ?? this.pick(fields, \"issuetype\"),\n\t\t\t\t\testimate: this.estimateOf(fields),\n\t\t\t\t\turl: this.issueUrl(strings, key, siteUrl),\n\t\t\t\t\trawText,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (issues.size >= this.MAX_CARDS) break;\n\t\t}\n\n\t\treturn [...issues.values()];\n\t};\n\n\tprivate pageCards = (\n\t\tobjects: readonly Record<string, unknown>[],\n\t\tstrings: readonly string[],\n\t\tsiteUrl: string | undefined,\n\t\tbodies: boolean,\n\t): PageCard[] => {\n\t\tconst pages = new Map<string, PageCard>();\n\n\t\tfor (const node of objects) {\n\t\t\tconst title = this.pick(node, \"title\");\n\n\t\t\tif (title === undefined) continue;\n\n\t\t\tconst id = this.pick(node, \"id\");\n\t\t\t// `_links.webui` is the canonical page path; `_links.base` is the\n\t\t\t// site's own root, which is not where a title should lead.\n\t\t\tconst raw =\n\t\t\t\tthis.pick(node, \"url\") ??\n\t\t\t\tthis.pick(this.asRecord(node._links), \"webui\");\n\n\t\t\tif (id === undefined && raw === undefined) continue;\n\n\t\t\tconst identity = raw ?? id ?? title;\n\n\t\t\tif (pages.has(identity)) continue;\n\n\t\t\tconst url =\n\t\t\t\tthis.absoluteUrl(raw, strings, siteUrl) ??\n\t\t\t\t(id !== undefined && siteUrl !== undefined\n\t\t\t\t\t? `${siteUrl}/wiki/pages/viewpage.action?pageId=${id}`\n\t\t\t\t\t: undefined);\n\n\t\t\t// A full page carries its body; a search result carries only an\n\t\t\t// HTML-ish snippet. Either is enough for the one sentence shown.\n\t\t\tconst rawText = !bodies\n\t\t\t\t? undefined\n\t\t\t\t: node.body === undefined\n\t\t\t\t\t? this.snippetText(node.excerpt)\n\t\t\t\t\t: this.plainText(node.body);\n\n\t\t\tpages.set(\n\t\t\t\tidentity,\n\t\t\t\tthis.compact({\n\t\t\t\t\tid,\n\t\t\t\t\ttitle,\n\t\t\t\t\turl,\n\t\t\t\t\tspace: this.pick(\n\t\t\t\t\t\tthis.asRecord(node.space) ?? node.space,\n\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\"key\",\n\t\t\t\t\t),\n\t\t\t\t\trawText,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tif (pages.size >= this.MAX_CARDS) break;\n\t\t}\n\n\t\treturn [...pages.values()];\n\t};\n}\n\nexport const extractor = new Extractor();\n", "/**\n * What Atlassian contributes to a chain.\n *\n * One entry kind, `task`: asked about a key, it answers with the Jira issue the\n * key names \u2014 its summary, its status, and whom it is with. It is the end of a\n * chain as written today, because a tracker issue does not itself point at code;\n * if it ever does, its own links become references and this source grows a kind\n * to answer for them.\n *\n * The source registers itself with the correlation registry rather than being\n * imported by it; a deployment without the correlation plugin never calls this.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport {\n\tCORRELATION_SOURCES,\n\ttype CorrelationFact,\n\ttype CorrelationRef,\n\ttype CorrelationSource,\n\ttype RuntimeState,\n} from \"dsh-integration-host\";\nimport { asRecord, type McpBridge, mcpJson, pickString } from \"dsh-mcp-oauth\";\nimport { extractor } from \"./presentation/extract\";\nimport type { SiteFacts } from \"./store/site\";\n\n/** The tool that reads one issue. */\nconst ISSUE_TOOL = \"getJiraIssue\";\n\n/** The fields a chain hop shows, and no more. */\nconst ISSUE_FIELDS = [\"summary\", \"status\", \"assignee\", \"issuetype\", \"priority\"];\n\n/** Everything the source reads through, wired once at apply time. */\nexport interface CorrelationDeps {\n\treadonly bridge: McpBridge;\n\treadonly state: RuntimeState<SiteFacts>;\n}\n\n/** The named value of a Jira field, which may arrive as an object or a string. */\nconst nameOf = (value: unknown): string | undefined =>\n\ttypeof value === \"string\" && value !== \"\"\n\t\t? value\n\t\t: pickString(asRecord(value) ?? {}, \"displayName\", \"name\", \"value\");\n\n/** What one issue answers with. */\nconst factOf = (\n\tkey: string,\n\tvalue: unknown,\n\tsiteUrl?: string,\n): CorrelationFact => {\n\tconst record =\n\t\tmcpJson(value)\n\t\t\t.map((root) => asRecord(root))\n\t\t\t.find(\n\t\t\t\t(candidate) =>\n\t\t\t\t\tcandidate !== undefined &&\n\t\t\t\t\t(candidate.fields !== undefined || candidate.key !== undefined),\n\t\t\t) ?? {};\n\tconst fields = asRecord(record.fields) ?? record;\n\tconst identity = pickString(record, \"key\") ?? key;\n\tconst summary = extractor.text(pickString(fields, \"summary\"));\n\tconst parts = [\n\t\tnameOf(fields.status),\n\t\tnameOf(fields.issuetype),\n\t\tnameOf(fields.priority),\n\t\tnameOf(fields.assignee),\n\t].filter((part): part is string => part !== undefined && part !== \"\");\n\n\treturn {\n\t\tid: `jira:${identity}`,\n\t\tprovider: \"Atlassian\",\n\t\tkind: \"task\",\n\t\tlabel: \"Jira issue\",\n\t\ttitle: summary === undefined ? identity : `${identity} \u00B7 ${summary}`,\n\t\t...(siteUrl === undefined ? {} : { url: `${siteUrl}/browse/${identity}` }),\n\t\t...(parts.length === 0 ? {} : { meta: parts.join(\" \u00B7 \") }),\n\t};\n};\n\n/** The Atlassian contribution. */\nexport const atlassianCorrelationSource = (\n\tdeps: CorrelationDeps,\n): CorrelationSource => ({\n\tprovider: \"Atlassian\",\n\tentry: \"task\",\n\tresolves: [\"task\"],\n\tresolve: async (ref: CorrelationRef) => {\n\t\tif (ref.kind !== \"task\") return [];\n\n\t\tconst cloudId = deps.state.facts?.cloudId;\n\n\t\tif (cloudId === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t\"no Atlassian site known yet; sign in again or run /atlassian sync\",\n\t\t\t);\n\n\t\tconst value = await deps.bridge.callTool(ISSUE_TOOL, {\n\t\t\tcloudId,\n\t\t\tissueIdOrKey: ref.key,\n\t\t\tfields: ISSUE_FIELDS,\n\t\t\tresponseContentFormat: \"markdown\",\n\t\t});\n\n\t\treturn [factOf(ref.key, value, deps.state.facts?.siteUrl)];\n\t},\n});\n\n/**\n * Offer Atlassian's chain contribution to the correlation plugin, if it is installed.\n * @param ctx - the plugin's host context.\n * @param deps - the MCP bridge and the site facts.\n */\nexport const registerCorrelationSource = (\n\tctx: Context,\n\tdeps: CorrelationDeps,\n): void => {\n\tctx.inject([CORRELATION_SOURCES], (scoped) => {\n\t\tscoped.effect(\n\t\t\t() =>\n\t\t\t\tscoped.correlationSources.register(atlassianCorrelationSource(deps)),\n\t\t\t\"atlassian: correlation source\",\n\t\t);\n\t});\n};\n", "/**\n * What Atlassian contributes to a digest.\n *\n * The two Jira blocks are the ones a morning digest is for: work already in\n * flight (including anything overdue), and work assigned but not started. Both\n * are asked for in JQL \u2014 the site's own vocabulary \u2014 and read through the same\n * extractor the `atlassian_present` tool uses, so the digest and the card can\n * never disagree about what a Jira issue looks like.\n *\n * The source is registered through the digest registry rather than imported by\n * it: a deployment without the digest plugin never calls this, and no plugin\n * reaches into another's code.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport {\n\tDIGEST_SOURCES,\n\ttype DigestItem,\n\ttype DigestLoadOptions,\n\ttype DigestSource,\n\ttype RuntimeState,\n} from \"dsh-integration-host\";\nimport type { McpBridge } from \"dsh-mcp-oauth\";\nimport type { IssueCard } from \"../shared/meta\";\nimport { extractor } from \"./presentation/extract\";\nimport type { SiteFacts } from \"./store/site\";\n\n/** The Jira tool the two blocks read through. */\nconst SEARCH_TOOL = \"searchJiraIssuesUsingJql\";\n\n/** The fields a digest row needs, and nothing a summary pass would want. */\nconst ISSUE_FIELDS = [\n\t\"summary\",\n\t\"status\",\n\t\"assignee\",\n\t\"priority\",\n\t\"issuetype\",\n\t\"duedate\",\n];\n\n/**\n * The query behind each block.\n *\n * `statusCategory != Done` keeps both blocks to open work; the active block adds\n * anything whose due date has passed, because an overdue task is work in flight\n * whatever its status column says.\n */\nconst JQL: Readonly<Record<string, string>> = {\n\t\"jira-active\":\n\t\t'assignee = currentUser() AND statusCategory != Done AND (statusCategory = \"In Progress\" OR duedate <= now()) ORDER BY duedate ASC',\n\t\"jira-queued\":\n\t\t'assignee = currentUser() AND statusCategory = \"To Do\" ORDER BY created DESC',\n};\n\n/** Everything the source reads through, wired once at apply time. */\nexport interface DigestDeps {\n\treadonly bridge: McpBridge;\n\treadonly state: RuntimeState<SiteFacts>;\n}\n\n/** One issue, as a digest row. */\nconst toItem = (issue: IssueCard): DigestItem => {\n\tconst meta = [issue.status, issue.priority].filter(Boolean).join(\" \u00B7 \");\n\n\treturn {\n\t\tid: issue.key,\n\t\ttitle: [issue.key, issue.summary].filter(Boolean).join(\" \u00B7 \"),\n\t\t...(issue.url === undefined ? {} : { url: issue.url }),\n\t\t...(meta === \"\" ? {} : { meta }),\n\t};\n};\n\n/** The Atlassian contribution. */\nexport const atlassianDigestSource = (deps: DigestDeps): DigestSource => ({\n\tprovider: \"Atlassian\",\n\tblocks: [\n\t\t{\n\t\t\tid: \"jira-active\",\n\t\t\ttitle: \"Jira \u00B7 In progress and overdue\",\n\t\t\tdefaultOn: true,\n\t\t},\n\t\t{\n\t\t\tid: \"jira-queued\",\n\t\t\ttitle: \"Jira \u00B7 Assigned, not started\",\n\t\t\tdefaultOn: true,\n\t\t},\n\t],\n\tload: async (\n\t\tblockId: string,\n\t\toptions: DigestLoadOptions,\n\t): Promise<readonly DigestItem[]> => {\n\t\tconst jql = JQL[blockId];\n\n\t\tif (jql === undefined) throw new Error(`no Jira block named \"${blockId}\"`);\n\n\t\tconst cloudId = deps.state.facts?.cloudId;\n\n\t\tif (cloudId === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t\"the Atlassian site is not known yet; run /atlassian sync\",\n\t\t\t);\n\n\t\tconst value = await deps.bridge.callTool(SEARCH_TOOL, {\n\t\t\tcloudId,\n\t\t\tjql,\n\t\t\tfields: ISSUE_FIELDS,\n\t\t\tmaxResults: options.limit,\n\t\t});\n\n\t\treturn extractor\n\t\t\t.extract({\n\t\t\t\ttool: SEARCH_TOOL,\n\t\t\t\targs: {},\n\t\t\t\tvalue,\n\t\t\t\tsiteUrl: deps.state.facts?.siteUrl,\n\t\t\t})\n\t\t\t.issues.slice(0, options.limit)\n\t\t\t.map(toItem);\n\t},\n});\n\n/**\n * Offer Atlassian's blocks to the digest plugin, if it is installed.\n *\n * A scoped `inject` is what makes the digest plugin optional: the registration\n * waits for the service and is withdrawn with it, and a deployment that never\n * loads the digest plugin simply leaves this fiber pending.\n * @param ctx - the plugin's host context.\n * @param deps - the MCP bridge and the site facts.\n */\nexport const registerDigestSource = (ctx: Context, deps: DigestDeps): void => {\n\tctx.inject([DIGEST_SOURCES], (scoped) => {\n\t\tscoped.effect(\n\t\t\t() => scoped.digestSources.register(atlassianDigestSource(deps)),\n\t\t\t\"atlassian: digest source\",\n\t\t);\n\t});\n};\n", "/**\n * Loading what the side panel shows.\n *\n * The panel is the second reader of the MCP connection: where a card wants a\n * summary, the panel wants the whole issue \u2014 description, comments, the\n * transitions the workflow offers right now \u2014 and it is the only place this\n * plugin writes to Atlassian at all (a status transition, an assignee).\n *\n * Every read is best-effort around the edges. A transition list that fails to\n * load, or a comment section the server declines, leaves the rest of the\n * document intact: a panel missing one section still answers the question the\n * user clicked on. Writes are the opposite \u2014 a write that fails is reported,\n * because silence there would look like success.\n */\n\nimport type { RuntimeState } from \"dsh-integration-host\";\nimport type { McpBridge } from \"dsh-mcp-oauth\";\nimport { asRecord, mcpJson, pickNumber, pickString } from \"dsh-mcp-oauth\";\nimport type {\n\tIssuePanel,\n\tPagePanel,\n\tPanelComment,\n\tPanelPerson,\n\tPanelTransition,\n} from \"../../shared/panel\";\nimport { extractor, formatDuration } from \"../presentation/extract\";\nimport type { SiteFacts } from \"../store/site\";\n\n/** How many comments to show on one panel. */\nconst COMMENT_LIMIT = 25;\n\n/** How many people one lookup answers with. */\nconst PEOPLE_LIMIT = 8;\n\nexport interface PanelDeps {\n\treadonly bridge: McpBridge;\n\treadonly state: RuntimeState<SiteFacts>;\n}\n\nexport class PanelApi {\n\tconstructor(private readonly deps: PanelDeps) {}\n\n\t/** The issue fields the panel renders. */\n\tprivate readonly ISSUE_FIELDS = [\n\t\t\"summary\",\n\t\t\"description\",\n\t\t\"status\",\n\t\t\"issuetype\",\n\t\t\"priority\",\n\t\t\"assignee\",\n\t\t\"reporter\",\n\t\t\"created\",\n\t\t\"updated\",\n\t\t\"duedate\",\n\t\t\"labels\",\n\t\t\"resolution\",\n\t\t\"timetracking\",\n\t\t\"timeoriginalestimate\",\n\t];\n\n\t/** Everything one issue panel shows. */\n\tissue = async (key: string): Promise<IssuePanel> => {\n\t\tconst cloudId = this.cloudId();\n\n\t\t// The transitions are a second call, and one that may legitimately fail\n\t\t// (a workflow the token cannot read): the issue is what matters.\n\t\tconst [detail, transitions] = await Promise.all([\n\t\t\tthis.deps.bridge.callTool(\"getJiraIssue\", {\n\t\t\t\tcloudId,\n\t\t\t\tissueIdOrKey: key,\n\t\t\t\tfields: [...this.ISSUE_FIELDS, \"comment\"],\n\t\t\t\tresponseContentFormat: \"markdown\",\n\t\t\t}),\n\t\t\tthis.deps.bridge\n\t\t\t\t.callTool(\"getTransitionsForJiraIssue\", {\n\t\t\t\t\tcloudId,\n\t\t\t\t\tissueIdOrKey: key,\n\t\t\t\t})\n\t\t\t\t.then((value) => this.transitionsOf(value))\n\t\t\t\t.catch(() => [] as PanelTransition[]),\n\t\t]);\n\n\t\tconst record = this.issueRecord(detail);\n\t\tif (record === undefined)\n\t\t\tthrow new Error(`Atlassian returned no issue for ${key}`);\n\n\t\tconst fields = asRecord(record.fields) ?? record;\n\t\tconst identity = pickString(record, \"key\") ?? key;\n\t\tconst summary = extractor.text(pickString(fields, \"summary\"));\n\n\t\treturn {\n\t\t\tkind: \"issue\",\n\t\t\tkey: identity,\n\t\t\t...(summary === undefined ? {} : { summary }),\n\t\t\ttype: pickString(asRecord(fields.issuetype) ?? fields.issuetype, \"name\"),\n\t\t\tstatus: pickString(asRecord(fields.status) ?? fields.status, \"name\"),\n\t\t\tpriority: pickString(\n\t\t\t\tasRecord(fields.priority) ?? fields.priority,\n\t\t\t\t\"name\",\n\t\t\t),\n\t\t\tassignee: this.personOf(fields.assignee),\n\t\t\treporter: pickString(\n\t\t\t\tasRecord(fields.reporter) ?? fields.reporter,\n\t\t\t\t\"displayName\",\n\t\t\t\t\"name\",\n\t\t\t),\n\t\t\testimate: this.estimateOf(fields),\n\t\t\tdue: pickString(fields, \"duedate\"),\n\t\t\tlabels: Array.isArray(fields.labels)\n\t\t\t\t? fields.labels.filter(\n\t\t\t\t\t\t(label): label is string => typeof label === \"string\",\n\t\t\t\t\t)\n\t\t\t\t: [],\n\t\t\tcreated: pickString(fields, \"created\"),\n\t\t\tupdated: pickString(fields, \"updated\"),\n\t\t\tdescription: extractor.text(fields.description),\n\t\t\turl:\n\t\t\t\tthis.deps.state.facts?.siteUrl === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: `${this.deps.state.facts?.siteUrl}/browse/${identity}`,\n\t\t\tcomments: this.commentsOf(fields.comment),\n\t\t\ttransitions,\n\t\t};\n\t};\n\n\t/** Everything one page panel shows. */\n\tpage = async (id: string): Promise<PagePanel> => {\n\t\tconst cloudId = this.cloudId();\n\n\t\tconst [detail, comments] = await Promise.all([\n\t\t\tthis.deps.bridge.callTool(\"getConfluencePage\", {\n\t\t\t\tcloudId,\n\t\t\t\tpageId: id,\n\t\t\t\tcontentFormat: \"markdown\",\n\t\t\t}),\n\t\t\tthis.deps.bridge\n\t\t\t\t.callTool(\"getConfluencePageFooterComments\", {\n\t\t\t\t\tcloudId,\n\t\t\t\t\tpageId: id,\n\t\t\t\t\tlimit: COMMENT_LIMIT,\n\t\t\t\t})\n\t\t\t\t.catch(() => undefined),\n\t\t]);\n\n\t\tconst record = this.firstRecord(detail);\n\t\tif (record === undefined)\n\t\t\tthrow new Error(`Atlassian returned no page for ${id}`);\n\n\t\tconst title = pickString(record, \"title\") ?? id;\n\t\tconst webui = pickString(asRecord(record._links), \"webui\");\n\n\t\treturn {\n\t\t\tkind: \"page\",\n\t\t\tid: pickString(record, \"id\") ?? id,\n\t\t\ttitle,\n\t\t\tspace: pickString(asRecord(record.space) ?? record.space, \"name\", \"key\"),\n\t\t\turl:\n\t\t\t\twebui === undefined\n\t\t\t\t\t? this.deps.state.facts?.siteUrl === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: `${this.deps.state.facts?.siteUrl}/wiki/pages/viewpage.action?pageId=${id}`\n\t\t\t\t\t: this.wikiUrl(webui, this.deps.state.facts?.siteUrl),\n\t\t\tbody: extractor.text(record.body),\n\t\t\tcomments: this.commentsOf(this.firstRecord(comments)?.results),\n\t\t};\n\t};\n\n\t/** People the site matches to one search string. */\n\tpeople = async (query: string): Promise<PanelPerson[]> => {\n\t\tconst value = await this.deps.bridge.callTool(\"lookupJiraAccountId\", {\n\t\t\tcloudId: this.cloudId(),\n\t\t\tsearchString: query,\n\t\t\tshowAvatar: false,\n\t\t});\n\n\t\tconst users = this.userList(value);\n\n\t\treturn users\n\t\t\t.map((user) => ({\n\t\t\t\tname: pickString(user, \"displayName\") ?? \"\",\n\t\t\t\taccountId: pickString(user, \"accountId\", \"account_id\") ?? \"\",\n\t\t\t}))\n\t\t\t.filter((person) => person.name !== \"\" && person.accountId !== \"\")\n\t\t\t.slice(0, PEOPLE_LIMIT);\n\t};\n\n\t/**\n\t * The signed-in user, as the site names them.\n\t *\n\t * `atlassianUserInfo` answers about the account the connection belongs to, in\n\t * a shape of its own; the first record that carries an account id is the\n\t * answer. A site that names none leaves the panel without a self-assignment\n\t * rather than offering one Jira would refuse.\n\t * @returns the reader, or undefined when the answer names no account.\n\t */\n\tme = async (): Promise<PanelPerson | undefined> => {\n\t\tconst value = await this.deps.bridge.callTool(\"atlassianUserInfo\", {});\n\n\t\treturn this.personIn(value);\n\t};\n\n\t/** Move one issue through a workflow transition. */\n\ttransition = async (key: string, transitionId: string): Promise<void> => {\n\t\tawait this.deps.bridge.callTool(\"transitionJiraIssue\", {\n\t\t\tcloudId: this.cloudId(),\n\t\t\tissueIdOrKey: key,\n\t\t\ttransition: { id: transitionId },\n\t\t});\n\t};\n\n\t/** Set or clear one issue's assignee. */\n\tassign = async (key: string, accountId: string | null): Promise<void> => {\n\t\tawait this.deps.bridge.callTool(\"editJiraIssue\", {\n\t\t\tcloudId: this.cloudId(),\n\t\t\tissueIdOrKey: key,\n\t\t\tfields: { assignee: accountId === null ? null : { accountId } },\n\t\t});\n\t};\n\n\t/** The connected site's cloud id, or a message the user can act on. */\n\tprivate cloudId = (): string => {\n\t\tif (this.deps.state.facts?.cloudId === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t\"no Atlassian site known yet \u2014 sign in from Settings, then try again\",\n\t\t\t);\n\n\t\treturn this.deps.state.facts?.cloudId;\n\t};\n\n\t/** Jira's own estimate words, or a duration rendered from raw seconds. */\n\tprivate estimateOf = (\n\t\tfields: Record<string, unknown>,\n\t): string | undefined => {\n\t\tconst tracking = asRecord(fields.timetracking);\n\t\tconst stated = pickString(\n\t\t\ttracking,\n\t\t\t\"originalEstimate\",\n\t\t\t\"remainingEstimate\",\n\t\t);\n\n\t\tif (stated !== undefined) return stated;\n\n\t\tconst seconds = pickNumber(\n\t\t\ttracking,\n\t\t\t\"originalEstimateSeconds\",\n\t\t\t\"remainingEstimateSeconds\",\n\t\t);\n\n\t\tif (seconds !== undefined) return formatDuration(seconds);\n\n\t\treturn formatDuration(\n\t\t\tpickNumber(fields, \"timeoriginalestimate\", \"timeestimate\") ?? 0,\n\t\t);\n\t};\n\n\tprivate personOf = (value: unknown): PanelPerson | undefined => {\n\t\tconst record = asRecord(value);\n\t\tconst accountId = pickString(record, \"accountId\", \"account_id\");\n\t\tconst name = pickString(record, \"displayName\", \"name\");\n\n\t\tif (accountId === undefined || name === undefined) return undefined;\n\n\t\treturn { accountId, name };\n\t};\n\n\t/** The Jira issue inside one `getJiraIssue` result. */\n\tprivate issueRecord = (value: unknown): Record<string, unknown> | undefined =>\n\t\tthis.firstRecord(value, (record) => asRecord(record.fields) !== undefined);\n\n\t/** The first JSON record in a result, or the first one a predicate accepts. */\n\tprivate firstRecord = (\n\t\tvalue: unknown,\n\t\taccept: (record: Record<string, unknown>) => boolean = () => true,\n\t): Record<string, unknown> | undefined => {\n\t\tfor (const root of mcpJson(value)) {\n\t\t\tconst record = asRecord(root);\n\t\t\tif (record !== undefined && accept(record)) return record;\n\n\t\t\tif (!Array.isArray(root)) continue;\n\n\t\t\tfor (const item of root) {\n\t\t\t\tconst candidate = asRecord(item);\n\t\t\t\tif (candidate !== undefined && accept(candidate)) return candidate;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t};\n\n\t/** The comment list one issue or page carries. */\n\tprivate commentsOf = (value: unknown): PanelComment[] => {\n\t\tconst list = Array.isArray(value)\n\t\t\t? value\n\t\t\t: Array.isArray(asRecord(value)?.comments)\n\t\t\t\t? (asRecord(value)?.comments as unknown[])\n\t\t\t\t: [];\n\n\t\tconst comments: PanelComment[] = [];\n\n\t\tfor (const item of list) {\n\t\t\tconst record = asRecord(item);\n\t\t\tif (record === undefined) continue;\n\n\t\t\tconst body = extractor.text(record.body);\n\t\t\tif (body === undefined) continue;\n\n\t\t\tconst author = pickString(\n\t\t\t\tasRecord(record.author) ?? record.author,\n\t\t\t\t\"displayName\",\n\t\t\t\t\"publicName\",\n\t\t\t\t\"name\",\n\t\t\t);\n\n\t\t\tcomments.push({\n\t\t\t\tbody,\n\t\t\t\t...(author === undefined ? {} : { author }),\n\t\t\t\t...(pickString(record, \"created\") === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { created: pickString(record, \"created\") }),\n\t\t\t});\n\n\t\t\tif (comments.length >= COMMENT_LIMIT) break;\n\t\t}\n\n\t\treturn comments;\n\t};\n\n\t/** The transitions a workflow currently offers. */\n\tprivate transitionsOf = (value: unknown): PanelTransition[] => {\n\t\tconst record = this.firstRecord(value);\n\t\tconst list = Array.isArray(record?.transitions) ? record.transitions : [];\n\n\t\tconst transitions: PanelTransition[] = [];\n\n\t\tfor (const item of list) {\n\t\t\tconst candidate = asRecord(item);\n\t\t\tif (candidate === undefined) continue;\n\n\t\t\tconst id = pickString(candidate, \"id\");\n\t\t\tconst name = pickString(candidate, \"name\");\n\t\t\tif (id === undefined || name === undefined) continue;\n\n\t\t\tconst to = asRecord(candidate.to);\n\n\t\t\ttransitions.push({\n\t\t\t\tid,\n\t\t\t\tname,\n\t\t\t\t...(pickString(to, \"name\") === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { to: pickString(to, \"name\") }),\n\t\t\t\t// Jira omits `isAvailable` when the transition is offered.\n\t\t\t\tavailable: candidate.isAvailable !== false,\n\t\t\t\tneedsFields: candidate.hasScreen === true,\n\t\t\t});\n\t\t}\n\n\t\treturn transitions;\n\t};\n\n\t/** The user list inside one `lookupJiraAccountId` result. */\n\tprivate userList = (value: unknown): Record<string, unknown>[] => {\n\t\tfor (const root of mcpJson(value)) {\n\t\t\tconst users = asRecord(asRecord(asRecord(root)?.data)?.users);\n\t\t\tconst list = users?.users;\n\n\t\t\tif (Array.isArray(list))\n\t\t\t\treturn list\n\t\t\t\t\t.map((item) => asRecord(item))\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(item): item is Record<string, unknown> => item !== undefined,\n\t\t\t\t\t);\n\t\t}\n\n\t\treturn [];\n\t};\n\n\t/** The first person named anywhere in one tool result. */\n\tprivate personIn = (value: unknown): PanelPerson | undefined => {\n\t\tfor (const root of mcpJson(value)) {\n\t\t\tconst person =\n\t\t\t\tthis.personOf(root) ?? this.personOf(asRecord(asRecord(root)?.data));\n\n\t\t\tif (person !== undefined) return person;\n\t\t}\n\n\t\treturn this.personOf(this.firstRecord(value));\n\t};\n\n\t/** A page's own path, resolved against the site's wiki. */\n\tprivate wikiUrl = (\n\t\traw: string,\n\t\tsiteUrl: string | undefined,\n\t): string | undefined => {\n\t\tif (raw.startsWith(\"http\")) return raw;\n\t\tif (!raw.startsWith(\"/\") || siteUrl === undefined) return undefined;\n\n\t\treturn `${raw.startsWith(\"/wiki/\") ? siteUrl : `${siteUrl}/wiki`}${raw}`;\n\t};\n}\n", "/**\n * The `/atlassian` slash command.\n *\n * A command runs directly against the agent without creating a model message,\n * which makes it the right surface for a human-driven action like signing in.\n * The command itself belongs to `dsh-integration-host`, which shares it with the\n * settings card so the two can never disagree about what \"signed in\" means.\n */\n\nimport {\n\ttype CommandResult,\n\tcreateIntegrationCommand,\n\ttype IntegrationAuthFace,\n} from \"dsh-integration-host\";\nimport type { AtlassianStatus } from \"../../shared/status\";\n\n/** The command's name, without the leading slash. */\nexport const COMMAND_NAME = \"atlassian\";\n\n/**\n * Build the handler the command registers.\n * @param auth - the one implementation of sign-in state.\n */\nexport const createCommandHandler = (\n\tauth: IntegrationAuthFace<AtlassianStatus>,\n): ((invocation: { rawInput?: unknown }) => Promise<CommandResult>) => {\n\treturn createIntegrationCommand<AtlassianStatus>(\n\t\t{ name: COMMAND_NAME, label: \"Atlassian\" },\n\t\tauth,\n\t);\n};\n", "/**\n * The localhost API the browser half calls.\n *\n * The settings card and the side panel live in the page, so they need a channel\n * to the host. A named route on the harness web server is that channel, and the\n * four paths every integration answers \u2014 status, login, logout, sync \u2014 come\n * from `dsh-integration-host`. What is left here is what is Atlassian's: the\n * reads and writes its side panel makes.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport {\n\tBadRequest,\n\tcreateIntegrationRoute,\n\ttype IntegrationAuthFace,\n\ttype RouteHandler,\n} from \"dsh-integration-host\";\nimport type { AtlassianStatus } from \"../../shared/status\";\nimport type { PanelApi } from \"../panels/panel\";\n\n/** The route prefix this plugin owns. */\nexport const ROUTE_PREFIX = \"/dsh-atlassian\";\n\n/** Everything the route answers with, wired once at apply time. */\nexport interface ApiDeps {\n\treadonly auth: IntegrationAuthFace<AtlassianStatus>;\n\treadonly panel: PanelApi;\n}\n\n/** The side panel's own paths, tried after the four shared ones. */\nconst panelRoutes = (panel: PanelApi): readonly RouteHandler[] => [\n\tasync (route) => {\n\t\tif (\n\t\t\troute.url.pathname !== `${ROUTE_PREFIX}/issue` ||\n\t\t\troute.method !== \"GET\"\n\t\t)\n\t\t\treturn false;\n\n\t\tconst key = route.url.searchParams.get(\"key\") ?? \"\";\n\n\t\tawait route.sendData(() => panel.issue(key));\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (route.url.pathname !== `${ROUTE_PREFIX}/page` || route.method !== \"GET\")\n\t\t\treturn false;\n\n\t\tconst id = route.url.searchParams.get(\"id\") ?? \"\";\n\n\t\tawait route.sendData(() => panel.page(id));\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (\n\t\t\troute.url.pathname !== `${ROUTE_PREFIX}/people` ||\n\t\t\troute.method !== \"GET\"\n\t\t)\n\t\t\treturn false;\n\n\t\tconst query = route.url.searchParams.get(\"q\") ?? \"\";\n\n\t\tawait route.sendData(() => panel.people(query));\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (route.url.pathname !== `${ROUTE_PREFIX}/me` || route.method !== \"GET\")\n\t\t\treturn false;\n\n\t\tawait route.sendData(async () => ({\n\t\t\tperson: (await panel.me()) ?? null,\n\t\t}));\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (\n\t\t\troute.url.pathname !== `${ROUTE_PREFIX}/issue/transition` ||\n\t\t\troute.method !== \"POST\"\n\t\t)\n\t\t\treturn false;\n\n\t\t// The request is read and validated before any work starts, so a caller's\n\t\t// mistake is answered as one instead of as a bad gateway.\n\t\tconst body = await route.read();\n\t\tconst key = body.required(\"key\");\n\t\tconst transitionId = body.required(\"transitionId\");\n\n\t\tawait route.sendData(async () => {\n\t\t\tawait panel.transition(key, transitionId);\n\n\t\t\treturn await panel.issue(key);\n\t\t});\n\n\t\treturn true;\n\t},\n\n\tasync (route) => {\n\t\tif (\n\t\t\troute.url.pathname !== `${ROUTE_PREFIX}/issue/assignee` ||\n\t\t\troute.method !== \"POST\"\n\t\t)\n\t\t\treturn false;\n\n\t\tconst body = await route.read();\n\t\tconst key = body.required(\"key\");\n\t\tconst accountId = body.fields?.accountId;\n\n\t\tif (accountId !== null && typeof accountId !== \"string\")\n\t\t\tthrow new BadRequest('the request needs an \"accountId\" or null');\n\n\t\tawait route.sendData(async () => {\n\t\t\tawait panel.assign(key, accountId);\n\n\t\t\treturn await panel.issue(key);\n\t\t});\n\n\t\treturn true;\n\t},\n];\n\n/**\n * Build the request handler.\n * @param deps - the plugin's collaborators.\n */\nexport const createApiHandler = (\n\tdeps: ApiDeps,\n): ((request: IncomingMessage, response: ServerResponse) => Promise<void>) => {\n\treturn createIntegrationRoute({\n\t\tprefix: ROUTE_PREFIX,\n\t\tauth: deps.auth,\n\t\troutes: panelRoutes(deps.panel),\n\t});\n};\n", "/**\n * Compress the long text behind a card into the one line it can show.\n *\n * A Jira description is a page of context and a Confluence body is more, so the\n * card carries neither: it carries one sentence per item, written by the same\n * model the conversation runs on. That keeps the card informative without\n * turning it into a wall of text, and it costs one auxiliary call per card.\n *\n * Everything here is best-effort. No model route, no `llm` service, a slow or\n * failing provider \u2014 each of those drops the sentences and leaves the rest of\n * the card intact, because a card without a summary is still a useful card.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type {} from \"@deepseek-ai/dsh-llm\";\nimport type { ToolExecution } from \"@deepseek-ai/dsh-tools\";\n\n/** One item to compress, as the card already knows it. */\nexport interface GistTarget {\n\t/** The caller's own identity for the item; echoed back, never shown. */\n\treadonly id: string;\n\t/** The item's own heading: an issue summary or a page title. */\n\treadonly title: string;\n\t/** The long text to compress. */\n\treadonly body: string;\n}\n\n/** One sentence, tied back to the item it describes. */\nexport interface Gist {\n\treadonly id: string;\n\treadonly text: string;\n}\n\n/** The provider and model an auxiliary call should use. */\nexport interface GistRoute {\n\treadonly provider: string;\n\treadonly model: string;\n}\n\nexport interface GistOptions {\n\t/** The turn's own route, or undefined when it could not be resolved. */\n\treadonly route: GistRoute | undefined;\n\t/** The tool call's signal; cancelling the call cancels the summary. */\n\treadonly signal: AbortSignal | undefined;\n}\n\n/** Longest body sent for one item. */\nconst MAX_BODY_CHARS = 900;\n\n/** Longest prompt built in total; beyond this, bodies are dropped, titles stay. */\nconst MAX_PROMPT_CHARS = 12_000;\n\n/** Longest sentence kept, so one runaway answer cannot stretch the card. */\nconst MAX_GIST_CHARS = 240;\n\n/** A card is not worth waiting longer than this for. */\nconst TIMEOUT_MS = 20_000;\n\nconst SYSTEM_PROMPT = [\n\t\"You write the one-line summary a card shows for each numbered item below.\",\n\t\"Each item is a Jira issue or a Confluence page; its text may be in any language.\",\n\t\"Answer with exactly one line per item, formatted `<number>: <sentence>`.\",\n\t\"Say what the item actually asks for or contains, in the item's own language.\",\n\t\"At most 25 words. No markdown, no quotes, no bullet characters, no preamble, no closing remarks.\",\n].join(\" \");\n\n/**\n * The provider and model the current turn runs on.\n *\n * The logged request header is the route the harness actually used; the agent's\n * own options are the fallback for a session whose header is not readable yet.\n * @param exec - the running tool call.\n * @returns the route, or undefined when neither source names a complete one.\n */\nexport function modelRoute(exec: ToolExecution): GistRoute | undefined {\n\tconst config = exec.agent?.session.requestHeader()?.config;\n\tconst provider = config?.provider ?? exec.agent?.options.provider;\n\tconst model = config?.model ?? exec.agent?.options.model;\n\n\tif (provider === undefined || model === undefined) return undefined;\n\tif (provider === \"\" || model === \"\") return undefined;\n\n\treturn { provider, model };\n}\n\n/**\n * Read the numbered sentences back out of one answer.\n *\n * Models decorate: a code fence, a \"Here you go:\", a bullet before the number.\n * Only lines that start with an in-range number are read, and the first line\n * for a number wins, so decoration is ignored rather than corrupting a gist.\n * @param text - the model's whole answer.\n * @param count - how many items were sent.\n * @returns one entry per item, in order; entries the model skipped are undefined.\n */\nexport function parseGists(\n\ttext: string,\n\tcount: number,\n): (string | undefined)[] {\n\tconst gists: (string | undefined)[] = new Array(count).fill(undefined);\n\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst match = line\n\t\t\t.trim()\n\t\t\t.replace(/^[-*\u2022]\\s*/u, \"\")\n\t\t\t.match(/^(\\d{1,3})\\s*[).:\\-\u2013\u2014]\\s*(.+)$/u);\n\n\t\tif (match === null) continue;\n\n\t\tconst index = Number(match[1]) - 1;\n\t\tconst sentence = match[2]\n\t\t\t?.replace(/^[\"'\u00AB\u201C]+|[\"'\u00BB\u201D]+$/gu, \"\")\n\t\t\t.replace(/\\s+/gu, \" \")\n\t\t\t.trim();\n\n\t\tif (index < 0 || index >= count) continue;\n\t\tif (gists[index] !== undefined) continue;\n\t\tif (sentence === undefined || sentence === \"\") continue;\n\n\t\tgists[index] = sentence.slice(0, MAX_GIST_CHARS);\n\t}\n\n\treturn gists;\n}\n\n/**\n * Write one sentence about each item that has long text.\n * @param ctx - host context; the LLM service is read from it when present.\n * @param targets - the items to compress.\n * @param options - the turn's route and the running call's signal.\n * @returns the sentences, or undefined when the pass did not produce any.\n */\nexport async function summarizeGists(\n\tctx: Context,\n\ttargets: readonly GistTarget[],\n\toptions: GistOptions,\n): Promise<readonly Gist[] | undefined> {\n\tif (targets.length === 0) return undefined;\n\n\tconst route = options.route;\n\tif (route === undefined) return undefined;\n\n\t// Read without an injection requirement: a deployment without a model must\n\t// still get its cards, just without the sentences.\n\tconst llm = ctx.get(\"llm\");\n\tif (llm === undefined) return undefined;\n\n\tconst prompt = buildPrompt(targets);\n\tif (prompt === \"\") return undefined;\n\n\tconst signal =\n\t\toptions.signal === undefined\n\t\t\t? AbortSignal.timeout(TIMEOUT_MS)\n\t\t\t: AbortSignal.any([options.signal, AbortSignal.timeout(TIMEOUT_MS)]);\n\n\tlet answer = \"\";\n\n\ttry {\n\t\tconst stream = llm.stream({\n\t\t\tprovider: route.provider,\n\t\t\tmodel: route.model,\n\t\t\tsystem: SYSTEM_PROMPT,\n\t\t\tmessages: [{ role: \"user\", content: [{ type: \"text\", text: prompt }] }],\n\t\t\ttemperature: 0,\n\t\t\tsignal,\n\t\t});\n\n\t\tfor await (const chunk of stream) {\n\t\t\tif (chunk.type === \"text-delta\") answer += chunk.text;\n\t\t}\n\t} catch {\n\t\t// A failed or cancelled summary is not a failed card.\n\t\treturn undefined;\n\t}\n\n\tconst sentences = parseGists(answer, targets.length);\n\tconst gists: Gist[] = [];\n\n\ttargets.forEach((target, index) => {\n\t\tconst sentence = sentences[index];\n\n\t\tif (sentence !== undefined) gists.push({ id: target.id, text: sentence });\n\t});\n\n\treturn gists.length === 0 ? undefined : gists;\n}\n\n/** The numbered items, bounded so one card cannot become a book. */\nfunction buildPrompt(targets: readonly GistTarget[]): string {\n\tconst parts: string[] = [];\n\tlet used = 0;\n\n\ttargets.forEach((target, index) => {\n\t\tconst heading = `${index + 1}. ${target.title}`;\n\t\tconst room = MAX_PROMPT_CHARS - used - heading.length;\n\n\t\t// Over budget: keep the item's place in the numbering but drop its body,\n\t\t// so the sentences that do come back stay attached to the right item.\n\t\tconst body =\n\t\t\troom <= 0 ? \"\" : target.body.slice(0, Math.min(room, MAX_BODY_CHARS));\n\n\t\tused += heading.length + body.length;\n\t\tparts.push(body === \"\" ? heading : `${heading}\\n${body}`);\n\t});\n\n\treturn parts.join(\"\\n\\n\");\n}\n", "/**\n * `atlassian_present` \u2014 the tool behind a card in the conversation.\n *\n * It resolves what to show (by key, by JQL, by page id, or by CQL), records one\n * durable presentation for the turn, and answers the model with a text summary.\n * The card is presentation only: the model reads the text result, and the record\n * is what the browser half renders under the closing prose.\n *\n * The tool resolves the cloud id itself when the plugin has learned it, so the\n * model does not have to thread that identifier through every call.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\nimport type { ToolDefinition, ToolExecution } from \"@deepseek-ai/dsh-tools\";\nimport type { RuntimeState } from \"dsh-integration-host\";\nimport {\n\tasRecord,\n\ttype JsonValue,\n\ttype McpBridge,\n\tmcpJson,\n} from \"dsh-mcp-oauth\";\nimport type { AtlassianMeta, IssueCard, PageCard } from \"../../shared/meta\";\nimport type {\n\tAtlassianPresentation,\n\tPresentationItem,\n\tPresentationLayout,\n\tPresentationLayoutInput,\n} from \"../../shared/presentation\";\nimport { extractor } from \"../presentation/extract\";\nimport { type Gist, modelRoute, summarizeGists } from \"../presentation/gist\";\nimport type { SiteFacts } from \"../store/site\";\n\n/** What the tool needs from the plugin to resolve and record one card. */\nexport interface PresentDeps {\n\treadonly ctx: Context;\n\treadonly bridge: McpBridge;\n\treadonly state: RuntimeState<SiteFacts>;\n}\n\n/** One tool call's text answer, before it becomes blocks. */\ninterface PresentOutcome {\n\treadonly title: string | undefined;\n\treadonly layout: PresentationLayout;\n\treadonly items: readonly PresentationItem[];\n\t/** The server's match count, when it exceeds what the card shows. */\n\treadonly total: number | undefined;\n\t/** Requests that could not be resolved, by key or id. */\n\treadonly failed: readonly string[];\n\t/** Whether the server held back another page of search results. */\n\treadonly truncated: boolean;\n}\n\n/** What one resolution pass found: card items and the count behind them. */\ninterface Found<Item> {\n\treadonly items: readonly Item[];\n\t/** The server's own match count, when it reported one. */\n\treadonly total: number | undefined;\n\t/** Whether the server named a next page. */\n\treadonly truncated?: boolean;\n}\n\n/** The arguments the model may send, after validation. */\ninterface PresentArgs {\n\treadonly title: string | undefined;\n\treadonly layout: PresentationLayoutInput | undefined;\n\treadonly cloudId: string | undefined;\n\treadonly issues: readonly string[];\n\treadonly pages: readonly string[];\n\treadonly jql: string | undefined;\n\treadonly cql: string | undefined;\n}\n\n/** Whether one search answer names another page. */\nfunction hasMore(value: unknown): boolean {\n\treturn mcpJson(value).some((root) => {\n\t\tconst record = asRecord(root);\n\n\t\treturn (\n\t\t\trecord?.isLast === false ||\n\t\t\t(typeof record?.nextPageToken === \"string\" && record.nextPageToken !== \"\")\n\t\t);\n\t});\n}\n\nfunction stringList(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter(\n\t\t(item): item is string => typeof item === \"string\" && item !== \"\",\n\t);\n}\n\nfunction optionalString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\nclass PresentTool {\n\t/** The wire name the agent calls. */\n\treadonly TOOL_NAME = \"atlassian_present\";\n\n\t/** Never put more than this many items on one card. */\n\tprivate readonly MAX_ITEMS = 25;\n\n\t/** Fields that make a card useful without making the call enormous. */\n\tprivate readonly ISSUE_FIELDS = [\n\t\t\"summary\",\n\t\t\"status\",\n\t\t\"assignee\",\n\t\t\"priority\",\n\t\t\"issuetype\",\n\t\t\"timetracking\",\n\t\t\"timeoriginalestimate\",\n\t\t// Read to write one sentence per issue, then dropped again: the recorded\n\t\t// card carries the sentence, never the description.\n\t\t\"description\",\n\t];\n\n\t/**\n\t * Build the tool definition for one plugin instance.\n\t * @param ctx - host context; the model service is read from it for summaries.\n\t * @param state - runtime state holding the MCP connection and cloud id.\n\t */\n\tcreate = (deps: PresentDeps): ToolDefinition => ({\n\t\tname: this.TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Show Jira issues and/or Confluence pages to the user as a card under your answer. \" +\n\t\t\t\"Use this whenever the user asks to see work \u2014 a daily plan, a shortlist, a single issue, \" +\n\t\t\t\"or search results \u2014 instead of only describing it in prose. Resolve the content by issue \" +\n\t\t\t\"keys, by page ids, by JQL, or by CQL. Each row carries a link to the item, its status, \" +\n\t\t\t\"assignee, priority, type and estimate, plus one sentence summarizing its description, \" +\n\t\t\t\"so the user can act without opening the issue. Call it once per card, then write your \" +\n\t\t\t\"answer; do not repeat the card's contents as a list in the text.\",\n\t\tparameters: {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttitle: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t'Optional heading for the card, such as \"\u041F\u043B\u0430\u043D \u043D\u0430 \u0434\u0435\u043D\u044C\". Omit for an untitled card.',\n\t\t\t\t},\n\t\t\t\tlayout: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tenum: [\"auto\", \"single\", \"compact\", \"list\"],\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"auto (default) picks by item count: one item as a full card, two to four as \" +\n\t\t\t\t\t\t\"compact rows, more as a dense list. A card longer than four rows scrolls and \" +\n\t\t\t\t\t\t\"shows how many items matched. Force one when the content calls for it.\",\n\t\t\t\t},\n\t\t\t\tissues: {\n\t\t\t\t\ttype: \"array\",\n\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\tdescription: 'Issue keys to show, for example [\"DEV-6477\", \"DQ-66\"].',\n\t\t\t\t},\n\t\t\t\tpages: {\n\t\t\t\t\ttype: \"array\",\n\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\tdescription: \"Confluence page ids to show.\",\n\t\t\t\t},\n\t\t\t\tjql: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"A JQL query whose results become the card, for example \" +\n\t\t\t\t\t\t'\"assignee = currentUser() AND status != Done ORDER BY priority DESC\".',\n\t\t\t\t},\n\t\t\t\tcql: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescription: \"A CQL query whose Confluence results become the card.\",\n\t\t\t\t},\n\t\t\t\tcloudId: {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescription:\n\t\t\t\t\t\t\"Atlassian cloud id. Omit unless the user has several sites: the plugin remembers it.\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\toutput: {\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tshown: { type: \"integer\" },\n\t\t\t\t\tsummary: { type: \"string\" },\n\t\t\t\t\tfailed: { type: \"array\", items: { type: \"string\" } },\n\t\t\t\t\t// The card payload, carried to the browser as `result.meta`.\n\t\t\t\t\t// Left open on purpose: it is presentation data, and the browser\n\t\t\t\t\t// half validates it before rendering.\n\t\t\t\t\tpresentation: { type: \"object\", additionalProperties: true },\n\t\t\t\t},\n\t\t\t\trequired: [\"shown\", \"summary\", \"failed\", \"presentation\"],\n\t\t\t},\n\t\t\trender: (_args, value) => [\n\t\t\t\t{ type: \"text\", text: (value as { summary: string }).summary },\n\t\t\t],\n\t\t\t// The browser half reads this back as `result.meta` and renders the\n\t\t\t// card. It never reaches the model: the text above is what the model\n\t\t\t// reads, and this is presentation only.\n\t\t\tpresentationMeta: (_args, value) =>\n\t\t\t\t(value as { presentation: JsonValue }).presentation,\n\t\t},\n\t\texecute: async (rawArgs, exec) => {\n\t\t\tconst args = this.parseArgs(rawArgs);\n\t\t\tconst outcome = await this.resolve(deps, args, exec);\n\n\t\t\tconst viewed = this.describe(outcome, args);\n\t\t\tconst summary =\n\t\t\t\toutcome.failed.length === 0\n\t\t\t\t\t? viewed\n\t\t\t\t\t: `${viewed}\\nCould not resolve: ${outcome.failed.join(\", \")}`;\n\n\t\t\tconst presentation: AtlassianPresentation = {\n\t\t\t\tcallId: String(exec.callId),\n\t\t\t\t...(outcome.title === undefined ? {} : { title: outcome.title }),\n\t\t\t\tlayout: outcome.layout,\n\t\t\t\t...(outcome.total === undefined ? {} : { total: outcome.total }),\n\t\t\t\t...(outcome.truncated ? { truncated: true } : {}),\n\t\t\t\titems: outcome.items,\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tshown: outcome.items.length,\n\t\t\t\tsummary,\n\t\t\t\tfailed: [...outcome.failed],\n\t\t\t\tpresentation,\n\t\t\t};\n\t\t},\n\t});\n\n\t/** Validate the model's arguments; unknown keys are ignored. */\n\tprivate parseArgs(rawArgs: unknown): PresentArgs {\n\t\tconst args =\n\t\t\trawArgs !== null && typeof rawArgs === \"object\"\n\t\t\t\t? (rawArgs as Record<string, unknown>)\n\t\t\t\t: {};\n\n\t\tconst layout = optionalString(args.layout);\n\n\t\treturn {\n\t\t\ttitle: optionalString(args.title),\n\t\t\tlayout:\n\t\t\t\tlayout === \"auto\" ||\n\t\t\t\tlayout === \"single\" ||\n\t\t\t\tlayout === \"compact\" ||\n\t\t\t\tlayout === \"list\"\n\t\t\t\t\t? layout\n\t\t\t\t\t: undefined,\n\t\t\tcloudId: optionalString(args.cloudId),\n\t\t\tissues: stringList(args.issues),\n\t\t\tpages: stringList(args.pages),\n\t\t\tjql: optionalString(args.jql),\n\t\t\tcql: optionalString(args.cql),\n\t\t};\n\t}\n\n\t/** Resolve every requested source into card items. */\n\tprivate resolve = async (\n\t\tdeps: PresentDeps,\n\t\targs: PresentArgs,\n\t\texec: ToolExecution,\n\t): Promise<PresentOutcome> => {\n\t\tconst cloudId = args.cloudId ?? deps.state.facts?.cloudId;\n\n\t\tif (cloudId === undefined)\n\t\t\tthrow new Error(\n\t\t\t\t\"no Atlassian site known yet; run a Jira or Confluence search first, or sign in again\",\n\t\t\t);\n\n\t\tconst requested =\n\t\t\targs.issues.length > 0 ||\n\t\t\targs.pages.length > 0 ||\n\t\t\targs.jql !== undefined ||\n\t\t\targs.cql !== undefined;\n\n\t\tif (!requested)\n\t\t\tthrow new Error(\"nothing to show: pass issues, pages, jql, or cql\");\n\n\t\tconst items: PresentationItem[] = [];\n\t\tconst failed: string[] = [];\n\t\tlet matched = 0;\n\t\tlet truncated = false;\n\n\t\tconst push = (candidate: readonly PresentationItem[]): void => {\n\t\t\tfor (const item of candidate) {\n\t\t\t\tif (items.length >= this.MAX_ITEMS) return;\n\t\t\t\titems.push(item);\n\t\t\t}\n\t\t};\n\n\t\tfor (const key of args.issues) {\n\t\t\ttry {\n\t\t\t\tconst value = await deps.bridge.callTool(\"getJiraIssue\", {\n\t\t\t\t\tcloudId,\n\t\t\t\t\tissueIdOrKey: key,\n\t\t\t\t\tfields: this.ISSUE_FIELDS,\n\t\t\t\t});\n\n\t\t\t\tconst found = this.issuesOf(value, deps.state.facts?.siteUrl, key);\n\t\t\t\tpush(found.items);\n\t\t\t\tmatched += found.total ?? found.items.length;\n\t\t\t} catch {\n\t\t\t\tfailed.push(key);\n\t\t\t}\n\t\t}\n\n\t\tif (args.jql !== undefined) {\n\t\t\tconst value = await deps.bridge.callTool(\"searchJiraIssuesUsingJql\", {\n\t\t\t\tcloudId,\n\t\t\t\tjql: args.jql,\n\t\t\t\tfields: this.ISSUE_FIELDS,\n\t\t\t\tmaxResults: this.MAX_ITEMS,\n\t\t\t});\n\n\t\t\tconst found = this.issuesOf(value, deps.state.facts?.siteUrl);\n\t\t\tpush(found.items);\n\t\t\tmatched += found.total ?? found.items.length;\n\t\t\tif (hasMore(value)) truncated = true;\n\t\t}\n\n\t\tfor (const id of args.pages) {\n\t\t\ttry {\n\t\t\t\tconst value = await deps.bridge.callTool(\"getConfluencePage\", {\n\t\t\t\t\tcloudId,\n\t\t\t\t\tpageId: id,\n\t\t\t\t});\n\n\t\t\t\tconst found = this.pagesOf(value, deps.state.facts?.siteUrl, id);\n\t\t\t\tpush(found.items);\n\t\t\t\tmatched += found.total ?? found.items.length;\n\t\t\t} catch {\n\t\t\t\tfailed.push(id);\n\t\t\t}\n\t\t}\n\n\t\tif (args.cql !== undefined) {\n\t\t\tconst value = await deps.bridge.callTool(\"searchConfluenceUsingCql\", {\n\t\t\t\tcloudId,\n\t\t\t\tcql: args.cql,\n\t\t\t\tlimit: this.MAX_ITEMS,\n\t\t\t});\n\n\t\t\tconst found = this.pagesOf(value, deps.state.facts?.siteUrl);\n\t\t\tpush(found.items);\n\t\t\tmatched += found.total ?? found.items.length;\n\t\t\tif (hasMore(value)) truncated = true;\n\t\t}\n\n\t\t// An empty answer is a result, not a bad request: the query ran and matched\n\t\t// nothing, which the model must be able to tell from \"you sent no query\".\n\n\t\t// One sentence per item, written by the conversation's own model. The\n\t\t// card is projected again afterwards, so no raw description survives.\n\t\tconst gists = await this.summarize(deps.ctx, items, exec);\n\t\tconst projected = items.map((item, index) =>\n\t\t\titem.kind === \"issue\"\n\t\t\t\t? {\n\t\t\t\t\t\tkind: \"issue\" as const,\n\t\t\t\t\t\tissue: this.issueWire(item.issue, gists.get(String(index))),\n\t\t\t\t\t}\n\t\t\t\t: {\n\t\t\t\t\t\tkind: \"page\" as const,\n\t\t\t\t\t\tpage: this.pageWire(item.page, gists.get(String(index))),\n\t\t\t\t\t},\n\t\t);\n\n\t\treturn {\n\t\t\ttitle: args.title,\n\t\t\tlayout: this.resolveLayout(args.layout, projected.length),\n\t\t\titems: projected,\n\t\t\ttotal: matched > projected.length ? matched : undefined,\n\t\t\tfailed,\n\t\t\ttruncated,\n\t\t};\n\t};\n\n\t/**\n\t * Compress the long text behind each item into one sentence.\n\t *\n\t * A card is worth waiting a moment for, not a minute: the pass is bounded by\n\t * its own timeout and any failure simply leaves the items without summaries.\n\t */\n\tprivate summarize = async (\n\t\tctx: Context,\n\t\titems: readonly PresentationItem[],\n\t\texec: ToolExecution,\n\t): Promise<ReadonlyMap<string, string>> => {\n\t\tconst targets = items.flatMap((item, index) => {\n\t\t\tconst title =\n\t\t\t\titem.kind === \"issue\"\n\t\t\t\t\t? (item.issue.summary ?? item.issue.key)\n\t\t\t\t\t: item.page.title;\n\t\t\tconst body =\n\t\t\t\t(item.kind === \"issue\" ? item.issue.rawText : item.page.rawText) ?? \"\";\n\n\t\t\treturn body === \"\" ? [] : [{ id: String(index), title, body }];\n\t\t});\n\n\t\tif (targets.length === 0) return new Map();\n\n\t\tconst gists: readonly Gist[] | undefined = await summarizeGists(\n\t\t\tctx,\n\t\t\ttargets,\n\t\t\t{ route: modelRoute(exec), signal: exec.signal },\n\t\t);\n\n\t\treturn new Map((gists ?? []).map((gist) => [gist.id, gist.text]));\n\t};\n\n\t/**\n\t * The recorded shape of one issue.\n\t *\n\t * Built field by field rather than spread: `rawText` is the description the\n\t * compression pass read, and the card must not carry a word of it.\n\t */\n\tprivate issueWire = (\n\t\tissue: IssueCard,\n\t\tgist: string | undefined,\n\t): IssueCard => ({\n\t\tkey: issue.key,\n\t\t...(issue.summary === undefined ? {} : { summary: issue.summary }),\n\t\t...(issue.status === undefined ? {} : { status: issue.status }),\n\t\t...(issue.assignee === undefined ? {} : { assignee: issue.assignee }),\n\t\t...(issue.priority === undefined ? {} : { priority: issue.priority }),\n\t\t...(issue.type === undefined ? {} : { type: issue.type }),\n\t\t...(issue.estimate === undefined ? {} : { estimate: issue.estimate }),\n\t\t...(gist === undefined ? {} : { description: gist }),\n\t\t...(issue.url === undefined ? {} : { url: issue.url }),\n\t});\n\n\t/** The recorded shape of one page; see {@link issueWire}. */\n\tprivate pageWire = (page: PageCard, gist: string | undefined): PageCard => ({\n\t\ttitle: page.title,\n\t\t...(page.id === undefined ? {} : { id: page.id }),\n\t\t...(page.url === undefined ? {} : { url: page.url }),\n\t\t...(page.space === undefined ? {} : { space: page.space }),\n\t\t...(gist === undefined ? {} : { excerpt: gist }),\n\t});\n\n\t/** Normalize one Jira result; `fallbackKey` keeps a single-issue fetch labelled. */\n\tprivate issuesOf(\n\t\tvalue: unknown,\n\t\tsiteUrl: string | undefined,\n\t\tfallbackKey?: string,\n\t): Found<PresentationItem> {\n\t\tconst meta: AtlassianMeta = extractor.extract({\n\t\t\ttool: \"getJiraIssue\",\n\t\t\targs: {},\n\t\t\tvalue,\n\t\t\tsiteUrl,\n\t\t\t// The description is read here and compressed before it is recorded.\n\t\t\tbodies: true,\n\t\t});\n\n\t\tconst issues = meta.issues as readonly IssueCard[];\n\n\t\tif (issues.length > 0)\n\t\t\treturn {\n\t\t\t\titems: issues.map((issue) => ({ kind: \"issue\", issue })),\n\t\t\t\ttotal: meta.total,\n\t\t\t};\n\n\t\treturn {\n\t\t\titems:\n\t\t\t\tfallbackKey === undefined\n\t\t\t\t\t? []\n\t\t\t\t\t: [{ kind: \"issue\", issue: { key: fallbackKey } }],\n\t\t\ttotal: meta.total,\n\t\t};\n\t}\n\n\t/** Normalize one Confluence result; `fallbackId` keeps a single fetch labelled. */\n\tprivate pagesOf(\n\t\tvalue: unknown,\n\t\tsiteUrl: string | undefined,\n\t\tfallbackId?: string,\n\t): Found<PresentationItem> {\n\t\tconst meta: AtlassianMeta = extractor.extract({\n\t\t\ttool: \"getConfluencePage\",\n\t\t\targs: {},\n\t\t\tvalue,\n\t\t\tsiteUrl,\n\t\t\tbodies: true,\n\t\t});\n\n\t\tconst pages = meta.pages as readonly PageCard[];\n\n\t\tif (pages.length > 0)\n\t\t\treturn {\n\t\t\t\titems: pages.map((page) => ({ kind: \"page\", page })),\n\t\t\t\ttotal: meta.total,\n\t\t\t};\n\n\t\treturn {\n\t\t\titems:\n\t\t\t\tfallbackId === undefined\n\t\t\t\t\t? []\n\t\t\t\t\t: [{ kind: \"page\", page: { id: fallbackId, title: fallbackId } }],\n\t\t\ttotal: meta.total,\n\t\t};\n\t}\n\n\t/** Pick the layout the caller asked for, or infer one from the item count. */\n\tprivate resolveLayout(\n\t\tinput: PresentationLayoutInput | undefined,\n\t\tcount: number,\n\t): PresentationLayout {\n\t\tif (input === \"single\" || input === \"compact\" || input === \"list\")\n\t\t\treturn input;\n\t\tif (count <= 1) return \"single\";\n\t\tif (count <= 4) return \"compact\";\n\n\t\treturn \"list\";\n\t}\n\n\t/** The text the model reads: what the card shows, in order. */\n\tprivate describe(outcome: PresentOutcome, args: PresentArgs): string {\n\t\tif (outcome.items.length === 0) {\n\t\t\tconst lines = [\"No issues or pages matched.\"];\n\n\t\t\tif (args.jql !== undefined) lines.push(`JQL: ${args.jql}`);\n\t\t\tif (args.cql !== undefined) lines.push(`CQL: ${args.cql}`);\n\n\t\t\treturn lines.join(\"\\n\");\n\t\t}\n\n\t\tconst issues = outcome.items.filter((item) => item.kind === \"issue\");\n\t\tconst pages = outcome.items.filter((item) => item.kind === \"page\");\n\n\t\tconst counts = [\n\t\t\tissues.length > 0\n\t\t\t\t? `${issues.length} issue${issues.length === 1 ? \"\" : \"s\"}`\n\t\t\t\t: \"\",\n\t\t\tpages.length > 0\n\t\t\t\t? `${pages.length} page${pages.length === 1 ? \"\" : \"s\"}`\n\t\t\t\t: \"\",\n\t\t].filter(Boolean);\n\n\t\tconst heading =\n\t\t\toutcome.title === undefined\n\t\t\t\t? \"Shown as a card\"\n\t\t\t\t: `Shown as a card titled \"${outcome.title}\"`;\n\n\t\tconst lines = [`${heading}: ${counts.join(\" and \")}.`];\n\n\t\tfor (const item of outcome.items) {\n\t\t\tif (item.kind === \"issue\") {\n\t\t\t\tconst parts = [\n\t\t\t\t\titem.issue.key,\n\t\t\t\t\titem.issue.summary,\n\t\t\t\t\titem.issue.status,\n\t\t\t\t\titem.issue.assignee,\n\t\t\t\t\titem.issue.estimate === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: `estimate ${item.issue.estimate}`,\n\t\t\t\t\titem.issue.url,\n\t\t\t\t].filter(\n\t\t\t\t\t(part): part is string => typeof part === \"string\" && part !== \"\",\n\t\t\t\t);\n\n\t\t\t\tlines.push(`- ${parts.join(\" \u00B7 \")}`);\n\t\t\t} else {\n\t\t\t\tconst parts = [item.page.title, item.page.space, item.page.url].filter(\n\t\t\t\t\t(part): part is string => typeof part === \"string\" && part !== \"\",\n\t\t\t\t);\n\n\t\t\t\tlines.push(`- ${parts.join(\" \u00B7 \")}`);\n\t\t\t}\n\t\t}\n\n\t\tif (args.jql !== undefined) lines.push(`JQL: ${args.jql}`);\n\t\tif (args.cql !== undefined) lines.push(`CQL: ${args.cql}`);\n\t\tif (outcome.truncated)\n\t\t\tlines.push(\"The search had more results than this card read.\");\n\n\t\treturn lines.join(\"\\n\");\n\t}\n}\n\nexport const presentTool = new PresentTool();\n", "/**\n * dsh-atlassian \u2014 Jira and Confluence for DeepSeek Harness.\n *\n * The plugin is a bundle: this host half owns what is Atlassian's \u2014 the site\n * facts, the card extractor, the panel data, and the `/atlassian` command's own\n * wording \u2014 while `dsh-mcp-oauth` owns the OAuth flow and the bridge to\n * Atlassian's remote MCP server, and `dsh-integration-host` owns the shell every\n * integration shares. `lib/client.js` contributes the settings card, the result\n * cards, and the side panel to the Web Client.\n *\n * This module is the composition root: it builds one of each collaborator and\n * hands them to the surfaces that need them.\n */\n\nimport type { Context } from \"@deepseek-ai/cordis\";\n// Each of these brings a service declaration into this program: `ctx.tools`,\n// `ctx.credentials`, `ctx.authorization`, and `ctx.commands` are declared by\n// the packages that own them, not by cordis itself.\nimport type {} from \"@deepseek-ai/dsh-commands\";\nimport type {} from \"@deepseek-ai/dsh-tools\";\nimport {\n\tIntegrationAuth,\n\ttype IntegrationAuthFace,\n\tmountIntegrationRoute,\n\tRuntimeState,\n} from \"dsh-integration-host\";\nimport {\n\tAuthorizationFlow,\n\tcredentialKeyOf,\n\tGrantStore,\n\tloggerOf,\n\tMcpBridge,\n} from \"dsh-mcp-oauth\";\nimport type { AtlassianStatus } from \"../shared/status\";\nimport { atlassianAuthSpec } from \"./auth\";\nimport {\n\tatlassianProfile,\n\ttype ConfigInput,\n\tPLUGIN_NAME,\n\tresolveConfig,\n} from \"./config\";\nimport { registerCorrelationSource } from \"./correlation\";\nimport { registerDigestSource } from \"./digest\";\nimport { PanelApi } from \"./panels/panel\";\nimport { extractor } from \"./presentation/extract\";\nimport type { SiteFacts } from \"./store/site\";\nimport { COMMAND_NAME, createCommandHandler } from \"./surface/command\";\nimport { createApiHandler, ROUTE_PREFIX } from \"./surface/http\";\nimport { presentTool } from \"./tools/present\";\n\nexport const name = PLUGIN_NAME;\n\n/** Services this plugin needs before it can activate. */\nexport const inject = [\"tools\", \"credentials\", \"commands\", \"authorization\"];\n\n/** The seam's method id for the browser-based flow. */\nconst OAUTH_METHOD = \"oauth\";\n\n/** How Atlassian's server sees this client; it gates payload detail on the name. */\nconst CLIENT_INFO = { name: \"dsh-atlassian\", version: \"0.3.0\" };\n\n/**\n * @param ctx - host context carrying tools, credentials, commands, and authorization.\n * @param configInput - `endpoint`, `redirectPort`, and `debug` from the bundle patch.\n */\nexport function apply(ctx: Context, configInput?: ConfigInput): void {\n\tconst config = resolveConfig(configInput);\n\tconst profile = atlassianProfile(config);\n\tconst state = new RuntimeState<SiteFacts>();\n\tconst logger = loggerOf(ctx);\n\n\tconst store = new GrantStore(ctx, credentialKeyOf(profile));\n\tconst flow = new AuthorizationFlow(profile, store);\n\tconst bridge = new McpBridge({\n\t\tprofile,\n\t\tstore,\n\t\tclientInfo: CLIENT_INFO,\n\t\t// The durable UI channel: the Web Client reads this back as `result.meta`.\n\t\tdecorate: (definition, tool) => ({\n\t\t\t...definition,\n\t\t\toutput: {\n\t\t\t\t...definition.output,\n\t\t\t\tpresentationMeta: (args, value) =>\n\t\t\t\t\textractor.extract({\n\t\t\t\t\t\ttool: tool.name,\n\t\t\t\t\t\targs,\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tsiteUrl: state.facts?.siteUrl,\n\t\t\t\t\t}),\n\t\t\t},\n\t\t}),\n\t});\n\tconst auth: IntegrationAuthFace<AtlassianStatus> = new IntegrationAuth<\n\t\tAtlassianStatus,\n\t\tSiteFacts\n\t>({ ctx, config, state, store, flow, bridge, spec: atlassianAuthSpec });\n\tconst panel = new PanelApi({ bridge, state });\n\n\t// The digest plugin, when it is installed, composes this plugin's Jira blocks\n\t// into its card; nothing is read until a digest actually asks.\n\tregisterDigestSource(ctx, { bridge, state });\n\n\t// The correlation plugin, when it is installed, asks this plugin about the\n\t// ticket a chain reaches; nothing is read until it asks.\n\tregisterCorrelationSource(ctx, { bridge, state });\n\n\t// Registering the flow tells the harness this credential can be authorized,\n\t// and routes each attempt's notices to whoever started it.\n\tconst disposeFlow = ctx.authorization.registerFlow({\n\t\tkey: store.credentialKey,\n\t\tlabel: \"Atlassian\",\n\t\tmethods: [{ id: OAUTH_METHOD, label: \"Sign in with Atlassian\" }],\n\t\trun: async (session) => {\n\t\t\tawait flow.run(ctx, session);\n\t\t},\n\t});\n\n\t// The command registration belongs to this plugin's Cordis fiber, so it is\n\t// withdrawn with the plugin; only the resources that outlive a plain\n\t// registration need an explicit disposer.\n\tctx.commands.register({\n\t\tname: COMMAND_NAME,\n\t\tdescription: \"Jira and Confluence: status | login | logout | sync\",\n\t\tinput: { hint: \"status | login | logout | sync\" },\n\t\thandler: createCommandHandler(auth),\n\t});\n\n\t// The presentation tool is registered beside the MCP tools it resolves\n\t// through: it shows what they return as a card under the agent's answer.\n\tctx.tools.register(presentTool.create({ ctx, bridge, state }));\n\n\t// The card's channel to the host: a named route is how a plugin serves the\n\t// page without a generated remote namespace.\n\tmountIntegrationRoute(ctx, {\n\t\tprefix: ROUTE_PREFIX,\n\t\thandler: createApiHandler({ auth, panel }),\n\t});\n\n\tctx.effect(\n\t\t() => () => {\n\t\t\tdisposeFlow();\n\t\t\t// Closing the MCP connection also withdraws every published Atlassian tool.\n\t\t\treturn bridge.drop();\n\t\t},\n\t\t\"atlassian: flow and MCP bridge\",\n\t);\n\n\t// A signed-in user gets tools without a restart. A fresh install stays dormant\n\t// until someone runs `/atlassian login` or uses the settings card.\n\tvoid auth.connect().then((ready) => {\n\t\tif (!ready) {\n\t\t\tlogger.info(\n\t\t\t\t\"[atlassian] not signed in yet \u2014 use the Atlassian settings card or /atlassian login\",\n\t\t\t);\n\t\t}\n\t});\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["name", "name", "name", "status", "Service", "name", "body", "name", "ISSUE_FIELDS", "name", "stringList", "lines"]
|
|
7
7
|
}
|