@indigoai-us/hq-cloud 5.1.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.
Files changed (108) hide show
  1. package/dist/auth.d.ts +21 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +116 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/cli/accept.d.ts +29 -0
  6. package/dist/cli/accept.d.ts.map +1 -0
  7. package/dist/cli/accept.js +67 -0
  8. package/dist/cli/accept.js.map +1 -0
  9. package/dist/cli/conflict.d.ts +33 -0
  10. package/dist/cli/conflict.d.ts.map +1 -0
  11. package/dist/cli/conflict.js +91 -0
  12. package/dist/cli/conflict.js.map +1 -0
  13. package/dist/cli/index.d.ts +19 -0
  14. package/dist/cli/index.d.ts.map +1 -0
  15. package/dist/cli/index.js +14 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/cli/invite.d.ts +51 -0
  18. package/dist/cli/invite.d.ts.map +1 -0
  19. package/dist/cli/invite.js +120 -0
  20. package/dist/cli/invite.js.map +1 -0
  21. package/dist/cli/invite.test.d.ts +5 -0
  22. package/dist/cli/invite.test.d.ts.map +1 -0
  23. package/dist/cli/invite.test.js +175 -0
  24. package/dist/cli/invite.test.js.map +1 -0
  25. package/dist/cli/promote.d.ts +30 -0
  26. package/dist/cli/promote.d.ts.map +1 -0
  27. package/dist/cli/promote.js +79 -0
  28. package/dist/cli/promote.js.map +1 -0
  29. package/dist/cli/share.d.ts +33 -0
  30. package/dist/cli/share.d.ts.map +1 -0
  31. package/dist/cli/share.js +153 -0
  32. package/dist/cli/share.js.map +1 -0
  33. package/dist/cli/share.test.d.ts +5 -0
  34. package/dist/cli/share.test.d.ts.map +1 -0
  35. package/dist/cli/share.test.js +121 -0
  36. package/dist/cli/share.test.js.map +1 -0
  37. package/dist/cli/sync.d.ts +30 -0
  38. package/dist/cli/sync.d.ts.map +1 -0
  39. package/dist/cli/sync.js +138 -0
  40. package/dist/cli/sync.js.map +1 -0
  41. package/dist/cli/sync.test.d.ts +5 -0
  42. package/dist/cli/sync.test.d.ts.map +1 -0
  43. package/dist/cli/sync.test.js +172 -0
  44. package/dist/cli/sync.test.js.map +1 -0
  45. package/dist/cognito-auth.d.ts +70 -0
  46. package/dist/cognito-auth.d.ts.map +1 -0
  47. package/dist/cognito-auth.js +280 -0
  48. package/dist/cognito-auth.js.map +1 -0
  49. package/dist/context.d.ts +30 -0
  50. package/dist/context.d.ts.map +1 -0
  51. package/dist/context.js +117 -0
  52. package/dist/context.js.map +1 -0
  53. package/dist/context.test.d.ts +7 -0
  54. package/dist/context.test.d.ts.map +1 -0
  55. package/dist/context.test.js +148 -0
  56. package/dist/context.test.js.map +1 -0
  57. package/dist/daemon-worker.d.ts +6 -0
  58. package/dist/daemon-worker.d.ts.map +1 -0
  59. package/dist/daemon-worker.js +26 -0
  60. package/dist/daemon-worker.js.map +1 -0
  61. package/dist/daemon.d.ts +10 -0
  62. package/dist/daemon.d.ts.map +1 -0
  63. package/dist/daemon.js +88 -0
  64. package/dist/daemon.js.map +1 -0
  65. package/dist/ignore.d.ts +10 -0
  66. package/dist/ignore.d.ts.map +1 -0
  67. package/dist/ignore.js +54 -0
  68. package/dist/ignore.js.map +1 -0
  69. package/dist/index.d.ts +33 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +138 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/journal.d.ts +12 -0
  74. package/dist/journal.d.ts.map +1 -0
  75. package/dist/journal.js +42 -0
  76. package/dist/journal.js.map +1 -0
  77. package/dist/s3.d.ts +15 -0
  78. package/dist/s3.d.ts.map +1 -0
  79. package/dist/s3.js +129 -0
  80. package/dist/s3.js.map +1 -0
  81. package/dist/types.d.ts +52 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +5 -0
  84. package/dist/types.js.map +1 -0
  85. package/dist/vault-client.d.ts +164 -0
  86. package/dist/vault-client.d.ts.map +1 -0
  87. package/dist/vault-client.js +209 -0
  88. package/dist/vault-client.js.map +1 -0
  89. package/dist/vault-client.test.d.ts +7 -0
  90. package/dist/vault-client.test.d.ts.map +1 -0
  91. package/dist/vault-client.test.js +257 -0
  92. package/dist/vault-client.test.js.map +1 -0
  93. package/dist/watcher.d.ts +18 -0
  94. package/dist/watcher.d.ts.map +1 -0
  95. package/dist/watcher.js +106 -0
  96. package/dist/watcher.js.map +1 -0
  97. package/package.json +32 -0
  98. package/src/auth.ts +146 -0
  99. package/src/cognito-auth.ts +375 -0
  100. package/src/daemon-worker.ts +32 -0
  101. package/src/daemon.ts +97 -0
  102. package/src/ignore.ts +61 -0
  103. package/src/index.ts +182 -0
  104. package/src/journal.ts +63 -0
  105. package/src/s3.ts +178 -0
  106. package/src/types.ts +59 -0
  107. package/src/watcher.ts +130 -0
  108. package/tsconfig.json +8 -0
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Cognito browser-OAuth helper (VLT-9).
3
+ *
4
+ * Drives the Cognito Hosted UI authorization-code + PKCE flow for the
5
+ * vault-service User Pool. Used by the CLI (`hq login`, `create-hq`) to
6
+ * obtain a JWT that is then passed to the vault-service API as
7
+ * `Authorization: Bearer <accessToken>`.
8
+ *
9
+ * Why PKCE: the CLI is a public client (no secret), so we use PKCE per
10
+ * RFC 7636 to prove that the same process that started the auth request
11
+ * is the one exchanging the code for tokens.
12
+ *
13
+ * Why a localhost callback: Cognito allows `http://localhost:*` as a
14
+ * redirect URI specifically for native/CLI apps (RFC 8252 §7). We spin
15
+ * up a one-shot HTTP server on the chosen port, capture exactly one
16
+ * callback, then close it.
17
+ */
18
+ import * as crypto from "crypto";
19
+ import * as fs from "fs";
20
+ import * as http from "http";
21
+ import * as path from "path";
22
+ import * as os from "os";
23
+ import open from "open";
24
+ /** Returned when an interactive login is needed but stdin/browser is unavailable. */
25
+ export class CognitoAuthError extends Error {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = "CognitoAuthError";
29
+ }
30
+ }
31
+ // ---------------------------------------------------------------------------
32
+ // Token cache (~/.hq/cognito-tokens.json)
33
+ // ---------------------------------------------------------------------------
34
+ const HQ_DIR = path.join(os.homedir(), ".hq");
35
+ const TOKEN_FILE = path.join(HQ_DIR, "cognito-tokens.json");
36
+ export function loadCachedTokens() {
37
+ if (!fs.existsSync(TOKEN_FILE))
38
+ return null;
39
+ try {
40
+ const raw = fs.readFileSync(TOKEN_FILE, "utf-8");
41
+ return JSON.parse(raw);
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ export function saveCachedTokens(tokens) {
48
+ if (!fs.existsSync(HQ_DIR)) {
49
+ fs.mkdirSync(HQ_DIR, { recursive: true, mode: 0o700 });
50
+ }
51
+ fs.writeFileSync(TOKEN_FILE, JSON.stringify(tokens, null, 2), { mode: 0o600 });
52
+ }
53
+ export function clearCachedTokens() {
54
+ if (fs.existsSync(TOKEN_FILE))
55
+ fs.unlinkSync(TOKEN_FILE);
56
+ }
57
+ /** True when the token expires within the given buffer (default 60s). */
58
+ export function isExpiring(tokens, bufferSeconds = 60) {
59
+ const expiresAt = new Date(tokens.expiresAt).getTime();
60
+ return expiresAt - Date.now() < bufferSeconds * 1000;
61
+ }
62
+ // ---------------------------------------------------------------------------
63
+ // PKCE
64
+ // ---------------------------------------------------------------------------
65
+ function base64UrlEncode(buf) {
66
+ return buf
67
+ .toString("base64")
68
+ .replace(/\+/g, "-")
69
+ .replace(/\//g, "_")
70
+ .replace(/=+$/, "");
71
+ }
72
+ function generatePkce() {
73
+ const verifier = base64UrlEncode(crypto.randomBytes(32));
74
+ const challenge = base64UrlEncode(crypto.createHash("sha256").update(verifier).digest());
75
+ return { verifier, challenge };
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Endpoint helpers
79
+ // ---------------------------------------------------------------------------
80
+ function authBaseUrl(config) {
81
+ return `https://${config.userPoolDomain}.auth.${config.region}.amazoncognito.com`;
82
+ }
83
+ function redirectUri(port) {
84
+ return `http://localhost:${port}/callback`;
85
+ }
86
+ // ---------------------------------------------------------------------------
87
+ // Browser login
88
+ // ---------------------------------------------------------------------------
89
+ /**
90
+ * Open the Cognito Hosted UI in the user's browser, wait for the redirect
91
+ * back to localhost, and exchange the auth code for tokens.
92
+ *
93
+ * Times out after 5 minutes if the user doesn't complete the flow.
94
+ */
95
+ export async function browserLogin(config) {
96
+ const port = config.port ?? 3000;
97
+ const scopes = (config.scopes ?? ["openid", "email", "profile"]).join(" ");
98
+ const { verifier, challenge } = generatePkce();
99
+ const state = base64UrlEncode(crypto.randomBytes(16));
100
+ const authUrl = new URL(`${authBaseUrl(config)}/login`);
101
+ authUrl.searchParams.set("client_id", config.clientId);
102
+ authUrl.searchParams.set("response_type", "code");
103
+ authUrl.searchParams.set("scope", scopes);
104
+ authUrl.searchParams.set("redirect_uri", redirectUri(port));
105
+ authUrl.searchParams.set("code_challenge", challenge);
106
+ authUrl.searchParams.set("code_challenge_method", "S256");
107
+ authUrl.searchParams.set("state", state);
108
+ const code = await waitForAuthCode(port, state);
109
+ const tokens = await exchangeCodeForTokens(config, code, verifier, port);
110
+ saveCachedTokens(tokens);
111
+ return tokens;
112
+ // -- inner: spin up loopback server and open browser ---------------------
113
+ function waitForAuthCode(port, expectedState) {
114
+ return new Promise((resolve, reject) => {
115
+ // cleanup() is a function declaration so it can be referenced from the
116
+ // server callbacks and the timeout closure below before its source
117
+ // position. It clears the 15-min login timer + closes the loopback
118
+ // server — without this both keep Node's event loop alive after the
119
+ // calling script "completes", making it look hung.
120
+ const server = http.createServer((req, res) => {
121
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
122
+ if (url.pathname !== "/callback") {
123
+ res.writeHead(404);
124
+ res.end("Not found");
125
+ return;
126
+ }
127
+ const code = url.searchParams.get("code");
128
+ const state = url.searchParams.get("state");
129
+ const error = url.searchParams.get("error");
130
+ if (error) {
131
+ res.writeHead(400, { "Content-Type": "text/html" });
132
+ res.end(`<h1>Authentication failed</h1><p>${escapeHtml(error)}</p>`);
133
+ cleanup();
134
+ reject(new CognitoAuthError(`Cognito returned error: ${error}`));
135
+ return;
136
+ }
137
+ if (state !== expectedState) {
138
+ res.writeHead(400, { "Content-Type": "text/html" });
139
+ res.end("<h1>State mismatch</h1><p>Possible CSRF — try again.</p>");
140
+ cleanup();
141
+ reject(new CognitoAuthError("Cognito state parameter mismatch"));
142
+ return;
143
+ }
144
+ if (!code) {
145
+ res.writeHead(400, { "Content-Type": "text/html" });
146
+ res.end("<h1>Missing code</h1>");
147
+ cleanup();
148
+ reject(new CognitoAuthError("Cognito callback missing code"));
149
+ return;
150
+ }
151
+ res.writeHead(200, { "Content-Type": "text/html" });
152
+ res.end(`<!doctype html><html><body style="font-family:system-ui;text-align:center;padding:48px;">
153
+ <h1>Signed in to HQ by Indigo</h1>
154
+ <p>You can close this tab and return to your terminal.</p>
155
+ <script>setTimeout(()=>window.close(),1500)</script>
156
+ </body></html>`);
157
+ cleanup();
158
+ resolve(code);
159
+ });
160
+ server.on("error", (err) => {
161
+ cleanup();
162
+ reject(err);
163
+ });
164
+ server.listen(port, "127.0.0.1", () => {
165
+ console.log(`\n Opening browser for HQ sign-in...`);
166
+ console.log(` If your browser doesn't open, visit:\n ${authUrl.toString()}\n`);
167
+ open(authUrl.toString()).catch(() => {
168
+ /* user can paste the URL manually */
169
+ });
170
+ });
171
+ const loginTimer = setTimeout(() => {
172
+ cleanup();
173
+ reject(new CognitoAuthError("Login timed out after 15 minutes"));
174
+ }, 15 * 60 * 1000);
175
+ function cleanup() {
176
+ clearTimeout(loginTimer);
177
+ server.close();
178
+ }
179
+ });
180
+ }
181
+ }
182
+ async function exchangeCodeForTokens(config, code, verifier, port) {
183
+ const body = new URLSearchParams({
184
+ grant_type: "authorization_code",
185
+ client_id: config.clientId,
186
+ code,
187
+ code_verifier: verifier,
188
+ redirect_uri: redirectUri(port),
189
+ });
190
+ const res = await fetch(`${authBaseUrl(config)}/oauth2/token`, {
191
+ method: "POST",
192
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
193
+ body: body.toString(),
194
+ });
195
+ if (!res.ok) {
196
+ const text = await res.text();
197
+ throw new CognitoAuthError(`Token exchange failed (${res.status}): ${text}`);
198
+ }
199
+ const data = (await res.json());
200
+ if (!data.refresh_token) {
201
+ throw new CognitoAuthError("Cognito did not return a refresh token — check OAuth scopes include offline_access semantics");
202
+ }
203
+ return {
204
+ accessToken: data.access_token,
205
+ idToken: data.id_token,
206
+ refreshToken: data.refresh_token,
207
+ expiresAt: new Date(Date.now() + data.expires_in * 1000).toISOString(),
208
+ tokenType: "Bearer",
209
+ };
210
+ }
211
+ /**
212
+ * Use the refresh token to obtain a fresh access token without user interaction.
213
+ * The refresh token itself is NOT rotated by Cognito on the refresh grant, so
214
+ * we preserve the existing one in the result.
215
+ */
216
+ export async function refreshTokens(config, currentRefreshToken) {
217
+ const body = new URLSearchParams({
218
+ grant_type: "refresh_token",
219
+ client_id: config.clientId,
220
+ refresh_token: currentRefreshToken,
221
+ });
222
+ const res = await fetch(`${authBaseUrl(config)}/oauth2/token`, {
223
+ method: "POST",
224
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
225
+ body: body.toString(),
226
+ });
227
+ if (!res.ok) {
228
+ const text = await res.text();
229
+ throw new CognitoAuthError(`Refresh failed (${res.status}): ${text}`);
230
+ }
231
+ const data = (await res.json());
232
+ const tokens = {
233
+ accessToken: data.access_token,
234
+ idToken: data.id_token,
235
+ refreshToken: data.refresh_token ?? currentRefreshToken,
236
+ expiresAt: new Date(Date.now() + data.expires_in * 1000).toISOString(),
237
+ tokenType: "Bearer",
238
+ };
239
+ saveCachedTokens(tokens);
240
+ return tokens;
241
+ }
242
+ /**
243
+ * High-level helper: return a non-expired access token, refreshing or
244
+ * launching browser login as needed.
245
+ *
246
+ * Pass `interactive: false` from automated contexts where you would rather
247
+ * fail fast than open a browser.
248
+ */
249
+ export async function getValidAccessToken(config, options = {}) {
250
+ const interactive = options.interactive ?? true;
251
+ const cached = loadCachedTokens();
252
+ if (cached && !isExpiring(cached))
253
+ return cached.accessToken;
254
+ if (cached) {
255
+ try {
256
+ const refreshed = await refreshTokens(config, cached.refreshToken);
257
+ return refreshed.accessToken;
258
+ }
259
+ catch {
260
+ // fall through to interactive login
261
+ }
262
+ }
263
+ if (!interactive) {
264
+ throw new CognitoAuthError("No valid HQ session and interactive login is disabled. Run `hq login` first.");
265
+ }
266
+ const fresh = await browserLogin(config);
267
+ return fresh.accessToken;
268
+ }
269
+ // ---------------------------------------------------------------------------
270
+ // Helpers
271
+ // ---------------------------------------------------------------------------
272
+ function escapeHtml(s) {
273
+ return s
274
+ .replace(/&/g, "&amp;")
275
+ .replace(/</g, "&lt;")
276
+ .replace(/>/g, "&gt;")
277
+ .replace(/"/g, "&quot;")
278
+ .replace(/'/g, "&#39;");
279
+ }
280
+ //# sourceMappingURL=cognito-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cognito-auth.js","sourceRoot":"","sources":["../src/cognito-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,IAAI,MAAM,MAAM,CAAC;AA4BxB,qFAAqF;AACrF,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,8EAA8E;AAC9E,0CAA0C;AAC1C,8EAA8E;AAE9E,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;AAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;AAE5D,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAqB;IACpD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC3D,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,UAAU,CAAC,MAAqB,EAAE,aAAa,GAAG,EAAE;IAClE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IACvD,OAAO,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,GAAG,IAAI,CAAC;AACvD,CAAC;AAED,8EAA8E;AAC9E,OAAO;AACP,8EAA8E;AAE9E,SAAS,eAAe,CAAC,GAAW;IAClC,OAAO,GAAG;SACP,QAAQ,CAAC,QAAQ,CAAC;SAClB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,eAAe,CAC/B,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CACtD,CAAC;IACF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,SAAS,WAAW,CAAC,MAAyB;IAC5C,OAAO,WAAW,MAAM,CAAC,cAAc,SAAS,MAAM,CAAC,MAAM,oBAAoB,CAAC;AACpF,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,oBAAoB,IAAI,WAAW,CAAC;AAC7C,CAAC;AAED,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAyB;IAEzB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;IACjC,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3E,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;IAEtD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IAClD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;IAC1D,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAEzC,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC;IAEd,2EAA2E;IAC3E,SAAS,eAAe,CAAC,IAAY,EAAE,aAAqB;QAC1D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,uEAAuE;YACvE,mEAAmE;YACnE,mEAAmE;YACnE,oEAAoE;YACpE,mDAAmD;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC;gBAChE,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;oBACjC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;oBACnB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;oBACrB,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC1C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAE5C,IAAI,KAAK,EAAE,CAAC;oBACV,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC,oCAAoC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;oBACrE,OAAO,EAAE,CAAC;oBACV,MAAM,CAAC,IAAI,gBAAgB,CAAC,2BAA2B,KAAK,EAAE,CAAC,CAAC,CAAC;oBACjE,OAAO;gBACT,CAAC;gBACD,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;oBAC5B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;oBACpE,OAAO,EAAE,CAAC;oBACV,MAAM,CAAC,IAAI,gBAAgB,CAAC,kCAAkC,CAAC,CAAC,CAAC;oBACjE,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;oBACjC,OAAO,EAAE,CAAC;oBACV,MAAM,CAAC,IAAI,gBAAgB,CAAC,+BAA+B,CAAC,CAAC,CAAC;oBAC9D,OAAO;gBACT,CAAC;gBAED,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;gBACpD,GAAG,CAAC,GAAG,CACL;;;;yBAIe,CAChB,CAAC;gBACF,OAAO,EAAE,CAAC;gBACV,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzB,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE;gBACpC,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;gBACrD,OAAO,CAAC,GAAG,CAAC,6CAA6C,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;gBACjF,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;oBAClC,qCAAqC;gBACvC,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,MAAM,UAAU,GAAG,UAAU,CAC3B,GAAG,EAAE;gBACH,OAAO,EAAE,CAAC;gBACV,MAAM,CAAC,IAAI,gBAAgB,CAAC,kCAAkC,CAAC,CAAC,CAAC;YACnE,CAAC,EACD,EAAE,GAAG,EAAE,GAAG,IAAI,CACf,CAAC;YAEF,SAAS,OAAO;gBACd,YAAY,CAAC,UAAU,CAAC,CAAC;gBACzB,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAcD,KAAK,UAAU,qBAAqB,CAClC,MAAyB,EACzB,IAAY,EACZ,QAAgB,EAChB,IAAY;IAEZ,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;QAC/B,UAAU,EAAE,oBAAoB;QAChC,SAAS,EAAE,MAAM,CAAC,QAAQ;QAC1B,IAAI;QACJ,aAAa,EAAE,QAAQ;QACvB,YAAY,EAAE,WAAW,CAAC,IAAI,CAAC;KAChC,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,eAAe,EAAE;QAC7D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;KACtB,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,gBAAgB,CACxB,0BAA0B,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CACjD,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAyB,CAAC;IACxD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QACxB,MAAM,IAAI,gBAAgB,CACxB,8FAA8F,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,YAAY;QAC9B,OAAO,EAAE,IAAI,CAAC,QAAQ;QACtB,YAAY,EAAE,IAAI,CAAC,aAAa;QAChC,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;QACtE,SAAS,EAAE,QAAQ;KACpB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAyB,EACzB,mBAA2B;IAE3B,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;QAC/B,UAAU,EAAE,eAAe;QAC3B,SAAS,EAAE,MAAM,CAAC,QAAQ;QAC1B,aAAa,EAAE,mBAAmB;KACnC,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,eAAe,EAAE;QAC7D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;QAChE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;KACtB,CAAC,CAAC;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,gBAAgB,CACxB,mBAAmB,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAC1C,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAyB,CAAC;IACxD,MAAM,MAAM,GAAkB;QAC5B,WAAW,EAAE,IAAI,CAAC,YAAY;QAC9B,OAAO,EAAE,IAAI,CAAC,QAAQ;QACtB,YAAY,EAAE,IAAI,CAAC,aAAa,IAAI,mBAAmB;QACvD,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;QACtE,SAAS,EAAE,QAAQ;KACpB,CAAC;IACF,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAyB,EACzB,UAAqC,EAAE;IAEvC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;IAChD,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAElC,IAAI,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC,WAAW,CAAC;IAE7D,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;YACnE,OAAO,SAAS,CAAC,WAAW,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,gBAAgB,CACxB,8EAA8E,CAC/E,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,WAAW,CAAC;AAC3B,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Entity context resolution (VLT-5 US-001).
3
+ *
4
+ * Resolves an entity (company) via vault-service, vends STS-scoped credentials,
5
+ * and returns an EntityContext for S3 operations. Handles auto-refresh when
6
+ * credentials are within 2 minutes of expiry.
7
+ */
8
+ import type { EntityContext, VaultServiceConfig } from "./types.js";
9
+ /**
10
+ * Look up an entity by slug or UID via vault-service, then vend STS-scoped
11
+ * credentials for that entity. Returns an EntityContext ready for S3 ops.
12
+ *
13
+ * Caches the result and auto-refreshes when the credentials are within
14
+ * 2 minutes of expiry.
15
+ */
16
+ export declare function resolveEntityContext(companyUidOrSlug: string, config: VaultServiceConfig): Promise<EntityContext>;
17
+ /**
18
+ * Check if credentials are expiring within the refresh threshold.
19
+ */
20
+ export declare function isExpiringSoon(expiresAt: string): boolean;
21
+ /**
22
+ * Force-refresh a cached context. Useful when an S3 operation fails with
23
+ * an expired credentials error.
24
+ */
25
+ export declare function refreshEntityContext(companyUidOrSlug: string, config: VaultServiceConfig): Promise<EntityContext>;
26
+ /**
27
+ * Clear the entire context cache. Useful for tests.
28
+ */
29
+ export declare function clearContextCache(): void;
30
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAWpE;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,CAAC,CAwCxB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAIzD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,CAAC,CAIxB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Entity context resolution (VLT-5 US-001).
3
+ *
4
+ * Resolves an entity (company) via vault-service, vends STS-scoped credentials,
5
+ * and returns an EntityContext for S3 operations. Handles auto-refresh when
6
+ * credentials are within 2 minutes of expiry.
7
+ */
8
+ /** Minimum remaining TTL before auto-refresh triggers (2 minutes). */
9
+ const REFRESH_THRESHOLD_MS = 2 * 60 * 1000;
10
+ /** STS session duration requested from vault-service (15 minutes). */
11
+ const DEFAULT_SESSION_DURATION_SECONDS = 900;
12
+ /** Cached contexts keyed by entity UID. */
13
+ const contextCache = new Map();
14
+ /**
15
+ * Look up an entity by slug or UID via vault-service, then vend STS-scoped
16
+ * credentials for that entity. Returns an EntityContext ready for S3 ops.
17
+ *
18
+ * Caches the result and auto-refreshes when the credentials are within
19
+ * 2 minutes of expiry.
20
+ */
21
+ export async function resolveEntityContext(companyUidOrSlug, config) {
22
+ // Check cache — return if credentials still fresh
23
+ const cached = contextCache.get(companyUidOrSlug);
24
+ if (cached && !isExpiringSoon(cached.expiresAt)) {
25
+ return cached;
26
+ }
27
+ // Step 1: Resolve entity — if it looks like a UID (cmp_*), fetch directly;
28
+ // otherwise look up by slug
29
+ const entity = companyUidOrSlug.startsWith("cmp_")
30
+ ? await fetchEntity(companyUidOrSlug, config)
31
+ : await fetchEntityBySlug("company", companyUidOrSlug, config);
32
+ if (!entity.bucketName) {
33
+ throw new Error(`Entity ${entity.uid} (${entity.slug}) has no bucket provisioned. ` +
34
+ `Run VLT-2 bucket provisioning first.`);
35
+ }
36
+ // Step 2: Vend STS-scoped credentials
37
+ const vendResult = await vendCredentials(entity.uid, config);
38
+ const ctx = {
39
+ uid: entity.uid,
40
+ bucketName: entity.bucketName,
41
+ region: config.region ?? "us-east-1",
42
+ credentials: {
43
+ accessKeyId: vendResult.credentials.accessKeyId,
44
+ secretAccessKey: vendResult.credentials.secretAccessKey,
45
+ sessionToken: vendResult.credentials.sessionToken,
46
+ },
47
+ expiresAt: vendResult.expiresAt,
48
+ };
49
+ // Cache by both UID and slug for fast lookups
50
+ contextCache.set(entity.uid, ctx);
51
+ contextCache.set(entity.slug, ctx);
52
+ return ctx;
53
+ }
54
+ /**
55
+ * Check if credentials are expiring within the refresh threshold.
56
+ */
57
+ export function isExpiringSoon(expiresAt) {
58
+ const expiryMs = new Date(expiresAt).getTime();
59
+ const nowMs = Date.now();
60
+ return expiryMs - nowMs < REFRESH_THRESHOLD_MS;
61
+ }
62
+ /**
63
+ * Force-refresh a cached context. Useful when an S3 operation fails with
64
+ * an expired credentials error.
65
+ */
66
+ export async function refreshEntityContext(companyUidOrSlug, config) {
67
+ // Evict cache entry to force fresh resolution
68
+ contextCache.delete(companyUidOrSlug);
69
+ return resolveEntityContext(companyUidOrSlug, config);
70
+ }
71
+ /**
72
+ * Clear the entire context cache. Useful for tests.
73
+ */
74
+ export function clearContextCache() {
75
+ contextCache.clear();
76
+ }
77
+ async function fetchEntity(uid, config) {
78
+ const res = await fetch(`${config.apiUrl}/entity/${uid}`, {
79
+ headers: { Authorization: `Bearer ${config.authToken}` },
80
+ });
81
+ if (!res.ok) {
82
+ const body = await res.text();
83
+ throw new Error(`Failed to fetch entity ${uid}: ${res.status} ${body}`);
84
+ }
85
+ const data = (await res.json());
86
+ return data.entity;
87
+ }
88
+ async function fetchEntityBySlug(type, slug, config) {
89
+ const res = await fetch(`${config.apiUrl}/entity/by-slug/${type}/${slug}`, {
90
+ headers: { Authorization: `Bearer ${config.authToken}` },
91
+ });
92
+ if (!res.ok) {
93
+ const body = await res.text();
94
+ throw new Error(`Failed to find entity by slug ${type}/${slug}: ${res.status} ${body}`);
95
+ }
96
+ const data = (await res.json());
97
+ return data.entity;
98
+ }
99
+ async function vendCredentials(companyUid, config) {
100
+ const res = await fetch(`${config.apiUrl}/sts/vend`, {
101
+ method: "POST",
102
+ headers: {
103
+ "Content-Type": "application/json",
104
+ Authorization: `Bearer ${config.authToken}`,
105
+ },
106
+ body: JSON.stringify({
107
+ companyUid,
108
+ durationSeconds: DEFAULT_SESSION_DURATION_SECONDS,
109
+ }),
110
+ });
111
+ if (!res.ok) {
112
+ const body = await res.text();
113
+ throw new Error(`STS vend failed for ${companyUid}: ${res.status} ${body}`);
114
+ }
115
+ return (await res.json());
116
+ }
117
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Unit tests for context.ts — entity context resolution (VLT-5 US-001).
3
+ *
4
+ * Uses a mock fetch to simulate vault-service API responses.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=context.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.test.d.ts","sourceRoot":"","sources":["../src/context.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Unit tests for context.ts — entity context resolution (VLT-5 US-001).
3
+ *
4
+ * Uses a mock fetch to simulate vault-service API responses.
5
+ */
6
+ import { describe, it, expect, beforeEach, vi } from "vitest";
7
+ import { resolveEntityContext, refreshEntityContext, clearContextCache, isExpiringSoon, } from "./context.js";
8
+ const mockConfig = {
9
+ apiUrl: "https://vault-api.test",
10
+ authToken: "test-jwt-token",
11
+ region: "us-east-1",
12
+ };
13
+ const mockEntity = {
14
+ uid: "cmp_01ABCDEF",
15
+ slug: "acme",
16
+ bucketName: "hq-vault-acme-123",
17
+ status: "active",
18
+ };
19
+ const mockVendResponse = {
20
+ credentials: {
21
+ accessKeyId: "ASIA_TEST_KEY",
22
+ secretAccessKey: "test-secret",
23
+ sessionToken: "test-session-token",
24
+ expiration: new Date(Date.now() + 15 * 60 * 1000).toISOString(),
25
+ },
26
+ expiresAt: new Date(Date.now() + 15 * 60 * 1000).toISOString(),
27
+ };
28
+ function setupFetchMock(overrides) {
29
+ const fetchMock = vi.fn();
30
+ fetchMock.mockImplementation(async (url) => {
31
+ const urlStr = String(url);
32
+ if (urlStr.includes("/entity/by-slug/")) {
33
+ return {
34
+ ok: (overrides?.entityStatus ?? 200) < 400,
35
+ status: overrides?.entityStatus ?? 200,
36
+ json: async () => overrides?.entityBody ?? { entity: mockEntity },
37
+ text: async () => JSON.stringify(overrides?.entityBody ?? { entity: mockEntity }),
38
+ };
39
+ }
40
+ if (urlStr.includes("/entity/cmp_")) {
41
+ return {
42
+ ok: (overrides?.entityStatus ?? 200) < 400,
43
+ status: overrides?.entityStatus ?? 200,
44
+ json: async () => overrides?.entityBody ?? { entity: mockEntity },
45
+ text: async () => JSON.stringify(overrides?.entityBody ?? { entity: mockEntity }),
46
+ };
47
+ }
48
+ if (urlStr.includes("/sts/vend")) {
49
+ return {
50
+ ok: (overrides?.vendStatus ?? 200) < 400,
51
+ status: overrides?.vendStatus ?? 200,
52
+ json: async () => overrides?.vendBody ?? mockVendResponse,
53
+ text: async () => JSON.stringify(overrides?.vendBody ?? mockVendResponse),
54
+ };
55
+ }
56
+ return { ok: false, status: 404, text: async () => "Not found" };
57
+ });
58
+ vi.stubGlobal("fetch", fetchMock);
59
+ return fetchMock;
60
+ }
61
+ describe("resolveEntityContext", () => {
62
+ beforeEach(() => {
63
+ clearContextCache();
64
+ vi.restoreAllMocks();
65
+ });
66
+ it("resolves context by slug", async () => {
67
+ const fetchMock = setupFetchMock();
68
+ const ctx = await resolveEntityContext("acme", mockConfig);
69
+ expect(ctx.uid).toBe("cmp_01ABCDEF");
70
+ expect(ctx.bucketName).toBe("hq-vault-acme-123");
71
+ expect(ctx.credentials.accessKeyId).toBe("ASIA_TEST_KEY");
72
+ expect(ctx.region).toBe("us-east-1");
73
+ // Verify entity lookup used by-slug endpoint
74
+ expect(fetchMock).toHaveBeenCalledTimes(2);
75
+ expect(String(fetchMock.mock.calls[0][0])).toContain("/entity/by-slug/company/acme");
76
+ expect(String(fetchMock.mock.calls[1][0])).toContain("/sts/vend");
77
+ });
78
+ it("resolves context by UID directly", async () => {
79
+ const fetchMock = setupFetchMock();
80
+ const ctx = await resolveEntityContext("cmp_01ABCDEF", mockConfig);
81
+ expect(ctx.uid).toBe("cmp_01ABCDEF");
82
+ // Verify entity lookup used direct UID endpoint
83
+ expect(String(fetchMock.mock.calls[0][0])).toContain("/entity/cmp_01ABCDEF");
84
+ });
85
+ it("returns cached context when credentials are fresh", async () => {
86
+ const fetchMock = setupFetchMock();
87
+ const ctx1 = await resolveEntityContext("acme", mockConfig);
88
+ const ctx2 = await resolveEntityContext("acme", mockConfig);
89
+ expect(ctx1).toBe(ctx2); // Same reference
90
+ expect(fetchMock).toHaveBeenCalledTimes(2); // Only 1 entity + 1 vend call
91
+ });
92
+ it("auto-refreshes when credentials expire soon", async () => {
93
+ const almostExpired = new Date(Date.now() + 60 * 1000).toISOString(); // 1 min left
94
+ const fetchMock = setupFetchMock({
95
+ vendBody: {
96
+ credentials: mockVendResponse.credentials,
97
+ expiresAt: almostExpired,
98
+ },
99
+ });
100
+ const ctx1 = await resolveEntityContext("acme", mockConfig);
101
+ // Second call should refresh because <2 min remaining
102
+ const ctx2 = await resolveEntityContext("acme", mockConfig);
103
+ expect(ctx2).not.toBe(ctx1);
104
+ expect(fetchMock).toHaveBeenCalledTimes(4); // 2 entity + 2 vend calls
105
+ });
106
+ it("throws when entity has no bucket", async () => {
107
+ setupFetchMock({
108
+ entityBody: { entity: { ...mockEntity, bucketName: undefined } },
109
+ });
110
+ await expect(resolveEntityContext("acme", mockConfig)).rejects.toThrow(/no bucket provisioned/);
111
+ });
112
+ it("throws on entity lookup failure", async () => {
113
+ setupFetchMock({ entityStatus: 404 });
114
+ await expect(resolveEntityContext("nonexistent", mockConfig)).rejects.toThrow(/Failed to find entity/);
115
+ });
116
+ it("throws on STS vend failure", async () => {
117
+ setupFetchMock({ vendStatus: 403 });
118
+ await expect(resolveEntityContext("acme", mockConfig)).rejects.toThrow(/STS vend failed/);
119
+ });
120
+ });
121
+ describe("refreshEntityContext", () => {
122
+ beforeEach(() => {
123
+ clearContextCache();
124
+ vi.restoreAllMocks();
125
+ });
126
+ it("evicts cache and fetches fresh credentials", async () => {
127
+ const fetchMock = setupFetchMock();
128
+ const ctx1 = await resolveEntityContext("acme", mockConfig);
129
+ const ctx2 = await refreshEntityContext("acme", mockConfig);
130
+ expect(ctx2).not.toBe(ctx1);
131
+ expect(fetchMock).toHaveBeenCalledTimes(4); // 2 initial + 2 refresh
132
+ });
133
+ });
134
+ describe("isExpiringSoon", () => {
135
+ it("returns false when well within TTL", () => {
136
+ const future = new Date(Date.now() + 10 * 60 * 1000).toISOString();
137
+ expect(isExpiringSoon(future)).toBe(false);
138
+ });
139
+ it("returns true when within 2 minutes", () => {
140
+ const soon = new Date(Date.now() + 90 * 1000).toISOString();
141
+ expect(isExpiringSoon(soon)).toBe(true);
142
+ });
143
+ it("returns true when already expired", () => {
144
+ const past = new Date(Date.now() - 1000).toISOString();
145
+ expect(isExpiringSoon(past)).toBe(true);
146
+ });
147
+ });
148
+ //# sourceMappingURL=context.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.test.js","sourceRoot":"","sources":["../src/context.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Daemon worker — runs as a detached child process
3
+ * Watches HQ directory and syncs changes to S3
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=daemon-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-worker.d.ts","sourceRoot":"","sources":["../src/daemon-worker.ts"],"names":[],"mappings":"AAAA;;;GAGG"}