@frontmcp/adapters 1.11.0 → 1.11.1-rc.2

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.
Files changed (46) hide show
  1. package/esm/index.mjs +2 -2
  2. package/esm/openapi/index.mjs +2 -2
  3. package/esm/package.json +6 -6
  4. package/esm/skills/index.mjs +1 -1
  5. package/index.js +2 -2
  6. package/openapi/index.js +2 -2
  7. package/openapi/openapi.adapter.d.ts.map +1 -1
  8. package/openapi/openapi.output-schema.d.ts +11 -0
  9. package/openapi/openapi.output-schema.d.ts.map +1 -0
  10. package/openapi/openapi.utils.d.ts.map +1 -1
  11. package/package.json +6 -6
  12. package/skills/audit/audit-record.types.d.ts +16 -0
  13. package/skills/audit/audit-record.types.d.ts.map +1 -1
  14. package/skills/audit/audit-writer.d.ts +3 -1
  15. package/skills/audit/audit-writer.d.ts.map +1 -1
  16. package/skills/audit/index.d.ts +1 -1
  17. package/skills/audit/index.d.ts.map +1 -1
  18. package/skills/bundle/bundle-diff.d.ts.map +1 -1
  19. package/skills/bundle/bundle-version.d.ts +11 -0
  20. package/skills/bundle/bundle-version.d.ts.map +1 -0
  21. package/skills/bundle/bundle.schema.d.ts +1 -0
  22. package/skills/bundle/bundle.schema.d.ts.map +1 -1
  23. package/skills/bundle/bundle.types.d.ts +2 -0
  24. package/skills/bundle/bundle.types.d.ts.map +1 -1
  25. package/skills/bundle/overlay-parser.d.ts.map +1 -1
  26. package/skills/index.d.ts +6 -4
  27. package/skills/index.d.ts.map +1 -1
  28. package/skills/index.js +1 -1
  29. package/skills/openapi/compile-skilled-bundle.d.ts +1 -0
  30. package/skills/openapi/compile-skilled-bundle.d.ts.map +1 -1
  31. package/skills/security/jwt-verifier.d.ts +1 -1
  32. package/skills/security/jwt-verifier.d.ts.map +1 -1
  33. package/skills/security/replay-nonce-store.d.ts +47 -0
  34. package/skills/security/replay-nonce-store.d.ts.map +1 -0
  35. package/skills/security/webhook-replay-guard.d.ts +14 -11
  36. package/skills/security/webhook-replay-guard.d.ts.map +1 -1
  37. package/skills/source-options.d.ts +7 -0
  38. package/skills/source-options.d.ts.map +1 -1
  39. package/skills/sources/index.d.ts +3 -3
  40. package/skills/sources/index.d.ts.map +1 -1
  41. package/skills/sources/saas-pull.source.d.ts +96 -19
  42. package/skills/sources/saas-pull.source.d.ts.map +1 -1
  43. package/skills/sources/skill-bundle-source.interface.d.ts +67 -5
  44. package/skills/sources/skill-bundle-source.interface.d.ts.map +1 -1
  45. package/skills/sources/static.source.d.ts +6 -2
  46. package/skills/sources/static.source.d.ts.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-replay-guard.d.ts","sourceRoot":"","sources":["../../../src/skills/security/webhook-replay-guard.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CAC7D;AACD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAAC;CACvE;AAED,MAAM,WAAW,yBAAyB;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,oBAAoB,CAAC;CAClC;AAgBD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;gBAEnD,OAAO,GAAE,yBAA8B;IAyBnD,2CAA2C;IAC3C,cAAc,CAAC,EAAE,EAAE,MAAM,MAAM,GAAG,IAAI;IAItC;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,iBAAiB;IA0DtE,iEAAiE;IACjE,KAAK,IAAI,MAAM;IAYf,mDAAmD;IACnD,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
