webdaemon 22.0.0 → 24.0.0
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/dist/index.js +17 -3
- package/dist/index.js.map +4 -4
- package/dist/types/index.d.ts +20 -18
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/js/BrowserApp.d.ts +1 -1
- package/dist/types/js/GenerativeChatBrowser.d.ts +192 -0
- package/dist/types/js/GenerativeChatBrowser.d.ts.map +1 -0
- package/dist/types/js/Token.d.ts +9 -9
- package/dist/types/js/Token.d.ts.map +1 -1
- package/dist/types/ts/GenerativeChatAgent.d.ts +45 -0
- package/dist/types/ts/GenerativeChatAgent.d.ts.map +1 -0
- package/dist/types/ts/McpServer.d.ts +1 -1
- package/dist/types/ts/McpServer.d.ts.map +1 -1
- package/dist/types/ts/McpToolAuthorization.d.ts +17 -2
- package/dist/types/ts/McpToolAuthorization.d.ts.map +1 -1
- package/dist/types/ts/YamlContent.d.ts +15 -1
- package/dist/types/ts/YamlContent.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../js/Digest.js", "../js/
|
|
4
|
-
"sourcesContent": ["/**\n * Generates a SHA-1 digest of the supplied string, and returns\n * it as a base64 string optionally truncated to the first 'length'\n * characters.\n *\n * @param {string} message to digest.\n * @return {Promise<string>} base64-encoded digest.\n */\nexport async function shortSafeDigest(message, length = 0) {\n const msgUint8 = new TextEncoder().encode(message)\n if (!crypto.subtle) {\n throw 'Requires secure origin (localhost or https:)'\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", msgUint8)\n const hashArray = new Uint8Array(hashBuffer)\n const byteString = String.fromCodePoint(...hashArray)\n const base64 = btoa(byteString)\n const base64Safe = base64\n .replaceAll('+','-')\n .replaceAll('/','_')\n .replaceAll('=','')\n return length ? base64Safe.substring(0, length) : base64Safe\n}\n\n/**\n * Generates a SHA-1 digest of the supplied string, and returns\n * it as a hex string optionally truncated to the first 'length'\n * characters.\n *\n * @param {string} message to digest.\n * @return {Promise<string>} hex-encoded digest.\n */\nexport async function shortHexDigest(message, length = 0) {\n const msgUint8 = new TextEncoder().encode(message)\n if (!crypto.subtle) {\n throw 'Requires secure origin (localhost or https:)'\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", msgUint8)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n const hashHex = hashArray\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n\n return length ? hashHex.substring(0, length) : hashHex\n}\n\n/**\n * Returns the standard app id for the supplied app URL.\n *\n * This is the shortSafeDigest of the URL truncated to 8 base64 chars,\n * i.e. distributed over a 48-bit space which seems reasonable for the\n * number of apps on a given daemon.\n *\n * @param {string | URL} url of the app whose id is to be computed.s\n * @return {Promise<string>} default id for the app, commonly used in prefixes.\n */\nexport async function getAppId(url) {\n if (typeof url === 'string') {\n url = new URL(url)\n }\n const appId = await shortSafeDigest(String(url), 8)\n return appId\n}\n", "const DEFAULT_ALGORITHM = {\n name: 'RSASSA-PKCS1-v1_5',\n modulusLength: 2048,\n publicExponent: new Uint8Array([0x01, 0x00, 0x01]),\n hash: 'SHA-256'\n}\n\nexport class KeyPair {\n #algorithm\n\n /** @type {CryptoKeyPair | undefined} */\n #keypair\n\n constructor(algorithm = DEFAULT_ALGORITHM) {\n this.#algorithm = algorithm\n if (!crypto.subtle) {\n throw 'Crypto.subtle requires secure environment'\n }\n }\n\n async generate() {\n this.#keypair = await crypto.subtle.generateKey(\n this.#algorithm,\n true,\n ['sign', 'verify']\n )\n }\n\n async publicJwk() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const publicKey = this.#keypair.publicKey\n const publicJwk = await crypto.subtle.exportKey('jwk', publicKey)\n return publicJwk\n }\n\n async privateJwk() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const privateKey = this.#keypair.privateKey\n const privateJwk = await crypto.subtle.exportKey('jwk', privateKey)\n return privateJwk\n }\n\n async publicPem() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const publicKey = this.#keypair.publicKey\n const spkiKey = await crypto.subtle.exportKey('spki', publicKey)\n const base64Key = btoa(String.fromCharCode(...new Uint8Array(spkiKey)))\n const base64KeyLines = base64Key.match(/.{1,64}/g)?.join('\\n') ?? ''\n const publicPem = `-----BEGIN PUBLIC KEY-----\\n${base64KeyLines}\\n-----END PUBLIC KEY-----`\n return publicPem\n }\n\n async privatePem() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const privateKey = this.#keypair.privateKey\n const pkcs8Key = await crypto.subtle.exportKey('pkcs8', privateKey)\n const base64Key = btoa(String.fromCharCode(...new Uint8Array(pkcs8Key)))\n const base64KeyLines = base64Key.match(/.{1,64}/g)?.join('\\n') ?? ''\n const privatePem = `-----BEGIN PRIVATE KEY-----\\n${base64KeyLines}\\n-----END PRIVATE KEY-----`\n return privatePem\n }\n}\n", "/**\n * Encapsulates the functionality associated with a bearer token normally\n * passed in a request `x-tabserver-token` header as a base64-encoded\n * JSON object.\n *\n * Note the type definition annotations which are helpful when using this\n * Javascript class in Typescript.\n *\n * Example token:\n *\n * {\n * \"iss\": \"http://foo.daemon/some/public.jwk\",\n * \"aud\": \"https://bar.daemon\",\n * \"sub\": \"https://foo.daemon\",\n * \"src\": \"https://some.com\",\n * \"scope\": {\n * \"http://localhost:9000/helloWorld1.html\": \"read write\",\n * \"party:control\": \"grant revoke\"\n * }\n * \"iat\": 1698576567000,\n * \"exp\": 1698576344000,\n * \"sig\": \"long base64 string\"\n * }\n *\n * Generally:\n * iss - the issuer URL references the public JWK used to verify the sig.\n * aud - the party handling the request, expressed as an origin.\n * sub - the counterparty making the request, expressed as an origin.\n * src - the source HTML file making the request, protocol://host/path only.\n * scope - a map of requested source -> scope pairs.\n * A scope is a space-separated list of capabilities.\n * iat - issued at time in absolute millis.\n * exp - expiry time, ditto.\n * sig - the base64 signature, verified by the JWK pointed to by iss.\n *\n * Normally the sub field must match the host portion of the iss URL. But\n * the possiblity remains open for an intermediary trusted by the party to\n * hold the public keys of counterparties.\n */\n\n/**\n * @typedef {Object.<string, string>} Scope\n */\n\n/**\n * @typedef TokenPayload\n * @property {string} iss // URL of public key of counterparty making request.\n * @property {string} aud // Party origin handling the request.\n * @property {string} sub // Counterparty origin making the request.\n * @property {string} src // Source HTML document context of the request excluding fragment and query.\n * @property {Scope} scope // Map of scope (space-separated capabilities) keyed by source URL.\n * @property {number} iat // Issued at time.\n * @property {number} exp // Expiry time.\n * @property {string} sig // Signature if signed.\n */\n\n/**\n * @typedef {Object} Signatory the object to be served on the iss URL.\n * @property {string} src which is 'party:control' if the shell, or source URL if a runner.\n * @property {JsonWebKey} jwk\n */\n\n/**\n * @typedef {Object} SignatoryCacheEntry\n * @property {Signatory} signatory\n * @property {CryptoKey} publicKey\n * @property {number} expiresAt\n */\n\n/**\n * @typedef TokenSet\n * @type {Object<string, Token>}\n */\n\n/** Must match algorithm in Keypair.js */\nconst ALGORITHM = {\n name: 'RSASSA-PKCS1-v1_5',\n hash: 'SHA-256'\n}\n\n/** Matches an origin with protocol and no path. */\nconst MATCH_ORIGIN = /https?:\\/\\/[^\\/]+$/\n\n/** Matches issuer paths for app runner public keys. */\nconst MATCH_APP_ISSUER_PATH = /^\\/issuer\\/[a-f0-9]{32}$/\n\n/**\n * Allow a 30s leeway to allow for clock difference when checking\n * token issued at time. We allow our clock to be up to 30s behind\n * the system that generated a token.\n */\nconst TOKEN_IAT_LEEWAY_MILLIS = 1000 * 30\n\nexport class Token {\n\n // Standard sources.\n static Source = {\n PARTY_CONTROL: 'party:control' // Party control subject, a pseudo-source with scope granted to counterparties.\n }\n\n // Standard counterparty pattern.\n static Counterparty = {\n PUBLIC: 'public' // Public counterparty, matching requests with no host specified in sub.\n }\n\n // Standard scope labels.\n static Capability = {\n OPEN: 'open', // Capability to open source files and start tab runners.\n CLOSE: 'close', // Capability to close source files and stop tab runners.\n GRANT: 'grant', // Capability to create relation records.\n REVOKE: 'revoke', // Capability to delete relation records.\n OFFER: 'offer', // Capability to create a party offer for a device to claim.\n RETRACT: 'retract', // Capability to delete a party offer before it is claimed.\n DELETE: 'delete', // Capability to delete devices.\n CATALOG: 'catalog', // Capability to get sources, devices, alerts etc.\n ALIAS: 'alias', // Capability to set the alias for a counterparty host.\n UNALIAS: 'unalias', // Capability to unset the alias for a counterparty host.\n GET_ITEM: 'getitem', // Capability to get a storage item.\n SET_ITEM: 'setitem', // Capability to set (and remove) a storage item.\n ALERT: 'alert', // Capability to post and delete alerts.\n LOG: 'log', // Capability to view logs.\n CHAT: 'chat', // Capability to use generative chat.\n READ_MEMORY: 'read_memory', // Capability to read memory.\n WRITE_MEMORY: 'write_memory', // Capability to write memory.\n READ: 'read', // Capability to read from drive.\n WRITE: 'write' // Capability to write to drive.\n }\n\n static DEFAULT_EXPIRY_MILLIS = 1000 * 60 * 60 * 24\n static TOKEN_HEADER = 'x-tabserver-token'\n static SCOPE_SEPARATOR = /[\\s,;\\|]+/ // Specification per String.split() function.\n static #SIGNATORY_CACHE_TTL_MILLIS = 1000 * 60\n static #SIGNATORY_CACHE_MAX_ENTRIES = 2048\n\n /** @type{Map<string, SignatoryCacheEntry>} */\n static #signatoryCache = new Map()\n\n /** @type{Map<string, Promise<SignatoryCacheEntry>>} */\n static #signatoryCacheInFlight = new Map()\n\n /** @type{TokenPayload | null} */\n #payload = null\n\n /** @type{string | null} */\n #signatureBase64 = null\n\n /** @type{URL} */\n #audUrl\n\n /** @type{URL} */\n #subUrl\n\n /** @type{URL} */\n #srcUrl\n\n /** @type {Signatory | undefined} */\n #signatory // Set upon successful verification of signature.\n\n\n /**\n * Constructor takes either a signed base64 token, or a payload object.\n *\n * If successful, the payload and optionally the signature fields are\n * populated.\n *\n * @param {object | string} source a payload, or a base64 token\n */\n constructor(source) {\n let payload = null\n\n if (typeof source == 'object') {\n payload = source\n }\n else if (typeof source == 'string') {\n try {\n payload = JSON.parse(atob(source))\n }\n catch (_e) {\n throw 'Invalid token format'\n }\n }\n else {\n throw `Cannot construct token from ${typeof source}`\n }\n\n // Separate out the signature from the payload.\n this.#signatureBase64 = payload.sig\n delete payload.sig\n this.#payload = payload\n\n const {\n iss,\n aud,\n sub,\n src,\n scope,\n iat,\n exp\n } = payload\n\n // Check payload is complete.\n if ((!iss || !aud || !sub || !src || !scope || !iat || !exp)) {\n throw 'Token must include iss, aud, sub, src, scope, iat and exp'\n }\n\n // Check aud, sub and src fields are origins (i.e. proto://host.name)\n if (\n !aud.match(MATCH_ORIGIN) ||\n !sub.match(MATCH_ORIGIN)\n ) {\n throw 'The aud and sub attributes must be origins'\n }\n\n this.#audUrl = new URL(aud)\n this.#subUrl = new URL(sub)\n this.#srcUrl = new URL(src)\n\n // The `src` attribute must not have query or fragment.\n if (this.#srcUrl.search || this.#srcUrl.hash) {\n throw 'The src attribute must have no query or fragment component.'\n }\n\n this.#payload = payload\n }\n\n /**\n * Generates the signature for the object using the private key provided by the\n * supplied callback and stores the result.\n *\n * @param {Promise<JsonWebKey>} privateJwkPromise the private key used for signing.\n * @return {Promise<string>} resolved with the base64 signature if signed successfully.\n */\n async signWith(privateJwkPromise) {\n if (this.#payload == null) {\n throw 'No payload to sign'\n }\n\n const privateJwk = await privateJwkPromise\n const privateKey = await crypto.subtle.importKey('jwk', privateJwk, ALGORITHM, true, ['sign'])\n const toSign = JSON.stringify(this.#payload)\n const messageBuffer = new TextEncoder().encode(toSign).buffer\n const bufferSource = new Uint8Array(messageBuffer)\n const signature = await crypto.subtle.sign(ALGORITHM, privateKey, bufferSource)\n const signatureBase64 = Token.bytesToBase64(new Uint8Array(signature))\n this.#signatureBase64 = signatureBase64\n return signatureBase64\n }\n\n /**\n * Retrieves the signatory using the `iss` field, checks the signature\n * and if valid sets the signatory property in this instance.\n *\n * @throws {string} if signatory cannot be retrieved or signature doesn't match.\n */\n async verifySignatory() {\n const issuer = String(this.getIssuer())\n const {\n signatory,\n publicKey\n } = await Token.#getCachedSignatory(issuer)\n this.#signatory = signatory\n this.#validateSignatory()\n await this.#checkSignatureWithPublicKey(publicKey)\n }\n\n /**\n * Fetches the signatory from the `iss` URL, setting the instance\n * property.\n *\n * The signatory comprises the `src` representing the app that has\n * signed the token and the public `jwk`.\n *\n * @throws {string} error if fetch fails or returns an error response.\n */\n async fetchSignatory() {\n const issuer = String(this.getIssuer())\n this.#signatory = await Token.#fetchSignatoryForIssuer(issuer)\n this.#validateSignatory()\n }\n\n /**\n * Checks the signature using the public key provided by the supplied callback,\n * and stores the result.\n *\n * @param {Promise<JsonWebKey>} publicJwkPromise the public key used to verify the signature.\n * @throws {string} exception if signature cannot be verified or the public key is null or error.\n */\n async checkSignature(publicJwkPromise) {\n if (!this.#payload || !this.#signatureBase64) {\n throw 'checkSignature: no payload or signatureBase64'\n }\n\n const publicJwk = await publicJwkPromise\n const publicKey = await Token.#importPublicKey(publicJwk, String(this.getIssuer()))\n await this.#checkSignatureWithPublicKey(publicKey)\n }\n\n /**\n * Checks the signature using an already imported public key.\n *\n * @param {CryptoKey} publicKey imported public key used to verify the signature.\n * @throws {string} exception if signature cannot be verified.\n */\n async #checkSignatureWithPublicKey(publicKey) {\n if (!this.#payload || !this.#signatureBase64) {\n throw 'checkSignature: no payload or signatureBase64'\n }\n const toCheck = JSON.stringify(this.#payload)\n const messageBuffer = new TextEncoder().encode(toCheck)\n const sigBuffer = Token.base64ToBytes(this.#signatureBase64)\n const isVerified = await crypto.subtle.verify(ALGORITHM, publicKey, sigBuffer, messageBuffer)\n if (!isVerified) {\n throw `Signature rejected by issuer ${this.getIssuer()}`\n }\n }\n\n /**\n * Validates that the issuer and signatory source are permitted to sign\n * tokens asserting this token source.\n *\n * @throws {string} exception if the signatory source does not match the token source.\n */\n #validateSignatory() {\n if (!this.#signatory) {\n throw 'validateSignatory: no signatory'\n }\n\n const signatorySrc = this.#signatory.src\n const tokenSrc = this.getSrc()\n const issuer = this.getIssuer()\n\n if (issuer.origin !== this.getSub()) {\n throw `Token issuer origin '${issuer.origin}' should be token sub '${this.getSub()}'`\n }\n\n if (tokenSrc === Token.Source.PARTY_CONTROL) {\n if (signatorySrc !== Token.Source.PARTY_CONTROL) {\n throw `Token src '${tokenSrc}' cannot be signed by signatory src '${signatorySrc}'`\n }\n } else if (\n this.#srcUrl.protocol === 'http:' || this.#srcUrl.protocol === 'https:'\n ) {\n if (\n signatorySrc !== tokenSrc &&\n signatorySrc !== Token.Source.PARTY_CONTROL\n ) {\n throw `Token src '${tokenSrc}' cannot be signed by signatory src '${signatorySrc}'`\n }\n } else {\n throw `Unsupported token src '${tokenSrc}'`\n }\n\n if (signatorySrc === Token.Source.PARTY_CONTROL) {\n if (!issuer.pathname.startsWith('/device/')) {\n throw `Party control signatory issuer path '${issuer.pathname}' must start with /device/`\n }\n return\n }\n\n const signatoryUrl = new URL(signatorySrc)\n if (\n signatoryUrl.protocol !== 'http:' && signatoryUrl.protocol !== 'https:'\n ) {\n throw `Invalid signatory src '${signatorySrc}'`\n }\n if (!MATCH_APP_ISSUER_PATH.test(issuer.pathname)) {\n throw `App signatory issuer path '${issuer.pathname}' must match /issuer/<appId>`\n }\n }\n\n /**\n * Returns a signatory cache entry from cache or network.\n *\n * @param {string} issuer issuer URL.\n * @returns {Promise<SignatoryCacheEntry>}\n */\n static async #getCachedSignatory(issuer) {\n const cached = Token.#getFreshCachedSignatory(issuer)\n if (cached) {\n return cached\n }\n\n const inFlight = Token.#signatoryCacheInFlight.get(issuer)\n if (inFlight) {\n return await inFlight\n }\n\n const promise = (async () => {\n const signatory = await Token.#fetchSignatoryForIssuer(issuer)\n const publicKey = await Token.#importPublicKey(signatory.jwk, issuer)\n const entry = {\n signatory,\n publicKey,\n expiresAt: Date.now() + Token.#SIGNATORY_CACHE_TTL_MILLIS\n }\n Token.#putCachedSignatory(issuer, entry)\n return entry\n })()\n\n Token.#signatoryCacheInFlight.set(issuer, promise)\n try {\n return await promise\n }\n finally {\n Token.#signatoryCacheInFlight.delete(issuer)\n }\n }\n\n /**\n * Returns a currently valid cached entry if present.\n *\n * @param {string} issuer issuer URL.\n * @returns {SignatoryCacheEntry | null}\n */\n static #getFreshCachedSignatory(issuer) {\n const entry = Token.#signatoryCache.get(issuer)\n if (!entry) {\n return null\n }\n\n if (entry.expiresAt <= Date.now()) {\n Token.#signatoryCache.delete(issuer)\n return null\n }\n\n // Refresh recency.\n Token.#signatoryCache.delete(issuer)\n Token.#signatoryCache.set(issuer, entry)\n return entry\n }\n\n /**\n * Adds/replaces a cache entry and enforces bounded size.\n *\n * @param {string} issuer issuer URL.\n * @param {SignatoryCacheEntry} entry signatory cache entry.\n */\n static #putCachedSignatory(issuer, entry) {\n if (Token.#signatoryCache.has(issuer)) {\n Token.#signatoryCache.delete(issuer)\n }\n Token.#signatoryCache.set(issuer, entry)\n\n while (Token.#signatoryCache.size > Token.#SIGNATORY_CACHE_MAX_ENTRIES) {\n const oldestKey = Token.#signatoryCache.keys().next().value\n if (!oldestKey) {\n break\n }\n console.log('Evicting token signatory cache entry due max size', {\n issuer,\n entryExpiresAt: entry.expiresAt,\n cacheMaxEntries: Token.#SIGNATORY_CACHE_MAX_ENTRIES\n })\n Token.#signatoryCache.delete(oldestKey)\n }\n }\n\n /**\n * Fetches signatory details for the supplied issuer.\n *\n * @param {string} issuer issuer URL.\n * @returns {Promise<Signatory>}\n * @throws {string} if issuer cannot be read.\n */\n static async #fetchSignatoryForIssuer(issuer) {\n try {\n const response = await fetch(issuer, {\n headers: {\n 'content-type': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n const signatory = json.ok\n if (\n !signatory ||\n typeof signatory !== 'object' ||\n !signatory.jwk ||\n typeof signatory.jwk !== 'object'\n ) {\n throw 'Invalid signatory response'\n }\n if (\n typeof signatory.src !== 'string'\n ) {\n throw 'Invalid signatory src'\n }\n return signatory\n }\n catch (e) {\n throw `Failed to read signatory at ${issuer}: ${e}`\n }\n }\n\n /**\n * Imports and validates a JWK for signature verification.\n *\n * @param {JsonWebKey} publicJwk public key as JWK.\n * @param {string} issuer issuer URL.\n * @returns {Promise<CryptoKey>}\n * @throws {string} if key is missing or invalid.\n */\n static async #importPublicKey(publicJwk, issuer) {\n if (!publicJwk || typeof publicJwk !== 'object' || 'error' in publicJwk) {\n throw `Missing or invalid public JWK at issuer ${issuer}`\n }\n try {\n return await crypto.subtle.importKey('jwk', publicJwk, ALGORITHM, true, ['verify'])\n }\n catch (_e) {\n throw `Missing or invalid public JWK at issuer ${issuer}`\n }\n }\n\n /**\n * Returns the payload with the `sig` property added.\n *\n * @return {TokenPayload} the payload with `sig` property.\n */\n getSignedPayload() {\n if (!this.#signatureBase64 || !this.#payload) {\n throw 'getSignedPayload: no signatureBase64 or payload'\n }\n\n return {\n ...this.#payload,\n sig: this.#signatureBase64\n }\n }\n\n /**\n * Returns the payload whether signed or not.\n *\n * @return {TokenPayload} the payload object without signature.\n */\n getPayload() {\n if (!this.#payload) {\n throw 'getPayload: no payload'\n }\n return this.#payload\n }\n\n /**\n * Returns the `aud` field as a string.\n *\n * @return {string} the `aud` field value.\n */\n getAud() {\n return this.#audUrl.origin\n }\n\n\n /**\n * Returns the `aud` host which is the party name handling the request.\n *\n * @return {string} the party name.\n */\n getParty() {\n return this.#audUrl.host\n }\n\n /**\n * Returns the `sub` field as a string.\n *\n * @return {string} the `sub` field value.\n */\n getSub() {\n return this.#subUrl.origin\n }\n\n /**\n * Returns the `src` field as a string.\n *\n * @return {string} the `src` field value.\n */\n getSrc() {\n return String(this.#srcUrl)\n }\n\n /**\n * Returns the `sub` host, which is the counterparty name making the\n * request.\n *\n * @return {string} the counterparty name.\n */\n getCounterparty() {\n return this.#subUrl.host\n }\n\n /**\n * Returns the source of the signatory who signed on behalf of the\n * counterparty. This is only available once the token is\n * verified, as it is provided by the `iss` endpoint.\n *\n * @returns {string} source of the verified signatory.\n */\n getSignatorySrc() {\n if (!this.#signatory) {\n throw 'getSignatorySrc: no signatory'\n }\n\n return this.#signatory.src\n }\n\n /**\n * Returns the `src` URL.\n *\n * @return {URL} the source URL.\n */\n getSourceUrl() {\n return this.#srcUrl\n }\n\n /**\n * Returns the `issuer` field as a URL. The counterparty is inferred from\n * the host name which generally matches the sub field.\n *\n * @return {URL} the issuer URL.\n */\n getIssuer() {\n return new URL(this.getPayload().iss)\n }\n\n /**\n * Returns the list of zero or more sources for which scope is requested.\n *\n * @return {string[]} a list of source URL strings.\n */\n getSources() {\n if (!this.#payload) {\n throw 'getSources: no payload'\n }\n\n const sources = []\n for (const source in this.#payload.scope) {\n sources.push(source)\n }\n return sources\n }\n\n /**\n * Returns the scope for the source as an array of capability tokens.\n *\n * These can be separated by any mix of space, comma, semicolon\n * or vertical bar.\n *\n * @param {string} source whose capabilities are returned.\n * @return {string[]} zero or more scope tokens.\n */\n getCapabilities(source) {\n if (!this.#payload) {\n throw 'getCapabilities: no payload'\n }\n\n const scope = this.#payload.scope\n if (! (source in scope)) {\n throw `Source '${source}' not in scope`\n }\n\n return scope[source].split(Token.SCOPE_SEPARATOR)\n }\n\n /**\n * Returns true if the supplied capability is present in the token\n * scope under the specified source, otherwise false.\n *\n * @param {string} source the source under which the capability is expected.\n * @param {string} capability the capability being tested.\n * @return {boolean} true if capability is present in the scope, otherwise false.\n */\n hasCapability(source, capability) {\n return this.getCapabilities(source).includes(capability)\n }\n\n /**\n * Checks the token is within the period defined by the `iat` and `exp`\n * date fields.\n *\n * @throws {string} exception if not within valid period.\n */\n checkPeriod() {\n if (!this.#payload) {\n throw 'checkPeriod: no payload'\n }\n\n const {\n iat,\n exp\n } = this.#payload\n\n const now = Date.now()\n if (now < iat - TOKEN_IAT_LEEWAY_MILLIS) {\n throw 'Token is not yet valid'\n }\n\n if (now > exp) {\n throw 'Token has expired'\n }\n }\n\n /**\n * Returns the signed object including the `sig` property as a base64 string.\n *\n * @return {string} the signed object as a base64 string.\n */\n asSignedBase64() {\n return btoa(JSON.stringify(this.getSignedPayload()))\n }\n\n /**\n * Returns a base64 JSON string corresponding to the provided byte array.\n *\n * @param {Uint8Array} bytes to be encoded as a base64 string.\n * @returns {string} the encoded bytes.\n */\n static bytesToBase64(bytes) {\n const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join('');\n return btoa(binString);\n }\n\n /**\n * Returns the byte array decoded from the base64 string.\n *\n * @param {string} base64\n * @returns {Uint8Array} the decoded bytes.\n */\n static base64ToBytes(base64) {\n const binString = atob(base64);\n return Uint8Array.from(binString, (m) => m.codePointAt(0) ?? 0);\n }\n\n /**\n * Returns a new token using the x-tabserver-token header on the\n * request.\n *\n * @param {Request} request the request.\n * @return {Token | null} the token, or null if no header is present.\n * @throws {string} exception if token cannot be constructed.\n */\n static from(request) {\n const tokenBase64 = request.headers.get(Token.TOKEN_HEADER)\n if (tokenBase64) {\n return new Token(tokenBase64)\n }\n\n return null\n }\n\n /**\n * Converts an object whose string keys map to token objects\n * into URL-friendly search params suitable for use in a\n * query string or fragment.\n *\n * The string keys are normally the audience hostname, or the\n * special key 'party' which holds the identity token whose\n * `aud` and `sub` are the same.\n *\n * @param {TokenSet} tokenSet the set of tokens keyed by string.\n * @returns {string}\n */\n static toSearchString(tokenSet) {\n const searchParams = new URLSearchParams()\n for (const key in tokenSet) {\n const token = tokenSet[key]\n const tokenBase64 = token.asSignedBase64()\n searchParams.append(key, tokenBase64)\n }\n return searchParams.toString()\n }\n}\n", "/**\n * @import { Token } from './Token.js'\n * @import { Ok, Error } from '../ts/Responses.ts'\n * @import { JSONValue } from '../ts/Assertions.ts'\n */\n\n/**\n * Storage keys are 1 or more slash-separated components\n * using the characters a-z, A-Z, 0-9, hyphen, colon and period.\n *\n * Examples:\n * - acmeSetting\n * - acme.com/applicant/22fc01-b0d3084870b-fcae49ac2-892668\n * - address/line1\n * - url/acme.com\n *\n * The key needs to work as part of a REST/HTTP pathname.\n */\nconst KEY_PATTERN = /^[\\w\\-.:]+(?:\\/[\\w\\-.:]+)*$/\n\n/**\n * As above, but % and _ are both allowed as a wildcards.\n */\nconst KEYLIKE_PATTERN = /^[\\w\\-.:%_]+(?:\\/[\\w\\-.:%_]+)*$/\n\n/**\n * An instance of this class is normally obtained from BrowserApp, which constructs\n * the instance using the party token.\n */\nexport class Storage {\n #token // Party token which must include getitem and setitem capabilities on party:control.\n\n /**\n * @param {Token} token to use for storage requests.\n */\n constructor(token) {\n this.#token = token\n }\n\n /**\n * Gets the item whose key is provided. Keys are in the form\n * 'some[/key]*' without leading or trailing '/'.\n *\n * @param {string} key used to retrieve the item.\n * @return {Promise<Ok<JSONValue> | Error>} ok or error result.\n */\n getItem(key) {\n return getItem(this.#token, key)\n }\n\n /**\n * Gets the list (possibly empty) of items matching the SQL-like\n * argument.\n *\n * @param {string} keylike a key matcher such as 'myApp/%'.\n * @return {Promise<Ok<JSONValue[]>>} a list of values.\n */\n getItemsLike(keylike) {\n return getItemsLike(this.#token, keylike)\n }\n\n /**\n * Stores an item with the supplied key and value.\n *\n * @param {string} key the key to use.\n * @param {JSONValue} value to store.\n * @return {Promise<Ok<true> | Error>} ok if successful, error otherwise.\n */\n setItem(key, value) {\n return setItem(this.#token, key, value)\n }\n\n /**\n * Removes the item with the specified key, if any.\n *\n * @param {string} key the key to remove.\n * @return {Promise<Ok<true> | Error>} ok if successful, error otherwise.\n */\n removeItem(key) {\n return removeItem(this.#token, key)\n }\n}\n\n/**\n * Throws an exception if the key being used is not\n * in a valid format.\n *\n * @param {string} key\n */\nfunction assertValid(key) {\n if (key.match(KEY_PATTERN) == null) {\n throw `DaemonStorage: Invalid key: '${key}`\n }\n}\n\n/**\n * Throws an exception if the key like pattern being used\n * is not in a valid format.\n *\n * @param {string} keylike\n */\nfunction assertValidLike(keylike) {\n if (keylike.match(KEYLIKE_PATTERN) == null) {\n throw `DaemonStorage: Invalid like key: ${keylike}`\n }\n}\n\n/**\n * Returns the value as a JSON string\n *\n * @param {JSONValue} value\n * @return {string} JSON string.\n * @throws {string} exception if the value is not JSON.\n */\nfunction serialise(value) {\n try {\n return JSON.stringify(value)\n }\n catch (_e) {\n throw `DaemonStorage: Invalid value`\n }\n}\n\n/**\n * Sets an item in daemon storage.\n *\n * The value must be an object or primitive\n * which is serialised before saving.\n *\n * @param {Token} token to use.\n * @param {string} key the item key.\n * @param {any} value the serialisable value for the item.\n * @return {Promise<Ok<true> | Error>} ok or error object.\n */\nexport async function setItem(token, key, value) {\n assertValid(key)\n const body = serialise(value)\n const url = `${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'PUT',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64(),\n 'Content-Type': 'application/json'\n },\n body\n })\n return response.json()\n}\n\n/**\n * Gets an item from daemon storage.\n *\n * Returns an 'ok' object with the value if present, otherwise\n * returns an 'error' object.\n *\n * @param {Token} token to use.\n * @param {string} key the item key to retrieve.\n * @return {Promise<Ok<JSONValue> | Error>} ok object with value, or error.\n */\nexport async function getItem(token, key) {\n assertValid(key)\n const url = `${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return response.json()\n}\n\n/**\n * Gets zero or more items from daemon storage, whose\n * keys match the keyLike pattern. This is normally\n * something like 'my/prefix/%' which gets all values\n * with that prefix.\n *\n * You can use both '%' and '_' as wildcards for string\n * of any length and single character respectively.\n *\n * @param {Token} token to use.\n * @param {string} keylike the item keylike pattern to match.\n * @return {Promise<Ok<JSONValue[]>>} ok object with list, or error.\n */\nexport async function getItemsLike(token, keylike) {\n assertValidLike(keylike)\n const url = new URL(`${token.getAud()}/storage`)\n url.searchParams.append('like', keylike)\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return await response.json()\n}\n\n/**\n * Removes an item from daemon storage.\n *\n * @param {Token} token to use.\n * @param {string} key the item key to remove.\n * @return {Promise<Ok<true> | Error>} ok or error object.\n */\nexport async function removeItem(token, key) {\n assertValid(key)\n const url =`${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'DELETE',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return response.json()\n}\n", "const Char = {\n Ash: '\u00E6',\n Hash: '#'\n}\n\nconst Encoded = {\n Ash: encodeURIComponent('\u00E6')\n}\n\nexport class ParamExtractor {\n params = new URLSearchParams()\n\n /**\n * @param {string} hash the hash string, with or without leading '#'.\n * @returns {string} the cleaned hash string.\n */\n extractHash(hash) {\n hash = hash.startsWith(Char.Hash) ? hash.substring(1) : hash\n const i = hash.indexOf(Encoded.Ash)\n if (i === -1) {\n return hash\n }\n\n const params = new URLSearchParams(hash.slice(i))\n this.extract(params)\n return hash.slice(0, i)\n }\n\n /**\n * @param {string} query the query string.\n * @returns {string} the cleaned query string.\n */\n extractQuery(query) {\n const params = new URLSearchParams(query)\n this.extract(params)\n return params.toString()\n }\n\n /**\n * Extracts \u00E6-prefixed params into this instance,\n * then deletes them from the supplied searchparams\n * object.\n *\n * @param {URLSearchParams} params\n */\n extract(params) {\n const keys = Array.from(params.keys())\n for (const key of keys) {\n if (key.startsWith(Char.Ash)) {\n this.params.set(key.substring(1), params.get(key) ?? '')\n params.delete(key)\n }\n }\n }\n\n getParams() {\n return this.params\n }\n}\n", "import { Token } from './Token.js'\nimport { Storage } from './Storage.js'\nimport { getAppId } from './Digest.js'\nimport { ParamExtractor } from './ParamExtractor.js'\n\n/**\n * @module\n * Provides convenience support for WebDaemon apps delivered\n * through browsers.\n *\n * Use the singleton pattern, providing app name on first call\n * to getInstance.\n */\n\nconst PARTY_PARAM = 'party'\nconst PREFIX_PARAM = 'prefix'\nconst LAST_KNOWN_DAEMON = 'lastKnownDaemon' // In app's localStorage.\n\nexport class BrowserApp {\n\n /** @type {BrowserApp | null} */\n static #instance\n\n #appName\n #partyKey\n #tokensKey\n #appUrlKey\n\n /**\n * Gets the singleton instance of BrowserApp. On invocations\n * after the first, the app name must be omitted or match\n * the first-used app name.\n *\n * @param {string=} appName\n * @return {Promise<BrowserApp>} instance of browser app.\n */\n static async getInstance(appName = undefined) {\n if (\n !appName &&\n !BrowserApp.#instance\n ) {\n throw `Must provide app name`\n }\n\n if (\n appName &&\n BrowserApp.#instance &&\n appName !== BrowserApp.#instance.#appName\n ) {\n throw `Cannot change app name from ${BrowserApp.#instance.#appName} to ${appName}`\n }\n\n if (appName && !BrowserApp.#instance) {\n BrowserApp.#instance = new BrowserApp(appName)\n await BrowserApp.#instance.init()\n }\n\n if (!BrowserApp.#instance) {\n throw `No instance of BrowserApp`\n }\n\n return BrowserApp.#instance\n }\n\n /**\n * Constructs a BrowserApp instance with the app name. This should be\n * unique per origin. Spaces are _NOT_ allowed in this name.\n *\n * @param {string} appName a unique name for this app per origin.\n */\n constructor(appName) {\n this.#appName = appName\n this.#partyKey = `${appName}Party`\n this.#tokensKey = `${appName}Tokens`\n this.#appUrlKey = `${appName}AppUrl`\n }\n\n /**\n * Initialises the instance with the party name and tokens passed\n * in the #fragment and ?query string search parameters.\n *\n * - party is passed in the special parameter 'party'.\n * - tokens are passed in the remaining parameters keyed by host name.\n *\n * The #fragment starting at the first '\u00E6' (if present) is used and\n * any params whose name starts with the special character '\u00E6' are extracted.\n *\n * Any fragment prior to this is retained in the browser location.\n *\n * Any query params whose name starts with the special character '\u00E6' are\n * extracted.\n *\n * The window location is replaced with the 'clean' version that\n * no longer includes the tokens.\n *\n * @return {Promise<BrowserApp>} instance promise.\n * @throws {string} error if party token `src` does not match our window location.\n */\n async init() {\n const ourUrl = new URL(globalThis.location.href)\n\n const extractor = new ParamExtractor()\n const newHash = extractor.extractHash(ourUrl.hash)\n const newSearch = extractor.extractQuery(ourUrl.search)\n const daemonParams = extractor.getParams()\n\n if (daemonParams.has(PARTY_PARAM)) {\n sessionStorage[this.#tokensKey] = daemonParams.toString()\n const identityToken = this.getToken()\n await identityToken.verifySignatory()\n sessionStorage[this.#partyKey] = identityToken.getParty()\n localStorage.setItem(LAST_KNOWN_DAEMON, identityToken.getParty())\n\n const srcUrl = identityToken.getSourceUrl()\n if (srcUrl.origin !== ourUrl.origin || srcUrl.pathname !== ourUrl.pathname) {\n throw `Party token is for different app: ${srcUrl}`\n }\n }\n\n ourUrl.hash = newHash\n ourUrl.search = newSearch\n if (\n daemonParams.has(PARTY_PARAM) ||\n sessionStorage[this.#appUrlKey] === undefined\n ) {\n sessionStorage[this.#appUrlKey] = ourUrl.toString()\n }\n globalThis.history.replaceState(null, '', ourUrl.toString())\n return this\n }\n\n /**\n * Returns true if this app is an orphan, i.e. has not been launched from\n * a D\u00E6mon shell.\n *\n * Otherwise returns false.\n *\n * @return {boolean} true if an orphan without D\u00E6mon, otherwise false.\n */\n isOrphan() {\n return (\n sessionStorage[this.#partyKey] === undefined ||\n sessionStorage[this.#tokensKey] === undefined\n )\n }\n\n /**\n * Returns the app name. This is normally used to disambiguate properties\n * of this app from others, such as those sharing a web origin and therefore\n * sharing localStorage or sessionStorage.\n *\n * Note that this is a client-side name not known to the tabserver.\n *\n * @return {string} the app name.\n */\n getAppName() {\n return this.#appName\n }\n\n /**\n * Returns the app url, obtained from window.location at time of app init,\n * or the empty string if not initialised.\n *\n * @return {string} the app url, or the empty string.\n */\n getAppUrl() {\n return sessionStorage[this.#appUrlKey]\n }\n\n /**\n * Returns the party hostname, being the D\u00E6mon that launched this app.\n *\n * @return {string} the party (D\u00E6mon) hostname.\n */\n getParty() {\n return sessionStorage[this.#partyKey]\n }\n\n /**\n * Returns the party origin, being the protocol (http: or https:)\n * and the party hostname.\n *\n * @return {string} the party origin.\n */\n getPartyOrigin() {\n return `${globalThis.location.protocol}//${this.getParty()}`\n }\n\n /**\n * Returns the default prefix associated with the app url if specified,\n * or this app if not. The default prefix is simply the app ID being a\n * short digest of the app URL, excluding any # fragment.\n *\n * @param {string | URL | null} appUrl for which the default prefix is required (optional).\n * @return {Promise<string>} the default prefix for the app.\n */\n getDefaultPrefix(appUrl = null) {\n if (!appUrl) {\n appUrl = this.getAppUrl()\n }\n\n const cleanUrl = new URL(appUrl)\n cleanUrl.hash = ''\n return getAppId(cleanUrl)\n }\n\n /**\n * Returns the likely prefix for this app.\n *\n * This is the value of the prefix parameter if present,\n * or the default prefix for the app if not.\n *\n * @return {Promise<string>} prefix obtained from prefix= param, or computed from the app URL.\n */\n getPrefix() {\n if (this.hasParam(PREFIX_PARAM)) {\n return Promise.resolve(this.getParam(PREFIX_PARAM) ?? '')\n }\n return this.getDefaultPrefix()\n }\n\n /**\n * Returns the likely url for the (optional) named tab on this app's agent.\n *\n * This will be wrong if the YAML file has a `prefix=` specified which\n * is not set to the value of a provided `prefix=` parameter.\n *\n * @param {string?} tab name, e.g. 'v1'.\n * @returns {Promise<string>} the url of this tab on this app.\n */\n async getAgentUrl(tab) {\n const origin = this.getPartyOrigin()\n const prefix = await this.getPrefix()\n const agentUrl = `${origin}/tab/${prefix}`\n return tab ? `${agentUrl}/${tab}` : agentUrl\n }\n\n /**\n * @typedef {object} AgentEndpointOptions\n * @property {string=} party party hostname. Defaults to the launching party.\n * @property {string | URL=} appUrl app URL used to derive the protocol and default prefix.\n * @property {string=} prefix agent prefix. Defaults to the app's default prefix.\n * @property {string=} tab optional tab name appended to the endpoint.\n */\n\n /**\n * Returns the agent endpoint associated with the party, app, prefix and tab\n * where all options are, well, optional.\n *\n * @param {AgentEndpointOptions} options\n * @returns {Promise<string>} the agent endpoint.\n */\n async getAgentEndpoint(options) {\n const {\n party = this.getParty(),\n appUrl = this.getAppUrl(),\n prefix = await this.getDefaultPrefix(appUrl),\n tab\n } = options\n\n const protocol = new URL(appUrl).protocol\n const appEndpoint = `${protocol}//${party}/tab/${prefix}`\n return tab ? `${appEndpoint}/${tab}` : appEndpoint\n }\n\n /**\n * Returns the base64-encoded token for the supplied party. If no\n * party is specified, returns the token for the launching party.\n *\n * @param {string} party the party, or the launching party if not specified.\n * @return {string} the base64-encoded token for the party.\n */\n getTokenBase64(party = PARTY_PARAM) {\n const tokens = sessionStorage[this.#tokensKey]\n const searchParams = new URLSearchParams(tokens)\n const tokenBase64 = searchParams.get(party)\n if (!tokenBase64) {\n throw `No token for ${party}, check 'audience' in YML`\n }\n return tokenBase64\n }\n\n\n /**\n * Gets the token for a party either as a base64-encoded string, or as a\n * Token object depending on the asBase64 argument.\n *\n * If the party is not passed or is null, the default is the launching party.\n * If the asBase64 is not passed, the default is true.\n *\n * @param {string=} party the audience for the token. Defaults to the identity token 'party'.\n * @return {Token} the pre-generated token for the given party.\n */\n getToken(party = PARTY_PARAM) {\n const tokenBase64 = this.getTokenBase64(party)\n return new Token(tokenBase64)\n }\n\n /**\n * Gets the value of the named parameter, or null if no value is supplied.\n *\n * The parameter name is case insensitive.\n *\n * The parameter is specified as:\n * - an attribute on the webdaemon meta tag, or\n * - a search parameter in the query string of the URL\n *\n * where the query string parameter takes priority if present.\n *\n * @param {string} name\n * @return {string | null} parameter value or null if not present.\n */\n getParam(name) {\n\n // Return a matching search parameter if present.\n const searchParams = new URL(globalThis.location.href).searchParams\n for (const [paramName, value] of searchParams) {\n if (name.toLowerCase() == paramName.toLowerCase()) {\n return value\n }\n }\n\n // Return meta element attribute if present, or null.\n const metaElement = document.querySelector('link[rel=webdaemon]')\n return metaElement?.getAttribute(name) ?? null\n }\n\n /**\n * Returns true if the named parameter is present, even if with empty\n * or null value.\n *\n * @param {string} name\n * @return {boolean} true if the parameter exists, otherwise false.\n */\n hasParam(name) {\n const searchParams = new URL(globalThis.location.href).searchParams\n if (searchParams.has(name)) {\n return true\n }\n\n const metaElement = document.querySelector('link[rel=webdaemon]')\n return metaElement?.hasAttribute(name) ?? false\n }\n\n /**\n * Returns the currently raised launch alert for this app,\n * or null if none.\n *\n * @typedef {import('./Alert.js').AlertEntry} AlertEntry\n *\n * @return { Promise<AlertEntry | null> } raised launch alert, or null if none.\n * @throws {string} exception if alert catalog cannot be retrieved.\n */\n async getLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/catalog`)\n url.searchParams.append('alert', '')\n const response = await fetch(url, {\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Accept': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const alert = json.ok.alert.find(\n /** @param {AlertEntry} alert */\n alert => (\n alert.type == 'LAUNCH' &&\n alert.sourceUrl == new URL(this.getAppUrl())\n )\n )\n return alert || null\n }\n\n /**\n * Resolves the currently raised launch alert for this app, if any.\n *\n * @throws {string} exception if alert cannot be resolved.\n */\n async resolveLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/resolve`)\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n type: 'LAUNCH',\n source: this.getAppUrl()\n })\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n }\n\n /**\n * Resolves the currently raised launch alert for this app, if any.\n *\n * @throws {string} exception if alert cannot be resolved.\n */\n async rejectLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/reject`)\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n type: 'LAUNCH',\n source: this.getAppUrl()\n })\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n }\n\n /**\n * Gets a Storage instance using the party token.\n *\n * @returns {Storage} instance.\n */\n getStorage() {\n return new Storage(this.getToken())\n }\n}\n", "import { BrowserApp } from './BrowserApp.js'\n\n/**\n * @import { JSONValue } from '../ts/Assertions.ts'\n */\n\n/**\n * Returns the currently raised launch alert for this app,\n * or null if none.\n *\n * @typedef {Object} AlertEntry\n * @property {URL} sourceUrl - The URL of the alert source.\n * @property {string} sourceTitle - The title of the alert source.\n * @property {string} type - The alert type.\n * @property {JSONValue} value - The value associated with the alert.\n * @property {Date} created - When the alert was created.\n * @property {Date|null} expiry - When the alert expires or null if it doesn't expire.\n *\n * @return { Promise<AlertEntry | null> } raised launch alert, or null if none.\n * @throws {string} exception if alert catalog cannot be retrieved.\n */\nexport async function getLaunchAlert() {\n const app = await BrowserApp.getInstance()\n const origin = app.getPartyOrigin()\n const url = new URL(`${origin}/catalog`)\n url.searchParams.append('alert', '')\n const response = await fetch(url, {\n headers: {\n 'X-Tabserver-Token': app.getTokenBase64(),\n 'Accept': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const alert = json.ok.alert.find(\n /** @param {AlertEntry} alert */\n alert => (\n alert.type == 'LAUNCH' &&\n alert.sourceUrl == new URL(app.getAppUrl())\n )\n )\n return alert || null\n}\n", "import { Token } from './Token.js'\n\n/**\n * This helper provides the parent capability for a website to\n * check and activate a daemon, and to create a device offer for\n * subsequent claim by consumer devices or browsers.\n *\n * The helper can operate either browser- or server-side.\n *\n * Usage\n * =====\n *\n * // Create the helper for a child daemon.\n * const helper = new ParentHelper()\n *\n * // Initialise with the child daemon being parented (aud), the\n * // source (src), the private key used to sign the token\n * // and the signatory url (iss).\n * await initWithKey(child, source, privateJwk, iss)\n *\n * // Succeeds if the child daemon is activated.\n * await helper.checkActivate()\n *\n * // Returns the claim code and check state of a new device offer.\n * await helper.fetchOffer()\n *\n * @typedef {Object} JsonWebKey\n * @typedef {string} TokenBase64\n *\n * @typedef {Object} OfferOptions\n * @property {'TRANSIENT' | 'PERMANENT'} [type] the type of the device.\n * @property {number} [ttl] the time-to-live of device (transient only), in seconds.\n * @property {number} [expiry] the number of seconds before the offer expires.\n * @property {Object} [payload] the optional offer payload.\n */\n\nexport class ParentHelper {\n /** @type {JsonWebKey | undefined} */\n #privateJwk // Signs the token used for activate and offer.\n\n /** @type {string | undefined} */\n #protocol // Protocol in use (http: or https:).\n\n /** @type {string | undefined} */\n #child // The host name of the daemon being parented.\n\n /** @type {string | undefined} */\n #iss // Callback-supplied URL for the pubicly visible issuer object.\n\n /** @type {string | undefined} */\n #src // The HTML source URL, which must match origin: header iff present in daemon request.\n\n /** @type {Token | undefined} */\n #token // The token used for check activate and offer calls to the daemon.\n\n /** @type {string | undefined} */\n #claimCode // The claim code returned by fetchOffer.\n\n /**\n * Initialises the helper with details already known to the caller.\n *\n * The private key is used to sign generated tokens, where the issUrl\n * references the public key used to verify the token.\n *\n * This method is used by tab runners wanting to parent daemons.\n *\n * @param {string} child\n * @param {string} src # Note can be party:control.\n * @param {JsonWebKey} privateJwk\n * @param {string} iss\n */\n async initWithKey(child, src, privateJwk, iss) {\n this.#protocol = src.startsWith('https:') ? 'https:' : 'http:'\n this.#child = child\n this.#src = src\n this.#privateJwk = privateJwk\n this.#iss = iss\n\n await this.#buildToken()\n }\n\n /**\n * Uses the token to check activate the daemon. An already\n * activated daemon with this parent is retained, or a new\n * daemon is created so long as the sub of the pre-generated\n * token matches a parent record in the provider.\n *\n * The `isNew` attribute in the ok result can be used to\n * determine if the daemon is newly activated or already\n * active.s\n *\n * @returns boolean true if newly activated, otherwise false.\n * @throws {string} exception if daemon cannot be activated.\n */\n async checkActivate() {\n if (!this.#token) {\n throw `checkActivate: No token`\n }\n const url = new URL(`${this.#token.getAud()}/activate`)\n const token = this.#token.asSignedBase64()\n const body = {}\n let response\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n\n const {\n ok,\n error\n } = await response.json()\n\n if (error) {\n throw error\n }\n\n if (ok.isNew) {\n return true\n }\n\n return false\n }\n catch (e) {\n console.error(e)\n throw `Cannot activate ${this.#token.getParty()}`\n }\n }\n\n /**\n * Uses the token to create a device offer on the daemon with default\n * type 'TRANSIENT' and ttl of 300s.\n *\n * @typedef {Object} Offer\n * @property {string} claimCode\n * @property {string} checkState\n *\n * @param {'NO_CHECK' | 'DO_CHECK'} flow to use. For QR offers, use checked flow.\n * @param {OfferOptions | null} options to use when making the offer, if any.\n * @return {Promise<Offer>} offer claimCode and checkState ('NO_CHECK' or 'AWAIT_CHECK').\n */\n async makeOffer(flow, options = null) {\n const {\n type = 'TRANSIENT',\n ttl = 300,\n expiry = 30,\n payload\n } = options ?? {}\n\n if (!this.#token) {\n throw `makeOffer: No token`\n }\n const url = new URL(`${this.#token.getAud()}/offer`)\n const token = this.#token.asSignedBase64()\n const body = {\n role: 'party',\n type,\n ttl,\n description: `Offered by ${this.#token.getCounterparty()}`,\n payload,\n expiry: new Date(Date.now() + 1000 * expiry),\n flow\n }\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const {\n claimCode,\n _checkState\n } = json.ok\n\n this.#claimCode = claimCode\n\n return json.ok\n }\n\n /**\n * Returns the check state for the offer. This is used when\n * awaiting claim.\n *\n * @return {Promise<string>}\n */\n async checkState() {\n if (!this.#token || !this.#claimCode) {\n throw 'checkState: No token or claimCode'\n }\n const url = new URL(`${this.#token.getAud()}/offer/check`)\n url.searchParams.append('claimCode', this.#claimCode)\n const token = this.#token.asSignedBase64()\n const response = await fetch(url, {\n headers: {\n 'x-tabserver-token': token\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n /**\n * Result has `claimCode` and `checkState`.\n */\n return json.ok.checkState\n }\n\n /**\n * Posts the check code that confirms the offering device has\n * got the expected claiming device, and returns the resulting check state.\n *\n * @typedef {Object} ConfirmResult\n * @property {string} checkState\n *\n * @param {string} checkCode the check code obtained from the claiming device.\n * @return {Promise<ConfirmResult>} the check state following confirmation.\n */\n async confirmClaim(checkCode) {\n if (!this.#token || !this.#claimCode) {\n throw 'confirmClaim: No token or claimCode'\n }\n\n const url = new URL(`${this.#token.getAud()}/offer/confirm`)\n const token = await this.#token.asSignedBase64()\n const body = {\n claimCode: this.#claimCode,\n checkCode\n }\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n return json.ok\n }\n\n /**\n * Returns the daemon name.\n *\n * @return {string} daemon name, e.g. 'daemon.once.id'.\n */\n getChild() {\n if (!this.#child) {\n throw 'getChild: no child'\n }\n return this.#child\n }\n\n /**\n * Returns the daemon origin, suitable for redirection or claiming\n * a code.\n *\n * @return {string} child daemon origin e.g. 'https://daemon.once.id'.\n */\n getChildOrigin() {\n if (!this.#iss || !this.#child) {\n throw 'getDaemonUrl: no issUrl or daemon'\n }\n return `${this.#protocol}//${this.#child}`\n }\n\n /**\n * Builds the token used in the check activate and offer calls toh\n * the daemon.\n *\n * @return {Promise<void>}\n */\n async #buildToken() {\n if (!this.#iss || !this.#child || !this.#privateJwk) {\n throw 'buildToken: missing iss, child or privateJwk'\n }\n\n const iss = this.#iss\n const aud = this.getChildOrigin()\n const sub = new URL(this.#iss).origin\n\n // The `src` attribute is origin and pathname only.\n let src\n if (this.#src) {\n const srcUrl = new URL(this.#src)\n src = `${srcUrl.origin}${srcUrl.pathname}`\n }\n else {\n src = 'party:control'\n }\n\n const payload = {\n iss,\n aud,\n sub,\n src,\n scope: {\n 'party:control': 'offer'\n },\n iat: Date.now(),\n exp: Date.now() + 1000 * 60 * 3\n }\n\n const token = new Token(payload)\n await token.signWith(Promise.resolve(this.#privateJwk))\n this.#token = token\n }\n}\n", "/**\n * Utility class to generate a QR code img element which is placed\n * under a parent element.\n *\n * Relies upon a script element in the page:\n *\n * <script src='https://unpkg.com/qrcode-generator@1.4.4/qrcode.js'>\n */\nconst QrType = 0 // Auto detection by library.\nconst QrEcc = 'L'\n\nexport const ImgClass = 'qrcode'\n\nexport class QrCode {\n #img\n #content\n\n /**\n * Constructor takes img element and\n * the string to show in the QR code.\n * @param {HTMLImageElement} img the image element to use.\n * @param {string} content the content of the qr code.\n */\n constructor(img, content) {\n this.#img = img\n this.#content = content\n }\n\n /**\n * Returns a promise that is resolved when the image loads\n * successfully, or rejected if the image load fails.\n */\n generate() {\n const qr = qrcode(QrType, QrEcc)\n qr.addData(this.#content)\n qr.make()\n const dataUrl = qr.createDataURL()\n this.#img.classList.add(ImgClass)\n this.#img.src = dataUrl\n return new Promise((resolve, reject) => {\n this.#img.onload = resolve\n this.#img.onerror = reject\n })\n }\n}\n", "/**\n * Use this type to reference a plain object with\n * random string keys.\n */\nexport interface Plain {\n // deno-lint-ignore no-explicit-any\n [key: string]: any\n}\n\n/**\n * Any JSON-serialisable type.\n */\nexport type JSONPrimitive = string | number | boolean | null\nexport type JSONArray = JSONValue[]\nexport interface JSONObject {\n [key: string]: JSONValue\n}\nexport type JSONValue = JSONPrimitive | JSONObject | JSONArray\n\n/**\n * Clones a JSONValue as defined above, if necessary.\n */\nexport function cloneJSONValue<T extends JSONValue>(value: T): T {\n if (value === null) return value\n if (Array.isArray(value)) {\n return value.map(cloneJSONValue) as T\n }\n if (typeof value === 'object') {\n const out: Record<string, JSONValue> = {}\n for (const [k, v] of Object.entries(value)) {\n out[k] = cloneJSONValue(v)\n }\n return out as T\n }\n return value\n}\n\n/**\n * Type predicates to be used in type-narrowing assertions, e.g.\n *\n * assert(notNull(someValue))\n * ... now the type of someValue does not include null ...\n *\n * or\n * if (isOk(returnValue)) {\n * ...in this block we know returnValue.ok is present and of the correct type.\n * }\n *\n * @param value the value to be tested.\n * @returns true if not null, otherwise false.\n */\nexport function notNull<T>(value: T | null): value is T {\n return value !== null\n}\n\nexport function isDefined<T>(value: T | undefined): value is T {\n return value !== undefined\n}\n", "import { Plain } from './Assertions.ts'\n\nexport interface Ok<T> {\n ok: T\n}\n\nexport interface Error {\n error: string\n}\n\n/**\n * Returns an ok response or an error response corresponding\n * to the standard ok or error return value.\n *\n * If the error string starts with a space-separated status code,\n * then that is used as the status code for the response.\n *\n * For example:\n *\n * {error: 'Ordinary Error'}\n * is sent as is with status 200.\n * {error: '401 Authentication Error'}\n * is sent as {error: 'Authentication Error'} with status 401\n */\nexport function response<T>(rvalue: Ok<T> | Error, extraHeaders?: Plain) {\n\n if ('ok' in rvalue) {\n const body = JSON.stringify(rvalue)\n return new Response(body, {\n status: 200,\n headers: {\n ...extraHeaders,\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n }\n })\n }\n\n const error = rvalue.error\n const [, status, message] = error.match(/^(\\d{3})\\s(.+)/) ?? [null, '200', error]\n const body = JSON.stringify({\n error: message\n })\n return new Response(body, {\n status: Number(status),\n headers: {\n ...extraHeaders,\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*'\n }\n })\n}\n\n/**\n * Returns a 404 error response, including\n * the standard JSON error return value.\n * @deprecated Use response instead using a status-prefixed error message.\n */\nexport function response404<T>(json: Error = {\n error: '404 Not Found'\n}) {\n const body = JSON.stringify(json)\n return new Response(body, {\n status: 404,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n }\n })\n}\n\n/**\n * Returns a 302 permanent redirect response.\n * @param url the url to redirect to.\n * @returns Response object.\n */\nexport function response302(url: URL, extraHeaders: Plain = []): Response {\n return new Response(null, {\n status: 302,\n headers: {\n 'Location': url.toString(),\n 'Access-Control-Allow-Origin': '*',\n ...extraHeaders\n }\n })\n}\n\n/**\n * Simple build-a-cookie support.\n *\n * If the `expires` attribute is not defined, it's a session cookie.\n * Otherwise it's a permanent cookie. To delete a cookie, use the\n * `expires` attribute set to `new Date(0)`.\n */\n\nexport interface CookiePayload {\n name: string\n value: string\n domain?: string\n sameSite?: 'Strict' | 'Lax' | 'None'\n path?: string\n expires?: Date\n secure?: boolean\n httpOnly?: boolean\n}\n\nexport type CookieString = string\n\nexport function buildCookie(payload: CookiePayload): CookieString {\n const {\n name,\n value,\n domain,\n sameSite,\n path = '/',\n expires,\n secure = true,\n httpOnly = true\n } = payload\n\n const builder: string[] = []\n builder.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`)\n if (domain) builder.push(`Domain=${domain}`)\n if (sameSite) builder.push(`SameSite=${sameSite}`)\n builder.push(`Path=${path}`)\n if (expires) builder.push(`Expires=${expires.toUTCString()}`)\n if (secure) builder.push(`Secure`)\n if (httpOnly) builder.push('HttpOnly')\n\n const cookie = builder.join('; ')\n return cookie\n}\n", "import { response } from './Responses.ts'\nimport { Plain, JSONValue } from './Assertions.ts'\nimport { Token, Scope } from '../js/Token.js'\nimport { Storage } from '../js/Storage.js'\nimport { HtmlContent } from './HtmlContent.ts'\nimport { YamlContent } from './YamlContent.ts'\n\n// Daemon configuration looks like this.\nexport interface DaemonConfig {\n system: {\n protocol: 'http:' | 'https:'\n party: string\n issuer: string\n source: string\n sourcePrefix: string\n tab: string,\n privateJwk: JsonWebKey\n }\n content: {\n html: HtmlContent\n yaml?: YamlContent\n }\n user: Plain\n}\n\n// System requests are on the /daemon/... path.\nexport const DAEMON_PREFIX = 'daemon'\n\n// Daemon lifecycle path components that come after DAEMON_PREFIX.\nexport const CONFIG_PATH = 'config'\nexport const EVENT_PATH = 'event'\n\n// Event names.\nconst Ev = {\n Config: 'config'\n}\n\n// SessionStorage keys for lifecycle objects.\nconst CONFIG_KEY = 'config'\n\n// Default time-to-live for a third party token we generate is 1 hour.\nconst DEFAULT_TTL_MILLIS = 1000 * 60 * 60\n\ninterface TabEvent {\n type: string,\n payload: JSONValue\n}\n\n/**\n * Encapsulates the lifecycle event requests that occur on\n * runner start and termination and when alerts are resolved\n * or rejected.\n *\n * The Lifecycle object also provides static methods to retrieve\n * configuration items, a public and private key pair, and also to\n * produce the signed token that is required in requests from this party\n * to third parties.\n */\nexport class Lifecycle extends EventTarget {\n static #instance: Lifecycle = new Lifecycle()\n\n // Expose event names to importers.\n static Ev = Ev\n\n static getInstance() {\n return this.#instance\n }\n\n /**\n * All system requests are under the /daemon/ path.\n *\n * @param {Request} request to be tested.\n * @returns {boolean} true if the request is a lifecycle request.\n */\n static shouldHandle(request: Request): boolean {\n return (\n Lifecycle.isConfig(request) ||\n Lifecycle.isEvent(request)\n )\n }\n\n static isConfig(request: Request): boolean {\n const url = new URL(request.url)\n return (\n request.method == 'POST' &&\n url.pathname == `/${DAEMON_PREFIX}/${CONFIG_PATH}` &&\n request.headers.get('Content-Type') == 'application/json'\n )\n }\n\n static isEvent(request: Request): boolean {\n const url = new URL(request.url)\n return (\n request.method == 'POST' &&\n url.pathname == `/${DAEMON_PREFIX}/${EVENT_PATH}` &&\n request.headers.get('Content-Type') == 'application/json'\n )\n }\n\n /**\n * Handles a lifecycle request.\n *\n * @param {Request} request to be handled.\n * @returns {Response} response for caller.\n */\n async handler(request: Request): Promise<Response> {\n if (Lifecycle.isConfig(request)) {\n return await this.handleConfig(request)\n }\n if (Lifecycle.isEvent(request)) {\n return await this.handleEvent(request)\n }\n return response({\n error: 'Invalid daemon request'\n })\n }\n\n /**\n * Saves the lifecycle config object provided in the request body.\n *\n * Fires a 'config' lifecycle event.\n *\n * @param {Request} request containing the configuration json.\n * @return {Response} ok response.\n */\n async handleConfig(request: Request): Promise<Response> {\n const configJson = await request.json()\n sessionStorage.setItem(CONFIG_KEY, JSON.stringify(configJson))\n this.dispatchEvent(new CustomEvent(Ev.Config, {\n detail: configJson\n }))\n return response({\n ok: true\n })\n }\n\n /**\n * Fires a lifecycle event upon receiving an event request.\n *\n * @param {Request} request containing the event.\n * @return {Response} ok response.\n */\n async handleEvent(request: Request): Promise<Response> {\n const {\n type,\n payload\n } = await request.json()\n this.dispatchEvent(new CustomEvent(type, {\n detail: payload\n }))\n return response({\n ok: true\n })\n }\n\n /**\n * Returns the configuration object stored on initialisation.\n * @return {DaemonConfig} the daemon configuration object.\n * @throws {string} exception if config is not yet initialised.\n */\n static getConfig(): DaemonConfig {\n const json = sessionStorage.getItem(CONFIG_KEY)\n if (!json) {\n throw 'DaemonConfig not ready'\n }\n return JSON.parse(json)\n }\n\n /**\n * Generates if necessary and returns a private key for\n * use in signing tokens.\n *\n * @returns {JSONWebKey} the private key for this session.\n */\n static getPrivateKey(): Promise<JsonWebKey> {\n const {\n system: {\n privateJwk\n }\n } = Lifecycle.getConfig()\n return Promise.resolve(privateJwk)\n }\n\n /**\n * Returns a token suitable for the `x-tabserver-token` header\n * in a request to the supplied party with the required\n * scope.\n *\n * If party is omitted, it defaults to this party.\n * If\n *\n * @param {Scope} scope map of string source -> space-separated capabilities.\n * @param {string=} party the party host name to whom the request will be sent.\n * @param { src=} src the HTML page from which the request is (actually or notionally) made.\n * @param {number=} ttlMillis the lifetime of this token.\n * @returns {Token} the signed token.\n * @throws {string} if configuration not yet initialised.\n *\n */\n static async getTokenFor(\n scope: Scope,\n party: string | null = null,\n src: string | null = null,\n ttlMillis: number = DEFAULT_TTL_MILLIS\n ): Promise<Token> {\n const config = Lifecycle.getConfig()\n if (!config) {\n throw 'Lifecycle configuration not yet available'\n }\n\n // Extract protocol, our party, issuer and source from system config.\n const {\n system: {\n protocol,\n party: us,\n issuer,\n source\n }\n } = config\n\n // Token src excludes query params.\n const appSourceUrl = new URL(source)\n\n const aud = party ? `${protocol}//${party}`: `${protocol}//${us}`\n const sub = `${protocol}//${us}`\n const now = Date.now()\n\n const payload = {\n iss: issuer,\n aud,\n sub,\n src: src ?? `${appSourceUrl.origin}${appSourceUrl.pathname}`,\n scope,\n iat: now,\n exp: now + ttlMillis\n }\n\n const token = new Token(payload)\n await token.signWith(Lifecycle.getPrivateKey())\n return token\n }\n\n static async getStorage(): Promise<Storage> {\n const token = await Lifecycle.getStorageToken()\n return new Storage(token)\n }\n\n /**\n * Returns a token for our party with the setitem and getitem\n * capabilities on the party control source.\n */\n static getStorageToken(): Promise<Token> {\n const {\n system: {\n party,\n }\n } = Lifecycle.getConfig()\n\n const scope = {\n [Token.Source.PARTY_CONTROL]: `${Token.Capability.GET_ITEM} ${Token.Capability.SET_ITEM}`\n }\n\n return Lifecycle.getTokenFor(scope, party)\n }\n}\n", "import type { JSONValue } from './Assertions.ts'\n\ntype JSONRecord = Record<string, unknown>\n\nexport interface JsonRpcMcpClientOptions {\n post?: (body: JSONRecord) => Promise<JSONRecord>\n url?: string\n headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>)\n fetch?: typeof fetch\n}\n\nexport class JsonRpcMcpClient {\n #post: (body: JSONRecord) => Promise<JSONRecord>\n #nextId = 1\n\n constructor(options: JsonRpcMcpClientOptions) {\n if (options.post) {\n this.#post = options.post\n return\n }\n\n if (!options.url) {\n throw new Error('JsonRpcMcpClient requires post or url')\n }\n\n const fetchFn = options.fetch ?? fetch\n this.#post = async (body) => {\n const headers = typeof options.headers === 'function'\n ? await options.headers()\n : options.headers ?? {}\n const response = await fetchFn(options.url!, {\n method: 'POST',\n headers: jsonHeaders(headers),\n body: JSON.stringify(body),\n })\n return await response.json()\n }\n }\n\n async listTools(): Promise<JSONValue[]> {\n const result = await this.#request('tools/list', {})\n const tools = (result as JSONRecord | undefined)?.tools\n return Array.isArray(tools) ? tools as JSONValue[] : []\n }\n\n async callTool(\n name: string,\n args: JSONRecord = {},\n ): Promise<JSONValue> {\n const result = await this.#request('tools/call', {\n name,\n arguments: args,\n })\n\n if ((result as JSONRecord | undefined)?.isError) {\n throw mcpToolErrorMessage(result)\n }\n\n const record = result as JSONRecord | null | undefined\n return record?.structuredContent as JSONValue ?? null\n }\n\n async #request(method: string, params: JSONRecord): Promise<JSONValue> {\n const json = await this.#post({\n jsonrpc: '2.0',\n id: this.#nextId++,\n method,\n params,\n })\n\n if (json.error) {\n const error = json.error as { message?: string }\n throw error.message ?? JSON.stringify(json.error)\n }\n\n return json.result as JSONValue\n }\n}\n\nexport function mcpToolErrorMessage(result: unknown): string {\n const content = (result as JSONRecord | undefined)?.content\n if (Array.isArray(content)) {\n const first = content[0] as { text?: unknown } | undefined\n if (typeof first?.text === 'string') return first.text\n }\n return 'MCP tool failed'\n}\n\nfunction jsonHeaders(headers: HeadersInit): HeadersInit {\n const out = new Headers(headers)\n out.set('Content-Type', 'application/json')\n return out\n}\n", "import { JsonRpcMcpClient } from './McpClient.ts'\n\nexport type JSONRecord = Record<string, unknown>\n\nexport interface ChatToolCall {\n id: string\n name: string\n input: JSONRecord\n}\n\nexport interface ChatToolDescriptor {\n type: 'function'\n name: string\n execution: 'server'\n description?: string\n parameters?: JSONRecord\n}\n\nexport interface ClientToolDescriptor {\n type: 'function'\n name: string\n execution: 'client'\n description?: string\n parameters?: JSONRecord\n}\n\nexport interface ToolResult {\n toolCallId: string\n name?: string\n content: string\n isError?: boolean\n}\n\nexport interface ToolRoute {\n kind: string\n method?: string\n path?: string\n mcpToolName?: string\n}\n\nexport interface ToolCatalogEntry {\n tool?: {\n name?: string\n description?: string\n inputSchema?: JSONRecord\n }\n route?: ToolRoute\n}\n\nexport interface AgentToolRuntimeOptions {\n postParty: (path: string, body: JSONRecord) => Promise<JSONRecord>\n postPartyRaw: (path: string, body: JSONRecord) => Promise<JSONRecord>\n}\n\nexport class AgentToolRuntime {\n #postParty: (path: string, body: JSONRecord) => Promise<JSONRecord>\n #postPartyRaw: (path: string, body: JSONRecord) => Promise<JSONRecord>\n #toolCatalog: ToolCatalogEntry[] = []\n #toolRoutes = new Map<string, ToolRoute>()\n #mcpClients = new Map<string, JsonRpcMcpClient>()\n\n constructor(options: AgentToolRuntimeOptions) {\n this.#postParty = options.postParty\n this.#postPartyRaw = options.postPartyRaw\n }\n\n get toolCatalog(): ToolCatalogEntry[] {\n return this.#toolCatalog\n }\n\n async refreshCatalog(): Promise<void> {\n const catalog = await this.#postParty('/ai/tools/catalog', {})\n this.setCatalog(catalog.tools)\n }\n\n setCatalog(value: unknown): void {\n const toolCatalog = Array.isArray(value) ? value : []\n this.#toolCatalog = toolCatalog as ToolCatalogEntry[]\n this.#toolRoutes = new Map(\n this.#toolCatalog\n .filter((entry) => entry.tool?.name && entry.route)\n .map((entry) => [entry.tool!.name!, entry.route!]),\n )\n this.#mcpClients.clear()\n }\n\n chatTools(options: {\n clientTools?: ClientToolDescriptor[]\n } = {}): Array<ChatToolDescriptor | ClientToolDescriptor> {\n return [\n ...this.#toolCatalog.map(toChatTool).filter(isDefined),\n ...(options.clientTools ?? []),\n ]\n }\n\n async executeToolCalls(toolCalls: ChatToolCall[]): Promise<ToolResult[]> {\n const results: ToolResult[] = []\n for (const call of toolCalls) {\n try {\n results.push({\n toolCallId: call.id,\n name: call.name,\n content: await this.executeToolCall(call),\n })\n } catch (e) {\n results.push({\n toolCallId: call.id,\n name: call.name,\n content: String(e),\n isError: true,\n })\n }\n }\n return results\n }\n\n async executeToolCall(call: ChatToolCall): Promise<string> {\n const route = this.#toolRoutes.get(call.name)\n if (!route) throw new Error(`No discovered tool named ${call.name}`)\n if (route.method !== 'POST' || !route.path) {\n throw new Error(`Unsupported route for tool ${call.name}`)\n }\n\n if (route.kind === 'platform') {\n const result = await this.#postParty(route.path, call.input)\n return JSON.stringify(result)\n }\n\n if (route.kind === 'mcp') {\n return await this.#executeMcpToolCall(route, call)\n }\n\n throw new Error(`Unsupported route kind for tool ${call.name}`)\n }\n\n async #executeMcpToolCall(\n route: ToolRoute,\n call: ChatToolCall,\n ): Promise<string> {\n let client = this.#mcpClients.get(route.path!)\n if (!client) {\n client = new JsonRpcMcpClient({\n post: (body) => this.#postPartyRaw(route.path!, body),\n })\n this.#mcpClients.set(route.path!, client)\n }\n const result = await client.callTool(route.mcpToolName || call.name, {\n ...call.input,\n })\n return JSON.stringify(result)\n }\n}\n\nexport function toChatTool(\n entry: ToolCatalogEntry,\n): ChatToolDescriptor | null {\n if (!entry?.tool?.name) return null\n return {\n type: 'function',\n name: entry.tool.name,\n execution: 'server',\n description: entry.tool.description,\n parameters: entry.tool.inputSchema,\n }\n}\n\nfunction isDefined<T>(value: T | null | undefined): value is T {\n return value !== null && value !== undefined\n}\n", "import type { JSONObject, JSONValue } from './Assertions.ts'\n\nconst PROTOCOL_VERSION = '2025-06-18'\nconst CORS_HEADERS = {\n 'Access-Control-Allow-Origin': '*',\n}\n\ntype JsonRpcId = string | number | null\nexport type McpToolArguments = Record<string, unknown>\n\nexport interface McpContext {\n party?: string\n source?: string\n protocol?: string\n sourcePrefix?: string\n}\n\nexport interface McpToolDefinition<TContext extends McpContext> {\n name: string\n title?: string\n description: string\n inputSchema: JSONValue\n outputSchema?: JSONValue\n annotations?: JSONObject\n capability?: string\n call: (\n args: McpToolArguments,\n context: TContext,\n ) => Promise<JSONValue> | JSONValue\n}\n\nexport interface McpToolProvider<TContext extends McpContext> {\n listTools(): JSONValue[]\n callTool(\n name: string,\n args: McpToolArguments,\n context: TContext,\n ): Promise<JSONValue>\n}\n\nexport interface JsonRpcMcpServerOptions<TContext extends McpContext> {\n serverInfo?: {\n name: string\n version: string\n }\n context: (request: Request) => TContext\n}\n\nexport class McpToolRegistry<TContext extends McpContext>\n implements McpToolProvider<TContext> {\n #tools: McpToolDefinition<TContext>[]\n\n constructor(tools: McpToolDefinition<TContext>[]) {\n this.#tools = tools\n }\n\n listTools(): JSONValue[] {\n return this.#tools.map((tool) => ({\n name: tool.name,\n ...(tool.title === undefined ? {} : { title: tool.title }),\n description: tool.description,\n inputSchema: tool.inputSchema,\n ...(tool.outputSchema === undefined\n ? {}\n : { outputSchema: tool.outputSchema }),\n ...(tool.annotations === undefined\n ? {}\n : { annotations: tool.annotations }),\n }))\n }\n\n async callTool(\n name: string,\n args: McpToolArguments,\n context: TContext,\n ): Promise<JSONValue> {\n const tool = this.#tools.find((candidate) => candidate.name === name)\n if (!tool) {\n throw new ProtocolError(-32602, `Unknown tool: ${name}`)\n }\n return await tool.call(args, context)\n }\n}\n\nexport class JsonRpcMcpServer<TContext extends McpContext = McpContext> {\n #tools: McpToolProvider<TContext>\n #context: (request: Request) => TContext\n #serverInfo: { name: string; version: string }\n\n constructor(\n tools: McpToolProvider<TContext>,\n contextOrOptions:\n | ((request: Request) => TContext)\n | JsonRpcMcpServerOptions<TContext>,\n ) {\n this.#tools = tools\n if (typeof contextOrOptions === 'function') {\n this.#context = contextOrOptions\n this.#serverInfo = {\n name: 'mcp-server',\n version: '1',\n }\n } else {\n this.#context = contextOrOptions.context\n this.#serverInfo = contextOrOptions.serverInfo ?? {\n name: 'mcp-server',\n version: '1',\n }\n }\n }\n\n async handle(request: Request): Promise<Response> {\n if (request.method !== 'POST') {\n return jsonResponse(jsonRpcError(null, -32600, 'MCP requires POST'))\n }\n\n let message: Record<string, unknown>\n try {\n message = await request.json()\n } catch (_e) {\n return jsonResponse(jsonRpcError(null, -32700, 'Parse error'))\n }\n\n const id = jsonRpcId(message.id)\n if (message.jsonrpc !== '2.0' || typeof message.method !== 'string') {\n return jsonResponse(jsonRpcError(id, -32600, 'Invalid Request'))\n }\n\n if (\n message.method === 'notifications/initialized' && message.id === undefined\n ) {\n return new Response(null, {\n status: 202,\n headers: CORS_HEADERS,\n })\n }\n\n try {\n const result = await this.#handleMethod(\n message.method,\n message.params,\n request,\n )\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n result,\n })\n } catch (e) {\n if (e instanceof ProtocolError) {\n return jsonResponse(jsonRpcError(id, e.code, e.message))\n }\n return jsonResponse(jsonRpcError(id, -32603, String(e)))\n }\n }\n\n async #handleMethod(\n method: string,\n params: unknown,\n request: Request,\n ): Promise<JSONValue> {\n switch (method) {\n case 'initialize':\n return {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {\n tools: {\n listChanged: false,\n },\n },\n serverInfo: this.#serverInfo,\n }\n\n case 'notifications/initialized':\n return {}\n\n case 'tools/list':\n return {\n tools: this.#tools.listTools(),\n }\n\n case 'tools/call':\n return await this.#callTool(params, request)\n\n default:\n throw new ProtocolError(-32601, `Method not found: ${method}`)\n }\n }\n\n async #callTool(params: unknown, request: Request): Promise<JSONValue> {\n if (!params || typeof params !== 'object' || Array.isArray(params)) {\n throw new ProtocolError(-32602, 'tools/call params must be an object')\n }\n\n const {\n name,\n arguments: args = {},\n } = params as Record<string, unknown>\n\n if (\n typeof name !== 'string' || !args || typeof args !== 'object' ||\n Array.isArray(args)\n ) {\n throw new ProtocolError(-32602, 'Invalid tools/call params')\n }\n\n try {\n const structuredContent = await this.#tools.callTool(\n name,\n args as McpToolArguments,\n this.#context(request),\n )\n return mcpToolResult(structuredContent)\n } catch (e) {\n if (e instanceof ProtocolError) throw e\n return mcpToolResult({ error: String(e) }, true)\n }\n }\n}\n\nexport class ProtocolError extends Error {\n code: number\n\n constructor(code: number, message: string) {\n super(message)\n this.code = code\n }\n}\n\nexport function mcpToolResult(\n structuredContent: JSONValue,\n isError = false,\n): JSONValue {\n return {\n content: [\n {\n type: 'text',\n text: typeof structuredContent === 'string'\n ? structuredContent\n : JSON.stringify(structuredContent),\n },\n ],\n structuredContent,\n isError,\n }\n}\n\nfunction jsonRpcError(id: JsonRpcId, code: number, message: string): JSONValue {\n return {\n jsonrpc: '2.0',\n id,\n error: {\n code,\n message,\n },\n }\n}\n\nfunction jsonResponse(value: JSONValue): Response {\n return new Response(JSON.stringify(value), {\n headers: {\n ...CORS_HEADERS,\n 'Content-Type': 'application/json',\n },\n })\n}\n\nfunction jsonRpcId(value: unknown): JsonRpcId {\n return typeof value === 'string' || typeof value === 'number' ||\n value === null\n ? value\n : null\n}\n", "import type { Grant, YamlContent } from \"./YamlContent.ts\";\n\nexport const USE_TOOLS_CAPABILITY = \"useTools\";\n\nconst CAPABILITY_SEPARATOR = /[\\s,;\\|]+/;\n\nexport interface McpCapabilityToken {\n hasCapability(source: string, capability: string): boolean;\n getCounterparty(): string;\n getSourceUrl(): URL;\n getSrc(): string;\n}\n\nexport interface McpToolAuthorization {\n token: McpCapabilityToken | null;\n providerSource: string;\n toolName: string;\n capability: string;\n providerYaml?: YamlContent;\n}\n\nexport function hasMcpToolCapability(\n authorization: McpToolAuthorization,\n): boolean {\n const {\n token,\n providerSource,\n toolName,\n capability,\n providerYaml,\n } = authorization;\n\n if (!token) return false;\n if (tokenHasCapability(token, providerSource, capability)) return true;\n if (!providerYaml) return false;\n\n const callerSource = String(token.getSourceUrl());\n return (\n tokenHasCapability(token, callerSource, USE_TOOLS_CAPABILITY) &&\n yamlDeclaresMcpToolCapability(providerYaml, toolName, capability) &&\n yamlGrantsCapability(\n providerYaml.grant,\n token.getCounterparty(),\n token.getSrc(),\n capability,\n )\n );\n}\n\nexport function tokenHasCapability(\n token: { hasCapability(source: string, capability: string): boolean },\n source: string,\n capability: string,\n): boolean {\n try {\n return token.hasCapability(source, capability);\n } catch (_e) {\n return false;\n }\n}\n\nexport function yamlDeclaresMcpToolCapability(\n yaml: Pick<YamlContent, \"mcp\">,\n toolName: string,\n capability: string,\n): boolean {\n return Boolean(\n yaml.mcp?.tools.some((tool) =>\n tool.name === toolName && tool.capability === capability\n ),\n );\n}\n\nexport function yamlGrantsCapability(\n grant: Grant | undefined,\n counterparty: string,\n src: string,\n capability: string,\n): boolean {\n for (const counterpartyPattern in grant ?? {}) {\n if (\n !matchesSqlLike(\n counterparty.toLowerCase(),\n counterpartyPattern.toLowerCase(),\n )\n ) {\n continue;\n }\n\n const grantEntry = grant![counterpartyPattern];\n if (Array.isArray(grantEntry)) {\n if (grantEntry.includes(capability)) return true;\n continue;\n }\n\n for (const srcPattern in grantEntry) {\n if (\n matchesSqlLike(src, srcPattern) &&\n splitCapabilities(grantEntry[srcPattern]).includes(capability)\n ) {\n return true;\n }\n }\n }\n return false;\n}\n\nfunction splitCapabilities(scope: string): string[] {\n return scope\n .split(CAPABILITY_SEPARATOR)\n .filter((capability) => capability.length > 0);\n}\n\nfunction matchesSqlLike(value: string, pattern: string): boolean {\n const regex = new RegExp(\n `^${\n pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/%/g, \".*\")\n .replace(/_/g, \".\")\n }$`,\n );\n return regex.test(value);\n}\n", "import { Plain } from \"./Assertions.ts\";\n\n// Standard HTTP headers.\nconst X_FORWARDED_HOST = 'x-forwarded-host'\nconst X_FORWARDED_PROTO = 'x-forwarded-proto'\n\n// Non-standard HTTP header used by tabserver.\nconst X_FORWARDED_PATHNAME = 'x-forwarded-pathname'\n\n/**\n * Returns the request protocol. This is the first of the following:\n *\n * 1. The `x-forwarded-proto` header, if present, with trailing colon.\n * 2. The request URL protocol, ditto.\n *\n * @param request the request.\n * @returns {string} the protocol with trailing colon.\n */\nexport function getClientProtocol(request: Request): string {\n if (request.headers.has(X_FORWARDED_PROTO)) {\n return (request.headers.get(X_FORWARDED_PROTO) as string) + ':'\n }\n\n return new URL(request.url).protocol\n}\n\n/**\n * Returns the request host name. This is the first of the following:\n *\n * 1. The `x-forwarded-host` header, if present.\n * 2. The request URL hostname (including port, if present)\n *\n * @param request the request.\n * @returns the protocol.\n */\nexport function getClientHost(request: Request): string {\n if (request.headers.has(X_FORWARDED_HOST)) {\n return request.headers.get(X_FORWARDED_HOST) || ''\n }\n\n return new URL(request.url).host\n}\n\n/**\n * Returns the request pathname. This is the first of the following:\n *\n * 1. The `x-forwarded-pathname` header, if present.\n * 2. The request URL pathname.\n */\nexport function getClientPathname(request: Request): string {\n if (request.headers.has(X_FORWARDED_PATHNAME)) {\n return request.headers.get(X_FORWARDED_PATHNAME) || ''\n }\n\n return new URL(request.url).pathname\n}\n\n/**\n * Returns the client request protocol, host and pathname components\n * only.\n *\n * @param request the request.\n * @return {URL} the protocol://host/pathname\n */\nexport function getClientUrlPhp(request: Request): URL {\n const protocol = getClientProtocol(request)\n const host = getClientHost(request)\n const pathname = getClientPathname(request)\n return new URL(`${protocol}//${host}${pathname}`)\n}\n\n/**\n * Returns the URL as requested by the client taking account\n * of the x-forwarded protocol and host.\n *\n * @param request the request.\n * @returns the URL.\n */\nexport function getClientUrl(request: Request): URL {\n const originalUrl = new URL(request.url)\n const newUrl = getClientUrlPhp(request)\n newUrl.search = originalUrl.search\n newUrl.hash = originalUrl.hash\n return newUrl\n}\n\n/**\n * Returns the pathname portion of the request URL, excluding\n * query and fragment components.\n *\n * @param {request} the request.\n * @returns {string} the pathname which starts with a forward slash.\n */\nexport function getPathname(request: Request): string {\n const url = new URL(request.url)\n return url.pathname\n}\n\n/**\n * Returns the string value of a named cookie, or null if it is not\n * present in the request.\n *\n * @param {Request} request the request.\n * @param {string} cookieName the name of the cookie\n * @returns {string} the cookie value, or null if not present.\n */\nexport function getCookie(request: Request, cookieName: string): string | null {\n const cookies = getCookies(request)\n if (cookieName in cookies) {\n return cookies[cookieName]\n }\n return null\n}\n\n/**\n * Returns a map of request cookie names to values, which can be empty.\n *\n * @param {Request} request the request.\n * @returns {Plain} the plain object with zero or more cookie keys.\n */\nexport function getCookies(request: Request): Plain {\n const cookies: Plain = {}\n const headerValue = request.headers.get('Cookie')\n if (headerValue) {\n for (const keyVal of headerValue.split('; ')) {\n const [key, value] = keyVal.split('=')\n cookies[key] = value\n }\n }\n return cookies\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["shortSafeDigest", "message", "length", "msgUint8", "hashBuffer", "hashArray", "byteString", "base64Safe", "shortHexDigest", "hashHex", "b", "getAppId", "url", "
|
|
3
|
+
"sources": ["../js/Digest.js", "../js/Token.js", "../js/Storage.js", "../js/ParamExtractor.js", "../js/BrowserApp.js", "../js/GenerativeChatBrowser.js", "../js/KeyPair.js", "../js/Alert.js", "../js/ParentHelper.js", "../js/QrCode.js", "../ts/Assertions.ts", "../ts/Responses.ts", "../ts/Lifecycle.ts", "../ts/McpClient.ts", "../ts/McpAgentTools.ts", "../ts/McpServer.ts", "../ts/McpToolAuthorization.ts", "../ts/GenerativeChatAgent.ts", "../ts/Requests.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Generates a SHA-1 digest of the supplied string, and returns\n * it as a base64 string optionally truncated to the first 'length'\n * characters.\n *\n * @param {string} message to digest.\n * @return {Promise<string>} base64-encoded digest.\n */\nexport async function shortSafeDigest(message, length = 0) {\n const msgUint8 = new TextEncoder().encode(message)\n if (!crypto.subtle) {\n throw 'Requires secure origin (localhost or https:)'\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", msgUint8)\n const hashArray = new Uint8Array(hashBuffer)\n const byteString = String.fromCodePoint(...hashArray)\n const base64 = btoa(byteString)\n const base64Safe = base64\n .replaceAll('+','-')\n .replaceAll('/','_')\n .replaceAll('=','')\n return length ? base64Safe.substring(0, length) : base64Safe\n}\n\n/**\n * Generates a SHA-1 digest of the supplied string, and returns\n * it as a hex string optionally truncated to the first 'length'\n * characters.\n *\n * @param {string} message to digest.\n * @return {Promise<string>} hex-encoded digest.\n */\nexport async function shortHexDigest(message, length = 0) {\n const msgUint8 = new TextEncoder().encode(message)\n if (!crypto.subtle) {\n throw 'Requires secure origin (localhost or https:)'\n }\n const hashBuffer = await crypto.subtle.digest(\"SHA-1\", msgUint8)\n const hashArray = Array.from(new Uint8Array(hashBuffer))\n const hashHex = hashArray\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n\n return length ? hashHex.substring(0, length) : hashHex\n}\n\n/**\n * Returns the standard app id for the supplied app URL.\n *\n * This is the shortSafeDigest of the URL truncated to 8 base64 chars,\n * i.e. distributed over a 48-bit space which seems reasonable for the\n * number of apps on a given daemon.\n *\n * @param {string | URL} url of the app whose id is to be computed.s\n * @return {Promise<string>} default id for the app, commonly used in prefixes.\n */\nexport async function getAppId(url) {\n if (typeof url === 'string') {\n url = new URL(url)\n }\n const appId = await shortSafeDigest(String(url), 8)\n return appId\n}\n", "/**\n * Encapsulates the functionality associated with a bearer token normally\n * passed in a request `x-tabserver-token` header as a base64-encoded\n * JSON object.\n *\n * Note the type definition annotations which are helpful when using this\n * Javascript class in Typescript.\n *\n * Example token:\n *\n * {\n * \"iss\": \"http://foo.daemon/some/public.jwk\",\n * \"aud\": \"https://bar.daemon\",\n * \"sub\": \"https://foo.daemon\",\n * \"src\": \"https://some.com\",\n * \"scope\": {\n * \"http://localhost:9000/helloWorld1.html\": \"read write\",\n * \"party:control\": \"grant revoke\"\n * }\n * \"iat\": 1698576567000,\n * \"exp\": 1698576344000,\n * \"sig\": \"long base64 string\"\n * }\n *\n * Generally:\n * iss - the issuer URL references the public JWK used to verify the sig.\n * aud - the party handling the request, expressed as an origin.\n * sub - the counterparty making the request, expressed as an origin.\n * src - the source HTML file making the request, protocol://host/path only.\n * scope - a map of requested source -> scope pairs.\n * A scope is a space-separated list of capabilities.\n * iat - issued at time in absolute millis.\n * exp - expiry time, ditto.\n * sig - the base64 signature, verified by the JWK pointed to by iss.\n *\n * Normally the sub field must match the host portion of the iss URL. But\n * the possiblity remains open for an intermediary trusted by the party to\n * hold the public keys of counterparties.\n */\n\n/**\n * @typedef {Object.<string, string>} Scope\n */\n\n/**\n * @typedef TokenPayload\n * @property {string} iss // URL of public key of counterparty making request.\n * @property {string} aud // Party origin handling the request.\n * @property {string} sub // Counterparty origin making the request.\n * @property {string} src // Source HTML document context of the request excluding fragment and query.\n * @property {Scope} scope // Map of scope (space-separated capabilities) keyed by source URL.\n * @property {number} iat // Issued at time.\n * @property {number} exp // Expiry time.\n * @property {string} sig // Signature if signed.\n */\n\n/**\n * @typedef {Object} Signatory the object to be served on the iss URL.\n * @property {string} src which is 'party:control' if the shell, or source URL if a runner.\n * @property {JsonWebKey} jwk\n */\n\n/**\n * @typedef {Object} SignatoryCacheEntry\n * @property {Signatory} signatory\n * @property {CryptoKey} publicKey\n * @property {number} expiresAt\n */\n\n/**\n * @typedef TokenSet\n * @type {Object<string, Token>}\n */\n\n/** Must match algorithm in Keypair.js */\nconst ALGORITHM = {\n name: 'RSASSA-PKCS1-v1_5',\n hash: 'SHA-256',\n}\n\n/** Matches an origin with protocol and no path. */\nconst MATCH_ORIGIN = /https?:\\/\\/[^\\/]+$/\n\n/** Matches issuer paths for app runner public keys. */\nconst MATCH_APP_ISSUER_PATH = /^\\/issuer\\/[a-f0-9]{32}$/\n\n/**\n * Allow a 30s leeway to allow for clock difference when checking\n * token issued at time. We allow our clock to be up to 30s behind\n * the system that generated a token.\n */\nconst TOKEN_IAT_LEEWAY_MILLIS = 1000 * 30\n\nexport class Token {\n // Standard sources.\n static Source = {\n PARTY_CONTROL: 'party:control', // Party control subject, a pseudo-source with scope granted to counterparties.\n }\n\n // Standard counterparty pattern.\n static Counterparty = {\n PUBLIC: 'public', // Public counterparty, matching requests with no host specified in sub.\n }\n\n // Standard scope labels.\n static Capability = {\n OPEN: 'open', // Capability to open source files and start tab runners.\n CLOSE: 'close', // Capability to close source files and stop tab runners.\n GRANT: 'grant', // Capability to create relation records.\n REVOKE: 'revoke', // Capability to delete relation records.\n OFFER: 'offer', // Capability to create a party offer for a device to claim.\n RETRACT: 'retract', // Capability to delete a party offer before it is claimed.\n DELETE: 'delete', // Capability to delete devices.\n CATALOG: 'catalog', // Capability to get sources, devices, alerts etc.\n ALIAS: 'alias', // Capability to set the alias for a counterparty host.\n UNALIAS: 'unalias', // Capability to unset the alias for a counterparty host.\n GET_ITEM: 'getitem', // Capability to get a storage item.\n SET_ITEM: 'setitem', // Capability to set (and remove) a storage item.\n ALERT: 'alert', // Capability to post and delete alerts.\n LOG: 'log', // Capability to view logs.\n CHAT: 'chat', // Capability to use generative chat.\n READ_MEMORY: 'read_memory', // Capability to read memory.\n WRITE_MEMORY: 'write_memory', // Capability to write memory.\n READ: 'read', // Capability to read from drive.\n WRITE: 'write', // Capability to write to drive.\n }\n\n static DEFAULT_EXPIRY_MILLIS = 1000 * 60 * 60 * 24\n static TOKEN_HEADER = 'x-tabserver-token'\n static SCOPE_SEPARATOR = /[\\s,;\\|]+/ // Specification per String.split() function.\n static #SIGNATORY_CACHE_TTL_MILLIS = 1000 * 60\n static #SIGNATORY_CACHE_MAX_ENTRIES = 2048\n\n /** @type{Map<string, SignatoryCacheEntry>} */\n static #signatoryCache = new Map()\n\n /** @type{Map<string, Promise<SignatoryCacheEntry>>} */\n static #signatoryCacheInFlight = new Map()\n\n /** @type{TokenPayload | null} */\n #payload = null\n\n /** @type{string | null} */\n #signatureBase64 = null\n\n /** @type{URL} */\n #audUrl\n\n /** @type{URL} */\n #subUrl\n\n /** @type{URL} */\n #srcUrl\n\n /** @type {Signatory | undefined} */\n #signatory // Set upon successful verification of signature.\n\n /**\n * Constructor takes either a signed base64 token, or a payload object.\n *\n * If successful, the payload and optionally the signature fields are\n * populated.\n *\n * @param {object | string} source a payload, or a base64 token\n */\n constructor(source) {\n let payload = null\n\n if (typeof source == 'object') {\n payload = source\n } else if (typeof source == 'string') {\n try {\n payload = JSON.parse(atob(source))\n } catch (_e) {\n throw 'Invalid token format'\n }\n } else {\n throw `Cannot construct token from ${typeof source}`\n }\n\n // Separate out the signature from the payload.\n this.#signatureBase64 = payload.sig\n delete payload.sig\n this.#payload = payload\n\n const {\n iss,\n aud,\n sub,\n src,\n scope,\n iat,\n exp,\n } = payload\n\n // Check payload is complete.\n if ((!iss || !aud || !sub || !src || !scope || !iat || !exp)) {\n throw 'Token must include iss, aud, sub, src, scope, iat and exp'\n }\n\n // Check aud, sub and src fields are origins (i.e. proto://host.name)\n if (\n !aud.match(MATCH_ORIGIN) ||\n !sub.match(MATCH_ORIGIN)\n ) {\n throw 'The aud and sub attributes must be origins'\n }\n\n this.#audUrl = new URL(aud)\n this.#subUrl = new URL(sub)\n this.#srcUrl = new URL(src)\n\n // The `src` attribute must not have query or fragment.\n if (this.#srcUrl.search || this.#srcUrl.hash) {\n throw 'The src attribute must have no query or fragment component.'\n }\n\n this.#payload = payload\n }\n\n /**\n * Generates the signature for the object using the private key provided by the\n * supplied callback and stores the result.\n *\n * @param {Promise<JsonWebKey>} privateJwkPromise the private key used for signing.\n * @return {Promise<string>} resolved with the base64 signature if signed successfully.\n */\n async signWith(privateJwkPromise) {\n if (this.#payload == null) {\n throw 'No payload to sign'\n }\n\n const privateJwk = await privateJwkPromise\n const privateKey = await crypto.subtle.importKey(\n 'jwk',\n privateJwk,\n ALGORITHM,\n true,\n ['sign'],\n )\n const toSign = JSON.stringify(this.#payload)\n const messageBuffer = new TextEncoder().encode(toSign).buffer\n const bufferSource = new Uint8Array(messageBuffer)\n const signature = await crypto.subtle.sign(\n ALGORITHM,\n privateKey,\n bufferSource,\n )\n const signatureBase64 = Token.bytesToBase64(new Uint8Array(signature))\n this.#signatureBase64 = signatureBase64\n return signatureBase64\n }\n\n /**\n * Retrieves the signatory using the `iss` field, checks the signature\n * and if valid sets the signatory property in this instance.\n *\n * @throws {string} if signatory cannot be retrieved or signature doesn't match.\n */\n async verifySignatory() {\n const issuer = String(this.getIssuer())\n const {\n signatory,\n publicKey,\n } = await Token.#getCachedSignatory(issuer)\n this.#signatory = signatory\n this.#validateSignatory()\n await this.#checkSignatureWithPublicKey(publicKey)\n }\n\n /**\n * Fetches the signatory from the `iss` URL, setting the instance\n * property.\n *\n * The signatory comprises the `src` representing the app that has\n * signed the token and the public `jwk`.\n *\n * @throws {string} error if fetch fails or returns an error response.\n */\n async fetchSignatory() {\n const issuer = String(this.getIssuer())\n this.#signatory = await Token.#fetchSignatoryForIssuer(issuer)\n this.#validateSignatory()\n }\n\n /**\n * Checks the signature using the public key provided by the supplied callback,\n * and stores the result.\n *\n * @param {Promise<JsonWebKey>} publicJwkPromise the public key used to verify the signature.\n * @throws {string} exception if signature cannot be verified or the public key is null or error.\n */\n async checkSignature(publicJwkPromise) {\n if (!this.#payload || !this.#signatureBase64) {\n throw 'checkSignature: no payload or signatureBase64'\n }\n\n const publicJwk = await publicJwkPromise\n const publicKey = await Token.#importPublicKey(\n publicJwk,\n String(this.getIssuer()),\n )\n await this.#checkSignatureWithPublicKey(publicKey)\n }\n\n /**\n * Checks the signature using an already imported public key.\n *\n * @param {CryptoKey} publicKey imported public key used to verify the signature.\n * @throws {string} exception if signature cannot be verified.\n */\n async #checkSignatureWithPublicKey(publicKey) {\n if (!this.#payload || !this.#signatureBase64) {\n throw 'checkSignature: no payload or signatureBase64'\n }\n const toCheck = JSON.stringify(this.#payload)\n const messageBuffer = new TextEncoder().encode(toCheck)\n const sigBuffer = Token.base64ToBytes(this.#signatureBase64)\n const isVerified = await crypto.subtle.verify(\n ALGORITHM,\n publicKey,\n sigBuffer,\n messageBuffer,\n )\n if (!isVerified) {\n throw `Signature rejected by issuer ${this.getIssuer()}`\n }\n }\n\n /**\n * Validates that the issuer and signatory source are permitted to sign\n * tokens asserting this token source.\n *\n * @throws {string} exception if the signatory source does not match the token source.\n */\n #validateSignatory() {\n if (!this.#signatory) {\n throw 'validateSignatory: no signatory'\n }\n\n const signatorySrc = this.#signatory.src\n const tokenSrc = this.getSrc()\n const issuer = this.getIssuer()\n\n if (issuer.origin !== this.getSub()) {\n throw `Token issuer origin '${issuer.origin}' should be token sub '${this.getSub()}'`\n }\n\n if (tokenSrc === Token.Source.PARTY_CONTROL) {\n if (signatorySrc !== Token.Source.PARTY_CONTROL) {\n throw `Token src '${tokenSrc}' cannot be signed by signatory src '${signatorySrc}'`\n }\n } else if (\n this.#srcUrl.protocol === 'http:' || this.#srcUrl.protocol === 'https:'\n ) {\n if (\n signatorySrc !== tokenSrc &&\n signatorySrc !== Token.Source.PARTY_CONTROL\n ) {\n throw `Token src '${tokenSrc}' cannot be signed by signatory src '${signatorySrc}'`\n }\n } else {\n throw `Unsupported token src '${tokenSrc}'`\n }\n\n if (signatorySrc === Token.Source.PARTY_CONTROL) {\n if (!issuer.pathname.startsWith('/device/')) {\n throw `Party control signatory issuer path '${issuer.pathname}' must start with /device/`\n }\n return\n }\n\n const signatoryUrl = new URL(signatorySrc)\n if (\n signatoryUrl.protocol !== 'http:' && signatoryUrl.protocol !== 'https:'\n ) {\n throw `Invalid signatory src '${signatorySrc}'`\n }\n if (!MATCH_APP_ISSUER_PATH.test(issuer.pathname)) {\n throw `App signatory issuer path '${issuer.pathname}' must match /issuer/<appId>`\n }\n }\n\n /**\n * Returns a signatory cache entry from cache or network.\n *\n * @param {string} issuer issuer URL.\n * @returns {Promise<SignatoryCacheEntry>}\n */\n static async #getCachedSignatory(issuer) {\n const cached = Token.#getFreshCachedSignatory(issuer)\n if (cached) {\n return cached\n }\n\n const inFlight = Token.#signatoryCacheInFlight.get(issuer)\n if (inFlight) {\n return await inFlight\n }\n\n const promise = (async () => {\n const signatory = await Token.#fetchSignatoryForIssuer(issuer)\n const publicKey = await Token.#importPublicKey(signatory.jwk, issuer)\n const entry = {\n signatory,\n publicKey,\n expiresAt: Date.now() + Token.#SIGNATORY_CACHE_TTL_MILLIS,\n }\n Token.#putCachedSignatory(issuer, entry)\n return entry\n })()\n\n Token.#signatoryCacheInFlight.set(issuer, promise)\n try {\n return await promise\n } finally {\n Token.#signatoryCacheInFlight.delete(issuer)\n }\n }\n\n /**\n * Returns a currently valid cached entry if present.\n *\n * @param {string} issuer issuer URL.\n * @returns {SignatoryCacheEntry | null}\n */\n static #getFreshCachedSignatory(issuer) {\n const entry = Token.#signatoryCache.get(issuer)\n if (!entry) {\n return null\n }\n\n if (entry.expiresAt <= Date.now()) {\n Token.#signatoryCache.delete(issuer)\n return null\n }\n\n // Refresh recency.\n Token.#signatoryCache.delete(issuer)\n Token.#signatoryCache.set(issuer, entry)\n return entry\n }\n\n /**\n * Adds/replaces a cache entry and enforces bounded size.\n *\n * @param {string} issuer issuer URL.\n * @param {SignatoryCacheEntry} entry signatory cache entry.\n */\n static #putCachedSignatory(issuer, entry) {\n if (Token.#signatoryCache.has(issuer)) {\n Token.#signatoryCache.delete(issuer)\n }\n Token.#signatoryCache.set(issuer, entry)\n\n while (Token.#signatoryCache.size > Token.#SIGNATORY_CACHE_MAX_ENTRIES) {\n const oldestKey = Token.#signatoryCache.keys().next().value\n if (!oldestKey) {\n break\n }\n console.log('Evicting token signatory cache entry due max size', {\n issuer,\n entryExpiresAt: entry.expiresAt,\n cacheMaxEntries: Token.#SIGNATORY_CACHE_MAX_ENTRIES,\n })\n Token.#signatoryCache.delete(oldestKey)\n }\n }\n\n /**\n * Fetches signatory details for the supplied issuer.\n *\n * @param {string} issuer issuer URL.\n * @returns {Promise<Signatory>}\n * @throws {string} if issuer cannot be read.\n */\n static async #fetchSignatoryForIssuer(issuer) {\n try {\n const response = await fetch(issuer, {\n headers: {\n 'content-type': 'application/json',\n },\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n const signatory = json.ok\n if (\n !signatory ||\n typeof signatory !== 'object' ||\n !signatory.jwk ||\n typeof signatory.jwk !== 'object'\n ) {\n throw 'Invalid signatory response'\n }\n if (\n typeof signatory.src !== 'string'\n ) {\n throw 'Invalid signatory src'\n }\n return signatory\n } catch (e) {\n throw `Failed to read signatory at ${issuer}: ${e}`\n }\n }\n\n /**\n * Imports and validates a JWK for signature verification.\n *\n * @param {JsonWebKey} publicJwk public key as JWK.\n * @param {string} issuer issuer URL.\n * @returns {Promise<CryptoKey>}\n * @throws {string} if key is missing or invalid.\n */\n static async #importPublicKey(publicJwk, issuer) {\n if (!publicJwk || typeof publicJwk !== 'object' || 'error' in publicJwk) {\n throw `Missing or invalid public JWK at issuer ${issuer}`\n }\n try {\n return await crypto.subtle.importKey('jwk', publicJwk, ALGORITHM, true, [\n 'verify',\n ])\n } catch (_e) {\n throw `Missing or invalid public JWK at issuer ${issuer}`\n }\n }\n\n /**\n * Returns the payload with the `sig` property added.\n *\n * @return {TokenPayload} the payload with `sig` property.\n */\n getSignedPayload() {\n if (!this.#signatureBase64 || !this.#payload) {\n throw 'getSignedPayload: no signatureBase64 or payload'\n }\n\n return {\n ...this.#payload,\n sig: this.#signatureBase64,\n }\n }\n\n /**\n * Returns the payload whether signed or not.\n *\n * @return {TokenPayload} the payload object without signature.\n */\n getPayload() {\n if (!this.#payload) {\n throw 'getPayload: no payload'\n }\n return this.#payload\n }\n\n /**\n * Returns the `aud` field as a string.\n *\n * @return {string} the `aud` field value.\n */\n getAud() {\n return this.#audUrl.origin\n }\n\n /**\n * Returns the `aud` host which is the party name handling the request.\n *\n * @return {string} the party name.\n */\n getParty() {\n return this.#audUrl.host\n }\n\n /**\n * Returns the `sub` field as a string.\n *\n * @return {string} the `sub` field value.\n */\n getSub() {\n return this.#subUrl.origin\n }\n\n /**\n * Returns the `src` field as a string.\n *\n * @return {string} the `src` field value.\n */\n getSrc() {\n return String(this.#srcUrl)\n }\n\n /**\n * Returns the `sub` host, which is the counterparty name making the\n * request.\n *\n * @return {string} the counterparty name.\n */\n getCounterparty() {\n return this.#subUrl.host\n }\n\n /**\n * Returns the source of the signatory who signed on behalf of the\n * counterparty. This is only available once the token is\n * verified, as it is provided by the `iss` endpoint.\n *\n * @returns {string} source of the verified signatory.\n */\n getSignatorySrc() {\n if (!this.#signatory) {\n throw 'getSignatorySrc: no signatory'\n }\n\n return this.#signatory.src\n }\n\n /**\n * Returns the `src` URL.\n *\n * @return {URL} the source URL.\n */\n getSourceUrl() {\n return this.#srcUrl\n }\n\n /**\n * Returns the `issuer` field as a URL. The counterparty is inferred from\n * the host name which generally matches the sub field.\n *\n * @return {URL} the issuer URL.\n */\n getIssuer() {\n return new URL(this.getPayload().iss)\n }\n\n /**\n * Returns the list of zero or more sources for which scope is requested.\n *\n * @return {string[]} a list of source URL strings.\n */\n getSources() {\n if (!this.#payload) {\n throw 'getSources: no payload'\n }\n\n const sources = []\n for (const source in this.#payload.scope) {\n sources.push(source)\n }\n return sources\n }\n\n /**\n * Returns the scope for the source as an array of capability tokens.\n *\n * These can be separated by any mix of space, comma, semicolon\n * or vertical bar.\n *\n * @param {string} source whose capabilities are returned.\n * @return {string[]} zero or more scope tokens.\n */\n getCapabilities(source) {\n if (!this.#payload) {\n throw 'getCapabilities: no payload'\n }\n\n const scope = this.#payload.scope\n if (!(source in scope)) {\n throw `Source '${source}' not in scope`\n }\n\n return scope[source].split(Token.SCOPE_SEPARATOR)\n }\n\n /**\n * Returns true if the supplied capability is present in the token\n * scope under the specified source, otherwise false.\n *\n * @param {string} source the source under which the capability is expected.\n * @param {string} capability the capability being tested.\n * @return {boolean} true if capability is present in the scope, otherwise false.\n */\n hasCapability(source, capability) {\n return this.getCapabilities(source).includes(capability)\n }\n\n /**\n * Checks the token is within the period defined by the `iat` and `exp`\n * date fields.\n *\n * @throws {string} exception if not within valid period.\n */\n checkPeriod() {\n if (!this.#payload) {\n throw 'checkPeriod: no payload'\n }\n\n const {\n iat,\n exp,\n } = this.#payload\n\n const now = Date.now()\n if (now < iat - TOKEN_IAT_LEEWAY_MILLIS) {\n throw 'Token is not yet valid'\n }\n\n if (now > exp) {\n throw 'Token has expired'\n }\n }\n\n /**\n * Returns the signed object including the `sig` property as a base64 string.\n *\n * @return {string} the signed object as a base64 string.\n */\n asSignedBase64() {\n return btoa(JSON.stringify(this.getSignedPayload()))\n }\n\n /**\n * Returns a base64 JSON string corresponding to the provided byte array.\n *\n * @param {Uint8Array} bytes to be encoded as a base64 string.\n * @returns {string} the encoded bytes.\n */\n static bytesToBase64(bytes) {\n const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join('')\n return btoa(binString)\n }\n\n /**\n * Returns the byte array decoded from the base64 string.\n *\n * @param {string} base64\n * @returns {Uint8Array} the decoded bytes.\n */\n static base64ToBytes(base64) {\n const binString = atob(base64)\n return Uint8Array.from(binString, (m) => m.codePointAt(0) ?? 0)\n }\n\n /**\n * Returns a new token using the x-tabserver-token header on the\n * request.\n *\n * @param {Request} request the request.\n * @return {Token | null} the token, or null if no header is present.\n * @throws {string} exception if token cannot be constructed.\n */\n static from(request) {\n const tokenBase64 = request.headers.get(Token.TOKEN_HEADER)\n if (tokenBase64) {\n return new Token(tokenBase64)\n }\n\n return null\n }\n\n /**\n * Converts an object whose string keys map to token objects\n * into URL-friendly search params suitable for use in a\n * query string or fragment.\n *\n * The string keys are normally the audience hostname, or the\n * special key 'party' which holds the identity token whose\n * `aud` and `sub` are the same.\n *\n * @param {TokenSet} tokenSet the set of tokens keyed by string.\n * @returns {string}\n */\n static toSearchString(tokenSet) {\n const searchParams = new URLSearchParams()\n for (const key in tokenSet) {\n const token = tokenSet[key]\n const tokenBase64 = token.asSignedBase64()\n searchParams.append(key, tokenBase64)\n }\n return searchParams.toString()\n }\n}\n", "/**\n * @import { Token } from './Token.js'\n * @import { Ok, Error } from '../ts/Responses.ts'\n * @import { JSONValue } from '../ts/Assertions.ts'\n */\n\n/**\n * Storage keys are 1 or more slash-separated components\n * using the characters a-z, A-Z, 0-9, hyphen, colon and period.\n *\n * Examples:\n * - acmeSetting\n * - acme.com/applicant/22fc01-b0d3084870b-fcae49ac2-892668\n * - address/line1\n * - url/acme.com\n *\n * The key needs to work as part of a REST/HTTP pathname.\n */\nconst KEY_PATTERN = /^[\\w\\-.:]+(?:\\/[\\w\\-.:]+)*$/\n\n/**\n * As above, but % and _ are both allowed as a wildcards.\n */\nconst KEYLIKE_PATTERN = /^[\\w\\-.:%_]+(?:\\/[\\w\\-.:%_]+)*$/\n\n/**\n * An instance of this class is normally obtained from BrowserApp, which constructs\n * the instance using the party token.\n */\nexport class Storage {\n #token // Party token which must include getitem and setitem capabilities on party:control.\n\n /**\n * @param {Token} token to use for storage requests.\n */\n constructor(token) {\n this.#token = token\n }\n\n /**\n * Gets the item whose key is provided. Keys are in the form\n * 'some[/key]*' without leading or trailing '/'.\n *\n * @param {string} key used to retrieve the item.\n * @return {Promise<Ok<JSONValue> | Error>} ok or error result.\n */\n getItem(key) {\n return getItem(this.#token, key)\n }\n\n /**\n * Gets the list (possibly empty) of items matching the SQL-like\n * argument.\n *\n * @param {string} keylike a key matcher such as 'myApp/%'.\n * @return {Promise<Ok<JSONValue[]>>} a list of values.\n */\n getItemsLike(keylike) {\n return getItemsLike(this.#token, keylike)\n }\n\n /**\n * Stores an item with the supplied key and value.\n *\n * @param {string} key the key to use.\n * @param {JSONValue} value to store.\n * @return {Promise<Ok<true> | Error>} ok if successful, error otherwise.\n */\n setItem(key, value) {\n return setItem(this.#token, key, value)\n }\n\n /**\n * Removes the item with the specified key, if any.\n *\n * @param {string} key the key to remove.\n * @return {Promise<Ok<true> | Error>} ok if successful, error otherwise.\n */\n removeItem(key) {\n return removeItem(this.#token, key)\n }\n}\n\n/**\n * Throws an exception if the key being used is not\n * in a valid format.\n *\n * @param {string} key\n */\nfunction assertValid(key) {\n if (key.match(KEY_PATTERN) == null) {\n throw `DaemonStorage: Invalid key: '${key}`\n }\n}\n\n/**\n * Throws an exception if the key like pattern being used\n * is not in a valid format.\n *\n * @param {string} keylike\n */\nfunction assertValidLike(keylike) {\n if (keylike.match(KEYLIKE_PATTERN) == null) {\n throw `DaemonStorage: Invalid like key: ${keylike}`\n }\n}\n\n/**\n * Returns the value as a JSON string\n *\n * @param {JSONValue} value\n * @return {string} JSON string.\n * @throws {string} exception if the value is not JSON.\n */\nfunction serialise(value) {\n try {\n return JSON.stringify(value)\n }\n catch (_e) {\n throw `DaemonStorage: Invalid value`\n }\n}\n\n/**\n * Sets an item in daemon storage.\n *\n * The value must be an object or primitive\n * which is serialised before saving.\n *\n * @param {Token} token to use.\n * @param {string} key the item key.\n * @param {any} value the serialisable value for the item.\n * @return {Promise<Ok<true> | Error>} ok or error object.\n */\nexport async function setItem(token, key, value) {\n assertValid(key)\n const body = serialise(value)\n const url = `${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'PUT',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64(),\n 'Content-Type': 'application/json'\n },\n body\n })\n return response.json()\n}\n\n/**\n * Gets an item from daemon storage.\n *\n * Returns an 'ok' object with the value if present, otherwise\n * returns an 'error' object.\n *\n * @param {Token} token to use.\n * @param {string} key the item key to retrieve.\n * @return {Promise<Ok<JSONValue> | Error>} ok object with value, or error.\n */\nexport async function getItem(token, key) {\n assertValid(key)\n const url = `${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return response.json()\n}\n\n/**\n * Gets zero or more items from daemon storage, whose\n * keys match the keyLike pattern. This is normally\n * something like 'my/prefix/%' which gets all values\n * with that prefix.\n *\n * You can use both '%' and '_' as wildcards for string\n * of any length and single character respectively.\n *\n * @param {Token} token to use.\n * @param {string} keylike the item keylike pattern to match.\n * @return {Promise<Ok<JSONValue[]>>} ok object with list, or error.\n */\nexport async function getItemsLike(token, keylike) {\n assertValidLike(keylike)\n const url = new URL(`${token.getAud()}/storage`)\n url.searchParams.append('like', keylike)\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return await response.json()\n}\n\n/**\n * Removes an item from daemon storage.\n *\n * @param {Token} token to use.\n * @param {string} key the item key to remove.\n * @return {Promise<Ok<true> | Error>} ok or error object.\n */\nexport async function removeItem(token, key) {\n assertValid(key)\n const url =`${token.getAud()}/storage/${key}`\n const response = await fetch(url, {\n method: 'DELETE',\n headers: {\n 'X-Tabserver-Token': token.asSignedBase64()\n }\n })\n return response.json()\n}\n", "const Char = {\n Ash: '\u00E6',\n Hash: '#'\n}\n\nconst Encoded = {\n Ash: encodeURIComponent('\u00E6')\n}\n\nexport class ParamExtractor {\n params = new URLSearchParams()\n\n /**\n * @param {string} hash the hash string, with or without leading '#'.\n * @returns {string} the cleaned hash string.\n */\n extractHash(hash) {\n hash = hash.startsWith(Char.Hash) ? hash.substring(1) : hash\n const i = hash.indexOf(Encoded.Ash)\n if (i === -1) {\n return hash\n }\n\n const params = new URLSearchParams(hash.slice(i))\n this.extract(params)\n return hash.slice(0, i)\n }\n\n /**\n * @param {string} query the query string.\n * @returns {string} the cleaned query string.\n */\n extractQuery(query) {\n const params = new URLSearchParams(query)\n this.extract(params)\n return params.toString()\n }\n\n /**\n * Extracts \u00E6-prefixed params into this instance,\n * then deletes them from the supplied searchparams\n * object.\n *\n * @param {URLSearchParams} params\n */\n extract(params) {\n const keys = Array.from(params.keys())\n for (const key of keys) {\n if (key.startsWith(Char.Ash)) {\n this.params.set(key.substring(1), params.get(key) ?? '')\n params.delete(key)\n }\n }\n }\n\n getParams() {\n return this.params\n }\n}\n", "import { Token } from './Token.js'\nimport { Storage } from './Storage.js'\nimport { getAppId } from './Digest.js'\nimport { ParamExtractor } from './ParamExtractor.js'\n\n/**\n * @module\n * Provides convenience support for WebDaemon apps delivered\n * through browsers.\n *\n * Use the singleton pattern, providing app name on first call\n * to getInstance.\n */\n\nconst PARTY_PARAM = 'party'\nconst PREFIX_PARAM = 'prefix'\nconst LAST_KNOWN_DAEMON = 'lastKnownDaemon' // In app's localStorage.\n\nexport class BrowserApp {\n\n /** @type {BrowserApp | null} */\n static #instance\n\n #appName\n #partyKey\n #tokensKey\n #appUrlKey\n\n /**\n * Gets the singleton instance of BrowserApp. On invocations\n * after the first, the app name must be omitted or match\n * the first-used app name.\n *\n * @param {string=} appName\n * @return {Promise<BrowserApp>} instance of browser app.\n */\n static async getInstance(appName = undefined) {\n if (\n !appName &&\n !BrowserApp.#instance\n ) {\n throw `Must provide app name`\n }\n\n if (\n appName &&\n BrowserApp.#instance &&\n appName !== BrowserApp.#instance.#appName\n ) {\n throw `Cannot change app name from ${BrowserApp.#instance.#appName} to ${appName}`\n }\n\n if (appName && !BrowserApp.#instance) {\n BrowserApp.#instance = new BrowserApp(appName)\n await BrowserApp.#instance.init()\n }\n\n if (!BrowserApp.#instance) {\n throw `No instance of BrowserApp`\n }\n\n return BrowserApp.#instance\n }\n\n /**\n * Constructs a BrowserApp instance with the app name. This should be\n * unique per origin. Spaces are _NOT_ allowed in this name.\n *\n * @param {string} appName a unique name for this app per origin.\n */\n constructor(appName) {\n this.#appName = appName\n this.#partyKey = `${appName}Party`\n this.#tokensKey = `${appName}Tokens`\n this.#appUrlKey = `${appName}AppUrl`\n }\n\n /**\n * Initialises the instance with the party name and tokens passed\n * in the #fragment and ?query string search parameters.\n *\n * - party is passed in the special parameter 'party'.\n * - tokens are passed in the remaining parameters keyed by host name.\n *\n * The #fragment starting at the first '\u00E6' (if present) is used and\n * any params whose name starts with the special character '\u00E6' are extracted.\n *\n * Any fragment prior to this is retained in the browser location.\n *\n * Any query params whose name starts with the special character '\u00E6' are\n * extracted.\n *\n * The window location is replaced with the 'clean' version that\n * no longer includes the tokens.\n *\n * @return {Promise<BrowserApp>} instance promise.\n * @throws {string} error if party token `src` does not match our window location.\n */\n async init() {\n const ourUrl = new URL(globalThis.location.href)\n\n const extractor = new ParamExtractor()\n const newHash = extractor.extractHash(ourUrl.hash)\n const newSearch = extractor.extractQuery(ourUrl.search)\n const daemonParams = extractor.getParams()\n\n if (daemonParams.has(PARTY_PARAM)) {\n sessionStorage[this.#tokensKey] = daemonParams.toString()\n const identityToken = this.getToken()\n await identityToken.verifySignatory()\n sessionStorage[this.#partyKey] = identityToken.getParty()\n localStorage.setItem(LAST_KNOWN_DAEMON, identityToken.getParty())\n\n const srcUrl = identityToken.getSourceUrl()\n if (srcUrl.origin !== ourUrl.origin || srcUrl.pathname !== ourUrl.pathname) {\n throw `Party token is for different app: ${srcUrl}`\n }\n }\n\n ourUrl.hash = newHash\n ourUrl.search = newSearch\n if (\n daemonParams.has(PARTY_PARAM) ||\n sessionStorage[this.#appUrlKey] === undefined\n ) {\n sessionStorage[this.#appUrlKey] = ourUrl.toString()\n }\n globalThis.history.replaceState(null, '', ourUrl.toString())\n return this\n }\n\n /**\n * Returns true if this app is an orphan, i.e. has not been launched from\n * a D\u00E6mon shell.\n *\n * Otherwise returns false.\n *\n * @return {boolean} true if an orphan without D\u00E6mon, otherwise false.\n */\n isOrphan() {\n return (\n sessionStorage[this.#partyKey] === undefined ||\n sessionStorage[this.#tokensKey] === undefined\n )\n }\n\n /**\n * Returns the app name. This is normally used to disambiguate properties\n * of this app from others, such as those sharing a web origin and therefore\n * sharing localStorage or sessionStorage.\n *\n * Note that this is a client-side name not known to the tabserver.\n *\n * @return {string} the app name.\n */\n getAppName() {\n return this.#appName\n }\n\n /**\n * Returns the app url, obtained from window.location at time of app init,\n * or the empty string if not initialised.\n *\n * @return {string} the app url, or the empty string.\n */\n getAppUrl() {\n return sessionStorage[this.#appUrlKey]\n }\n\n /**\n * Returns the party hostname, being the D\u00E6mon that launched this app.\n *\n * @return {string} the party (D\u00E6mon) hostname.\n */\n getParty() {\n return sessionStorage[this.#partyKey]\n }\n\n /**\n * Returns the party origin, being the protocol (http: or https:)\n * and the party hostname.\n *\n * @return {string} the party origin.\n */\n getPartyOrigin() {\n return `${globalThis.location.protocol}//${this.getParty()}`\n }\n\n /**\n * Returns the default prefix associated with the app url if specified,\n * or this app if not. The default prefix is simply the app ID being a\n * short digest of the app URL, excluding any # fragment.\n *\n * @param {string | URL | null} appUrl for which the default prefix is required (optional).\n * @return {Promise<string>} the default prefix for the app.\n */\n getDefaultPrefix(appUrl = null) {\n if (!appUrl) {\n appUrl = this.getAppUrl()\n }\n\n const cleanUrl = new URL(appUrl)\n cleanUrl.hash = ''\n return getAppId(cleanUrl)\n }\n\n /**\n * Returns the likely prefix for this app.\n *\n * This is the value of the prefix parameter if present,\n * or the default prefix for the app if not.\n *\n * @return {Promise<string>} prefix obtained from prefix= param, or computed from the app URL.\n */\n getPrefix() {\n if (this.hasParam(PREFIX_PARAM)) {\n return Promise.resolve(this.getParam(PREFIX_PARAM) ?? '')\n }\n return this.getDefaultPrefix()\n }\n\n /**\n * Returns the likely url for the (optional) named tab on this app's agent.\n *\n * This will be wrong if the YAML file has a `prefix=` specified which\n * is not set to the value of a provided `prefix=` parameter.\n *\n * @param {string?} tab name, e.g. 'v1'.\n * @returns {Promise<string>} the url of this tab on this app.\n */\n async getAgentUrl(tab) {\n const origin = this.getPartyOrigin()\n const prefix = await this.getPrefix()\n const agentUrl = `${origin}/tab/${prefix}`\n return tab ? `${agentUrl}/${tab}` : agentUrl\n }\n\n /**\n * @typedef {object} AgentEndpointOptions\n * @property {string=} party party hostname. Defaults to the launching party.\n * @property {string | URL=} appUrl app URL used to derive the protocol and default prefix.\n * @property {string=} prefix agent prefix. Defaults to the app's default prefix.\n * @property {string=} tab optional tab name appended to the endpoint.\n */\n\n /**\n * Returns the agent endpoint associated with the party, app, prefix and tab\n * where all options are, well, optional.\n *\n * @param {AgentEndpointOptions} options\n * @returns {Promise<string>} the agent endpoint.\n */\n async getAgentEndpoint(options) {\n const {\n party = this.getParty(),\n appUrl = this.getAppUrl(),\n prefix = await this.getDefaultPrefix(appUrl),\n tab\n } = options\n\n const protocol = new URL(appUrl).protocol\n const appEndpoint = `${protocol}//${party}/tab/${prefix}`\n return tab ? `${appEndpoint}/${tab}` : appEndpoint\n }\n\n /**\n * Returns the base64-encoded token for the supplied party. If no\n * party is specified, returns the token for the launching party.\n *\n * @param {string} party the party, or the launching party if not specified.\n * @return {string} the base64-encoded token for the party.\n */\n getTokenBase64(party = PARTY_PARAM) {\n const tokens = sessionStorage[this.#tokensKey]\n const searchParams = new URLSearchParams(tokens)\n const tokenBase64 = searchParams.get(party)\n if (!tokenBase64) {\n throw `No token for ${party}, check 'audience' in YML`\n }\n return tokenBase64\n }\n\n\n /**\n * Gets the token for a party either as a base64-encoded string, or as a\n * Token object depending on the asBase64 argument.\n *\n * If the party is not passed or is null, the default is the launching party.\n * If the asBase64 is not passed, the default is true.\n *\n * @param {string=} party the audience for the token. Defaults to the identity token 'party'.\n * @return {Token} the pre-generated token for the given party.\n */\n getToken(party = PARTY_PARAM) {\n const tokenBase64 = this.getTokenBase64(party)\n return new Token(tokenBase64)\n }\n\n /**\n * Gets the value of the named parameter, or null if no value is supplied.\n *\n * The parameter name is case insensitive.\n *\n * The parameter is specified as:\n * - an attribute on the webdaemon meta tag, or\n * - a search parameter in the query string of the URL\n *\n * where the query string parameter takes priority if present.\n *\n * @param {string} name\n * @return {string | null} parameter value or null if not present.\n */\n getParam(name) {\n\n // Return a matching search parameter if present.\n const searchParams = new URL(globalThis.location.href).searchParams\n for (const [paramName, value] of searchParams) {\n if (name.toLowerCase() == paramName.toLowerCase()) {\n return value\n }\n }\n\n // Return meta element attribute if present, or null.\n const metaElement = document.querySelector('link[rel=webdaemon]')\n return metaElement?.getAttribute(name) ?? null\n }\n\n /**\n * Returns true if the named parameter is present, even if with empty\n * or null value.\n *\n * @param {string} name\n * @return {boolean} true if the parameter exists, otherwise false.\n */\n hasParam(name) {\n const searchParams = new URL(globalThis.location.href).searchParams\n if (searchParams.has(name)) {\n return true\n }\n\n const metaElement = document.querySelector('link[rel=webdaemon]')\n return metaElement?.hasAttribute(name) ?? false\n }\n\n /**\n * Returns the currently raised launch alert for this app,\n * or null if none.\n *\n * @typedef {import('./Alert.js').AlertEntry} AlertEntry\n *\n * @return { Promise<AlertEntry | null> } raised launch alert, or null if none.\n * @throws {string} exception if alert catalog cannot be retrieved.\n */\n async getLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/catalog`)\n url.searchParams.append('alert', '')\n const response = await fetch(url, {\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Accept': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const alert = json.ok.alert.find(\n /** @param {AlertEntry} alert */\n alert => (\n alert.type == 'LAUNCH' &&\n alert.sourceUrl == new URL(this.getAppUrl())\n )\n )\n return alert || null\n }\n\n /**\n * Resolves the currently raised launch alert for this app, if any.\n *\n * @throws {string} exception if alert cannot be resolved.\n */\n async resolveLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/resolve`)\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n type: 'LAUNCH',\n source: this.getAppUrl()\n })\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n }\n\n /**\n * Resolves the currently raised launch alert for this app, if any.\n *\n * @throws {string} exception if alert cannot be resolved.\n */\n async rejectLaunchAlert() {\n const origin = this.getPartyOrigin()\n const url = new URL(`${origin}/reject`)\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'X-Tabserver-Token': this.getTokenBase64(),\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n type: 'LAUNCH',\n source: this.getAppUrl()\n })\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n }\n\n /**\n * Gets a Storage instance using the party token.\n *\n * @returns {Storage} instance.\n */\n getStorage() {\n return new Storage(this.getToken())\n }\n}\n", "import { BrowserApp } from \"./BrowserApp.js\";\n\nconst DEFAULT_AGENT_TAB = \"agent\";\n\n/**\n * @typedef {{\n * isOrphan: Function,\n * getAgentUrl: Function,\n * getTokenBase64: Function,\n * getParty: Function,\n * getToken: Function,\n * }} BrowserChatApp\n */\n\n/**\n * @typedef {{\n * events?: object[],\n * clientToolCalls?: Array<{ id: string, name: string, input?: object }>,\n * }} ConversationRound\n */\n\nexport class ClientToolRegistry {\n /** @type {Map<string, { descriptor: object, execute: Function }>} */\n #tools = new Map();\n\n /**\n * @param {string} name\n * @param {object} descriptor\n * @param {Function} execute\n * @return {ClientToolRegistry}\n */\n register(name, descriptor, execute) {\n this.#tools.set(name, { descriptor, execute });\n return this;\n }\n\n /**\n * @return {object[]}\n */\n descriptors() {\n return [...this.#tools.values()].map((tool) => tool.descriptor);\n }\n\n /**\n * @param {Array<{ id: string, name: string, input?: object }>} toolCalls\n * @param {object=} context\n * @return {Promise<Array<object>>}\n */\n async executeToolCalls(toolCalls, context = {}) {\n const results = [];\n\n for (const call of toolCalls) {\n const clientTool = this.#tools.get(call.name);\n if (!clientTool) {\n results.push({\n toolCallId: call.id,\n name: call.name,\n content: `No client tool named ${call.name}`,\n isError: true,\n });\n continue;\n }\n\n try {\n const content = await clientTool.execute(call.input || {}, context);\n results.push({\n toolCallId: call.id,\n name: call.name,\n content: JSON.stringify(content),\n });\n } catch (e) {\n results.push({\n toolCallId: call.id,\n name: call.name,\n content: String(e),\n isError: true,\n });\n }\n }\n\n return results;\n }\n}\n\nexport class GenerativeChatClient {\n #app;\n #appName;\n #agentTab;\n #agentUrl = null;\n #clientTools;\n\n /**\n * @param {{\n * app?: BrowserChatApp,\n * appName?: string,\n * agentTab?: string,\n * clientTools?: ClientToolRegistry,\n * }=} options\n */\n constructor(options = {}) {\n this.#app = options.app;\n this.#appName = options.appName;\n this.#agentTab = options.agentTab ?? DEFAULT_AGENT_TAB;\n this.#clientTools = options.clientTools ?? new ClientToolRegistry();\n }\n\n /**\n * @return {BrowserChatApp | null}\n */\n get app() {\n return this.#app ?? null;\n }\n\n /**\n * @return {string | null}\n */\n get agentUrl() {\n return this.#agentUrl;\n }\n\n /**\n * @return {ClientToolRegistry}\n */\n get clientTools() {\n return this.#clientTools;\n }\n\n /**\n * @return {Promise<GenerativeChatClient>}\n */\n async init() {\n this.#app ??= await BrowserApp.getInstance(this.#appName);\n if (!this.#app.isOrphan()) {\n this.#agentUrl = await this.#app.getAgentUrl(this.#agentTab);\n }\n return this;\n }\n\n /**\n * @return {boolean}\n */\n isOrphan() {\n return Boolean(this.#app?.isOrphan());\n }\n\n /**\n * @return {string}\n */\n getParty() {\n return this.#requireApp().getParty();\n }\n\n /**\n * @param {{ targetParty: string }} options\n * @return {Promise<object>}\n */\n listChats({ targetParty }) {\n return this.postAgent(\"/chats\", { targetParty });\n }\n\n /**\n * @param {{\n * targetParty: string,\n * chatId?: string | null,\n * systemPrompt?: string,\n * temperature?: number,\n * }} options\n * @return {Promise<object>}\n */\n createSession(options) {\n return this.postAgent(\"/session\", {\n targetParty: options.targetParty,\n ...(options.chatId ? { chatId: options.chatId } : {}),\n systemPrompt: options.systemPrompt,\n temperature: options.temperature,\n clientTools: this.#clientTools.descriptors(),\n });\n }\n\n /**\n * @param {{\n * targetParty: string,\n * chatId: string,\n * message: string,\n * temperature?: number,\n * toolsEnabled?: boolean,\n * toolDetails?: boolean,\n * onConversationResult?: Function,\n * onClientToolCalls?: Function,\n * }} options\n * @return {Promise<object>}\n */\n async sendMessage(options) {\n /** @type {ConversationRound} */\n let result = await this.postAgent(\"/send\", {\n targetParty: options.targetParty,\n chatId: options.chatId,\n message: options.message,\n temperature: options.temperature,\n toolsEnabled: options.toolsEnabled,\n toolDetails: options.toolDetails,\n });\n await options.onConversationResult?.(result);\n\n while (result.clientToolCalls?.length) {\n await options.onClientToolCalls?.(result.clientToolCalls);\n const toolResults = await this.#clientTools.executeToolCalls(\n result.clientToolCalls,\n { targetParty: options.targetParty },\n );\n result = await this.sendClientToolResults({\n targetParty: options.targetParty,\n chatId: options.chatId,\n toolResults,\n temperature: options.temperature,\n toolsEnabled: options.toolsEnabled,\n toolDetails: options.toolDetails,\n });\n await options.onConversationResult?.(result);\n }\n\n return result;\n }\n\n /**\n * @param {{\n * targetParty: string,\n * chatId: string,\n * toolResults: Array<object>,\n * temperature?: number,\n * toolsEnabled?: boolean,\n * toolDetails?: boolean,\n * }} options\n * @return {Promise<object>}\n */\n sendClientToolResults(options) {\n return this.postAgent(\"/client-tool-results\", {\n targetParty: options.targetParty,\n chatId: options.chatId,\n toolResults: options.toolResults,\n temperature: options.temperature,\n toolsEnabled: options.toolsEnabled,\n toolDetails: options.toolDetails,\n });\n }\n\n /**\n * @param {{ targetParty: string, chatId: string }} options\n * @return {Promise<object>}\n */\n closeChat({ targetParty, chatId }) {\n return this.postAgent(\"/close\", { targetParty, chatId });\n }\n\n /**\n * @param {{ targetParty: string, chatId: string }} options\n * @return {Promise<object>}\n */\n deleteChat({ targetParty, chatId }) {\n return this.postAgent(\"/delete\", { targetParty, chatId });\n }\n\n /**\n * @param {string} path\n * @param {object} body\n * @return {Promise<object>}\n */\n async postAgent(path, body) {\n if (!this.#agentUrl) throw \"Agent URL is not available\";\n const response = await fetch(`${this.#agentUrl}${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Tabserver-Token\": this.#requireApp().getTokenBase64(),\n },\n body: JSON.stringify(body),\n });\n\n const json = await response.json();\n if (\"error\" in json) throw json.error;\n return json.ok;\n }\n\n /**\n * @param {unknown} value\n * @return {string}\n */\n normalizeTargetParty(value) {\n const trimmed = String(value ?? \"\").trim();\n if (!trimmed) throw \"Daemon host is required\";\n\n try {\n const url = trimmed.includes(\"://\")\n ? new URL(trimmed)\n : new URL(`${globalThis.location.protocol}//${trimmed}`);\n if (!url.host) throw \"missing host\";\n return url.host;\n } catch (_e) {\n throw `Invalid daemon host: ${value}`;\n }\n }\n\n /**\n * @return {BrowserChatApp}\n */\n #requireApp() {\n if (!this.#app) throw \"Browser app is not initialised\";\n return this.#app;\n }\n}\n\n/**\n * @param {BrowserChatApp=} app\n * @return {ClientToolRegistry}\n */\nexport function createDefaultBrowserChatTools(app = undefined) {\n const registry = new ClientToolRegistry();\n registry.register(\n \"browser_daemon_identity\",\n {\n type: \"function\",\n name: \"browser_daemon_identity\",\n execution: \"client\",\n description:\n \"Get the active chat daemon and the counterparty it is talking to, derived from the browser launch token and active chat target.\",\n parameters: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n },\n /**\n * @param {object} _input\n * @param {{ targetParty?: string, targetAud?: string }} context\n */\n async (_input, context = {}) => {\n const browserApp = app ?? await BrowserApp.getInstance();\n const token = browserApp.getToken();\n const party = context.targetParty || token.getParty();\n const aud = context.targetAud ||\n `${globalThis.location.protocol}//${party}`;\n\n return {\n aud,\n sub: token.getSub(),\n party,\n counterparty: token.getCounterparty(),\n src: token.getSrc(),\n };\n },\n );\n registry.register(\n \"browser_language\",\n {\n type: \"function\",\n name: \"browser_language\",\n execution: \"client\",\n description: \"Get the browser language preferences for this page.\",\n parameters: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n },\n () => ({\n language: navigator.language,\n languages: Array.from(navigator.languages ?? []),\n }),\n );\n registry.register(\n \"browser_time\",\n {\n type: \"function\",\n name: \"browser_time\",\n execution: \"client\",\n description: \"Get the current timezone and local time from the browser.\",\n parameters: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n },\n () => {\n const now = new Date();\n const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n return {\n timeZone,\n isoTime: now.toISOString(),\n localTime: now.toLocaleString(undefined, { timeZone }),\n offsetMinutes: -now.getTimezoneOffset(),\n };\n },\n );\n return registry;\n}\n", "const DEFAULT_ALGORITHM = {\n name: 'RSASSA-PKCS1-v1_5',\n modulusLength: 2048,\n publicExponent: new Uint8Array([0x01, 0x00, 0x01]),\n hash: 'SHA-256'\n}\n\nexport class KeyPair {\n #algorithm\n\n /** @type {CryptoKeyPair | undefined} */\n #keypair\n\n constructor(algorithm = DEFAULT_ALGORITHM) {\n this.#algorithm = algorithm\n if (!crypto.subtle) {\n throw 'Crypto.subtle requires secure environment'\n }\n }\n\n async generate() {\n this.#keypair = await crypto.subtle.generateKey(\n this.#algorithm,\n true,\n ['sign', 'verify']\n )\n }\n\n async publicJwk() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const publicKey = this.#keypair.publicKey\n const publicJwk = await crypto.subtle.exportKey('jwk', publicKey)\n return publicJwk\n }\n\n async privateJwk() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const privateKey = this.#keypair.privateKey\n const privateJwk = await crypto.subtle.exportKey('jwk', privateKey)\n return privateJwk\n }\n\n async publicPem() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const publicKey = this.#keypair.publicKey\n const spkiKey = await crypto.subtle.exportKey('spki', publicKey)\n const base64Key = btoa(String.fromCharCode(...new Uint8Array(spkiKey)))\n const base64KeyLines = base64Key.match(/.{1,64}/g)?.join('\\n') ?? ''\n const publicPem = `-----BEGIN PUBLIC KEY-----\\n${base64KeyLines}\\n-----END PUBLIC KEY-----`\n return publicPem\n }\n\n async privatePem() {\n if (!this.#keypair) {\n throw `Must generate keypair`\n }\n const privateKey = this.#keypair.privateKey\n const pkcs8Key = await crypto.subtle.exportKey('pkcs8', privateKey)\n const base64Key = btoa(String.fromCharCode(...new Uint8Array(pkcs8Key)))\n const base64KeyLines = base64Key.match(/.{1,64}/g)?.join('\\n') ?? ''\n const privatePem = `-----BEGIN PRIVATE KEY-----\\n${base64KeyLines}\\n-----END PRIVATE KEY-----`\n return privatePem\n }\n}\n", "import { BrowserApp } from './BrowserApp.js'\n\n/**\n * @import { JSONValue } from '../ts/Assertions.ts'\n */\n\n/**\n * Returns the currently raised launch alert for this app,\n * or null if none.\n *\n * @typedef {Object} AlertEntry\n * @property {URL} sourceUrl - The URL of the alert source.\n * @property {string} sourceTitle - The title of the alert source.\n * @property {string} type - The alert type.\n * @property {JSONValue} value - The value associated with the alert.\n * @property {Date} created - When the alert was created.\n * @property {Date|null} expiry - When the alert expires or null if it doesn't expire.\n *\n * @return { Promise<AlertEntry | null> } raised launch alert, or null if none.\n * @throws {string} exception if alert catalog cannot be retrieved.\n */\nexport async function getLaunchAlert() {\n const app = await BrowserApp.getInstance()\n const origin = app.getPartyOrigin()\n const url = new URL(`${origin}/catalog`)\n url.searchParams.append('alert', '')\n const response = await fetch(url, {\n headers: {\n 'X-Tabserver-Token': app.getTokenBase64(),\n 'Accept': 'application/json'\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const alert = json.ok.alert.find(\n /** @param {AlertEntry} alert */\n alert => (\n alert.type == 'LAUNCH' &&\n alert.sourceUrl == new URL(app.getAppUrl())\n )\n )\n return alert || null\n}\n", "import { Token } from './Token.js'\n\n/**\n * This helper provides the parent capability for a website to\n * check and activate a daemon, and to create a device offer for\n * subsequent claim by consumer devices or browsers.\n *\n * The helper can operate either browser- or server-side.\n *\n * Usage\n * =====\n *\n * // Create the helper for a child daemon.\n * const helper = new ParentHelper()\n *\n * // Initialise with the child daemon being parented (aud), the\n * // source (src), the private key used to sign the token\n * // and the signatory url (iss).\n * await initWithKey(child, source, privateJwk, iss)\n *\n * // Succeeds if the child daemon is activated.\n * await helper.checkActivate()\n *\n * // Returns the claim code and check state of a new device offer.\n * await helper.fetchOffer()\n *\n * @typedef {Object} JsonWebKey\n * @typedef {string} TokenBase64\n *\n * @typedef {Object} OfferOptions\n * @property {'TRANSIENT' | 'PERMANENT'} [type] the type of the device.\n * @property {number} [ttl] the time-to-live of device (transient only), in seconds.\n * @property {number} [expiry] the number of seconds before the offer expires.\n * @property {Object} [payload] the optional offer payload.\n */\n\nexport class ParentHelper {\n /** @type {JsonWebKey | undefined} */\n #privateJwk // Signs the token used for activate and offer.\n\n /** @type {string | undefined} */\n #protocol // Protocol in use (http: or https:).\n\n /** @type {string | undefined} */\n #child // The host name of the daemon being parented.\n\n /** @type {string | undefined} */\n #iss // Callback-supplied URL for the pubicly visible issuer object.\n\n /** @type {string | undefined} */\n #src // The HTML source URL, which must match origin: header iff present in daemon request.\n\n /** @type {Token | undefined} */\n #token // The token used for check activate and offer calls to the daemon.\n\n /** @type {string | undefined} */\n #claimCode // The claim code returned by fetchOffer.\n\n /**\n * Initialises the helper with details already known to the caller.\n *\n * The private key is used to sign generated tokens, where the issUrl\n * references the public key used to verify the token.\n *\n * This method is used by tab runners wanting to parent daemons.\n *\n * @param {string} child\n * @param {string} src # Note can be party:control.\n * @param {JsonWebKey} privateJwk\n * @param {string} iss\n */\n async initWithKey(child, src, privateJwk, iss) {\n this.#protocol = src.startsWith('https:') ? 'https:' : 'http:'\n this.#child = child\n this.#src = src\n this.#privateJwk = privateJwk\n this.#iss = iss\n\n await this.#buildToken()\n }\n\n /**\n * Uses the token to check activate the daemon. An already\n * activated daemon with this parent is retained, or a new\n * daemon is created so long as the sub of the pre-generated\n * token matches a parent record in the provider.\n *\n * The `isNew` attribute in the ok result can be used to\n * determine if the daemon is newly activated or already\n * active.s\n *\n * @returns boolean true if newly activated, otherwise false.\n * @throws {string} exception if daemon cannot be activated.\n */\n async checkActivate() {\n if (!this.#token) {\n throw `checkActivate: No token`\n }\n const url = new URL(`${this.#token.getAud()}/activate`)\n const token = this.#token.asSignedBase64()\n const body = {}\n let response\n try {\n response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n\n const {\n ok,\n error\n } = await response.json()\n\n if (error) {\n throw error\n }\n\n if (ok.isNew) {\n return true\n }\n\n return false\n }\n catch (e) {\n console.error(e)\n throw `Cannot activate ${this.#token.getParty()}`\n }\n }\n\n /**\n * Uses the token to create a device offer on the daemon with default\n * type 'TRANSIENT' and ttl of 300s.\n *\n * @typedef {Object} Offer\n * @property {string} claimCode\n * @property {string} checkState\n *\n * @param {'NO_CHECK' | 'DO_CHECK'} flow to use. For QR offers, use checked flow.\n * @param {OfferOptions | null} options to use when making the offer, if any.\n * @return {Promise<Offer>} offer claimCode and checkState ('NO_CHECK' or 'AWAIT_CHECK').\n */\n async makeOffer(flow, options = null) {\n const {\n type = 'TRANSIENT',\n ttl = 300,\n expiry = 30,\n payload\n } = options ?? {}\n\n if (!this.#token) {\n throw `makeOffer: No token`\n }\n const url = new URL(`${this.#token.getAud()}/offer`)\n const token = this.#token.asSignedBase64()\n const body = {\n role: 'party',\n type,\n ttl,\n description: `Offered by ${this.#token.getCounterparty()}`,\n payload,\n expiry: new Date(Date.now() + 1000 * expiry),\n flow\n }\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n const {\n claimCode,\n _checkState\n } = json.ok\n\n this.#claimCode = claimCode\n\n return json.ok\n }\n\n /**\n * Returns the check state for the offer. This is used when\n * awaiting claim.\n *\n * @return {Promise<string>}\n */\n async checkState() {\n if (!this.#token || !this.#claimCode) {\n throw 'checkState: No token or claimCode'\n }\n const url = new URL(`${this.#token.getAud()}/offer/check`)\n url.searchParams.append('claimCode', this.#claimCode)\n const token = this.#token.asSignedBase64()\n const response = await fetch(url, {\n headers: {\n 'x-tabserver-token': token\n }\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n\n /**\n * Result has `claimCode` and `checkState`.\n */\n return json.ok.checkState\n }\n\n /**\n * Posts the check code that confirms the offering device has\n * got the expected claiming device, and returns the resulting check state.\n *\n * @typedef {Object} ConfirmResult\n * @property {string} checkState\n *\n * @param {string} checkCode the check code obtained from the claiming device.\n * @return {Promise<ConfirmResult>} the check state following confirmation.\n */\n async confirmClaim(checkCode) {\n if (!this.#token || !this.#claimCode) {\n throw 'confirmClaim: No token or claimCode'\n }\n\n const url = new URL(`${this.#token.getAud()}/offer/confirm`)\n const token = await this.#token.asSignedBase64()\n const body = {\n claimCode: this.#claimCode,\n checkCode\n }\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'x-tabserver-token': token,\n 'content-type': 'application/json'\n },\n body: JSON.stringify(body)\n })\n const json = await response.json()\n if ('error' in json) {\n throw json.error\n }\n return json.ok\n }\n\n /**\n * Returns the daemon name.\n *\n * @return {string} daemon name, e.g. 'daemon.once.id'.\n */\n getChild() {\n if (!this.#child) {\n throw 'getChild: no child'\n }\n return this.#child\n }\n\n /**\n * Returns the daemon origin, suitable for redirection or claiming\n * a code.\n *\n * @return {string} child daemon origin e.g. 'https://daemon.once.id'.\n */\n getChildOrigin() {\n if (!this.#iss || !this.#child) {\n throw 'getDaemonUrl: no issUrl or daemon'\n }\n return `${this.#protocol}//${this.#child}`\n }\n\n /**\n * Builds the token used in the check activate and offer calls toh\n * the daemon.\n *\n * @return {Promise<void>}\n */\n async #buildToken() {\n if (!this.#iss || !this.#child || !this.#privateJwk) {\n throw 'buildToken: missing iss, child or privateJwk'\n }\n\n const iss = this.#iss\n const aud = this.getChildOrigin()\n const sub = new URL(this.#iss).origin\n\n // The `src` attribute is origin and pathname only.\n let src\n if (this.#src) {\n const srcUrl = new URL(this.#src)\n src = `${srcUrl.origin}${srcUrl.pathname}`\n }\n else {\n src = 'party:control'\n }\n\n const payload = {\n iss,\n aud,\n sub,\n src,\n scope: {\n 'party:control': 'offer'\n },\n iat: Date.now(),\n exp: Date.now() + 1000 * 60 * 3\n }\n\n const token = new Token(payload)\n await token.signWith(Promise.resolve(this.#privateJwk))\n this.#token = token\n }\n}\n", "/**\n * Utility class to generate a QR code img element which is placed\n * under a parent element.\n *\n * Relies upon a script element in the page:\n *\n * <script src='https://unpkg.com/qrcode-generator@1.4.4/qrcode.js'>\n */\nconst QrType = 0 // Auto detection by library.\nconst QrEcc = 'L'\n\nexport const ImgClass = 'qrcode'\n\nexport class QrCode {\n #img\n #content\n\n /**\n * Constructor takes img element and\n * the string to show in the QR code.\n * @param {HTMLImageElement} img the image element to use.\n * @param {string} content the content of the qr code.\n */\n constructor(img, content) {\n this.#img = img\n this.#content = content\n }\n\n /**\n * Returns a promise that is resolved when the image loads\n * successfully, or rejected if the image load fails.\n */\n generate() {\n const qr = qrcode(QrType, QrEcc)\n qr.addData(this.#content)\n qr.make()\n const dataUrl = qr.createDataURL()\n this.#img.classList.add(ImgClass)\n this.#img.src = dataUrl\n return new Promise((resolve, reject) => {\n this.#img.onload = resolve\n this.#img.onerror = reject\n })\n }\n}\n", "/**\n * Use this type to reference a plain object with\n * random string keys.\n */\nexport interface Plain {\n // deno-lint-ignore no-explicit-any\n [key: string]: any\n}\n\n/**\n * Any JSON-serialisable type.\n */\nexport type JSONPrimitive = string | number | boolean | null\nexport type JSONArray = JSONValue[]\nexport interface JSONObject {\n [key: string]: JSONValue\n}\nexport type JSONValue = JSONPrimitive | JSONObject | JSONArray\n\n/**\n * Clones a JSONValue as defined above, if necessary.\n */\nexport function cloneJSONValue<T extends JSONValue>(value: T): T {\n if (value === null) return value\n if (Array.isArray(value)) {\n return value.map(cloneJSONValue) as T\n }\n if (typeof value === 'object') {\n const out: Record<string, JSONValue> = {}\n for (const [k, v] of Object.entries(value)) {\n out[k] = cloneJSONValue(v)\n }\n return out as T\n }\n return value\n}\n\n/**\n * Type predicates to be used in type-narrowing assertions, e.g.\n *\n * assert(notNull(someValue))\n * ... now the type of someValue does not include null ...\n *\n * or\n * if (isOk(returnValue)) {\n * ...in this block we know returnValue.ok is present and of the correct type.\n * }\n *\n * @param value the value to be tested.\n * @returns true if not null, otherwise false.\n */\nexport function notNull<T>(value: T | null): value is T {\n return value !== null\n}\n\nexport function isDefined<T>(value: T | undefined): value is T {\n return value !== undefined\n}\n", "import { Plain } from './Assertions.ts'\n\nexport interface Ok<T> {\n ok: T\n}\n\nexport interface Error {\n error: string\n}\n\n/**\n * Returns an ok response or an error response corresponding\n * to the standard ok or error return value.\n *\n * If the error string starts with a space-separated status code,\n * then that is used as the status code for the response.\n *\n * For example:\n *\n * {error: 'Ordinary Error'}\n * is sent as is with status 200.\n * {error: '401 Authentication Error'}\n * is sent as {error: 'Authentication Error'} with status 401\n */\nexport function response<T>(rvalue: Ok<T> | Error, extraHeaders?: Plain) {\n\n if ('ok' in rvalue) {\n const body = JSON.stringify(rvalue)\n return new Response(body, {\n status: 200,\n headers: {\n ...extraHeaders,\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n }\n })\n }\n\n const error = rvalue.error\n const [, status, message] = error.match(/^(\\d{3})\\s(.+)/) ?? [null, '200', error]\n const body = JSON.stringify({\n error: message\n })\n return new Response(body, {\n status: Number(status),\n headers: {\n ...extraHeaders,\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*'\n }\n })\n}\n\n/**\n * Returns a 404 error response, including\n * the standard JSON error return value.\n * @deprecated Use response instead using a status-prefixed error message.\n */\nexport function response404<T>(json: Error = {\n error: '404 Not Found'\n}) {\n const body = JSON.stringify(json)\n return new Response(body, {\n status: 404,\n headers: {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n }\n })\n}\n\n/**\n * Returns a 302 permanent redirect response.\n * @param url the url to redirect to.\n * @returns Response object.\n */\nexport function response302(url: URL, extraHeaders: Plain = []): Response {\n return new Response(null, {\n status: 302,\n headers: {\n 'Location': url.toString(),\n 'Access-Control-Allow-Origin': '*',\n ...extraHeaders\n }\n })\n}\n\n/**\n * Simple build-a-cookie support.\n *\n * If the `expires` attribute is not defined, it's a session cookie.\n * Otherwise it's a permanent cookie. To delete a cookie, use the\n * `expires` attribute set to `new Date(0)`.\n */\n\nexport interface CookiePayload {\n name: string\n value: string\n domain?: string\n sameSite?: 'Strict' | 'Lax' | 'None'\n path?: string\n expires?: Date\n secure?: boolean\n httpOnly?: boolean\n}\n\nexport type CookieString = string\n\nexport function buildCookie(payload: CookiePayload): CookieString {\n const {\n name,\n value,\n domain,\n sameSite,\n path = '/',\n expires,\n secure = true,\n httpOnly = true\n } = payload\n\n const builder: string[] = []\n builder.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`)\n if (domain) builder.push(`Domain=${domain}`)\n if (sameSite) builder.push(`SameSite=${sameSite}`)\n builder.push(`Path=${path}`)\n if (expires) builder.push(`Expires=${expires.toUTCString()}`)\n if (secure) builder.push(`Secure`)\n if (httpOnly) builder.push('HttpOnly')\n\n const cookie = builder.join('; ')\n return cookie\n}\n", "import { response } from './Responses.ts'\nimport { Plain, JSONValue } from './Assertions.ts'\nimport { Token, Scope } from '../js/Token.js'\nimport { Storage } from '../js/Storage.js'\nimport { HtmlContent } from './HtmlContent.ts'\nimport { YamlContent } from './YamlContent.ts'\n\n// Daemon configuration looks like this.\nexport interface DaemonConfig {\n system: {\n protocol: 'http:' | 'https:'\n party: string\n issuer: string\n source: string\n sourcePrefix: string\n tab: string,\n privateJwk: JsonWebKey\n }\n content: {\n html: HtmlContent\n yaml?: YamlContent\n }\n user: Plain\n}\n\n// System requests are on the /daemon/... path.\nexport const DAEMON_PREFIX = 'daemon'\n\n// Daemon lifecycle path components that come after DAEMON_PREFIX.\nexport const CONFIG_PATH = 'config'\nexport const EVENT_PATH = 'event'\n\n// Event names.\nconst Ev = {\n Config: 'config'\n}\n\n// SessionStorage keys for lifecycle objects.\nconst CONFIG_KEY = 'config'\n\n// Default time-to-live for a third party token we generate is 1 hour.\nconst DEFAULT_TTL_MILLIS = 1000 * 60 * 60\n\ninterface TabEvent {\n type: string,\n payload: JSONValue\n}\n\n/**\n * Encapsulates the lifecycle event requests that occur on\n * runner start and termination and when alerts are resolved\n * or rejected.\n *\n * The Lifecycle object also provides static methods to retrieve\n * configuration items, a public and private key pair, and also to\n * produce the signed token that is required in requests from this party\n * to third parties.\n */\nexport class Lifecycle extends EventTarget {\n static #instance: Lifecycle = new Lifecycle()\n\n // Expose event names to importers.\n static Ev = Ev\n\n static getInstance() {\n return this.#instance\n }\n\n /**\n * All system requests are under the /daemon/ path.\n *\n * @param {Request} request to be tested.\n * @returns {boolean} true if the request is a lifecycle request.\n */\n static shouldHandle(request: Request): boolean {\n return (\n Lifecycle.isConfig(request) ||\n Lifecycle.isEvent(request)\n )\n }\n\n static isConfig(request: Request): boolean {\n const url = new URL(request.url)\n return (\n request.method == 'POST' &&\n url.pathname == `/${DAEMON_PREFIX}/${CONFIG_PATH}` &&\n request.headers.get('Content-Type') == 'application/json'\n )\n }\n\n static isEvent(request: Request): boolean {\n const url = new URL(request.url)\n return (\n request.method == 'POST' &&\n url.pathname == `/${DAEMON_PREFIX}/${EVENT_PATH}` &&\n request.headers.get('Content-Type') == 'application/json'\n )\n }\n\n /**\n * Handles a lifecycle request.\n *\n * @param {Request} request to be handled.\n * @returns {Response} response for caller.\n */\n async handler(request: Request): Promise<Response> {\n if (Lifecycle.isConfig(request)) {\n return await this.handleConfig(request)\n }\n if (Lifecycle.isEvent(request)) {\n return await this.handleEvent(request)\n }\n return response({\n error: 'Invalid daemon request'\n })\n }\n\n /**\n * Saves the lifecycle config object provided in the request body.\n *\n * Fires a 'config' lifecycle event.\n *\n * @param {Request} request containing the configuration json.\n * @return {Response} ok response.\n */\n async handleConfig(request: Request): Promise<Response> {\n const configJson = await request.json()\n sessionStorage.setItem(CONFIG_KEY, JSON.stringify(configJson))\n this.dispatchEvent(new CustomEvent(Ev.Config, {\n detail: configJson\n }))\n return response({\n ok: true\n })\n }\n\n /**\n * Fires a lifecycle event upon receiving an event request.\n *\n * @param {Request} request containing the event.\n * @return {Response} ok response.\n */\n async handleEvent(request: Request): Promise<Response> {\n const {\n type,\n payload\n } = await request.json()\n this.dispatchEvent(new CustomEvent(type, {\n detail: payload\n }))\n return response({\n ok: true\n })\n }\n\n /**\n * Returns the configuration object stored on initialisation.\n * @return {DaemonConfig} the daemon configuration object.\n * @throws {string} exception if config is not yet initialised.\n */\n static getConfig(): DaemonConfig {\n const json = sessionStorage.getItem(CONFIG_KEY)\n if (!json) {\n throw 'DaemonConfig not ready'\n }\n return JSON.parse(json)\n }\n\n /**\n * Generates if necessary and returns a private key for\n * use in signing tokens.\n *\n * @returns {JSONWebKey} the private key for this session.\n */\n static getPrivateKey(): Promise<JsonWebKey> {\n const {\n system: {\n privateJwk\n }\n } = Lifecycle.getConfig()\n return Promise.resolve(privateJwk)\n }\n\n /**\n * Returns a token suitable for the `x-tabserver-token` header\n * in a request to the supplied party with the required\n * scope.\n *\n * If party is omitted, it defaults to this party.\n * If\n *\n * @param {Scope} scope map of string source -> space-separated capabilities.\n * @param {string=} party the party host name to whom the request will be sent.\n * @param { src=} src the HTML page from which the request is (actually or notionally) made.\n * @param {number=} ttlMillis the lifetime of this token.\n * @returns {Token} the signed token.\n * @throws {string} if configuration not yet initialised.\n *\n */\n static async getTokenFor(\n scope: Scope,\n party: string | null = null,\n src: string | null = null,\n ttlMillis: number = DEFAULT_TTL_MILLIS\n ): Promise<Token> {\n const config = Lifecycle.getConfig()\n if (!config) {\n throw 'Lifecycle configuration not yet available'\n }\n\n // Extract protocol, our party, issuer and source from system config.\n const {\n system: {\n protocol,\n party: us,\n issuer,\n source\n }\n } = config\n\n // Token src excludes query params.\n const appSourceUrl = new URL(source)\n\n const aud = party ? `${protocol}//${party}`: `${protocol}//${us}`\n const sub = `${protocol}//${us}`\n const now = Date.now()\n\n const payload = {\n iss: issuer,\n aud,\n sub,\n src: src ?? `${appSourceUrl.origin}${appSourceUrl.pathname}`,\n scope,\n iat: now,\n exp: now + ttlMillis\n }\n\n const token = new Token(payload)\n await token.signWith(Lifecycle.getPrivateKey())\n return token\n }\n\n static async getStorage(): Promise<Storage> {\n const token = await Lifecycle.getStorageToken()\n return new Storage(token)\n }\n\n /**\n * Returns a token for our party with the setitem and getitem\n * capabilities on the party control source.\n */\n static getStorageToken(): Promise<Token> {\n const {\n system: {\n party,\n }\n } = Lifecycle.getConfig()\n\n const scope = {\n [Token.Source.PARTY_CONTROL]: `${Token.Capability.GET_ITEM} ${Token.Capability.SET_ITEM}`\n }\n\n return Lifecycle.getTokenFor(scope, party)\n }\n}\n", "import type { JSONValue } from './Assertions.ts'\n\ntype JSONRecord = Record<string, unknown>\n\nexport interface JsonRpcMcpClientOptions {\n post?: (body: JSONRecord) => Promise<JSONRecord>\n url?: string\n headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>)\n fetch?: typeof fetch\n}\n\nexport class JsonRpcMcpClient {\n #post: (body: JSONRecord) => Promise<JSONRecord>\n #nextId = 1\n\n constructor(options: JsonRpcMcpClientOptions) {\n if (options.post) {\n this.#post = options.post\n return\n }\n\n if (!options.url) {\n throw new Error('JsonRpcMcpClient requires post or url')\n }\n\n const fetchFn = options.fetch ?? fetch\n this.#post = async (body) => {\n const headers = typeof options.headers === 'function'\n ? await options.headers()\n : options.headers ?? {}\n const response = await fetchFn(options.url!, {\n method: 'POST',\n headers: jsonHeaders(headers),\n body: JSON.stringify(body),\n })\n return await response.json()\n }\n }\n\n async listTools(): Promise<JSONValue[]> {\n const result = await this.#request('tools/list', {})\n const tools = (result as JSONRecord | undefined)?.tools\n return Array.isArray(tools) ? tools as JSONValue[] : []\n }\n\n async callTool(\n name: string,\n args: JSONRecord = {},\n ): Promise<JSONValue> {\n const result = await this.#request('tools/call', {\n name,\n arguments: args,\n })\n\n if ((result as JSONRecord | undefined)?.isError) {\n throw mcpToolErrorMessage(result)\n }\n\n const record = result as JSONRecord | null | undefined\n return record?.structuredContent as JSONValue ?? null\n }\n\n async #request(method: string, params: JSONRecord): Promise<JSONValue> {\n const json = await this.#post({\n jsonrpc: '2.0',\n id: this.#nextId++,\n method,\n params,\n })\n\n if (json.error) {\n const error = json.error as { message?: string }\n throw error.message ?? JSON.stringify(json.error)\n }\n\n return json.result as JSONValue\n }\n}\n\nexport function mcpToolErrorMessage(result: unknown): string {\n const content = (result as JSONRecord | undefined)?.content\n if (Array.isArray(content)) {\n const first = content[0] as { text?: unknown } | undefined\n if (typeof first?.text === 'string') return first.text\n }\n return 'MCP tool failed'\n}\n\nfunction jsonHeaders(headers: HeadersInit): HeadersInit {\n const out = new Headers(headers)\n out.set('Content-Type', 'application/json')\n return out\n}\n", "import { JsonRpcMcpClient } from './McpClient.ts'\n\nexport type JSONRecord = Record<string, unknown>\n\nexport interface ChatToolCall {\n id: string\n name: string\n input: JSONRecord\n}\n\nexport interface ChatToolDescriptor {\n type: 'function'\n name: string\n execution: 'server'\n description?: string\n parameters?: JSONRecord\n}\n\nexport interface ClientToolDescriptor {\n type: 'function'\n name: string\n execution: 'client'\n description?: string\n parameters?: JSONRecord\n}\n\nexport interface ToolResult {\n toolCallId: string\n name?: string\n content: string\n isError?: boolean\n}\n\nexport interface ToolRoute {\n kind: string\n method?: string\n path?: string\n mcpToolName?: string\n}\n\nexport interface ToolCatalogEntry {\n tool?: {\n name?: string\n description?: string\n inputSchema?: JSONRecord\n }\n route?: ToolRoute\n}\n\nexport interface AgentToolRuntimeOptions {\n postParty: (path: string, body: JSONRecord) => Promise<JSONRecord>\n postPartyRaw: (path: string, body: JSONRecord) => Promise<JSONRecord>\n}\n\nexport class AgentToolRuntime {\n #postParty: (path: string, body: JSONRecord) => Promise<JSONRecord>\n #postPartyRaw: (path: string, body: JSONRecord) => Promise<JSONRecord>\n #toolCatalog: ToolCatalogEntry[] = []\n #toolRoutes = new Map<string, ToolRoute>()\n #mcpClients = new Map<string, JsonRpcMcpClient>()\n\n constructor(options: AgentToolRuntimeOptions) {\n this.#postParty = options.postParty\n this.#postPartyRaw = options.postPartyRaw\n }\n\n get toolCatalog(): ToolCatalogEntry[] {\n return this.#toolCatalog\n }\n\n async refreshCatalog(): Promise<void> {\n const catalog = await this.#postParty('/ai/tools/catalog', {})\n this.setCatalog(catalog.tools)\n }\n\n setCatalog(value: unknown): void {\n const toolCatalog = Array.isArray(value) ? value : []\n this.#toolCatalog = toolCatalog as ToolCatalogEntry[]\n this.#toolRoutes = new Map(\n this.#toolCatalog\n .filter((entry) => entry.tool?.name && entry.route)\n .map((entry) => [entry.tool!.name!, entry.route!]),\n )\n this.#mcpClients.clear()\n }\n\n chatTools(options: {\n clientTools?: ClientToolDescriptor[]\n } = {}): Array<ChatToolDescriptor | ClientToolDescriptor> {\n return [\n ...this.#toolCatalog.map(toChatTool).filter(isDefined),\n ...(options.clientTools ?? []),\n ]\n }\n\n async executeToolCalls(toolCalls: ChatToolCall[]): Promise<ToolResult[]> {\n const results: ToolResult[] = []\n for (const call of toolCalls) {\n try {\n results.push({\n toolCallId: call.id,\n name: call.name,\n content: await this.executeToolCall(call),\n })\n } catch (e) {\n results.push({\n toolCallId: call.id,\n name: call.name,\n content: String(e),\n isError: true,\n })\n }\n }\n return results\n }\n\n async executeToolCall(call: ChatToolCall): Promise<string> {\n const route = this.#toolRoutes.get(call.name)\n if (!route) throw new Error(`No discovered tool named ${call.name}`)\n if (route.method !== 'POST' || !route.path) {\n throw new Error(`Unsupported route for tool ${call.name}`)\n }\n\n if (route.kind === 'platform') {\n const result = await this.#postParty(route.path, call.input)\n return JSON.stringify(result)\n }\n\n if (route.kind === 'mcp') {\n return await this.#executeMcpToolCall(route, call)\n }\n\n throw new Error(`Unsupported route kind for tool ${call.name}`)\n }\n\n async #executeMcpToolCall(\n route: ToolRoute,\n call: ChatToolCall,\n ): Promise<string> {\n let client = this.#mcpClients.get(route.path!)\n if (!client) {\n client = new JsonRpcMcpClient({\n post: (body) => this.#postPartyRaw(route.path!, body),\n })\n this.#mcpClients.set(route.path!, client)\n }\n const result = await client.callTool(route.mcpToolName || call.name, {\n ...call.input,\n })\n return JSON.stringify(result)\n }\n}\n\nexport function toChatTool(\n entry: ToolCatalogEntry,\n): ChatToolDescriptor | null {\n if (!entry?.tool?.name) return null\n return {\n type: 'function',\n name: entry.tool.name,\n execution: 'server',\n description: entry.tool.description,\n parameters: entry.tool.inputSchema,\n }\n}\n\nfunction isDefined<T>(value: T | null | undefined): value is T {\n return value !== null && value !== undefined\n}\n", "import type { JSONObject, JSONValue } from './Assertions.ts'\n\nconst PROTOCOL_VERSION = '2025-06-18'\nconst CORS_HEADERS = {\n 'Access-Control-Allow-Origin': '*',\n}\nconst MCP_METHODS = 'POST, GET'\n\ntype JsonRpcId = string | number | null\nexport type McpToolArguments = Record<string, unknown>\n\nexport interface McpContext {\n party?: string\n source?: string\n protocol?: string\n sourcePrefix?: string\n}\n\nexport interface McpToolDefinition<TContext extends McpContext> {\n name: string\n title?: string\n description: string\n inputSchema: JSONValue\n outputSchema?: JSONValue\n annotations?: JSONObject\n capability?: string\n call: (\n args: McpToolArguments,\n context: TContext,\n ) => Promise<JSONValue> | JSONValue\n}\n\nexport interface McpToolProvider<TContext extends McpContext> {\n listTools(context?: TContext): Promise<JSONValue[]> | JSONValue[]\n callTool(\n name: string,\n args: McpToolArguments,\n context: TContext,\n ): Promise<JSONValue>\n}\n\nexport interface JsonRpcMcpServerOptions<TContext extends McpContext> {\n serverInfo?: {\n name: string\n version: string\n }\n context: (request: Request) => TContext\n}\n\nexport class McpToolRegistry<TContext extends McpContext>\n implements McpToolProvider<TContext> {\n #tools: McpToolDefinition<TContext>[]\n\n constructor(tools: McpToolDefinition<TContext>[]) {\n this.#tools = tools\n }\n\n listTools(): JSONValue[] {\n return this.#tools.map((tool) => ({\n name: tool.name,\n ...(tool.title === undefined ? {} : { title: tool.title }),\n description: tool.description,\n inputSchema: tool.inputSchema,\n ...(tool.outputSchema === undefined\n ? {}\n : { outputSchema: tool.outputSchema }),\n ...(tool.annotations === undefined\n ? {}\n : { annotations: tool.annotations }),\n }))\n }\n\n async callTool(\n name: string,\n args: McpToolArguments,\n context: TContext,\n ): Promise<JSONValue> {\n const tool = this.#tools.find((candidate) => candidate.name === name)\n if (!tool) {\n throw new ProtocolError(-32602, `Unknown tool: ${name}`)\n }\n return await tool.call(args, context)\n }\n}\n\nexport class JsonRpcMcpServer<TContext extends McpContext = McpContext> {\n #tools: McpToolProvider<TContext>\n #context: (request: Request) => TContext\n #serverInfo: { name: string; version: string }\n\n constructor(\n tools: McpToolProvider<TContext>,\n contextOrOptions:\n | ((request: Request) => TContext)\n | JsonRpcMcpServerOptions<TContext>,\n ) {\n this.#tools = tools\n if (typeof contextOrOptions === 'function') {\n this.#context = contextOrOptions\n this.#serverInfo = {\n name: 'mcp-server',\n version: '1',\n }\n } else {\n this.#context = contextOrOptions.context\n this.#serverInfo = contextOrOptions.serverInfo ?? {\n name: 'mcp-server',\n version: '1',\n }\n }\n }\n\n async handle(request: Request): Promise<Response> {\n if (request.method === 'GET') {\n return sseEndpointResponse(request)\n }\n\n if (request.method === 'DELETE') {\n return methodNotAllowed()\n }\n\n if (request.method !== 'POST') {\n return methodNotAllowed()\n }\n\n let message: Record<string, unknown>\n try {\n message = await request.json()\n } catch (_e) {\n return jsonResponse(jsonRpcError(null, -32700, 'Parse error'))\n }\n\n const id = jsonRpcId(message.id)\n if (message.jsonrpc !== '2.0' || typeof message.method !== 'string') {\n return jsonResponse(jsonRpcError(id, -32600, 'Invalid Request'))\n }\n\n if (message.id === undefined) {\n return accepted()\n }\n\n try {\n const result = await this.#handleMethod(\n message.method,\n message.params,\n request,\n )\n return jsonResponse({\n jsonrpc: '2.0',\n id,\n result,\n })\n } catch (e) {\n if (e instanceof ProtocolError) {\n return jsonResponse(jsonRpcError(id, e.code, e.message))\n }\n return jsonResponse(jsonRpcError(id, -32603, String(e)))\n }\n }\n\n async #handleMethod(\n method: string,\n params: unknown,\n request: Request,\n ): Promise<JSONValue> {\n switch (method) {\n case 'initialize':\n return {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {\n tools: {\n listChanged: false,\n },\n },\n serverInfo: this.#serverInfo,\n }\n\n case 'notifications/initialized':\n return {}\n\n case 'tools/list':\n return {\n tools: await this.#tools.listTools(this.#context(request)),\n }\n\n case 'tools/call':\n return await this.#callTool(params, request)\n\n default:\n throw new ProtocolError(-32601, `Method not found: ${method}`)\n }\n }\n\n async #callTool(params: unknown, request: Request): Promise<JSONValue> {\n if (!params || typeof params !== 'object' || Array.isArray(params)) {\n throw new ProtocolError(-32602, 'tools/call params must be an object')\n }\n\n const {\n name,\n arguments: args = {},\n } = params as Record<string, unknown>\n\n if (\n typeof name !== 'string' || !args || typeof args !== 'object' ||\n Array.isArray(args)\n ) {\n throw new ProtocolError(-32602, 'Invalid tools/call params')\n }\n\n try {\n const structuredContent = await this.#tools.callTool(\n name,\n args as McpToolArguments,\n this.#context(request),\n )\n return mcpToolResult(structuredContent)\n } catch (e) {\n if (e instanceof ProtocolError) throw e\n return mcpToolResult({ error: String(e) }, true)\n }\n }\n}\n\nexport class ProtocolError extends Error {\n code: number\n\n constructor(code: number, message: string) {\n super(message)\n this.code = code\n }\n}\n\nexport function mcpToolResult(\n structuredContent: JSONValue,\n isError = false,\n): JSONValue {\n return {\n content: [\n {\n type: 'text',\n text: typeof structuredContent === 'string'\n ? structuredContent\n : JSON.stringify(structuredContent),\n },\n ],\n structuredContent,\n isError,\n }\n}\n\nfunction jsonRpcError(id: JsonRpcId, code: number, message: string): JSONValue {\n return {\n jsonrpc: '2.0',\n id,\n error: {\n code,\n message,\n },\n }\n}\n\nfunction jsonResponse(value: JSONValue): Response {\n return new Response(JSON.stringify(value), {\n headers: {\n ...CORS_HEADERS,\n 'Content-Type': 'application/json',\n },\n })\n}\n\nfunction accepted(): Response {\n return new Response(null, {\n status: 202,\n headers: CORS_HEADERS,\n })\n}\n\nfunction sseEndpointResponse(request: Request): Response {\n const url = new URL(request.url)\n return new Response(`event: endpoint\\ndata: ${url.pathname}\\n\\n`, {\n headers: {\n ...CORS_HEADERS,\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n },\n })\n}\n\nfunction methodNotAllowed(): Response {\n return new Response(null, {\n status: 405,\n headers: {\n ...CORS_HEADERS,\n 'Allow': MCP_METHODS,\n },\n })\n}\n\nfunction jsonRpcId(value: unknown): JsonRpcId {\n return typeof value === 'string' || typeof value === 'number' ||\n value === null\n ? value\n : null\n}\n", "import type { Grant, YamlContent } from './YamlContent.ts'\n\nexport const USE_TOOLS_CAPABILITY = 'useTools'\n\nconst CAPABILITY_SEPARATOR = /[\\s,;\\|]+/\n\nexport interface McpCapabilityToken {\n hasCapability(source: string, capability: string): boolean\n getCounterparty(): string\n getSourceUrl(): URL\n getSrc(): string\n}\n\nexport interface OAuthMcpContext {\n party?: string\n counterparty: string\n src: string\n scope: Record<string, string>\n}\n\nexport class OAuthMcpToken implements McpCapabilityToken {\n #context: OAuthMcpContext\n\n constructor(context: OAuthMcpContext) {\n this.#context = context\n }\n\n hasCapability(source: string, capability: string): boolean {\n return (this.#context.scope[source] ?? '')\n .split(CAPABILITY_SEPARATOR)\n .includes(capability)\n }\n\n getParty(): string {\n return this.#context.party ?? ''\n }\n\n getCounterparty(): string {\n return this.#context.counterparty\n }\n\n getSourceUrl(): URL {\n return new URL(this.#context.src)\n }\n\n getSrc(): string {\n return this.#context.src\n }\n}\n\nexport interface McpToolAuthorization {\n token: McpCapabilityToken | null\n providerSource: string\n toolName: string\n capability: string\n providerYaml?: YamlContent\n}\n\nexport function hasMcpToolCapability(\n authorization: McpToolAuthorization,\n): boolean {\n const {\n token,\n providerSource,\n toolName,\n capability,\n providerYaml,\n } = authorization\n\n if (!token) return false\n if (tokenHasCapability(token, providerSource, capability)) return true\n if (!providerYaml) return false\n\n const callerSource = String(token.getSourceUrl())\n return (\n tokenHasCapability(token, callerSource, USE_TOOLS_CAPABILITY) &&\n yamlDeclaresMcpToolCapability(providerYaml, toolName, capability) &&\n yamlGrantsCapability(\n providerYaml.grant,\n token.getCounterparty(),\n token.getSrc(),\n capability,\n )\n )\n}\n\nexport function tokenHasCapability(\n token: { hasCapability(source: string, capability: string): boolean },\n source: string,\n capability: string,\n): boolean {\n try {\n return token.hasCapability(source, capability)\n } catch (_e) {\n return false\n }\n}\n\nexport function yamlDeclaresMcpToolCapability(\n yaml: Pick<YamlContent, 'mcp'>,\n toolName: string,\n capability: string,\n): boolean {\n return Boolean(\n yaml.mcp?.tools.some((tool) =>\n tool.name === toolName && tool.capability === capability\n ),\n )\n}\n\nexport function yamlGrantsCapability(\n grant: Grant | undefined,\n counterparty: string,\n src: string,\n capability: string,\n): boolean {\n for (const counterpartyPattern in grant ?? {}) {\n if (\n !matchesSqlLike(\n counterparty.toLowerCase(),\n counterpartyPattern.toLowerCase(),\n )\n ) {\n continue\n }\n\n const grantEntry = grant![counterpartyPattern]\n if (Array.isArray(grantEntry)) {\n if (grantEntry.includes(capability)) return true\n continue\n }\n\n for (const srcPattern in grantEntry) {\n if (\n matchesSqlLike(src, srcPattern) &&\n splitCapabilities(grantEntry[srcPattern]).includes(capability)\n ) {\n return true\n }\n }\n }\n return false\n}\n\nfunction splitCapabilities(scope: string): string[] {\n return scope\n .split(CAPABILITY_SEPARATOR)\n .filter((capability) => capability.length > 0)\n}\n\nfunction matchesSqlLike(value: string, pattern: string): boolean {\n const regex = new RegExp(\n `^${\n pattern.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/%/g, '.*')\n .replace(/_/g, '.')\n }$`,\n )\n return regex.test(value)\n}\n", "import { type DaemonConfig, Lifecycle } from './Lifecycle.ts'\nimport { Token } from '../js/Token.js'\nimport {\n AgentToolRuntime,\n type ChatToolCall,\n type ClientToolDescriptor,\n type JSONRecord,\n type ToolResult,\n} from './McpAgentTools.ts'\nimport {\n JsonRpcMcpServer,\n type JsonRpcMcpServerOptions,\n type McpContext,\n type McpToolArguments,\n type McpToolProvider,\n ProtocolError,\n} from './McpServer.ts'\nimport { hasMcpToolCapability, OAuthMcpToken } from './McpToolAuthorization.ts'\nimport type { McpCapabilityToken } from './McpToolAuthorization.ts'\nimport { response } from './Responses.ts'\nimport type { JSONValue } from './Assertions.ts'\nimport type { YamlContent } from './YamlContent.ts'\nimport { getAppId } from '../js/Digest.js'\n\nconst DEFAULT_MAX_TOOL_ROUNDS = 4\nconst DEFAULT_TEMPERATURE = 0.2\nconst TOKEN_HEADER = 'x-tabserver-token'\nconst OAUTH_CONTEXT_HEADER = 'x-tabserver-oauth-context'\nconst EXPERT_ASK = 'expert_ask'\nconst EXPERT_ASK_CAPABILITY = 'expertAsk'\n\nexport interface GenerativeChatAgentOptions {\n targetScope: string\n defaultSystemPromptUrl?: URL | string\n defaultTemperature?: number\n maxToolRounds?: number\n mcpPath?: string\n}\n\nexport interface GenerativeChatMessage {\n role?: string\n content?: string\n toolCalls?: ChatToolCall[]\n toolResults?: ToolResult[]\n}\n\ninterface GenerativeChatSession {\n chatId: string\n targetParty: string\n oauthContext: string | null\n toolRuntime: AgentToolRuntime\n clientTools: ClientToolDescriptor[]\n clientToolNames: Set<string>\n pendingClientToolCalls: Map<string, ChatToolCall>\n pendingToolResults: ToolResult[]\n toolRounds: number\n}\n\ninterface GenerativeChatSettings {\n systemPrompt?: string\n temperature: number\n toolsEnabled: boolean\n toolDetails: boolean\n}\n\ninterface SemanticMemoryPromptResource {\n root?: unknown\n collection?: unknown\n description?: unknown\n schema?: unknown\n}\n\nexport interface GenerativeChatRenderEvent {\n role: 'user' | 'assistant' | 'tool' | 'tool-result'\n content: string\n error?: boolean\n}\n\nexport interface GenerativeChatConversationResult {\n events: GenerativeChatRenderEvent[]\n clientToolCalls?: ChatToolCall[]\n}\n\nexport interface GenerativeChatMcpContext extends McpContext {\n party: string\n source: string\n protocol: string\n sourcePrefix: string\n yaml?: YamlContent\n token: Token | McpCapabilityToken | null\n request: Request\n}\n\nexport class GenerativeChatAgent {\n #targetScope: string\n #defaultSystemPromptUrl?: URL | string\n #defaultTemperature: number\n #maxToolRounds: number\n #mcpPath: string\n #sessions = new Map<string, GenerativeChatSession>()\n #lifecycle = new Lifecycle()\n #configReady: Promise<DaemonConfig>\n #defaultSystemPromptPromise: Promise<string> | null = null\n #settings: GenerativeChatSettings\n #mcp: JsonRpcMcpServer<GenerativeChatMcpContext>\n\n constructor(options: GenerativeChatAgentOptions) {\n this.#targetScope = options.targetScope\n this.#defaultSystemPromptUrl = options.defaultSystemPromptUrl\n this.#defaultTemperature = options.defaultTemperature ??\n DEFAULT_TEMPERATURE\n this.#maxToolRounds = options.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS\n this.#mcpPath = options.mcpPath ?? '/rpc'\n this.#settings = {\n temperature: this.#defaultTemperature,\n toolsEnabled: true,\n toolDetails: true,\n }\n this.#configReady = new Promise<DaemonConfig>((resolve) => {\n this.#lifecycle.addEventListener('config', (event) => {\n resolve((event as CustomEvent<DaemonConfig>).detail)\n }, { once: true })\n })\n this.#mcp = new JsonRpcMcpServer(\n new GenerativeChatExpertTools(this),\n {\n context: lifecycleContext,\n serverInfo: {\n name: 'ai-chat-expert',\n version: '1',\n },\n } satisfies JsonRpcMcpServerOptions<GenerativeChatMcpContext>,\n )\n }\n\n async handler(request: Request): Promise<Response> {\n if (Lifecycle.shouldHandle(request)) {\n return await this.#lifecycle.handler(request)\n }\n\n try {\n if (request.method !== 'POST') throw '405 Method Not Allowed'\n\n const url = new URL(request.url)\n if (url.pathname === this.#mcpPath) {\n return await this.#mcp.handle(request)\n }\n\n switch (url.pathname) {\n case '/session':\n return response({ ok: await this.#getSession(request) })\n\n case '/settings':\n return response({ ok: await this.#getSettings(request) })\n\n case '/chats':\n return response({ ok: await this.#listChats(request) })\n\n case '/send':\n return response({ ok: await this.#send(request) })\n\n case '/client-tool-results':\n return response({ ok: await this.#clientToolResults(request) })\n\n case '/close':\n return response({ ok: await this.#close(request) })\n\n case '/delete':\n return response({ ok: await this.#deleteChat(request) })\n\n default:\n throw '404 Not Found'\n }\n } catch (e) {\n console.error(e)\n return response({ error: String(e) })\n }\n }\n\n async askExpert(\n request: Request,\n question: string,\n ): Promise<{ answer: string }> {\n const trimmed = question.trim()\n if (!trimmed) throw 'Question is required'\n\n const targetParty = this.#normalizeTargetParty(undefined)\n const systemPrompt = await this.#systemPromptForChat(targetParty, {\n expert: true,\n })\n const temperature = this.#settings.temperature\n const session = await this.#sessionForRequest(\n request,\n targetParty,\n undefined,\n systemPrompt,\n temperature,\n )\n clearPendingClientTools(session)\n session.toolRounds = 0\n await this.#refreshToolCatalog(session)\n const events: GenerativeChatRenderEvent[] = []\n\n try {\n const result = await this.#postParty(\n session.targetParty,\n '/ai/chat/send',\n {\n chatId: session.chatId,\n message: trimmed,\n options: this.#sendOptions(\n session,\n this.#settings.toolsEnabled,\n temperature,\n ),\n },\n session.oauthContext,\n )\n const conversation = await this.#continueConversation(\n session,\n result.message as GenerativeChatMessage,\n this.#settings.toolsEnabled,\n false,\n temperature,\n events,\n )\n if (conversation.clientToolCalls?.length) {\n throw 'Expert MCP cannot execute browser client tools'\n }\n const answer = latestAssistantAnswer(conversation.events)\n if (!answer) throw 'Expert did not return an answer'\n return { answer }\n } finally {\n const key = sessionKey(request, session.targetParty, session.chatId)\n this.#sessions.delete(key)\n await this.#postParty(session.targetParty, '/ai/chat/close', {\n chatId: session.chatId,\n }).catch((e) => console.error(e))\n }\n }\n\n async #send(request: Request): Promise<GenerativeChatConversationResult> {\n const body = await jsonBody(request)\n if (typeof body.message !== 'string' || !body.message.trim()) {\n throw 'Message is required'\n }\n\n const toolsEnabled = this.#toolsEnabledFrom(body)\n const toolDetails = this.#toolDetailsFrom(body)\n const temperature = this.#temperatureFrom(body)\n const targetParty = this.#targetPartyFrom(body)\n const chatId = chatIdFrom(body)\n const session = await this.#sessionForRequest(\n request,\n targetParty,\n chatId,\n undefined,\n temperature,\n )\n clearPendingClientTools(session)\n session.toolRounds = 0\n updateClientTools(session, body)\n if (toolsEnabled) await this.#refreshToolCatalog(session)\n const events: GenerativeChatRenderEvent[] = []\n\n const result = await this.#postParty(session.targetParty, '/ai/chat/send', {\n chatId: session.chatId,\n message: body.message.trim(),\n options: this.#sendOptions(session, toolsEnabled, temperature),\n }, session.oauthContext)\n\n return await this.#continueConversation(\n session,\n result.message as GenerativeChatMessage,\n toolsEnabled,\n toolDetails,\n temperature,\n events,\n )\n }\n\n async #clientToolResults(\n request: Request,\n ): Promise<GenerativeChatConversationResult> {\n const body = await jsonBody(request)\n const toolsEnabled = this.#toolsEnabledFrom(body)\n const toolDetails = this.#toolDetailsFrom(body)\n const temperature = this.#temperatureFrom(body)\n const targetParty = this.#targetPartyFrom(body)\n const chatId = chatIdFrom(body)\n const session = await this.#sessionForRequest(\n request,\n targetParty,\n chatId,\n undefined,\n temperature,\n )\n updateClientTools(session, body)\n\n const clientResults = validateClientToolResults(session, body.toolResults)\n const events: GenerativeChatRenderEvent[] = []\n for (const result of clientResults) {\n appendToolResultEvent(\n events,\n result.name ?? 'client_tool',\n result.content,\n toolDetails,\n result.isError,\n )\n }\n const toolResults = [...session.pendingToolResults, ...clientResults]\n clearPendingClientTools(session)\n\n return await this.#continueAfterToolResults(\n session,\n toolResults,\n toolsEnabled,\n toolDetails,\n temperature,\n events,\n )\n }\n\n async #continueConversation(\n session: GenerativeChatSession,\n message: GenerativeChatMessage,\n toolsEnabled: boolean,\n toolDetails: boolean,\n temperature: number,\n events: GenerativeChatRenderEvent[],\n ): Promise<GenerativeChatConversationResult> {\n const assistantMessage = message\n\n while (true) {\n appendAssistantEvent(events, assistantMessage)\n\n const toolCalls = assistantMessage.toolCalls ?? []\n if (!toolCalls.length) return { events }\n\n if (session.toolRounds >= this.#maxToolRounds) {\n events.push({\n role: 'tool-result',\n content: 'Stopped after too many tool rounds.',\n error: true,\n })\n return { events }\n }\n\n const { toolResults, clientToolCalls } = await resolveToolCalls(\n session,\n toolCalls,\n toolDetails,\n events,\n )\n\n if (clientToolCalls.length) {\n session.pendingClientToolCalls = new Map(\n clientToolCalls.map((call) => [call.id, call]),\n )\n session.pendingToolResults = toolResults\n return { events, clientToolCalls }\n }\n\n const result = await this.#continueAfterToolResults(\n session,\n toolResults,\n toolsEnabled,\n toolDetails,\n temperature,\n events,\n )\n if (result.clientToolCalls?.length) return result\n return result\n }\n }\n\n async #continueAfterToolResults(\n session: GenerativeChatSession,\n toolResults: ToolResult[],\n toolsEnabled: boolean,\n toolDetails: boolean,\n temperature: number,\n events: GenerativeChatRenderEvent[],\n ): Promise<GenerativeChatConversationResult> {\n if (session.toolRounds >= this.#maxToolRounds) {\n events.push({\n role: 'tool-result',\n content: 'Stopped after too many tool rounds.',\n error: true,\n })\n return { events }\n }\n\n session.toolRounds++\n const result = await this.#postParty(session.targetParty, '/ai/chat/send', {\n chatId: session.chatId,\n message: {\n role: 'user',\n content: '',\n toolResults,\n },\n options: this.#sendOptions(session, toolsEnabled, temperature),\n }, session.oauthContext)\n\n return await this.#continueConversation(\n session,\n result.message as GenerativeChatMessage,\n toolsEnabled,\n toolDetails,\n temperature,\n events,\n )\n }\n\n async #getSession(\n request: Request,\n ): Promise<{\n chatId: string\n history: GenerativeChatMessage[]\n closed: string | null\n }> {\n const body = await optionalJsonBody(request)\n const targetParty = this.#targetPartyFrom(body)\n const chatId = chatIdFrom(body)\n const session = await this.#sessionForRequest(\n request,\n targetParty,\n chatId,\n chatId ? undefined : await this.#systemPromptFrom(body, targetParty),\n this.#temperatureFrom(body),\n )\n setClientTools(session, body.clientTools)\n await this.#refreshToolCatalog(session)\n return {\n chatId: session.chatId,\n history: await this.#chatHistory(session),\n closed: chatId ? await this.#chatClosed(targetParty, chatId) : null,\n }\n }\n\n async #getSettings(request: Request): Promise<GenerativeChatSettings> {\n const body = await optionalJsonBody(request)\n if (Object.keys(body).length) {\n await this.#updateSettings(body)\n }\n return await this.#settingsWithPrompt()\n }\n\n async #listChats(request: Request): Promise<{ chats: JSONRecord[] }> {\n const body = await optionalJsonBody(request)\n const targetParty = this.#targetPartyFrom(body)\n const result = await this.#postParty(targetParty, '/ai/chat/list', {})\n return {\n chats: Array.isArray(result.chats) ? result.chats as JSONRecord[] : [],\n }\n }\n\n async #close(request: Request): Promise<boolean> {\n const body = await optionalJsonBody(request)\n const targetParty = this.#targetPartyFrom(body)\n const chatId = chatIdFrom(body)\n if (!chatId) throw 'chatId is required'\n const key = sessionKey(request, targetParty, chatId)\n const session = this.#sessions.get(key)\n if (!session) {\n await this.#postParty(targetParty, '/ai/chat/close', { chatId })\n return true\n }\n\n await this.#postParty(session.targetParty, '/ai/chat/close', {\n chatId: session.chatId,\n })\n this.#sessions.delete(key)\n return true\n }\n\n async #deleteChat(request: Request): Promise<boolean> {\n const body = await optionalJsonBody(request)\n const targetParty = this.#targetPartyFrom(body)\n const chatId = chatIdFrom(body)\n if (!chatId) throw 'chatId is required'\n const key = sessionKey(request, targetParty, chatId)\n\n await this.#postParty(targetParty, '/ai/chat/delete', { chatId })\n\n this.#sessions.delete(key)\n return true\n }\n\n async #sessionForRequest(\n request: Request,\n targetParty: string,\n requestedChatId?: string,\n systemPrompt?: string,\n temperature = this.#defaultTemperature,\n ): Promise<GenerativeChatSession> {\n let key = sessionKey(request, targetParty, requestedChatId)\n if (requestedChatId) {\n const existing = this.#sessions.get(key)\n if (existing) return existing\n }\n\n const chat = requestedChatId\n ? { id: requestedChatId }\n : await this.#postParty(targetParty, '/ai/chat/create', {\n system: systemPrompt ??\n await this.#systemPromptForChat(targetParty, { expert: false }),\n temperature,\n })\n key = sessionKey(request, targetParty, String(chat.id))\n const session: GenerativeChatSession = {\n chatId: String(chat.id),\n targetParty,\n oauthContext: request.headers.get(OAUTH_CONTEXT_HEADER),\n toolRuntime: this.#toolRuntimeForTarget(targetParty),\n clientTools: [],\n clientToolNames: new Set<string>(),\n pendingClientToolCalls: new Map<string, ChatToolCall>(),\n pendingToolResults: [],\n toolRounds: 0,\n }\n await this.#refreshToolCatalog(session)\n this.#sessions.set(key, session)\n return session\n }\n\n async #chatHistory(\n session: GenerativeChatSession,\n ): Promise<GenerativeChatMessage[]> {\n const result = await this.#postParty(\n session.targetParty,\n '/ai/chat/history',\n {\n chatId: session.chatId,\n },\n )\n return Array.isArray(result.messages)\n ? result.messages as unknown as GenerativeChatMessage[]\n : []\n }\n\n async #chatClosed(\n targetParty: string,\n chatId: string,\n ): Promise<string | null> {\n const result = await this.#postParty(targetParty, '/ai/chat/list', {})\n const chats = Array.isArray(result.chats)\n ? result.chats as JSONRecord[]\n : []\n const chat = chats.find((item) => item.id === chatId)\n return typeof chat?.closed === 'string' ? chat.closed : null\n }\n\n #toolRuntimeForTarget(targetParty: string): AgentToolRuntime {\n return new AgentToolRuntime({\n postParty: (path, body) => this.#postParty(targetParty, path, body),\n postPartyRaw: (path, body) => this.#postPartyRaw(targetParty, path, body),\n })\n }\n\n async #refreshToolCatalog(session: GenerativeChatSession): Promise<void> {\n await session.toolRuntime.refreshCatalog()\n }\n\n #sendOptions(\n session: GenerativeChatSession,\n toolsEnabled: boolean,\n temperature: number,\n ): JSONRecord | undefined {\n if (!toolsEnabled) return { temperature }\n\n const tools = session.toolRuntime.chatTools({\n clientTools: session.clientTools,\n })\n if (!tools.length) return { temperature }\n\n return {\n temperature,\n tools,\n toolChoice: 'auto',\n }\n }\n\n async #postParty(\n targetParty: string,\n path: string,\n body: JSONRecord,\n oauthContext?: string | null,\n ): Promise<JSONRecord> {\n const json = await this.#postPartyRaw(targetParty, path, body, oauthContext)\n if ('error' in json) throw json.error\n return json.ok as JSONRecord\n }\n\n async #postPartyRaw(\n targetParty: string,\n path: string,\n body: JSONRecord,\n oauthContext?: string | null,\n ): Promise<JSONRecord> {\n const config = Lifecycle.getConfig()\n const token = await Lifecycle.getTokenFor(\n { [config.system.source]: this.#targetScope },\n targetParty,\n )\n const url = `${config.system.protocol}//${targetParty}${path}`\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Tabserver-Token': token.asSignedBase64(),\n }\n if (oauthContext) {\n headers[OAUTH_CONTEXT_HEADER] = oauthContext\n }\n const result = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n })\n return await result.json()\n }\n\n async #systemPromptFrom(\n body: JSONRecord,\n targetParty: string,\n ): Promise<string> {\n const basePrompt = typeof body.systemPrompt === 'string' &&\n body.systemPrompt.trim()\n ? body.systemPrompt.trim()\n : await this.#settingsSystemPrompt()\n return await this.#systemPromptForChat(targetParty, {\n basePrompt,\n expert: false,\n })\n }\n\n #temperatureFrom(body: JSONRecord): number {\n return typeof body.temperature === 'number' &&\n Number.isFinite(body.temperature) &&\n body.temperature >= 0 &&\n body.temperature <= 2\n ? Math.round(body.temperature * 10) / 10\n : this.#settings.temperature\n }\n\n #toolsEnabledFrom(body: JSONRecord): boolean {\n return typeof body.toolsEnabled === 'boolean'\n ? body.toolsEnabled\n : this.#settings.toolsEnabled\n }\n\n #toolDetailsFrom(body: JSONRecord): boolean {\n return typeof body.toolDetails === 'boolean'\n ? body.toolDetails\n : this.#settings.toolDetails\n }\n\n #targetPartyFrom(body: JSONRecord): string {\n return this.#normalizeTargetParty(body.targetParty)\n }\n\n #normalizeTargetParty(value: unknown): string {\n const {\n system: {\n party,\n protocol,\n },\n } = Lifecycle.getConfig()\n if (value === undefined || value === null || value === '') return party\n if (typeof value !== 'string') throw 'targetParty must be a string'\n\n const trimmed = value.trim()\n if (!trimmed) return party\n try {\n const url = trimmed.includes('://')\n ? new URL(trimmed)\n : new URL(`${protocol}//${trimmed}`)\n if (!url.host) throw 'missing host'\n return url.host\n } catch (_e) {\n throw `Invalid targetParty: ${value}`\n }\n }\n\n #defaultSystemPrompt(): Promise<string> {\n this.#defaultSystemPromptPromise ??= this.#loadDefaultSystemPrompt()\n return this.#defaultSystemPromptPromise\n }\n\n async #systemPromptForChat(\n targetParty: string,\n options: { basePrompt?: string; expert: boolean },\n ): Promise<string> {\n const basePrompt = options.basePrompt ?? await this.#settingsSystemPrompt()\n const root = await expertRoot()\n const expertInstruction = options.expert && root\n ? `\\nWhen answering expert MCP questions, prefer semantic and structured memory under root \"${root}\" when looking up remembered information.`\n : ''\n const semanticInventory = await this.#semanticMemoryPrompt(targetParty)\n return `${basePrompt}${expertInstruction}${semanticInventory}`\n }\n\n async #semanticMemoryPrompt(targetParty: string): Promise<string> {\n try {\n const configuredRoot = configuredMemoryRoot()\n const resources = configuredRoot\n ? await this.#semanticMemoryResourcesForRootWithFallback(\n targetParty,\n configuredRoot,\n )\n : await this.#semanticMemoryResources(targetParty, {})\n return semanticMemoryPromptSection(resources)\n } catch (_e) {\n return '\\n\\nSemantic memory catalog is unavailable for this session.'\n }\n }\n\n async #semanticMemoryResourcesForRootWithFallback(\n targetParty: string,\n root: string,\n ): Promise<SemanticMemoryPromptResource[]> {\n const rooted = await this.#semanticMemoryResources(targetParty, { root })\n return rooted.length ? rooted : await this.#semanticMemoryResources(\n targetParty,\n {},\n )\n }\n\n async #semanticMemoryResources(\n targetParty: string,\n options: { root?: string },\n ): Promise<SemanticMemoryPromptResource[]> {\n const result = await this.#postParty(targetParty, '/ai/memory/catalog', {\n kind: 'semantic',\n ...(options.root ? { root: options.root } : {}),\n })\n return Array.isArray(result.resources)\n ? result.resources as SemanticMemoryPromptResource[]\n : []\n }\n\n async #settingsSystemPrompt(): Promise<string> {\n return this.#settings.systemPrompt ?? await this.#defaultSystemPrompt()\n }\n\n async #settingsWithPrompt(): Promise<GenerativeChatSettings> {\n return {\n ...this.#settings,\n systemPrompt: await this.#settingsSystemPrompt(),\n }\n }\n\n async #updateSettings(body: JSONRecord): Promise<void> {\n if (body.systemPrompt !== undefined) {\n if (typeof body.systemPrompt !== 'string') {\n throw 'systemPrompt must be a string'\n }\n this.#settings.systemPrompt = body.systemPrompt.trim()\n ? body.systemPrompt\n : await this.#defaultSystemPrompt()\n }\n if (body.temperature !== undefined) {\n if (\n typeof body.temperature !== 'number' ||\n !Number.isFinite(body.temperature) ||\n body.temperature < 0 ||\n body.temperature > 2\n ) {\n throw 'temperature must be between 0 and 2'\n }\n this.#settings.temperature = Math.round(body.temperature * 10) / 10\n }\n if (body.toolsEnabled !== undefined) {\n if (typeof body.toolsEnabled !== 'boolean') {\n throw 'toolsEnabled must be a boolean'\n }\n this.#settings.toolsEnabled = body.toolsEnabled\n }\n if (body.toolDetails !== undefined) {\n if (typeof body.toolDetails !== 'boolean') {\n throw 'toolDetails must be a boolean'\n }\n this.#settings.toolDetails = body.toolDetails\n }\n }\n\n async #loadDefaultSystemPrompt(): Promise<string> {\n const url = await this.#resolvedDefaultSystemPromptUrl()\n const result = await fetch(url)\n if (!result.ok) {\n throw `Failed to load default system prompt: ${result.status}`\n }\n return await result.text()\n }\n\n async #resolvedDefaultSystemPromptUrl(): Promise<URL> {\n if (this.#defaultSystemPromptUrl instanceof URL) {\n return this.#defaultSystemPromptUrl\n }\n\n if (typeof this.#defaultSystemPromptUrl === 'string') {\n try {\n return new URL(this.#defaultSystemPromptUrl)\n } catch (_e) {\n const {\n system: { source },\n } = await this.#configReady\n return new URL(this.#defaultSystemPromptUrl, source)\n }\n }\n\n return await this.#defaultSystemPromptUrlFromConfig()\n }\n\n async #defaultSystemPromptUrlFromConfig(): Promise<URL> {\n const {\n system: { source },\n } = await this.#configReady\n return new URL('system-prompt.txt', source)\n }\n}\n\nasync function jsonBody(request: Request): Promise<JSONRecord> {\n if (!request.headers.get('Content-Type')?.startsWith('application/json')) {\n throw \"Content-Type must be 'application/json'\"\n }\n const body = await request.json()\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n throw 'Request body must be an object'\n }\n return body as JSONRecord\n}\n\nasync function optionalJsonBody(request: Request): Promise<JSONRecord> {\n if (!request.headers.get('Content-Type')?.startsWith('application/json')) {\n return {}\n }\n const body = await request.json()\n if (!body || typeof body !== 'object' || Array.isArray(body)) return {}\n return body as JSONRecord\n}\n\nfunction updateClientTools(\n session: GenerativeChatSession,\n body: JSONRecord,\n): void {\n if (Object.hasOwn(body, 'clientTools')) {\n setClientTools(session, body.clientTools)\n }\n}\n\nfunction setClientTools(\n session: GenerativeChatSession,\n value: unknown,\n): void {\n const clientTools = Array.isArray(value)\n ? value.filter(isClientToolDescriptor)\n : []\n session.clientTools = clientTools\n session.clientToolNames = new Set(clientTools.map((tool) => tool.name))\n}\n\nfunction isClientToolDescriptor(value: unknown): value is ClientToolDescriptor {\n return (\n Boolean(value) &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n (value as { type?: unknown }).type === 'function' &&\n typeof (value as { name?: unknown }).name === 'string' &&\n (value as { execution?: unknown }).execution === 'client'\n )\n}\n\nfunction validateClientToolResults(\n session: GenerativeChatSession,\n value: unknown,\n): ToolResult[] {\n if (!Array.isArray(value)) throw 'toolResults must be an array'\n\n const pendingIds = new Set(session.pendingClientToolCalls.keys())\n if (!pendingIds.size) throw 'No pending client tool calls'\n\n const results: ToolResult[] = []\n for (const item of value) {\n if (!isToolResult(item)) throw 'Invalid client tool result'\n if (!pendingIds.delete(item.toolCallId)) {\n throw `Unexpected client tool result: ${item.toolCallId}`\n }\n const pendingCall = session.pendingClientToolCalls.get(item.toolCallId)\n if (pendingCall?.name !== item.name) {\n throw `Client tool result name mismatch: ${item.toolCallId}`\n }\n results.push(item)\n }\n\n if (pendingIds.size) {\n throw `Missing client tool results: ${[...pendingIds].join(', ')}`\n }\n\n return results\n}\n\nfunction isToolResult(value: unknown): value is ToolResult {\n return (\n Boolean(value) &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n typeof (value as { toolCallId?: unknown }).toolCallId === 'string' &&\n typeof (value as { name?: unknown }).name === 'string' &&\n typeof (value as { content?: unknown }).content === 'string' &&\n (\n (value as { isError?: unknown }).isError === undefined ||\n typeof (value as { isError?: unknown }).isError === 'boolean'\n )\n )\n}\n\nfunction clearPendingClientTools(session: GenerativeChatSession): void {\n session.pendingClientToolCalls.clear()\n session.pendingToolResults = []\n}\n\nfunction chatIdFrom(body: JSONRecord): string | undefined {\n if (body.chatId === undefined || body.chatId === null || body.chatId === '') {\n return undefined\n }\n if (typeof body.chatId !== 'string') throw 'chatId must be a string'\n return body.chatId\n}\n\nfunction sessionKey(\n request: Request,\n targetParty: string,\n chatId?: string,\n): string {\n return `${requestIdentity(request)}\\n${targetParty}\\n${chatId ?? 'default'}`\n}\n\nfunction requestIdentity(request: Request): string {\n const token = request.headers.get(TOKEN_HEADER)\n if (token) return token\n const oauthContext = request.headers.get(OAUTH_CONTEXT_HEADER)\n if (oauthContext) return oauthContext\n throw `This request must provide a token in '${TOKEN_HEADER}'`\n}\n\nasync function resolveToolCalls(\n session: GenerativeChatSession,\n toolCalls: ChatToolCall[],\n toolDetails: boolean,\n events: GenerativeChatRenderEvent[],\n): Promise<{ toolResults: ToolResult[]; clientToolCalls: ChatToolCall[] }> {\n const toolResults: ToolResult[] = []\n const clientToolCalls: ChatToolCall[] = []\n\n for (const call of toolCalls) {\n const isClientTool = session.clientToolNames.has(call.name)\n if (toolDetails) {\n events.push({\n role: 'tool',\n content: toolCallMessage(call, isClientTool),\n })\n }\n\n if (isClientTool) {\n clientToolCalls.push(call)\n continue\n }\n\n try {\n const content = await session.toolRuntime.executeToolCall(call)\n appendToolResultEvent(events, call.name, content, toolDetails)\n toolResults.push({\n toolCallId: call.id,\n name: call.name,\n content,\n })\n } catch (e) {\n const content = String(e)\n appendToolResultEvent(events, call.name, content, toolDetails, true)\n toolResults.push({\n toolCallId: call.id,\n name: call.name,\n content,\n isError: true,\n })\n }\n }\n\n return { toolResults, clientToolCalls }\n}\n\nfunction appendAssistantEvent(\n events: GenerativeChatRenderEvent[],\n message: GenerativeChatMessage,\n): void {\n if (message.content) {\n events.push({ role: 'assistant', content: message.content })\n } else if (!message.toolCalls?.length) {\n events.push({ role: 'assistant', content: '' })\n }\n}\n\nfunction toolCallMessage(call: ChatToolCall, isClientTool: boolean): string {\n const label = isClientTool ? 'Client tool call' : 'Agent tool call'\n return `${label}: ${call.name}\\n${JSON.stringify(call.input, null, 2)}`\n}\n\nfunction appendToolResultEvent(\n events: GenerativeChatRenderEvent[],\n name: string,\n content: string,\n toolDetails: boolean,\n isError = false,\n): void {\n if (!toolDetails) return\n events.push({\n role: 'tool-result',\n content: toolResultMessage(name, content, isError),\n ...(isError ? { error: true } : {}),\n })\n}\n\nfunction toolResultMessage(\n name: string,\n content: string,\n isError = false,\n): string {\n const label = isError ? 'Tool error' : 'Tool result'\n return `${label}: ${name}\\n${content}`\n}\n\nclass GenerativeChatExpertTools\n implements McpToolProvider<GenerativeChatMcpContext> {\n #agent: GenerativeChatAgent\n\n constructor(agent: GenerativeChatAgent) {\n this.#agent = agent\n }\n\n listTools(): JSONValue[] {\n return [expertAskDescriptor()]\n }\n\n async callTool(\n name: string,\n args: McpToolArguments,\n context: GenerativeChatMcpContext,\n ): Promise<JSONValue> {\n if (name !== EXPERT_ASK) {\n throw new ProtocolError(-32602, `Unknown tool: ${name}`)\n }\n assertExpertAskCapability(context)\n if (typeof args.question !== 'string' || !args.question.trim()) {\n throw 'question is required'\n }\n return await this.#agent.askExpert(context.request, args.question)\n }\n}\n\nfunction expertAskDescriptor(): JSONValue {\n return {\n name: EXPERT_ASK,\n title: 'Ask Expert',\n description:\n 'Ask this AI Chat expert a question. The expert may use its configured memory and tools before answering.',\n inputSchema: {\n type: 'object',\n properties: {\n question: {\n type: 'string',\n description: 'Question to ask the expert.',\n },\n },\n required: ['question'],\n additionalProperties: false,\n },\n outputSchema: {\n type: 'object',\n properties: {\n answer: {\n type: 'string',\n },\n },\n required: ['answer'],\n additionalProperties: false,\n },\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n },\n }\n}\n\nfunction assertExpertAskCapability(context: GenerativeChatMcpContext): void {\n if (\n !hasMcpToolCapability({\n token: context.token,\n providerSource: context.source,\n toolName: EXPERT_ASK,\n capability: EXPERT_ASK_CAPABILITY,\n providerYaml: context.yaml,\n })\n ) {\n throw `Missing capability: ${EXPERT_ASK_CAPABILITY}`\n }\n}\n\nfunction lifecycleContext(request: Request): GenerativeChatMcpContext {\n const {\n system: {\n party,\n source,\n protocol,\n sourcePrefix,\n },\n content: {\n yaml,\n },\n } = Lifecycle.getConfig()\n\n return {\n party,\n source,\n protocol,\n sourcePrefix,\n yaml,\n token: mcpTokenFromRequest(request),\n request,\n }\n}\n\nfunction mcpTokenFromRequest(\n request: Request,\n): Token | McpCapabilityToken | null {\n const token = Token.from(request)\n if (token) return token\n\n const json = request.headers.get(OAUTH_CONTEXT_HEADER)\n if (!json) return null\n return new OAuthMcpToken(JSON.parse(json))\n}\n\nfunction latestAssistantAnswer(\n events: GenerativeChatRenderEvent[],\n): string | null {\n for (let index = events.length - 1; index >= 0; index--) {\n const event = events[index]\n if (event.role === 'assistant' && !event.error) {\n return event.content\n }\n }\n return null\n}\n\nfunction semanticMemoryPromptSection(\n resources: SemanticMemoryPromptResource[],\n): string {\n if (!resources.length) {\n return '\\n\\nAvailable semantic memories:\\nNone discovered.'\n }\n\n const lines = [\n '',\n '',\n 'Available semantic memories:',\n 'Use semantic_query with the exact root and collection when these memories are relevant.',\n ]\n for (const resource of resources) {\n const root = stringValue(resource.root)\n const collection = stringValue(resource.collection)\n if (!root || !collection) continue\n lines.push(`- root: ${root}`)\n lines.push(` collection: ${collection}`)\n const description = stringValue(resource.description)\n if (description) lines.push(` description: ${description}`)\n const schema = objectValue(resource.schema)\n const textProperties = stringArrayValue(schema?.textProperties)\n if (textProperties.length) {\n lines.push(` textProperties: ${textProperties.join(', ')}`)\n }\n const propertyTypes = objectValue(schema?.propertyTypes)\n if (propertyTypes) {\n lines.push(` propertyTypes: ${JSON.stringify(propertyTypes)}`)\n }\n lines.push(\n ` query with: semantic_query({ root: ${\n JSON.stringify(root)\n }, collection: ${\n JSON.stringify(collection)\n }, query: { text: \"<search text>\", limit: 5 } })`,\n )\n }\n\n return lines.join('\\n')\n}\n\nfunction stringValue(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction objectValue(value: unknown): JSONRecord | null {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as JSONRecord\n : null\n}\n\nfunction stringArrayValue(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === 'string')\n : []\n}\n\nfunction configuredMemoryRoot(): string | undefined {\n const {\n system: { source },\n } = Lifecycle.getConfig()\n const sourceUrl = new URL(source)\n const root = sourceUrl.searchParams.get('root')?.trim()\n .replace(/^\\/+|\\/+$/g, '')\n return root || undefined\n}\n\nasync function expertRoot(): Promise<string> {\n const {\n system: { source },\n } = Lifecycle.getConfig()\n const sourceUrl = new URL(source)\n const configuredRoot = configuredMemoryRoot()\n if (configuredRoot) return configuredRoot\n sourceUrl.hash = ''\n return await getAppId(sourceUrl)\n}\n", "import { Plain } from \"./Assertions.ts\";\n\n// Standard HTTP headers.\nconst X_FORWARDED_HOST = 'x-forwarded-host'\nconst X_FORWARDED_PROTO = 'x-forwarded-proto'\n\n// Non-standard HTTP header used by tabserver.\nconst X_FORWARDED_PATHNAME = 'x-forwarded-pathname'\n\n/**\n * Returns the request protocol. This is the first of the following:\n *\n * 1. The `x-forwarded-proto` header, if present, with trailing colon.\n * 2. The request URL protocol, ditto.\n *\n * @param request the request.\n * @returns {string} the protocol with trailing colon.\n */\nexport function getClientProtocol(request: Request): string {\n if (request.headers.has(X_FORWARDED_PROTO)) {\n return (request.headers.get(X_FORWARDED_PROTO) as string) + ':'\n }\n\n return new URL(request.url).protocol\n}\n\n/**\n * Returns the request host name. This is the first of the following:\n *\n * 1. The `x-forwarded-host` header, if present.\n * 2. The request URL hostname (including port, if present)\n *\n * @param request the request.\n * @returns the protocol.\n */\nexport function getClientHost(request: Request): string {\n if (request.headers.has(X_FORWARDED_HOST)) {\n return request.headers.get(X_FORWARDED_HOST) || ''\n }\n\n return new URL(request.url).host\n}\n\n/**\n * Returns the request pathname. This is the first of the following:\n *\n * 1. The `x-forwarded-pathname` header, if present.\n * 2. The request URL pathname.\n */\nexport function getClientPathname(request: Request): string {\n if (request.headers.has(X_FORWARDED_PATHNAME)) {\n return request.headers.get(X_FORWARDED_PATHNAME) || ''\n }\n\n return new URL(request.url).pathname\n}\n\n/**\n * Returns the client request protocol, host and pathname components\n * only.\n *\n * @param request the request.\n * @return {URL} the protocol://host/pathname\n */\nexport function getClientUrlPhp(request: Request): URL {\n const protocol = getClientProtocol(request)\n const host = getClientHost(request)\n const pathname = getClientPathname(request)\n return new URL(`${protocol}//${host}${pathname}`)\n}\n\n/**\n * Returns the URL as requested by the client taking account\n * of the x-forwarded protocol and host.\n *\n * @param request the request.\n * @returns the URL.\n */\nexport function getClientUrl(request: Request): URL {\n const originalUrl = new URL(request.url)\n const newUrl = getClientUrlPhp(request)\n newUrl.search = originalUrl.search\n newUrl.hash = originalUrl.hash\n return newUrl\n}\n\n/**\n * Returns the pathname portion of the request URL, excluding\n * query and fragment components.\n *\n * @param {request} the request.\n * @returns {string} the pathname which starts with a forward slash.\n */\nexport function getPathname(request: Request): string {\n const url = new URL(request.url)\n return url.pathname\n}\n\n/**\n * Returns the string value of a named cookie, or null if it is not\n * present in the request.\n *\n * @param {Request} request the request.\n * @param {string} cookieName the name of the cookie\n * @returns {string} the cookie value, or null if not present.\n */\nexport function getCookie(request: Request, cookieName: string): string | null {\n const cookies = getCookies(request)\n if (cookieName in cookies) {\n return cookies[cookieName]\n }\n return null\n}\n\n/**\n * Returns a map of request cookie names to values, which can be empty.\n *\n * @param {Request} request the request.\n * @returns {Plain} the plain object with zero or more cookie keys.\n */\nexport function getCookies(request: Request): Plain {\n const cookies: Plain = {}\n const headerValue = request.headers.get('Cookie')\n if (headerValue) {\n for (const keyVal of headerValue.split('; ')) {\n const [key, value] = keyVal.split('=')\n cookies[key] = value\n }\n }\n return cookies\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["shortSafeDigest", "message", "length", "msgUint8", "hashBuffer", "hashArray", "byteString", "base64Safe", "shortHexDigest", "hashHex", "b", "getAppId", "url", "ALGORITHM", "MATCH_ORIGIN", "MATCH_APP_ISSUER_PATH", "TOKEN_IAT_LEEWAY_MILLIS", "Token", "_Token", "#SIGNATORY_CACHE_TTL_MILLIS", "#SIGNATORY_CACHE_MAX_ENTRIES", "#signatoryCache", "#signatoryCacheInFlight", "#payload", "#signatureBase64", "#audUrl", "#subUrl", "#srcUrl", "#signatory", "source", "payload", "iss", "aud", "sub", "src", "scope", "iat", "exp", "privateJwkPromise", "privateJwk", "privateKey", "toSign", "messageBuffer", "bufferSource", "signature", "signatureBase64", "issuer", "signatory", "publicKey", "#getCachedSignatory", "#validateSignatory", "#checkSignatureWithPublicKey", "#fetchSignatoryForIssuer", "publicJwkPromise", "publicJwk", "#importPublicKey", "toCheck", "sigBuffer", "signatorySrc", "tokenSrc", "signatoryUrl", "cached", "#getFreshCachedSignatory", "inFlight", "promise", "entry", "#putCachedSignatory", "oldestKey", "json", "sources", "capability", "now", "bytes", "binString", "x", "base64", "m", "request", "tokenBase64", "tokenSet", "searchParams", "key", "KEY_PATTERN", "KEYLIKE_PATTERN", "Storage", "#token", "token", "key", "getItem", "keylike", "getItemsLike", "value", "setItem", "removeItem", "assertValid", "assertValidLike", "serialise", "body", "url", "Char", "Encoded", "ParamExtractor", "hash", "i", "params", "query", "keys", "key", "PARTY_PARAM", "PREFIX_PARAM", "LAST_KNOWN_DAEMON", "BrowserApp", "_BrowserApp", "#instance", "#appName", "#partyKey", "#tokensKey", "#appUrlKey", "appName", "ourUrl", "extractor", "ParamExtractor", "newHash", "newSearch", "daemonParams", "identityToken", "srcUrl", "appUrl", "cleanUrl", "getAppId", "tab", "origin", "prefix", "agentUrl", "options", "party", "appEndpoint", "tokens", "tokenBase64", "Token", "name", "searchParams", "paramName", "value", "url", "json", "alert", "Storage", "DEFAULT_AGENT_TAB", "ClientToolRegistry", "#tools", "name", "descriptor", "execute", "tool", "toolCalls", "context", "results", "call", "clientTool", "content", "e", "GenerativeChatClient", "#app", "#appName", "#agentTab", "#agentUrl", "#clientTools", "options", "BrowserApp", "#requireApp", "targetParty", "result", "toolResults", "chatId", "path", "body", "json", "value", "trimmed", "url", "createDefaultBrowserChatTools", "app", "registry", "_input", "token", "party", "now", "timeZone", "DEFAULT_ALGORITHM", "KeyPair", "#algorithm", "#keypair", "algorithm", "publicKey", "privateKey", "spkiKey", "pkcs8Key", "getLaunchAlert", "app", "BrowserApp", "origin", "url", "json", "alert", "ParentHelper", "#privateJwk", "#protocol", "#child", "#iss", "#src", "#token", "#claimCode", "child", "src", "privateJwk", "iss", "#buildToken", "url", "token", "body", "response", "ok", "error", "e", "flow", "options", "type", "ttl", "expiry", "payload", "json", "claimCode", "_checkState", "checkCode", "aud", "sub", "srcUrl", "Token", "ImgClass", "QrCode", "#img", "#content", "img", "content", "qr", "dataUrl", "resolve", "reject", "cloneJSONValue", "value", "out", "k", "v", "notNull", "isDefined", "response", "rvalue", "extraHeaders", "body", "error", "status", "message", "response404", "json", "response302", "url", "buildCookie", "payload", "name", "value", "domain", "sameSite", "path", "expires", "secure", "httpOnly", "builder", "DAEMON_PREFIX", "CONFIG_PATH", "EVENT_PATH", "Ev", "CONFIG_KEY", "DEFAULT_TTL_MILLIS", "Lifecycle", "_Lifecycle", "#instance", "request", "url", "response", "configJson", "type", "payload", "json", "privateJwk", "scope", "party", "src", "ttlMillis", "config", "protocol", "us", "issuer", "source", "appSourceUrl", "aud", "sub", "now", "token", "Token", "Storage", "JsonRpcMcpClient", "#post", "#nextId", "options", "fetchFn", "body", "headers", "jsonHeaders", "tools", "#request", "name", "args", "result", "mcpToolErrorMessage", "method", "params", "json", "content", "first", "out", "AgentToolRuntime", "#postParty", "#postPartyRaw", "#toolCatalog", "#toolRoutes", "#mcpClients", "options", "catalog", "value", "toolCatalog", "entry", "toChatTool", "isDefined", "toolCalls", "results", "call", "e", "route", "result", "#executeMcpToolCall", "client", "JsonRpcMcpClient", "body", "PROTOCOL_VERSION", "CORS_HEADERS", "MCP_METHODS", "McpToolRegistry", "#tools", "tools", "tool", "name", "args", "context", "candidate", "ProtocolError", "JsonRpcMcpServer", "#context", "#serverInfo", "contextOrOptions", "request", "sseEndpointResponse", "methodNotAllowed", "message", "jsonResponse", "jsonRpcError", "id", "jsonRpcId", "accepted", "result", "#handleMethod", "e", "method", "params", "#callTool", "structuredContent", "mcpToolResult", "code", "isError", "value", "url", "USE_TOOLS_CAPABILITY", "CAPABILITY_SEPARATOR", "OAuthMcpToken", "#context", "context", "source", "capability", "hasMcpToolCapability", "authorization", "token", "providerSource", "toolName", "providerYaml", "tokenHasCapability", "callerSource", "yamlDeclaresMcpToolCapability", "yamlGrantsCapability", "yaml", "tool", "grant", "counterparty", "src", "counterpartyPattern", "matchesSqlLike", "grantEntry", "srcPattern", "splitCapabilities", "scope", "value", "pattern", "DEFAULT_MAX_TOOL_ROUNDS", "DEFAULT_TEMPERATURE", "TOKEN_HEADER", "OAUTH_CONTEXT_HEADER", "EXPERT_ASK", "EXPERT_ASK_CAPABILITY", "GenerativeChatAgent", "#targetScope", "#defaultSystemPromptUrl", "#defaultTemperature", "#maxToolRounds", "#mcpPath", "#sessions", "#lifecycle", "Lifecycle", "#configReady", "#defaultSystemPromptPromise", "#settings", "#mcp", "options", "resolve", "event", "JsonRpcMcpServer", "GenerativeChatExpertTools", "lifecycleContext", "request", "url", "response", "#getSession", "#getSettings", "#listChats", "#send", "#clientToolResults", "#close", "#deleteChat", "question", "trimmed", "targetParty", "#normalizeTargetParty", "systemPrompt", "#systemPromptForChat", "temperature", "session", "#sessionForRequest", "clearPendingClientTools", "#refreshToolCatalog", "events", "result", "#postParty", "#sendOptions", "conversation", "#continueConversation", "answer", "latestAssistantAnswer", "key", "sessionKey", "e", "body", "jsonBody", "toolsEnabled", "#toolsEnabledFrom", "toolDetails", "#toolDetailsFrom", "#temperatureFrom", "#targetPartyFrom", "chatId", "chatIdFrom", "updateClientTools", "clientResults", "validateClientToolResults", "appendToolResultEvent", "toolResults", "#continueAfterToolResults", "message", "assistantMessage", "appendAssistantEvent", "toolCalls", "clientToolCalls", "resolveToolCalls", "call", "optionalJsonBody", "#systemPromptFrom", "setClientTools", "#chatHistory", "#chatClosed", "#updateSettings", "#settingsWithPrompt", "requestedChatId", "existing", "chat", "#toolRuntimeForTarget", "item", "AgentToolRuntime", "path", "#postPartyRaw", "tools", "oauthContext", "json", "config", "token", "headers", "basePrompt", "#settingsSystemPrompt", "value", "party", "protocol", "#defaultSystemPrompt", "#loadDefaultSystemPrompt", "root", "expertRoot", "expertInstruction", "semanticInventory", "#semanticMemoryPrompt", "configuredRoot", "configuredMemoryRoot", "resources", "#semanticMemoryResourcesForRootWithFallback", "#semanticMemoryResources", "semanticMemoryPromptSection", "rooted", "#resolvedDefaultSystemPromptUrl", "source", "#defaultSystemPromptUrlFromConfig", "clientTools", "isClientToolDescriptor", "tool", "pendingIds", "results", "isToolResult", "requestIdentity", "isClientTool", "toolCallMessage", "content", "name", "isError", "toolResultMessage", "#agent", "agent", "expertAskDescriptor", "args", "context", "ProtocolError", "assertExpertAskCapability", "hasMcpToolCapability", "sourcePrefix", "yaml", "mcpTokenFromRequest", "Token", "OAuthMcpToken", "index", "lines", "resource", "stringValue", "collection", "description", "schema", "objectValue", "textProperties", "stringArrayValue", "propertyTypes", "sourceUrl", "getAppId", "X_FORWARDED_HOST", "X_FORWARDED_PROTO", "X_FORWARDED_PATHNAME", "getClientProtocol", "request", "getClientHost", "getClientPathname", "getClientUrlPhp", "protocol", "host", "pathname", "getClientUrl", "originalUrl", "newUrl", "getPathname", "getCookie", "cookieName", "cookies", "getCookies", "headerValue", "keyVal", "key", "value"]
|
|
7
7
|
}
|