runbios-mcp 0.2.1-dev.163 → 0.2.1-dev.165

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/config.d.ts CHANGED
@@ -31,16 +31,33 @@ export declare function resolveLaunchGates(env?: LaunchGateEnv): LaunchGates;
31
31
  * environment gates above answer "is this surface open in this deployment?";
32
32
  * the training-beta grant answers "is THIS organization admitted?". When an
33
33
  * env gate is closed and the session knows its org, we ask the control plane
34
- * once — GET /api/organizations/{id}/features, the same endpoint the console
34
+ * — GET /api/organizations/{id}/features, the same endpoint the console
35
35
  * reads — and a granted org gets the creation tools registered for its
36
36
  * session even where the environment stays gated for everyone else.
37
37
  *
38
38
  * Fail-closed in both directions, matching the services: any failure to
39
- * resolve (no org, non-200, malformed body, network down) reads as NOT
40
- * granted and the env gates stand as resolved.
39
+ * resolve (no org, non-200, malformed body, network down, TIMEOUT) reads as
40
+ * NOT granted and the env gates stand as resolved.
41
+ *
42
+ * TWO THINGS GUARD THE HOT PATH, because in the environments that matter the
43
+ * gates are CLOSED, so this resolution runs on EVERY hosted MCP request —
44
+ * including tool calls that have nothing to do with training:
45
+ *
46
+ * * A deadline. The control-plane read is not allowed to be slower than
47
+ * GRANT_RESOLVE_TIMEOUT_MS; past that the answer is "not granted". Without
48
+ * it, a workspace-service that hangs rather than refuses would hang every
49
+ * MCP request behind it — failing closed only helps if it fails PROMPTLY.
50
+ * * A short per-org cache. A session is many POSTs, and an entitlement does
51
+ * not change between two tool calls a second apart. The TTL is deliberately
52
+ * seconds, not minutes: an operator who admits a customer expects the next
53
+ * reconnect to work, not to wait out a cache.
41
54
  */
55
+ export declare const GRANT_RESOLVE_TIMEOUT_MS = 2000;
56
+ export declare const GRANT_CACHE_TTL_MS = 30000;
57
+ /** Test seam: drop memoised grants so a case starts from a cold cache. */
58
+ export declare function resetGrantCache(): void;
42
59
  export declare function sessionLaunchGates(orgId: string, getFeatures: () => Promise<{
43
60
  features?: Record<string, boolean>;
44
- }>, env?: LaunchGateEnv): Promise<LaunchGates>;
61
+ }>, env?: LaunchGateEnv, now?: () => number): Promise<LaunchGates>;
45
62
  export {};