1
+ {"version":3,"file":"webhook-replay-guard.d.ts","sourceRoot":"","sources":["../../../src/skills/security/webhook-replay-guard.ts"],"names":[],"mappings":"AAMA,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAErF,8FAA8F;AAC9F,MAAM,MAAM,gBAAgB,GACxB,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,cAAc,GACd,mBAAmB,CAAC;AAExB,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CAC7D;AACD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAAC;CACvE;AAED,MAAM,WAAW,yBAAyB;IACxC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,oBAAoB,CAAC;CAClC;AAID,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IACzC,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;gBAEnD,OAAO,GAAE,yBAA8B;IAwBnD,2CAA2C;IAC3C,cAAc,CAAC,EAAE,EAAE,MAAM,MAAM,GAAG,IAAI;IAItC;;;OAGG;IACG,KAAK,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAiCtF"}
@@ -1,4 +1,5 @@
1
1
  import { z } from '@frontmcp/lazy-zod';
2
+ import type { StorageConfig } from '@frontmcp/utils';
2
3
  export declare const staticSourceSchema: import("@frontmcp/lazy-zod").ZodObject<{
3
4
  type: import("@frontmcp/lazy-zod").ZodLiteral<"static">;
4
5
  path: import("@frontmcp/lazy-zod").ZodString;
@@ -17,6 +18,9 @@ export declare const saasSourceSchema: import("@frontmcp/lazy-zod").ZodObject<{
17
18
  expectedAudience: import("@frontmcp/lazy-zod").ZodString;
18
19
  pollIntervalMs: import("@frontmcp/lazy-zod").ZodDefault<import("@frontmcp/lazy-zod").ZodNumber>;
19
20
  enableWebhook: import("@frontmcp/lazy-zod").ZodDefault<import("@frontmcp/lazy-zod").ZodBoolean>;
21
+ requirePushBodyDigest: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodBoolean>;
22
+ allowRollback: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodBoolean>;
23
+ pushReplayStorage: import("@frontmcp/lazy-zod").ZodOptional<import("zod").ZodCustom<StorageConfig, StorageConfig>>;
20
24
  jwksUrl: import("@frontmcp/lazy-zod").ZodString;
21
25
  expectedIssuer: import("@frontmcp/lazy-zod").ZodString;
22
26
  }, import("zod/v4/core").$strip>;
@@ -40,6 +44,9 @@ export declare const bundleSourceSchema: import("@frontmcp/lazy-zod").ZodDiscrim
40
44
  expectedAudience: import("@frontmcp/lazy-zod").ZodString;
41
45
  pollIntervalMs: import("@frontmcp/lazy-zod").ZodDefault<import("@frontmcp/lazy-zod").ZodNumber>;
42
46
  enableWebhook: import("@frontmcp/lazy-zod").ZodDefault<import("@frontmcp/lazy-zod").ZodBoolean>;
47
+ requirePushBodyDigest: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodBoolean>;
48
+ allowRollback: import("@frontmcp/lazy-zod").ZodOptional<import("@frontmcp/lazy-zod").ZodBoolean>;
49
+ pushReplayStorage: import("@frontmcp/lazy-zod").ZodOptional<import("zod").ZodCustom<StorageConfig, StorageConfig>>;
43
50
  jwksUrl: import("@frontmcp/lazy-zod").ZodString;
44
51
  expectedIssuer: import("@frontmcp/lazy-zod").ZodString;
45
52
  }, import("zod/v4/core").$strip>, import("@frontmcp/lazy-zod").ZodObject<{
@@ -1 +1 @@
1
- {"version":3,"file":"source-options.d.ts","sourceRoot":"","sources":["../../src/skills/source-options.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AAkBvC,eAAO,MAAM,kBAAkB;;;;gCAM7B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;gCAiB1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;gCA0B3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;gCAS7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;0CAK7B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AACjE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACrE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAIrE,eAAO,MAAM,kBAAkB;;;;;;;gCAO7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
1
+ {"version":3,"file":"source-options.d.ts","sourceRoot":"","sources":["../../src/skills/source-options.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,iBAAiB,CAAC;AA0ClE,eAAO,MAAM,kBAAkB;;;;gCAM7B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;gCAiB1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;gCA4C3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;gCAS7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;0CAK7B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AACjE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACrE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAIrE,eAAO,MAAM,kBAAkB;;;;;;;gCAO7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -2,10 +2,10 @@ import type { FrontMcpLogger } from '@frontmcp/sdk';
2
2
  import type { BundleSourceOptions } from '../source-options';
3
3
  import { InlineSource } from './inline.source';
4
4
  import { NpmSource } from './npm.source';
5
- import { SaasPullSource } from './saas-pull.source';
5
+ import { PUSH_BODY_DIGEST_CLAIM, pushBodyDigest, SaasJwksUnavailableError, SaasPullSource, SaasPullTokenRejectedError, SaasPushRefusedError, tokenFailureOf } from './saas-pull.source';
6
6
  import type { BundleSourceDeps, SkillBundleSource } from './skill-bundle-source.interface';
7
7
  import { StaticSource } from './static.source';
8
- export { StaticSource, NpmSource, SaasPullSource, InlineSource };
9
- export type { BundleCacheStore, BundleSourceDeps, BundleSourceListener, SkillBundleSource, } from './skill-bundle-source.interface';
8
+ export { StaticSource, NpmSource, SaasPullSource, SaasPullTokenRejectedError, SaasJwksUnavailableError, SaasPushRefusedError, tokenFailureOf, PUSH_BODY_DIGEST_CLAIM, pushBodyDigest, InlineSource, };
9
+ export type { BundleCacheStore, BundleOrigin, BundlePullFailureListener, BundlePushReceiver, BundleSourceDeps, BundleSourceListener, BundleTokenFailure, SkillBundleSource, } from './skill-bundle-source.interface';
10
10
  export declare function createBundleSource(source: BundleSourceOptions, cacheDir: string | undefined, logger: FrontMcpLogger, deps?: BundleSourceDeps): SkillBundleSource;
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACjE,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AAEzC,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,cAAc,EAGtB,IAAI,CAAC,EAAE,gBAAgB,GACtB,iBAAiB,CAWnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,wBAAwB,EACxB,cAAc,EACd,0BAA0B,EAC1B,oBAAoB,EACpB,cAAc,EACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EACL,YAAY,EACZ,SAAS,EACT,cAAc,EACd,0BAA0B,EAC1B,wBAAwB,EACxB,oBAAoB,EACpB,cAAc,EACd,sBAAsB,EACtB,cAAc,EACd,YAAY,GACb,CAAC;AACF,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,yBAAyB,EACzB,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AAEzC,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,cAAc,EAGtB,IAAI,CAAC,EAAE,gBAAgB,GACtB,iBAAiB,CAWnB"}
@@ -1,29 +1,63 @@
1
1
  import type { FrontMcpLogger } from '@frontmcp/sdk';
2
2
  import type { ResolvedBundle } from '../bundle/bundle.types';
3
3
  import type { SaasSourceOptions } from '../source-options';
4
- import type { BundleSourceDeps, BundleSourceListener, SkillBundleSource } from './skill-bundle-source.interface';
4
+ import type { BundlePullFailureListener, BundlePushReceiver, BundleSourceDeps, BundleSourceListener, BundleTokenFailure, SkillBundleSource } from './skill-bundle-source.interface';
5
5
  /**
6
6
  * The pinned pull token failed verification against `jwksUrl`, `expectedIssuer`
7
7
  * or `expectedAudience`. Unlike an outage, this never falls back to a cached
8
8
  * bundle: the configured SaaS is not the one the server was set up to trust.
9
9
  */
10
10
  export declare class SaasPullTokenRejectedError extends Error {
11
- constructor(reason: string);
11
+ readonly tokenFailure: BundleTokenFailure;
12
+ constructor(reason: string, tokenFailure: BundleTokenFailure);
12
13
  }
14
+ /** The issuer's JWKS could not be read, or holds no usable signing key: no token can be checked, an outage. */
15
+ export declare class SaasJwksUnavailableError extends Error {
16
+ readonly tokenFailure = "jwks_unavailable";
17
+ constructor(message: string, options?: ErrorOptions);
18
+ }
19
+ /**
20
+ * The token of a pushed bundle was refused: it fails the checks the pull token passes (`invalid-token`), a push
21
+ * already used it (`replayed`), or the replay store could not record its `jti` (`replay-unchecked`). The pushed
22
+ * bundle is not applied.
23
+ */
24
+ export declare class SaasPushRefusedError extends Error {
25
+ readonly refusal: 'invalid-token' | 'replayed' | 'replay-unchecked';
26
+ /** The check that refused the token, when the issuer's checks did rather than the replay guard. */
27
+ readonly tokenFailure?: BundleTokenFailure | undefined;
28
+ constructor(refusal: 'invalid-token' | 'replayed' | 'replay-unchecked', reason: string,
29
+ /** The check that refused the token, when the issuer's checks did rather than the replay guard. */
30
+ tokenFailure?: BundleTokenFailure | undefined);
31
+ }
32
+ /** The token check a pull or push failed on, from the error it threw; undefined for any other failure. */
33
+ export declare function tokenFailureOf(error: unknown): BundleTokenFailure | undefined;
34
+ /** The push token claim that binds it to the body it came with: {@link pushBodyDigest} of that body. */
35
+ export declare const PUSH_BODY_DIGEST_CLAIM = "body_sha256";
36
+ /** The `body_sha256` claim a push token carries for `body`: the base64url SHA-256 of its exact bytes, unpadded. */
37
+ export declare function pushBodyDigest(body: string | Uint8Array): string;
13
38
  /**
14
39
  * Pulls bundles from a configured SaaS endpoint via authenticated HTTPS.
15
40
  *
16
41
  * Boot semantics:
17
42
  * - Always attempts a fresh pull on `start()`.
18
- * - If the pull fails AND a cached bundle exists on disk, falls back to it
19
- * and emits a warning. This prevents a SaaS outage at boot from killing
20
- * the customer's MCP server.
21
- * - If both pull and cache fail, `start()` throws.
43
+ * - If the pull fails AND a cached bundle exists on disk, falls back to it,
44
+ * emits a warning, and hands it to its listeners with why the pull failed.
45
+ * This prevents a SaaS outage at boot from killing the customer's MCP
46
+ * server. The cache holds the last bundle the plugin applied
47
+ * ({@link SaasPullSource.keepBundle}): a pulled bundle that fails its
48
+ * signature or apply never replaces it, and the plugin doesn't write back
49
+ * the cached bundle it serves. While no bundle serves, the plugin reads the
50
+ * cache ({@link SaasPullSource.keptBundle}), and serves it in place of a
51
+ * pulled bundle that is older or refused.
52
+ * - If both pull and cache fail, `start()` throws. Polling still starts, so a
53
+ * later pull that succeeds loads the bundle.
22
54
  *
23
55
  * Refresh: poll on `pollIntervalMs`. Single-flight per source — overlapping
24
- * polls are skipped, never queued. Webhook support is gated behind
25
- * `enableWebhook` and deferred to v1.2.x (route mounting requires plugin HTTP
26
- * infrastructure not yet in scope).
56
+ * polls are skipped, never queued. A poll that fails is logged and reported to
57
+ * the {@link SaasPullSource.onPullFailure} listeners. With `enableWebhook` the
58
+ * source also takes pushed bundles ({@link SaasPullSource.verifyPushToken}):
59
+ * the plugin serves them at `POST /__skilled_openapi/push`, and a push token
60
+ * with a `body_sha256` claim is good only for the body it was issued for.
27
61
  *
28
62
  * Before every pull the pinned `authToken` is verified: it must be a JWT signed
29
63
  * by a key in the issuer's JWKS (`jwksUrl`), with `iss` equal to
@@ -34,30 +68,64 @@ export declare class SaasPullTokenRejectedError extends Error {
34
68
  * other pull failure (the cache fallback still applies).
35
69
  *
36
70
  * NOTE: Bundle signature verification is applied by the bundle-sync service
37
- * before the bundle becomes active.
71
+ * before the bundle becomes active, and so is the version order: the source is
72
+ * {@link SaasPullSource.forwardOnly}, so a bundle older than the served one is refused.
38
73
  */
39
- export declare class SaasPullSource implements SkillBundleSource {
74
+ export declare class SaasPullSource implements SkillBundleSource, BundlePushReceiver {
40
75
  private readonly options;
41
76
  private readonly cacheDir;
42
77
  private readonly logger;
43
78
  private readonly deps;
44
79
  readonly id: string;
45
80
  private listeners;
81
+ private pullFailureListeners;
46
82
  private pollHandle;
47
- private inFlight;
83
+ /** The pull running now, from start, a poll or a refresh; only one runs at a time. */
84
+ private inFlightPull;
48
85
  private stopped;
49
86
  private readonly jwksService;
87
+ private readonly replayGuard?;
88
+ private pushSigningKeys?;
89
+ private pushSigningKeysLoading?;
90
+ private warnedOfUnboundPushToken;
50
91
  constructor(options: SaasSourceOptions, cacheDir: string | undefined, logger: FrontMcpLogger, deps?: BundleSourceDeps);
92
+ /** The store an injected `pushReplayStore` or the `pushReplayStorage` option names; none keeps the jtis in memory. */
93
+ private pushReplayStore;
94
+ /** This source, when `enableWebhook` is on: it then takes bundles pushed to the server. */
95
+ get push(): BundlePushReceiver | undefined;
96
+ /** A pulled or pushed bundle older than the one being served is not applied, unless `allowRollback` is on. */
97
+ get forwardOnly(): boolean;
98
+ /**
99
+ * Check the token of a pushed bundle: by the rules the pull token passes (a key in `jwksUrl`, `iss`, `aud`,
100
+ * `resource`, expiry), then against `body`, the exact bytes of the request body ({@link PUSH_BODY_DIGEST_CLAIM}),
101
+ * then by the replay guard, so its `iat` must be within five minutes and its `jti` unused by every instance sharing
102
+ * the replay store. A token refused for its body leaves its `jti` unused. Throws {@link SaasPushRefusedError}, or
103
+ * {@link SaasJwksUnavailableError} when the JWKS can't be read.
104
+ */
105
+ verifyPushToken(token: string, body: Uint8Array): Promise<void>;
106
+ /** Refuse a push whose body isn't the one its token was issued for, or, with `requirePushBodyDigest`, unbound. */
107
+ private checkPushBody;
108
+ /** Keep a bundle the plugin applied, pulled or pushed, as the last good one a start falls back to. */
109
+ keepBundle(bundle: ResolvedBundle): Promise<void>;
110
+ /** The last good bundle {@link keepBundle} kept, schema-checked as a pull is; undefined when there is none. */
111
+ keptBundle(): Promise<ResolvedBundle | undefined>;
51
112
  start(): Promise<void>;
113
+ private pullAtStart;
114
+ /** Run `pull` as the one pull in flight, so a start, a poll and a refresh never pull at once. */
115
+ private exclusivePull;
52
116
  onChange(listener: BundleSourceListener): () => void;
117
+ /** Subscribe to the polls that fail, with why; a failed start or refresh throws to its caller instead. */
118
+ onPullFailure(listener: BundlePullFailureListener): () => void;
53
119
  stop(): Promise<void>;
54
120
  private schedulePoll;
55
121
  /**
56
- * Manually pull a fresh bundle, persist it, and notify listeners. Drive this
122
+ * Manually pull a fresh bundle and notify listeners, resolving once they are
123
+ * done with it (the plugin applied and kept it, or refused it). Drive this
57
124
  * from a Cron Trigger / Durable Object alarm on runtimes without background
58
- * timers (where `disablePolling` is set). Single-flight: returns `undefined`
59
- * if a pull is already in flight; throws if the pull itself fails (the caller
60
- * — e.g. a scheduled handler — decides how to surface it).
125
+ * timers (where `disablePolling` is set). Single-flight: while a pull is
126
+ * already in flight it waits for that pull to finish, applied and kept, and
127
+ * returns `undefined` without pulling again; throws if its own pull fails (the
128
+ * caller — e.g. a scheduled handler — decides how to surface it).
61
129
  */
62
130
  refresh(): Promise<ResolvedBundle | undefined>;
63
131
  private pollOnce;
@@ -66,11 +134,19 @@ export declare class SaasPullSource implements SkillBundleSource {
66
134
  body: string;
67
135
  }>;
68
136
  /**
69
- * Verify the pinned pull token against the issuer's JWKS, issuer and audience.
70
- * Throws {@link SaasPullTokenRejectedError} when the token is refused, and a
71
- * plain Error when the JWKS can't be fetched (an outage).
137
+ * Verify the pinned pull token against the issuer's JWKS, issuer and audience, and log the check it fails on.
138
+ * Throws {@link SaasPullTokenRejectedError} when the token is refused, and {@link SaasJwksUnavailableError} when
139
+ * the JWKS can't be fetched (an outage).
72
140
  */
73
141
  private verifyPullToken;
142
+ /**
143
+ * The issuer's signing keys for checking a push token: fetched at most once a minute, and shared by concurrent
144
+ * pushes. A pull always fetches them, so a key the issuer removed stops a pull at once.
145
+ */
146
+ private signingKeysForPush;
147
+ private fetchSigningKeys;
148
+ /** Check a token the issuer signed, against its JWKS, issuer and audience. */
149
+ private verifyIssuedToken;
74
150
  private fetchOnce;
75
151
  /** Where the last-good bundle is kept, for messages: an injected store (a Worker's KV) has no file path. */
76
152
  private cacheLocation;
@@ -78,5 +154,6 @@ export declare class SaasPullSource implements SkillBundleSource {
78
154
  private persistCache;
79
155
  private loadCache;
80
156
  private notify;
157
+ private notifyPullFailure;
81
158
  }
82
159
  //# sourceMappingURL=saas-pull.source.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"saas-pull.source.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/saas-pull.source.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAMjH;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,MAAM,EAAE,MAAM;CAI3B;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,cAAe,YAAW,iBAAiB;IAUpD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;gBAGhC,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE,gBAAqB;IAKxC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC5B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAO9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,OAAO,CAAC,YAAY;IAcpB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;YAatC,QAAQ;cAyBN,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAoBhH;;;;OAIG;YACW,eAAe;YA2Cf,SAAS;IAkBvB,4GAA4G;IAC5G,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,SAAS;YAKH,YAAY;YAeZ,SAAS;IAkBvB,OAAO,CAAC,MAAM;CASf"}
1
+ {"version":3,"file":"saas-pull.source.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/saas-pull.source.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
+ import type { TokenVerifyFailure } from '@frontmcp/auth';
1
2
  import type { ResolvedBundle } from '../bundle/bundle.types';
3
+ import type { ReplayNonceStore } from '../security/replay-nonce-store';
2
4
  /**
3
5
  * Pluggable source of skill bundles. Static, npm-installed, and SaaS-pull
4
6
  * implementations all conform to this interface.
@@ -8,12 +10,15 @@ import type { ResolvedBundle } from '../bundle/bundle.types';
8
10
  * its initial fetch and begins any background polling/watching.
9
11
  * - `onChange()` registers a listener invoked every time a new bundle is
10
12
  * available (initial fetch + every refresh).
13
+ * - `keepBundle()`, on a source with a last-good cache, keeps a bundle once
14
+ * the plugin has applied it.
11
15
  * - `stop()` halts all background work and releases resources.
12
16
  *
13
17
  * Sources are responsible for parsing/validating their input into a
14
18
  * ResolvedBundle (typically by calling `parseOverlay` from bundle/overlay-parser).
15
19
  * They MUST NOT verify signatures themselves — the bundle-sync service applies
16
- * the signature gate before swapping the bundle into the registry.
20
+ * the signature gate before swapping the bundle into the registry. For the same
21
+ * reason they never cache a bundle they fetched until they are asked to keep it.
17
22
  */
18
23
  export interface SkillBundleSource {
19
24
  /** Stable id of this source (e.g. 'static:/path/to/bundle.yaml'). */
@@ -29,20 +34,70 @@ export interface SkillBundleSource {
29
34
  * notification fires on or shortly after `start()` resolves.
30
35
  */
31
36
  onChange(listener: BundleSourceListener): () => void;
37
+ /**
38
+ * Subscribe to the pulls that fail in the background (a poll), which no caller sees: `start()` and `refresh()` throw
39
+ * theirs. Returns an unsubscribe function. Present on a polling source: the `saas` source.
40
+ */
41
+ onPullFailure?(listener: BundlePullFailureListener): () => void;
32
42
  /**
33
43
  * Pull a fresh bundle on demand and notify listeners — the manual,
34
44
  * externally-driven counterpart to background polling. Optional: only
35
45
  * remote/pulling sources implement it. On runtimes with no background
36
46
  * execution (Cloudflare Workers) a Cron Trigger / Durable Object alarm
37
47
  * calls this instead of relying on an internal timer. Resolves to the new
38
- * bundle, or `undefined` if a pull is already in flight / the source is
39
- * stopped.
48
+ * bundle once its listeners are done with it (the plugin has applied it, or
49
+ * refused it), or to `undefined` if a pull is already in flight / the source
50
+ * is stopped.
40
51
  */
41
52
  refresh?(): Promise<ResolvedBundle | undefined>;
53
+ /**
54
+ * Keep a bundle the plugin applied as the last good one, which a start that can't fetch falls back to. Present on
55
+ * a source with a last-good cache: the `saas` source.
56
+ */
57
+ keepBundle?(bundle: ResolvedBundle): Promise<void>;
58
+ /**
59
+ * The last good bundle {@link keepBundle} kept, read back from the cache; undefined when none is kept or it can't be
60
+ * read. While no bundle serves, the plugin compares a new bundle with it, and serves it in place of one it refuses.
61
+ */
62
+ keptBundle?(): Promise<ResolvedBundle | undefined>;
63
+ /** Present on a source that takes pushed bundles: the `saas` source with `enableWebhook`. */
64
+ readonly push?: BundlePushReceiver;
65
+ /**
66
+ * True on a source whose bundles only move forward: the `saas` source, unless `allowRollback`. The plugin then
67
+ * refuses a bundle older than the one being served, as `compareBundleVersions` orders them.
68
+ */
69
+ readonly forwardOnly?: boolean;
42
70
  /** Halt background work; idempotent. */
43
71
  stop(): Promise<void>;
44
72
  }
45
- export type BundleSourceListener = (bundle: ResolvedBundle) => void;
73
+ /** The check a pushed bundle's token passes before the bundle is applied. */
74
+ export interface BundlePushReceiver {
75
+ /**
76
+ * Resolves when the push token is accepted for `body`, the exact bytes of the request body; throws when it is
77
+ * refused or can't be checked.
78
+ */
79
+ verifyPushToken(token: string, body: Uint8Array): Promise<void>;
80
+ }
81
+ /** Takes each new bundle; a pulling source waits for a promise it returns before its pull counts as done. */
82
+ export type BundleSourceListener = (bundle: ResolvedBundle, origin?: BundleOrigin) => unknown;
83
+ /** How a source came by a bundle it hands its listeners. */
84
+ export interface BundleOrigin {
85
+ /**
86
+ * Set when the pull failed and the bundle is the last good one the source kept, served in its place: why it failed.
87
+ * The plugin doesn't keep such a bundle again, since the cache holds it.
88
+ */
89
+ pullFailure?: string;
90
+ /** Set when that pull failed on its token: the check that refused it, or `jwks_unavailable`. */
91
+ tokenFailure?: BundleTokenFailure;
92
+ }
93
+ /**
94
+ * Which check refused a pull or push token, or `jwks_unavailable` when the issuer's keys could not be read to check
95
+ * it; `body_mismatch` is a push token issued for another body. For the server's logs and `/readyz`: a caller learns
96
+ * only that its token was refused.
97
+ */
98
+ export type BundleTokenFailure = TokenVerifyFailure | 'wrong_audience' | 'body_mismatch';
99
+ /** Takes the reason a background pull failed, and the token check it failed on, if any. */
100
+ export type BundlePullFailureListener = (reason: string, tokenFailure?: BundleTokenFailure) => void;
46
101
  /**
47
102
  * Pluggable last-good bundle cache. The default (Node) implementation writes to
48
103
  * disk; V8-isolate runtimes (Cloudflare Workers) inject a KV-backed store
@@ -59,7 +114,9 @@ export interface BundleCacheStore {
59
114
  * - `cache` replaces the on-disk last-good cache (no filesystem on a Worker),
60
115
  * - `disablePolling` turns off the internal `setInterval` loop (no background
61
116
  * execution on a Worker; a Cron Trigger / DO alarm drives {@link
62
- * SkillBundleSource.refresh} instead).
117
+ * SkillBundleSource.refresh} instead),
118
+ * - `pushReplayStore` records the pushed tokens' `jti`s where every instance
119
+ * reaches them (a Durable Object on a Worker).
63
120
  */
64
121
  export interface BundleSourceDeps {
65
122
  /**
@@ -73,5 +130,10 @@ export interface BundleSourceDeps {
73
130
  * {@link SkillBundleSource.refresh} instead.
74
131
  */
75
132
  disablePolling?: boolean;
133
+ /**
134
+ * Where the push replay guard records the `jti`s it accepted, shared by every instance that serves the server.
135
+ * Replaces the `saas` source's `pushReplayStorage` option and its in-memory default.
136
+ */
137
+ pushReplayStore?: ReplayNonceStore;
76
138
  }
77
139
  //# sourceMappingURL=skill-bundle-source.interface.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill-bundle-source.interface.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/skill-bundle-source.interface.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAAC;IAErD;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAEhD,wCAAwC;IACxC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;AAEpE;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAC5C,KAAK,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B"}
1
+ {"version":3,"file":"skill-bundle-source.interface.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/skill-bundle-source.interface.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAAC;IAErD;;;OAGG;IACH,aAAa,CAAC,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,IAAI,CAAC;IAEhE;;;;;;;;;OASG;IACH,OAAO,CAAC,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAEhD;;;OAGG;IACH,UAAU,CAAC,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnD;;;OAGG;IACH,UAAU,CAAC,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAEnD,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAEnC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAE/B,wCAAwC;IACxC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAED,6GAA6G;AAC7G,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC;AAE9F,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gGAAgG;IAChG,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAEzF,2FAA2F;AAC3F,MAAM,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAEpG;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IAC5C,KAAK,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;CACpC"}
@@ -3,7 +3,9 @@ import type { StaticSourceOptions } from '../source-options';
3
3
  import type { BundleSourceListener, SkillBundleSource } from './skill-bundle-source.interface';
4
4
  /**
5
5
  * Loads a bundle from a local file (YAML or JSON). Optionally watches the
6
- * file for changes via `fs.watch` and re-fetches on edit.
6
+ * file for changes via `fs.watch` and re-fetches on edit. The watch starts
7
+ * before the first read, so fixing a file that failed to load reloads it. A
8
+ * refresh that reads the same content as the last read applies nothing.
7
9
  *
8
10
  * Bundle file extensions:
9
11
  * - `.yaml` / `.yml` — parsed as YAML
@@ -18,11 +20,13 @@ export declare class StaticSource implements SkillBundleSource {
18
20
  private watcher;
19
21
  private debounceTimer;
20
22
  private stopped;
23
+ private lastContent;
21
24
  constructor(options: StaticSourceOptions, logger: FrontMcpLogger);
22
25
  start(): Promise<void>;
23
26
  onChange(listener: BundleSourceListener): () => void;
24
27
  stop(): Promise<void>;
25
- private fetchOnce;
28
+ private readBundleFile;
29
+ private parse;
26
30
  private notify;
27
31
  private beginWatch;
28
32
  private scheduleRefresh;
@@ -1 +1 @@
1
- {"version":3,"file":"static.source.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/static.source.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAKpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAE/F;;;;;;;;GAQG;AACH,qBAAa,YAAa,YAAW,iBAAiB;IASlD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IATzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,OAAO,CAAS;gBAGL,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc;IAKnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAO9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAab,SAAS;IAoBvB,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,eAAe;CAaxB"}
1
+ {"version":3,"file":"static.source.d.ts","sourceRoot":"","sources":["../../../src/skills/sources/static.source.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAKpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAE/F;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,YAAW,iBAAiB;IAUlD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAVzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAqB;gBAGrB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,cAAc;IAKnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAO9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAa3B,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,KAAK;IAkBb,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,eAAe;CAexB"}