@the-open-engine/zeroshot 6.28.0 → 6.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/README.md +3 -3
  2. package/cli/index.js +20 -224
  3. package/docker/zeroshot-cluster/Dockerfile +2 -3
  4. package/lib/cluster/client.cjs +30 -7
  5. package/lib/cluster/client.d.cts +52 -0
  6. package/lib/cluster/client.d.mts +52 -0
  7. package/lib/cluster/client.d.ts +4 -6
  8. package/lib/cluster/client.mjs +32 -9
  9. package/lib/cluster/connection-state.cjs +25 -0
  10. package/lib/cluster/connection-state.d.cts +13 -0
  11. package/lib/cluster/connection-state.d.mts +13 -0
  12. package/lib/cluster/connection-state.d.ts +13 -0
  13. package/lib/cluster/connection-state.mjs +21 -0
  14. package/lib/cluster/connection-types.cjs +2 -0
  15. package/lib/cluster/connection-types.d.cts +33 -0
  16. package/lib/cluster/connection-types.d.mts +33 -0
  17. package/lib/cluster/connection-types.d.ts +33 -0
  18. package/lib/cluster/connection-types.mjs +1 -0
  19. package/lib/cluster/connection.cjs +18 -46
  20. package/lib/cluster/connection.d.cts +26 -0
  21. package/lib/cluster/connection.d.mts +26 -0
  22. package/lib/cluster/connection.d.ts +7 -25
  23. package/lib/cluster/connection.mjs +12 -42
  24. package/lib/cluster/errors.cjs +9 -1
  25. package/lib/cluster/errors.d.cts +29 -0
  26. package/lib/cluster/errors.d.mts +29 -0
  27. package/lib/cluster/errors.d.ts +4 -0
  28. package/lib/cluster/errors.mjs +7 -0
  29. package/lib/cluster/frames.d.cts +12 -0
  30. package/lib/cluster/frames.d.mts +12 -0
  31. package/lib/cluster/generated/protocol-schema.d.cts +1 -0
  32. package/lib/cluster/generated/protocol-schema.d.mts +1 -0
  33. package/lib/cluster/generated/protocol.d.cts +781 -0
  34. package/lib/cluster/generated/protocol.d.mts +781 -0
  35. package/lib/cluster/index.cjs +2 -2
  36. package/lib/cluster/index.d.cts +13 -0
  37. package/lib/cluster/index.d.mts +13 -0
  38. package/lib/cluster/index.d.ts +3 -3
  39. package/lib/cluster/index.mjs +2 -2
  40. package/lib/cluster/json-source.cjs +1 -0
  41. package/lib/cluster/json-source.d.cts +5 -0
  42. package/lib/cluster/json-source.d.mts +5 -0
  43. package/lib/cluster/json-source.d.ts +1 -0
  44. package/lib/cluster/json-source.mjs +1 -0
  45. package/lib/cluster/payload-value.cjs +1 -0
  46. package/lib/cluster/payload-value.d.cts +8 -0
  47. package/lib/cluster/payload-value.d.mts +8 -0
  48. package/lib/cluster/payload-value.d.ts +1 -0
  49. package/lib/cluster/payload-value.mjs +1 -0
  50. package/lib/cluster/queue.d.cts +15 -0
  51. package/lib/cluster/queue.d.mts +15 -0
  52. package/lib/cluster/socket.cjs +8 -0
  53. package/lib/cluster/socket.d.cts +13 -0
  54. package/lib/cluster/socket.d.mts +13 -0
  55. package/lib/cluster/socket.d.ts +2 -0
  56. package/lib/cluster/socket.mjs +7 -0
  57. package/lib/cluster/subscriptions.d.cts +73 -0
  58. package/lib/cluster/subscriptions.d.mts +73 -0
  59. package/lib/cluster/subscriptions.d.ts +5 -1
  60. package/lib/cluster/validators.cjs +1 -0
  61. package/lib/cluster/validators.d.cts +7 -0
  62. package/lib/cluster/validators.d.mts +7 -0
  63. package/lib/cluster/validators.d.ts +1 -0
  64. package/lib/cluster/validators.mjs +1 -0
  65. package/lib/cluster-worker/engine-adapter.js +1 -11
  66. package/lib/cluster-worker/engine-input.js +14 -0
  67. package/lib/hosted-session/authority.cjs +61 -0
  68. package/lib/hosted-session/authority.d.cts +3 -0
  69. package/lib/hosted-session/authority.d.mts +3 -0
  70. package/lib/hosted-session/authority.d.ts +3 -0
  71. package/lib/hosted-session/authority.mjs +57 -0
  72. package/lib/hosted-session/coordinator.cjs +78 -51
  73. package/lib/hosted-session/coordinator.d.cts +11 -0
  74. package/lib/hosted-session/coordinator.d.mts +11 -0
  75. package/lib/hosted-session/coordinator.d.ts +4 -2
  76. package/lib/hosted-session/coordinator.mjs +75 -51
  77. package/lib/hosted-session/errors.cjs +25 -0
  78. package/lib/hosted-session/errors.d.cts +10 -0
  79. package/lib/hosted-session/errors.d.mts +10 -0
  80. package/lib/hosted-session/errors.d.ts +10 -0
  81. package/lib/hosted-session/errors.mjs +19 -0
  82. package/lib/hosted-session/index.cjs +4 -1
  83. package/lib/hosted-session/index.d.cts +2 -0
  84. package/lib/hosted-session/index.d.mts +2 -0
  85. package/lib/hosted-session/index.d.ts +2 -2
  86. package/lib/hosted-session/index.mjs +1 -1
  87. package/lib/hosted-session/reconnecting-watch.cjs +131 -0
  88. package/lib/hosted-session/reconnecting-watch.d.cts +13 -0
  89. package/lib/hosted-session/reconnecting-watch.d.mts +13 -0
  90. package/lib/hosted-session/reconnecting-watch.d.ts +13 -0
  91. package/lib/hosted-session/reconnecting-watch.mjs +127 -0
  92. package/lib/hosted-session/types.d.cts +32 -0
  93. package/lib/hosted-session/types.d.mts +32 -0
  94. package/lib/hosted-session/types.d.ts +19 -7
  95. package/lib/hosted-target/access-url.cjs +44 -0
  96. package/lib/hosted-target/access-url.d.cts +2 -0
  97. package/lib/hosted-target/access-url.d.mts +2 -0
  98. package/lib/hosted-target/access-url.d.ts +2 -0
  99. package/lib/hosted-target/access-url.mjs +41 -0
  100. package/lib/hosted-target/adapter-request.cjs +12 -0
  101. package/lib/hosted-target/adapter-request.d.cts +17 -0
  102. package/lib/hosted-target/adapter-request.d.mts +17 -0
  103. package/lib/hosted-target/adapter-request.d.ts +17 -0
  104. package/lib/hosted-target/adapter-request.mjs +9 -0
  105. package/lib/hosted-target/adapter-types.cjs +2 -0
  106. package/lib/hosted-target/adapter-types.d.cts +27 -0
  107. package/lib/hosted-target/adapter-types.d.mts +27 -0
  108. package/lib/hosted-target/adapter-types.d.ts +27 -0
  109. package/lib/hosted-target/adapter-types.mjs +1 -0
  110. package/lib/hosted-target/bounds.cjs +10 -0
  111. package/lib/hosted-target/bounds.d.cts +7 -0
  112. package/lib/hosted-target/bounds.d.mts +7 -0
  113. package/lib/hosted-target/bounds.d.ts +7 -0
  114. package/lib/hosted-target/bounds.mjs +7 -0
  115. package/lib/hosted-target/capsule-error-response.cjs +76 -0
  116. package/lib/hosted-target/capsule-error-response.d.cts +2 -0
  117. package/lib/hosted-target/capsule-error-response.d.mts +2 -0
  118. package/lib/hosted-target/capsule-error-response.d.ts +2 -0
  119. package/lib/hosted-target/capsule-error-response.mjs +73 -0
  120. package/lib/hosted-target/errors.cjs +88 -0
  121. package/lib/hosted-target/errors.d.cts +36 -0
  122. package/lib/hosted-target/errors.d.mts +36 -0
  123. package/lib/hosted-target/errors.d.ts +36 -0
  124. package/lib/hosted-target/errors.mjs +75 -0
  125. package/lib/hosted-target/index.cjs +16 -0
  126. package/lib/hosted-target/index.d.cts +3 -0
  127. package/lib/hosted-target/index.d.mts +3 -0
  128. package/lib/hosted-target/index.d.ts +3 -0
  129. package/lib/hosted-target/index.mjs +2 -0
  130. package/lib/hosted-target/response-validation.cjs +169 -0
  131. package/lib/hosted-target/response-validation.d.cts +5 -0
  132. package/lib/hosted-target/response-validation.d.mts +5 -0
  133. package/lib/hosted-target/response-validation.d.ts +5 -0
  134. package/lib/hosted-target/response-validation.mjs +163 -0
  135. package/lib/hosted-target/retry-executor.cjs +51 -0
  136. package/lib/hosted-target/retry-executor.d.cts +8 -0
  137. package/lib/hosted-target/retry-executor.d.mts +8 -0
  138. package/lib/hosted-target/retry-executor.d.ts +8 -0
  139. package/lib/hosted-target/retry-executor.mjs +48 -0
  140. package/lib/hosted-target/retry.cjs +78 -0
  141. package/lib/hosted-target/retry.d.cts +9 -0
  142. package/lib/hosted-target/retry.d.mts +9 -0
  143. package/lib/hosted-target/retry.d.ts +9 -0
  144. package/lib/hosted-target/retry.mjs +73 -0
  145. package/lib/hosted-target/target-adapter.cjs +10 -0
  146. package/lib/hosted-target/target-adapter.d.cts +3 -0
  147. package/lib/hosted-target/target-adapter.d.mts +3 -0
  148. package/lib/hosted-target/target-adapter.d.ts +3 -0
  149. package/lib/hosted-target/target-adapter.mjs +7 -0
  150. package/lib/hosted-target/types.cjs +4 -0
  151. package/lib/hosted-target/types.d.cts +50 -0
  152. package/lib/hosted-target/types.d.mts +50 -0
  153. package/lib/hosted-target/types.d.ts +50 -0
  154. package/lib/hosted-target/types.mjs +1 -0
  155. package/lib/hosted-target/zero-cloud-v1-adapter.cjs +185 -0
  156. package/lib/hosted-target/zero-cloud-v1-adapter.d.cts +13 -0
  157. package/lib/hosted-target/zero-cloud-v1-adapter.d.mts +13 -0
  158. package/lib/hosted-target/zero-cloud-v1-adapter.d.ts +13 -0
  159. package/lib/hosted-target/zero-cloud-v1-adapter.mjs +181 -0
  160. package/lib/target/bounded-response.cjs +51 -0
  161. package/lib/target/bounded-response.d.cts +1 -0
  162. package/lib/target/bounded-response.d.mts +1 -0
  163. package/lib/target/bounded-response.d.ts +1 -0
  164. package/lib/target/bounded-response.js +51 -0
  165. package/lib/target/bounded-response.mjs +48 -0
  166. package/lib/target/credential-lock.js +1 -3
  167. package/lib/target/credential-store.js +9 -4
  168. package/lib/target/device-flow.cjs +213 -0
  169. package/lib/target/device-flow.d.cts +54 -0
  170. package/lib/target/device-flow.d.mts +54 -0
  171. package/lib/target/device-flow.d.ts +26 -10
  172. package/lib/target/device-flow.js +162 -53
  173. package/lib/target/device-flow.mjs +203 -0
  174. package/lib/target/discovery-errors.cjs +10 -0
  175. package/lib/target/discovery-errors.d.cts +3 -0
  176. package/lib/target/discovery-errors.d.mts +3 -0
  177. package/lib/target/discovery-errors.d.ts +3 -0
  178. package/lib/target/discovery-errors.js +10 -0
  179. package/lib/target/discovery-errors.mjs +6 -0
  180. package/lib/target/discovery-sections.cjs +191 -0
  181. package/lib/target/discovery-sections.d.cts +52 -0
  182. package/lib/target/discovery-sections.d.mts +52 -0
  183. package/lib/target/discovery-sections.d.ts +52 -0
  184. package/lib/target/discovery-sections.js +191 -0
  185. package/lib/target/discovery-sections.mjs +179 -0
  186. package/lib/target/discovery-validation.cjs +109 -0
  187. package/lib/target/discovery-validation.d.cts +28 -0
  188. package/lib/target/discovery-validation.d.mts +28 -0
  189. package/lib/target/discovery-validation.d.ts +28 -0
  190. package/lib/target/discovery-validation.js +109 -0
  191. package/lib/target/discovery-validation.mjs +99 -0
  192. package/lib/target/discovery.cjs +112 -0
  193. package/lib/target/discovery.d.cts +74 -0
  194. package/lib/target/discovery.d.mts +74 -0
  195. package/lib/target/discovery.d.ts +68 -5
  196. package/lib/target/discovery.js +93 -101
  197. package/lib/target/discovery.mjs +105 -0
  198. package/lib/target/index.d.ts +6 -6
  199. package/lib/target/index.js +36 -36
  200. package/lib/target/oauth-http.d.ts +2 -0
  201. package/lib/target/oauth-http.js +25 -0
  202. package/lib/target/refresh-exchange.d.ts +11 -0
  203. package/lib/target/refresh-exchange.js +24 -0
  204. package/lib/target/refresh-revocation.d.ts +8 -0
  205. package/lib/target/refresh-revocation.js +19 -0
  206. package/lib/target/route-template.cjs +85 -0
  207. package/lib/target/route-template.d.cts +7 -0
  208. package/lib/target/route-template.d.mts +7 -0
  209. package/lib/target/route-template.d.ts +7 -0
  210. package/lib/target/route-template.js +85 -0
  211. package/lib/target/route-template.mjs +81 -0
  212. package/lib/target/session-errors.d.ts +4 -0
  213. package/lib/target/session-errors.js +12 -0
  214. package/lib/target/session-verification.d.ts +10 -0
  215. package/lib/target/session-verification.js +42 -0
  216. package/lib/target/target-registry.d.ts +7 -3
  217. package/lib/target/target-registry.js +41 -10
  218. package/lib/target/target-session-manager.d.ts +48 -0
  219. package/lib/target/target-session-manager.js +226 -0
  220. package/lib/target/target-session.d.ts +39 -31
  221. package/lib/target/target-session.js +63 -150
  222. package/lib/target/token-response.cjs +51 -0
  223. package/lib/target/token-response.d.cts +7 -0
  224. package/lib/target/token-response.d.mts +7 -0
  225. package/lib/target/token-response.d.ts +7 -0
  226. package/lib/target/token-response.js +51 -0
  227. package/lib/target/token-response.mjs +48 -0
  228. package/package.json +16 -6
  229. package/scripts/audit-production-dependencies.js +150 -0
  230. package/scripts/build-cluster.js +23 -3
  231. package/scripts/opcore-agent-gate.js +159 -0
  232. package/scripts/opcore-agent-tool-overlays.js +154 -0
  233. package/scripts/opcore-introduced-check.js +288 -0
  234. package/src/cluster/client.ts +42 -15
  235. package/src/cluster/connection-state.ts +33 -0
  236. package/src/cluster/connection-types.ts +28 -0
  237. package/src/cluster/connection.ts +37 -68
  238. package/src/cluster/errors.ts +5 -0
  239. package/src/cluster/index.ts +3 -1
  240. package/src/cluster/json-source.ts +1 -0
  241. package/src/cluster/payload-value.ts +1 -0
  242. package/src/cluster/socket.ts +13 -0
  243. package/src/cluster/subscriptions.ts +7 -2
  244. package/src/cluster/validators.ts +1 -0
  245. package/src/cluster/ws.d.ts +1 -1
  246. package/src/hosted-session/authority.ts +77 -0
  247. package/src/hosted-session/coordinator.ts +113 -56
  248. package/src/hosted-session/errors.ts +21 -0
  249. package/src/hosted-session/index.ts +14 -2
  250. package/src/hosted-session/reconnecting-watch.ts +148 -0
  251. package/src/hosted-session/types.ts +22 -8
  252. package/src/hosted-target/access-url.ts +46 -0
  253. package/src/hosted-target/adapter-request.ts +28 -0
  254. package/src/hosted-target/adapter-types.ts +39 -0
  255. package/src/hosted-target/bounds.ts +1 -0
  256. package/src/hosted-target/capsule-error-response.ts +107 -0
  257. package/src/hosted-target/errors.ts +36 -35
  258. package/src/hosted-target/index.ts +10 -23
  259. package/src/hosted-target/response-validation.ts +170 -62
  260. package/src/hosted-target/retry-executor.ts +62 -0
  261. package/src/hosted-target/retry.ts +47 -11
  262. package/src/hosted-target/target-adapter.ts +12 -8
  263. package/src/hosted-target/types.ts +19 -18
  264. package/src/hosted-target/zero-cloud-v1-adapter.ts +229 -316
  265. package/src/target/bounded-response.ts +68 -0
  266. package/src/target/credential-lock.ts +1 -3
  267. package/src/target/credential-store.ts +14 -10
  268. package/src/target/device-flow.ts +206 -71
  269. package/src/target/discovery-errors.ts +6 -0
  270. package/src/target/discovery-sections.ts +251 -0
  271. package/src/target/discovery-validation.ts +144 -0
  272. package/src/target/discovery.ts +163 -115
  273. package/src/target/index.ts +15 -11
  274. package/src/target/oauth-http.ts +27 -0
  275. package/src/target/refresh-exchange.ts +36 -0
  276. package/src/target/refresh-revocation.ts +29 -0
  277. package/src/target/route-template.ts +105 -0
  278. package/src/target/session-errors.ts +9 -0
  279. package/src/target/session-verification.ts +51 -0
  280. package/src/target/target-registry.ts +60 -27
  281. package/src/target/target-session-manager.ts +285 -0
  282. package/src/target/target-session.ts +105 -222
  283. package/src/target/token-response.ts +62 -0