46
63
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB,+BAA+B,CAAC;AAE7D,KAAK,UAAU,GAAG,OAAO,CACvB,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,kBAAkB,GAAG,eAAe,CAAC,CAC9D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,UAAwB,GAAG,MAAM,CAEpE;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,KAAK,aAAa,GAAG,OAAO,CAC1B,IAAI,CACF,MAAM,CAAC,UAAU,EACf,8BAA8B,GAC9B,2BAA2B,GAC3B,8BAA8B,GAC9B,2BAA2B,CAC9B,CACF,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,aAA2B,GAAG,WAAW,CAKhF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,OAAO,CAAC;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,EAClE,GAAG,GAAE,aAA2B,GAC/B,OAAO,CAAC,WAAW,CAAC,CAUtB"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB,+BAA+B,CAAC;AAE7D,KAAK,UAAU,GAAG,OAAO,CACvB,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,kBAAkB,GAAG,eAAe,CAAC,CAC9D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,UAAwB,GAAG,MAAM,CAEpE;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,KAAK,aAAa,GAAG,OAAO,CAC1B,IAAI,CACF,MAAM,CAAC,UAAU,EACf,8BAA8B,GAC9B,2BAA2B,GAC3B,8BAA8B,GAC9B,2BAA2B,CAC9B,CACF,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,aAA2B,GAAG,WAAW,CAKhF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAC9C,eAAO,MAAM,kBAAkB,QAAS,CAAC;AAKzC,0EAA0E;AAC1E,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,OAAO,CAAC;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,EAClE,GAAG,GAAE,aAA2B,EAChC,GAAG,GAAE,MAAM,MAAiB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAqCtB"}
package/dist/config.js CHANGED
@@ -18,26 +18,70 @@ export function resolveLaunchGates(env = process.env) {
18
18
  * environment gates above answer "is this surface open in this deployment?";
19
19
  * the training-beta grant answers "is THIS organization admitted?". When an
20
20
  * env gate is closed and the session knows its org, we ask the control plane
21
- * once — GET /api/organizations/{id}/features, the same endpoint the console
21
+ * — GET /api/organizations/{id}/features, the same endpoint the console
22
22
  * reads — and a granted org gets the creation tools registered for its
23
23
  * session even where the environment stays gated for everyone else.
24
24
  *
25
25
  * Fail-closed in both directions, matching the services: any failure to
26
- * resolve (no org, non-200, malformed body, network down) reads as NOT
27
- * granted and the env gates stand as resolved.
26
+ * resolve (no org, non-200, malformed body, network down, TIMEOUT) reads as
27
+ * NOT granted and the env gates stand as resolved.
28
+ *
29
+ * TWO THINGS GUARD THE HOT PATH, because in the environments that matter the
30
+ * gates are CLOSED, so this resolution runs on EVERY hosted MCP request —
31
+ * including tool calls that have nothing to do with training:
32
+ *
33
+ * * A deadline. The control-plane read is not allowed to be slower than
34
+ * GRANT_RESOLVE_TIMEOUT_MS; past that the answer is "not granted". Without
35
+ * it, a workspace-service that hangs rather than refuses would hang every
36
+ * MCP request behind it — failing closed only helps if it fails PROMPTLY.
37
+ * * A short per-org cache. A session is many POSTs, and an entitlement does
38
+ * not change between two tool calls a second apart. The TTL is deliberately
39
+ * seconds, not minutes: an operator who admits a customer expects the next
40
+ * reconnect to work, not to wait out a cache.
28
41
  */
29
- export async function sessionLaunchGates(orgId, getFeatures, env = process.env) {
42
+ export const GRANT_RESOLVE_TIMEOUT_MS = 2_000;
43
+ export const GRANT_CACHE_TTL_MS = 30_000;
44
+ const grantCache = new Map();
45
+ /** Test seam: drop memoised grants so a case starts from a cold cache. */
46
+ export function resetGrantCache() {
47
+ grantCache.clear();
48
+ }
49
+ export async function sessionLaunchGates(orgId, getFeatures, env = process.env, now = Date.now) {
30
50
  const gates = resolveLaunchGates(env);
31
51
  if (!orgId || (!gates.training && !gates.datasets))
32
52
  return gates;
53
+ const open = { training: false, datasets: false };
54
+ const cached = grantCache.get(orgId);
55
+ if (cached && now() - cached.at < GRANT_CACHE_TTL_MS) {
56
+ return cached.granted ? open : gates;
57
+ }
58
+ let granted = false;
59
+ let deadline;
33
60
  try {
34
- const res = await getFeatures();
35
- if (res?.features?.training !== true)
36
- return gates;
37
- return { training: false, datasets: false };
61
+ // Promise.race, not an AbortSignal: the caller hands us an opaque
62
+ // thunk, so the deadline belongs here rather than in each call site.
63
+ // A timed-out read is abandoned, never awaited — the request moves on.
64
+ const res = await Promise.race([
65
+ getFeatures(),
66
+ new Promise((_, reject) => {
67
+ deadline = setTimeout(() => reject(new Error("grant resolution timed out")), GRANT_RESOLVE_TIMEOUT_MS);
68
+ }),
69
+ ]);
70
+ granted = res?.features?.training === true;
38
71
  }
39
72
  catch {
73
+ // Every failure direction lands here and leaves `granted` false. A
74
+ // failure is NOT cached: a blip must not lock a paying customer out for
75
+ // the whole TTL, and retrying next request is cheap.
40
76
  return gates;
41
77
  }
78
+ finally {
79
+ // Always clear it. A 2s timer left per request is a slow leak on an
80
+ // endpoint that serves one of these per tool call, and an uncleared
81
+ // one keeps the process alive past when it should exit.
82
+ clearTimeout(deadline);
83
+ }
84
+ grantCache.set(orgId, { granted, at: now() });
85
+ return granted ? open : gates;
42
86
  }
43
87
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,CAAC,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAM7D;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAkB,OAAO,CAAC,GAAG;IAC1D,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC,aAAa,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC7F,CAAC;AAgCD,MAAM,UAAU,kBAAkB,CAAC,MAAqB,OAAO,CAAC,GAAG;IACjE,OAAO;QACL,QAAQ,EAAE,CAAC,GAAG,CAAC,4BAA4B,IAAI,GAAG,CAAC,yBAAyB,CAAC,KAAK,OAAO;QACzF,QAAQ,EAAE,CAAC,GAAG,CAAC,4BAA4B,IAAI,GAAG,CAAC,yBAAyB,CAAC,KAAK,OAAO;KAC1F,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,WAAkE,EAClE,MAAqB,OAAO,CAAC,GAAG;IAEhC,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACjE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,WAAW,EAAE,CAAC;QAChC,IAAI,GAAG,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QACnD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,CAAC,MAAM,gBAAgB,GAAG,4BAA4B,CAAC;AAM7D;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAkB,OAAO,CAAC,GAAG;IAC1D,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC,aAAa,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC7F,CAAC;AAgCD,MAAM,UAAU,kBAAkB,CAAC,MAAqB,OAAO,CAAC,GAAG;IACjE,OAAO;QACL,QAAQ,EAAE,CAAC,GAAG,CAAC,4BAA4B,IAAI,GAAG,CAAC,yBAAyB,CAAC,KAAK,OAAO;QACzF,QAAQ,EAAE,CAAC,GAAG,CAAC,4BAA4B,IAAI,GAAG,CAAC,yBAAyB,CAAC,KAAK,OAAO;KAC1F,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAC9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAGzC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;AAEjD,0EAA0E;AAC1E,MAAM,UAAU,eAAe;IAC7B,UAAU,CAAC,KAAK,EAAE,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,WAAkE,EAClE,MAAqB,OAAO,CAAC,GAAG,EAChC,MAAoB,IAAI,CAAC,GAAG;IAE5B,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IAEjE,MAAM,IAAI,GAAgB,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC/D,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,kBAAkB,EAAE,CAAC;QACrD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IACvC,CAAC;IAED,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,QAAmD,CAAC;IACxD,IAAI,CAAC;QACH,kEAAkE;QAClE,qEAAqE;QACrE,uEAAuE;QACvE,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAC7B,WAAW,EAAE;YACb,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC;YACzG,CAAC,CAAC;SACH,CAAC,CAAC;QACH,OAAO,GAAG,GAAG,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;QACnE,wEAAwE;QACxE,qDAAqD;QACrD,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,oEAAoE;QACpE,oEAAoE;QACpE,wDAAwD;QACxD,YAAY,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAED,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9C,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAChC,CAAC"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.2.1-dev.163";
1
+ export declare const VERSION = "0.2.1-dev.165";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = "0.2.1-dev.163";
1
+ export const VERSION = "0.2.1-dev.165";
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runbios-mcp",
3
- "version": "0.2.1-dev.163",
3
+ "version": "0.2.1-dev.165",
4
4
  "description": "Run BiOS MCP server — train and deploy AI models from MCP-compatible automation clients",
5
5
  "license": "MIT",
6
6
  "type": "module",