@@ -11,6 +11,8 @@ exports.removeTarget = removeTarget;
11
11
  exports.getTarget = getTarget;
12
12
  exports.listTargets = listTargets;
13
13
  exports.updateTargetOrganization = updateTargetOrganization;
14
+ exports.targetRefreshIsInvalidated = targetRefreshIsInvalidated;
15
+ exports.setTargetRefreshInvalidated = setTargetRefreshInvalidated;
14
16
  const node_crypto_1 = __importDefault(require("node:crypto"));
15
17
  class TargetNameInvalidError extends Error {
16
18
  constructor(name) {
@@ -41,13 +43,20 @@ class TargetUrlInvalidError extends Error {
41
43
  }
42
44
  exports.TargetUrlInvalidError = TargetUrlInvalidError;
43
45
  const TARGET_NAME_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])?$/;
44
- const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]']);
46
+ const LOOPBACK_HOSTS = Object.freeze({
47
+ '127.0.0.1': true,
48
+ '::1': true,
49
+ '[::1]': true,
50
+ });
45
51
  function validateTargetName(name) {
46
52
  if (!TARGET_NAME_PATTERN.test(name) || name.length > 64) {
47
53
  throw new TargetNameInvalidError(name);
48
54
  }
49
55
  }
50
56
  function normalizeAndValidateUrl(rawUrl) {
57
+ if (/[\u0000-\u0020\u007f]|\s/u.test(rawUrl)) {
58
+ throw new TargetUrlInvalidError(rawUrl, 'URL contains forbidden whitespace or controls');
59
+ }
51
60
  let parsed;
52
61
  try {
53
62
  parsed = new URL(rawUrl);
@@ -61,27 +70,30 @@ function normalizeAndValidateUrl(rawUrl) {
61
70
  if (parsed.search || parsed.hash) {
62
71
  throw new TargetUrlInvalidError(rawUrl, 'URL must not contain query or fragment');
63
72
  }
64
- const isLoopback = LOOPBACK_HOSTS.has(parsed.hostname);
65
- if (parsed.protocol !== 'https:' && !isLoopback) {
66
- throw new TargetUrlInvalidError(rawUrl, 'HTTPS required for non-loopback targets');
73
+ const isLoopback = LOOPBACK_HOSTS[parsed.hostname] === true;
74
+ const protocolAllowed = parsed.protocol === 'https:' || (parsed.protocol === 'http:' && isLoopback);
75
+ if (!protocolAllowed) {
76
+ throw new TargetUrlInvalidError(rawUrl, 'HTTPS required except for literal loopback HTTP targets');
67
77
  }
68
- let normalized = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
69
- if (normalized.endsWith('/') && normalized.length > 1) {
70
- normalized = normalized.slice(0, -1);
78
+ if (parsed.pathname !== '/') {
79
+ throw new TargetUrlInvalidError(rawUrl, 'URL must contain only an origin');
71
80
  }
72
- return normalized;
81
+ return parsed.origin;
73
82
  }
74
- function addTarget(name, rawUrl, settings) {
83
+ function addTarget(name, rawUrl, settings, descriptor) {
75
84
  validateTargetName(name);
76
85
  const url = normalizeAndValidateUrl(rawUrl);
77
86
  const existing = settings.load();
78
87
  if (existing._targets?.[name]) {
79
88
  throw new TargetNameExistsError(name);
80
89
  }
90
+ if (!descriptor || descriptor.origin !== url || descriptor.adapter.majorVersion !== 1) {
91
+ throw new TargetUrlInvalidError(rawUrl, 'validated discovery does not match the target origin');
92
+ }
81
93
  const record = {
82
94
  id: node_crypto_1.default.randomUUID(),
83
95
  url,
84
- adapterVersion: 'v1',
96
+ adapterVersion: `v${descriptor.adapter.majorVersion}`,
85
97
  deviceToken: node_crypto_1.default.randomUUID(),
86
98
  createdAt: new Date().toISOString(),
87
99
  };
@@ -130,3 +142,22 @@ function updateTargetOrganization(name, organization, settings) {
130
142
  }
131
143
  });
132
144
  }
145
+ function targetRefreshIsInvalidated(name, settings) {
146
+ return settings.load()._targets?.[name]?.refreshInvalidated === true;
147
+ }
148
+ function setTargetRefreshInvalidated(name, invalidated, settings) {
149
+ settings.mutate((state) => {
150
+ const targets = state._targets;
151
+ const target = targets?.[name];
152
+ if (targets === undefined || target === undefined)
153
+ throw new TargetNotFoundError(name);
154
+ if (invalidated) {
155
+ targets[name] = { ...target, refreshInvalidated: true };
156
+ }
157
+ else {
158
+ const active = { ...target };
159
+ Reflect.deleteProperty(active, 'refreshInvalidated');
160
+ targets[name] = active;
161
+ }
162
+ });
163
+ }
@@ -0,0 +1,48 @@
1
+ import type { TargetCredentialStore } from './credential-store.js';
2
+ import type { TargetRecord, SettingsPort } from './target-registry.js';
3
+ import { type Clock, type HttpTransport } from './device-flow.js';
4
+ import type { TargetSessionEndpoints } from './discovery.js';
5
+ export { LoginRequiredError } from './session-errors.js';
6
+ export interface BrowserOpener {
7
+ open(url: string): Promise<void>;
8
+ }
9
+ export interface TargetSessionDeps {
10
+ readonly http: HttpTransport;
11
+ readonly clock: Clock;
12
+ readonly browserOpener: BrowserOpener;
13
+ readonly stderr: {
14
+ write(s: string): void;
15
+ };
16
+ readonly discoveryEndpoints: TargetSessionEndpoints;
17
+ }
18
+ export interface TargetSessionManagerInit {
19
+ readonly targetName: string;
20
+ readonly target: TargetRecord;
21
+ readonly credentialStore: TargetCredentialStore;
22
+ readonly acquireLock: () => Promise<() => Promise<void>>;
23
+ readonly settings: SettingsPort;
24
+ readonly deps: TargetSessionDeps;
25
+ }
26
+ /**
27
+ * Sole owner of one target's rotating refresh family and audience-scoped access cache.
28
+ * The cross-process lock covers durable reread, exchange, replacement, and invalidation.
29
+ */
30
+ export declare class TargetSessionManager {
31
+ #private;
32
+ constructor(init: TargetSessionManagerInit);
33
+ login(signal?: AbortSignal): Promise<{
34
+ organization: {
35
+ id: string;
36
+ };
37
+ }>;
38
+ getAccessTokenWithLifetime(audience: string, signal?: AbortSignal): Promise<{
39
+ accessToken: string;
40
+ expiresIn: number;
41
+ }>;
42
+ getAccessToken(audience: string, signal?: AbortSignal): Promise<string>;
43
+ tokenProvider(audience: string): {
44
+ getAccessToken(signal?: AbortSignal): Promise<string>;
45
+ };
46
+ revoke(force: boolean): Promise<void>;
47
+ clearMemory(): void;
48
+ }
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TargetSessionManager = exports.LoginRequiredError = void 0;
4
+ const refresh_exchange_js_1 = require("./refresh-exchange.js");
5
+ const device_flow_js_1 = require("./device-flow.js");
6
+ const credential_store_js_1 = require("./credential-store.js");
7
+ const target_registry_js_1 = require("./target-registry.js");
8
+ const session_errors_js_1 = require("./session-errors.js");
9
+ var session_errors_js_2 = require("./session-errors.js");
10
+ Object.defineProperty(exports, "LoginRequiredError", { enumerable: true, get: function () { return session_errors_js_2.LoginRequiredError; } });
11
+ const session_verification_js_1 = require("./session-verification.js");
12
+ const refresh_revocation_js_1 = require("./refresh-revocation.js");
13
+ const DEVICE_LABEL = 'zeroshot-cli';
14
+ const TOKEN_EXPIRY_SKEW_MS = 30_000;
15
+ const AUDIENCE_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
16
+ const INVALID_REFRESH_FAMILY = 'zeroshot.invalidated-refresh-family/v1';
17
+ function abortReason(signal) {
18
+ return signal?.reason ?? new DOMException('Aborted', 'AbortError');
19
+ }
20
+ function requireAudience(audience) {
21
+ if (!AUDIENCE_PATTERN.test(audience))
22
+ throw new Error('Requested target audience is invalid');
23
+ }
24
+ /**
25
+ * Sole owner of one target's rotating refresh family and audience-scoped access cache.
26
+ * The cross-process lock covers durable reread, exchange, replacement, and invalidation.
27
+ */
28
+ class TargetSessionManager {
29
+ #targetName;
30
+ #target;
31
+ #store;
32
+ #acquireLock;
33
+ #settings;
34
+ #deps;
35
+ #access = new Map();
36
+ constructor(init) {
37
+ this.#targetName = init.targetName;
38
+ this.#target = init.target;
39
+ this.#store = init.credentialStore;
40
+ this.#acquireLock = init.acquireLock;
41
+ this.#settings = init.settings;
42
+ this.#deps = init.deps;
43
+ }
44
+ #cachedAccess(audience) {
45
+ const cached = this.#access.get(audience);
46
+ if (cached === undefined ||
47
+ this.#deps.clock.now() >= cached.expiresAt - TOKEN_EXPIRY_SKEW_MS) {
48
+ return null;
49
+ }
50
+ return {
51
+ accessToken: cached.token,
52
+ expiresIn: Math.max(0, Math.floor((cached.expiresAt - this.#deps.clock.now()) / 1_000)),
53
+ };
54
+ }
55
+ async login(signal) {
56
+ const endpoints = this.#deps.discoveryEndpoints;
57
+ const code = await (0, device_flow_js_1.requestDeviceCode)(endpoints.deviceAuthorizationEndpoint, endpoints.clientId, this.#deps.http, signal);
58
+ this.#deps.stderr.write(`\nOpen this URL to authorize:\n ${code.verification_uri}\n\nEnter code: ${code.user_code}\n\n`);
59
+ if (code.verification_uri_complete) {
60
+ try {
61
+ await this.#deps.browserOpener.open(code.verification_uri_complete);
62
+ }
63
+ catch {
64
+ // Opening a browser is explicitly optional; polling remains authoritative.
65
+ }
66
+ }
67
+ const release = await this.#acquireLock();
68
+ try {
69
+ const storedPrevious = await this.#store.get(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT);
70
+ const previous = storedPrevious === INVALID_REFRESH_FAMILY ? null : storedPrevious;
71
+ const token = await (0, device_flow_js_1.pollForToken)({
72
+ tokenEndpoint: endpoints.tokenEndpoint,
73
+ clientId: endpoints.clientId,
74
+ deviceCode: code.device_code,
75
+ interval: code.interval,
76
+ expiresIn: code.expires_in,
77
+ http: this.#deps.http,
78
+ clock: this.#deps.clock,
79
+ ...(signal === undefined ? {} : { signal }),
80
+ exchange: {
81
+ grantType: endpoints.deviceGrantType,
82
+ deviceToken: this.#target.deviceToken,
83
+ deviceLabel: DEVICE_LABEL,
84
+ audience: endpoints.audience,
85
+ },
86
+ });
87
+ const organization = await (0, session_verification_js_1.readVerifiedOrganization)(token.access_token, signal, this.#deps).catch(async () => {
88
+ await this.#bestEffortRevoke(token.refresh_token);
89
+ throw new session_errors_js_1.LoginRequiredError(this.#targetName);
90
+ });
91
+ try {
92
+ await this.#store.set(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT, token.refresh_token);
93
+ (0, target_registry_js_1.setTargetRefreshInvalidated)(this.#targetName, false, this.#settings);
94
+ this.#access.set(endpoints.audience, {
95
+ token: token.access_token,
96
+ expiresAt: this.#deps.clock.now() + token.expires_in * 1_000,
97
+ });
98
+ (0, target_registry_js_1.updateTargetOrganization)(this.#targetName, organization, this.#settings);
99
+ if (previous && previous !== token.refresh_token) {
100
+ const revoked = await (0, refresh_revocation_js_1.revokeRefreshToken)(previous, this.#deps);
101
+ if (!revoked.ok)
102
+ throw new Error('Prior refresh family revocation failed');
103
+ }
104
+ }
105
+ catch {
106
+ await this.#invalidateFamily(token.refresh_token);
107
+ throw new session_errors_js_1.LoginRequiredError(this.#targetName);
108
+ }
109
+ return { organization };
110
+ }
111
+ finally {
112
+ await release();
113
+ }
114
+ }
115
+ async getAccessTokenWithLifetime(audience, signal) {
116
+ requireAudience(audience);
117
+ const cached = this.#cachedAccess(audience);
118
+ if (cached !== null)
119
+ return cached;
120
+ if (signal?.aborted)
121
+ throw abortReason(signal);
122
+ const release = await this.#acquireLock();
123
+ try {
124
+ const afterLock = this.#cachedAccess(audience);
125
+ if (afterLock !== null)
126
+ return afterLock;
127
+ if ((0, target_registry_js_1.targetRefreshIsInvalidated)(this.#targetName, this.#settings)) {
128
+ throw new session_errors_js_1.LoginRequiredError(this.#targetName);
129
+ }
130
+ const refreshToken = await this.#store.get(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT);
131
+ if (!refreshToken || refreshToken === INVALID_REFRESH_FAMILY) {
132
+ throw new session_errors_js_1.LoginRequiredError(this.#targetName);
133
+ }
134
+ if (signal?.aborted)
135
+ throw abortReason(signal);
136
+ (0, target_registry_js_1.setTargetRefreshInvalidated)(this.#targetName, true, this.#settings);
137
+ await this.#store.set(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT, INVALID_REFRESH_FAMILY);
138
+ let replacement;
139
+ try {
140
+ replacement = await (0, refresh_exchange_js_1.exchangeRefreshToken)({
141
+ tokenEndpoint: this.#deps.discoveryEndpoints.tokenEndpoint,
142
+ clientId: this.#deps.discoveryEndpoints.clientId,
143
+ refreshToken,
144
+ audience,
145
+ http: this.#deps.http,
146
+ ...(signal === undefined ? {} : { signal }),
147
+ });
148
+ await this.#store.set(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT, replacement.refresh_token);
149
+ (0, target_registry_js_1.setTargetRefreshInvalidated)(this.#targetName, false, this.#settings);
150
+ this.#access.clear();
151
+ this.#access.set(audience, {
152
+ token: replacement.access_token,
153
+ expiresAt: this.#deps.clock.now() + replacement.expires_in * 1_000,
154
+ });
155
+ return { accessToken: replacement.access_token, expiresIn: replacement.expires_in };
156
+ }
157
+ catch {
158
+ await this.#invalidateFamily(replacement?.refresh_token);
159
+ throw new session_errors_js_1.LoginRequiredError(this.#targetName);
160
+ }
161
+ }
162
+ finally {
163
+ await release();
164
+ }
165
+ }
166
+ async getAccessToken(audience, signal) {
167
+ return (await this.getAccessTokenWithLifetime(audience, signal)).accessToken;
168
+ }
169
+ tokenProvider(audience) {
170
+ requireAudience(audience);
171
+ return Object.freeze({
172
+ getAccessToken: (signal) => this.getAccessToken(audience, signal),
173
+ });
174
+ }
175
+ async revoke(force) {
176
+ const release = await this.#acquireLock();
177
+ try {
178
+ const token = await this.#store.get(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT);
179
+ if (token && token !== INVALID_REFRESH_FAMILY) {
180
+ try {
181
+ const response = await (0, refresh_revocation_js_1.revokeRefreshToken)(token, this.#deps);
182
+ if (!response.ok && !force)
183
+ throw new Error('Remote revocation failed. Use --force to remove anyway.');
184
+ }
185
+ catch (error) {
186
+ if (!force)
187
+ throw error;
188
+ }
189
+ }
190
+ this.#access.clear();
191
+ await this.#store.delete(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT);
192
+ (0, target_registry_js_1.setTargetRefreshInvalidated)(this.#targetName, false, this.#settings);
193
+ }
194
+ finally {
195
+ await release();
196
+ }
197
+ }
198
+ clearMemory() {
199
+ this.#access.clear();
200
+ }
201
+ async #invalidateFamily(replacement) {
202
+ this.#access.clear();
203
+ (0, target_registry_js_1.setTargetRefreshInvalidated)(this.#targetName, true, this.#settings);
204
+ try {
205
+ await this.#store.set(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT, INVALID_REFRESH_FAMILY);
206
+ }
207
+ catch {
208
+ // A direct delete remains safe if overwriting the stale family was unavailable.
209
+ }
210
+ if (replacement)
211
+ await this.#bestEffortRevoke(replacement);
212
+ await this.#store.delete(this.#serviceKey(), credential_store_js_1.TARGET_ACCOUNT);
213
+ }
214
+ async #bestEffortRevoke(token) {
215
+ try {
216
+ await (0, refresh_revocation_js_1.revokeRefreshToken)(token, this.#deps);
217
+ }
218
+ catch {
219
+ // Revocation is compensating cleanup; callers still receive login-required.
220
+ }
221
+ }
222
+ #serviceKey() {
223
+ return (0, credential_store_js_1.targetServiceKey)(this.#target.id);
224
+ }
225
+ }
226
+ exports.TargetSessionManager = TargetSessionManager;
@@ -1,39 +1,47 @@
1
- import type { TargetCredentialStore } from './credential-store.ts';
2
- import type { TargetRecord, SettingsPort } from './target-registry.ts';
3
- import { type HttpTransport, type Clock } from './device-flow.ts';
4
- export declare class LoginRequiredError extends Error {
5
- readonly targetName: string;
6
- constructor(targetName: string);
7
- }
8
- export interface BrowserOpener {
9
- open(url: string): Promise<void>;
10
- }
11
- export interface TargetSessionDeps {
12
- readonly http: HttpTransport;
13
- readonly clock: Clock;
14
- readonly browserOpener: BrowserOpener;
15
- readonly stderr: {
16
- write(s: string): void;
17
- };
18
- readonly discoveryEndpoints: {
19
- readonly deviceAuthorizationEndpoint: string;
20
- readonly tokenEndpoint: string;
21
- readonly revocationEndpoint?: string;
22
- readonly clientId: string;
23
- };
1
+ import type { TargetCredentialStore } from './credential-store.js';
2
+ import type { Clock } from './device-flow.js';
3
+ import { type TargetSessionDeps } from './target-session-manager.js';
4
+ import type { SettingsPort, TargetRecord } from './target-registry.js';
5
+ export { LoginRequiredError, TargetSessionManager, type BrowserOpener, type TargetSessionDeps, type TargetSessionManagerInit, } from './target-session-manager.js';
6
+ export interface TargetAccessTokenProvider {
7
+ getAccessToken(signal?: AbortSignal): Promise<string>;
24
8
  }
25
- export declare function targetLogin(targetName: string, target: TargetRecord, credentialStore: TargetCredentialStore, acquireLock: () => Promise<() => Promise<void>>, settings: SettingsPort, deps: TargetSessionDeps): Promise<{
9
+ type LegacySessionDeps = Pick<TargetSessionDeps, 'http' | 'discoveryEndpoints'> & {
10
+ readonly settings?: SettingsPort;
11
+ readonly targetName?: string;
12
+ readonly clock?: Clock;
13
+ };
14
+ type TargetLoginArgs = readonly [
15
+ targetName: string,
16
+ target: TargetRecord,
17
+ credentialStore: TargetCredentialStore,
18
+ acquireLock: () => Promise<() => Promise<void>>,
19
+ settings: SettingsPort,
20
+ deps: TargetSessionDeps
21
+ ];
22
+ type RefreshAccessArgs = readonly [
23
+ targetName: string,
24
+ target: TargetRecord,
25
+ credentialStore: TargetCredentialStore,
26
+ acquireLock: () => Promise<() => Promise<void>>,
27
+ deps: LegacySessionDeps,
28
+ clock?: Clock
29
+ ];
30
+ type RevokeArgs = readonly [
31
+ target: TargetRecord,
32
+ credentialStore: TargetCredentialStore,
33
+ acquireLock: () => Promise<() => Promise<void>>,
34
+ deps: LegacySessionDeps,
35
+ force: boolean
36
+ ];
37
+ export declare function targetLogin(...args: TargetLoginArgs): Promise<{
26
38
  organization: {
27
39
  id: string;
28
- name: string;
29
40
  };
30
41
  }>;
31
- export declare function refreshAccessToken(targetName: string, target: TargetRecord, credentialStore: TargetCredentialStore, acquireLock: () => Promise<() => Promise<void>>, deps: Pick<TargetSessionDeps, 'http' | 'discoveryEndpoints'>): Promise<{
42
+ export declare function refreshAccessToken(...args: RefreshAccessArgs): Promise<{
32
43
  accessToken: string;
33
44
  expiresIn: number;
34
45
  }>;
35
- export interface TargetAccessTokenProvider {
36
- getAccessToken(signal?: AbortSignal): Promise<string>;
37
- }
38
- export declare function getAccessTokenProvider(targetName: string, target: TargetRecord, credentialStore: TargetCredentialStore, acquireLock: () => Promise<() => Promise<void>>, deps: Pick<TargetSessionDeps, 'http' | 'discoveryEndpoints'>, clock?: Clock): TargetAccessTokenProvider;
39
- export declare function revokeAndCleanup(target: TargetRecord, credentialStore: TargetCredentialStore, acquireLock: () => Promise<() => Promise<void>>, deps: Pick<TargetSessionDeps, 'http' | 'discoveryEndpoints'>, force: boolean): Promise<void>;
46
+ export declare function getAccessTokenProvider(...args: RefreshAccessArgs): TargetAccessTokenProvider;
47
+ export declare function revokeAndCleanup(...args: RevokeArgs): Promise<void>;