@indigoai-us/hq-cloud 5.18.1 → 5.19.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 (46) hide show
  1. package/dist/cli/invite.js +4 -1
  2. package/dist/cli/invite.js.map +1 -1
  3. package/dist/cli/invite.test.js +3 -0
  4. package/dist/cli/invite.test.js.map +1 -1
  5. package/dist/cli/promote.js +3 -0
  6. package/dist/cli/promote.js.map +1 -1
  7. package/dist/cli/share.test.js +12 -1
  8. package/dist/cli/share.test.js.map +1 -1
  9. package/dist/cli/sync.test.js +12 -0
  10. package/dist/cli/sync.test.js.map +1 -1
  11. package/dist/client-info.d.ts +44 -0
  12. package/dist/client-info.d.ts.map +1 -0
  13. package/dist/client-info.js +112 -0
  14. package/dist/client-info.js.map +1 -0
  15. package/dist/client-info.test.d.ts +11 -0
  16. package/dist/client-info.test.d.ts.map +1 -0
  17. package/dist/client-info.test.js +168 -0
  18. package/dist/client-info.test.js.map +1 -0
  19. package/dist/context.d.ts.map +1 -1
  20. package/dist/context.js +117 -20
  21. package/dist/context.js.map +1 -1
  22. package/dist/context.test.js +63 -14
  23. package/dist/context.test.js.map +1 -1
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +3 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/types.d.ts +22 -0
  29. package/dist/types.d.ts.map +1 -1
  30. package/dist/vault-client.d.ts +25 -0
  31. package/dist/vault-client.d.ts.map +1 -1
  32. package/dist/vault-client.js +33 -0
  33. package/dist/vault-client.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/cli/invite.test.ts +3 -0
  36. package/src/cli/invite.ts +4 -1
  37. package/src/cli/promote.ts +3 -0
  38. package/src/cli/share.test.ts +12 -1
  39. package/src/cli/sync.test.ts +12 -0
  40. package/src/client-info.test.ts +214 -0
  41. package/src/client-info.ts +121 -0
  42. package/src/context.test.ts +73 -14
  43. package/src/context.ts +126 -22
  44. package/src/index.ts +12 -0
  45. package/src/types.ts +23 -0
  46. package/src/vault-client.ts +42 -1
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Client identification — every HQ client that talks to hq-cloud-api should
3
+ * stamp its name and version on outbound requests so the server can attribute
4
+ * traffic, gate on minimum versions, and surface deprecation warnings.
5
+ *
6
+ * The CLI in particular sends a third header (`x-hq-core-version`) when it's
7
+ * invoked from inside an hq-core checkout, so the server sees which scaffold
8
+ * generation the user is running against.
9
+ */
10
+ import { readFileSync } from "node:fs";
11
+ import { dirname, join, resolve } from "node:path";
12
+ export const HEADER_CLIENT_NAME = "x-hq-client-name";
13
+ export const HEADER_CLIENT_VERSION = "x-hq-client-version";
14
+ export const HEADER_HQ_CORE_VERSION = "x-hq-core-version";
15
+ /**
16
+ * Build the set of `x-hq-*` headers (plus a derived `User-Agent`) for a
17
+ * ClientInfo. Returns an empty object when info is undefined so callers can
18
+ * spread the result unconditionally.
19
+ */
20
+ export function buildClientHeaders(info) {
21
+ if (!info)
22
+ return {};
23
+ const headers = {
24
+ "User-Agent": `${info.name}/${info.version}`,
25
+ [HEADER_CLIENT_NAME]: info.name,
26
+ [HEADER_CLIENT_VERSION]: info.version,
27
+ };
28
+ if (info.hqCoreVersion) {
29
+ headers[HEADER_HQ_CORE_VERSION] = info.hqCoreVersion;
30
+ }
31
+ if (info.extra) {
32
+ for (const [k, v] of Object.entries(info.extra)) {
33
+ headers[`x-hq-client-${k}`] = v;
34
+ }
35
+ }
36
+ return headers;
37
+ }
38
+ /**
39
+ * Build a ClientInfo from a parsed package.json. Most consumers will call this
40
+ * once at startup and pass the result into every VaultServiceConfig.
41
+ */
42
+ export function clientInfoFromPackage(pkg) {
43
+ if (typeof pkg.name !== "string" || pkg.name.length === 0) {
44
+ throw new Error("clientInfoFromPackage: package.json is missing a name");
45
+ }
46
+ if (typeof pkg.version !== "string" || pkg.version.length === 0) {
47
+ throw new Error("clientInfoFromPackage: package.json is missing a version");
48
+ }
49
+ return { name: pkg.name, version: pkg.version };
50
+ }
51
+ /**
52
+ * Walk upward from `startDir` looking for `core/core.yaml`. When found, parse
53
+ * out the `hqVersion` field and return it. Returns undefined if we never find
54
+ * an hq-core checkout — i.e. the caller is running from a plain user dir.
55
+ *
56
+ * Honors `HQ_HOME` env var as an explicit override so multi-checkout setups
57
+ * (e.g. CI bots, the menubar app pointing at a non-cwd HQ root) can pin the
58
+ * detection without relying on cwd.
59
+ *
60
+ * Why a regex instead of a YAML parser: `core.yaml` lives at the very top of
61
+ * the file and the field has a stable shape — adding a YAML dep just to read
62
+ * one string would balloon every consumer's bundle. The current shape is
63
+ * `hqVersion: "X.Y.Z"` (quoted) per the canonical seed; we tolerate unquoted
64
+ * too.
65
+ */
66
+ export function detectHqCoreVersion(startDir) {
67
+ const fromEnv = process.env.HQ_HOME ?? process.env.HQ_ROOT;
68
+ if (fromEnv) {
69
+ const v = readCoreVersionAt(fromEnv);
70
+ if (v)
71
+ return v;
72
+ }
73
+ let dir = resolve(startDir ?? process.cwd());
74
+ while (true) {
75
+ const v = readCoreVersionAt(dir);
76
+ if (v)
77
+ return v;
78
+ const parent = dirname(dir);
79
+ if (parent === dir)
80
+ return undefined;
81
+ dir = parent;
82
+ }
83
+ }
84
+ function readCoreVersionAt(hqRoot) {
85
+ // hq-core identity requires BOTH core/core.yaml AND companies/ — matches the
86
+ // CLI's own detection (commit bc827d0). Without this guard, any directory
87
+ // containing a stray `core/core.yaml` (e.g. a test fixture) would be
88
+ // misidentified as an hq-core root.
89
+ const yamlPath = join(hqRoot, "core", "core.yaml");
90
+ const companiesPath = join(hqRoot, "companies");
91
+ let content;
92
+ try {
93
+ content = readFileSync(yamlPath, "utf8");
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ try {
99
+ // statSync would be marginally cleaner, but readdirSync of a nonexistent
100
+ // path throws synchronously which is what we want.
101
+ readFileSync(companiesPath, { flag: "r" });
102
+ }
103
+ catch (err) {
104
+ const e = err;
105
+ // EISDIR is the success case — companies/ exists and is a directory.
106
+ if (e.code !== "EISDIR")
107
+ return undefined;
108
+ }
109
+ const match = /^hqVersion:\s*["']?([^"'\s]+)/m.exec(content);
110
+ return match ? match[1] : undefined;
111
+ }
112
+ //# sourceMappingURL=client-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-info.js","sourceRoot":"","sources":["../src/client-info.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tests for the client-info helpers and header injection.
3
+ *
4
+ * Two layers are exercised here:
5
+ * 1. The pure functions in `client-info.ts` — buildClientHeaders,
6
+ * clientInfoFromPackage, detectHqCoreVersion.
7
+ * 2. End-to-end injection into VaultClient.request, since "the headers
8
+ * actually land on outbound fetch" is the property consumers care about.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=client-info.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-info.test.d.ts","sourceRoot":"","sources":["../src/client-info.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Tests for the client-info helpers and header injection.
3
+ *
4
+ * Two layers are exercised here:
5
+ * 1. The pure functions in `client-info.ts` — buildClientHeaders,
6
+ * clientInfoFromPackage, detectHqCoreVersion.
7
+ * 2. End-to-end injection into VaultClient.request, since "the headers
8
+ * actually land on outbound fetch" is the property consumers care about.
9
+ */
10
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
11
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { buildClientHeaders, clientInfoFromPackage, detectHqCoreVersion, HEADER_CLIENT_NAME, HEADER_CLIENT_VERSION, HEADER_HQ_CORE_VERSION, } from "./client-info.js";
15
+ import { VaultClient } from "./vault-client.js";
16
+ describe("buildClientHeaders", () => {
17
+ it("returns empty object when info is undefined", () => {
18
+ expect(buildClientHeaders(undefined)).toEqual({});
19
+ });
20
+ it("emits User-Agent + x-hq-client-{name,version} from name/version", () => {
21
+ const headers = buildClientHeaders({
22
+ name: "@indigoai-us/hq-cli",
23
+ version: "5.15.0",
24
+ });
25
+ expect(headers["User-Agent"]).toBe("@indigoai-us/hq-cli/5.15.0");
26
+ expect(headers[HEADER_CLIENT_NAME]).toBe("@indigoai-us/hq-cli");
27
+ expect(headers[HEADER_CLIENT_VERSION]).toBe("5.15.0");
28
+ expect(headers[HEADER_HQ_CORE_VERSION]).toBeUndefined();
29
+ });
30
+ it("includes x-hq-core-version only when hqCoreVersion is set", () => {
31
+ const headers = buildClientHeaders({
32
+ name: "@indigoai-us/hq-cli",
33
+ version: "5.15.0",
34
+ hqCoreVersion: "14.1.0",
35
+ });
36
+ expect(headers[HEADER_HQ_CORE_VERSION]).toBe("14.1.0");
37
+ });
38
+ it("emits arbitrary extra fields as x-hq-client-<key> headers", () => {
39
+ const headers = buildClientHeaders({
40
+ name: "x",
41
+ version: "1.0.0",
42
+ extra: { machine: "ec2-bot-7", channel: "stable" },
43
+ });
44
+ expect(headers["x-hq-client-machine"]).toBe("ec2-bot-7");
45
+ expect(headers["x-hq-client-channel"]).toBe("stable");
46
+ });
47
+ });
48
+ describe("clientInfoFromPackage", () => {
49
+ it("extracts name and version from a parsed package.json", () => {
50
+ expect(clientInfoFromPackage({ name: "foo", version: "1.2.3" })).toEqual({
51
+ name: "foo",
52
+ version: "1.2.3",
53
+ });
54
+ });
55
+ it("throws when name is missing", () => {
56
+ expect(() => clientInfoFromPackage({ version: "1.0.0" })).toThrow(/name/);
57
+ });
58
+ it("throws when version is missing", () => {
59
+ expect(() => clientInfoFromPackage({ name: "foo" })).toThrow(/version/);
60
+ });
61
+ it("throws on non-string fields", () => {
62
+ expect(() => clientInfoFromPackage({ name: 42, version: "1.0.0" })).toThrow();
63
+ });
64
+ });
65
+ describe("detectHqCoreVersion", () => {
66
+ let tmpRoot;
67
+ const origHqHome = process.env.HQ_HOME;
68
+ const origHqRoot = process.env.HQ_ROOT;
69
+ beforeEach(() => {
70
+ tmpRoot = mkdtempSync(join(tmpdir(), "hq-core-detect-"));
71
+ delete process.env.HQ_HOME;
72
+ delete process.env.HQ_ROOT;
73
+ });
74
+ afterEach(() => {
75
+ rmSync(tmpRoot, { recursive: true, force: true });
76
+ if (origHqHome !== undefined)
77
+ process.env.HQ_HOME = origHqHome;
78
+ if (origHqRoot !== undefined)
79
+ process.env.HQ_ROOT = origHqRoot;
80
+ });
81
+ function seedHqCore(root, version) {
82
+ mkdirSync(join(root, "core"), { recursive: true });
83
+ mkdirSync(join(root, "companies"), { recursive: true });
84
+ writeFileSync(join(root, "core", "core.yaml"), `version: 1\nhqVersion: "${version}"\nupdatedAt: "2026-05-13T00:00:00Z"\n`);
85
+ }
86
+ it("returns undefined when nothing on the walk-up has core.yaml", () => {
87
+ expect(detectHqCoreVersion(tmpRoot)).toBeUndefined();
88
+ });
89
+ it("returns hqVersion when startDir is the hq-core root", () => {
90
+ seedHqCore(tmpRoot, "14.1.0");
91
+ expect(detectHqCoreVersion(tmpRoot)).toBe("14.1.0");
92
+ });
93
+ it("walks upward to find core.yaml from a nested cwd", () => {
94
+ seedHqCore(tmpRoot, "14.2.0");
95
+ const nested = join(tmpRoot, "companies", "acme", "projects", "p1");
96
+ mkdirSync(nested, { recursive: true });
97
+ expect(detectHqCoreVersion(nested)).toBe("14.2.0");
98
+ });
99
+ it("ignores directories that have core.yaml but no companies/ — disambiguates fixtures", () => {
100
+ mkdirSync(join(tmpRoot, "core"), { recursive: true });
101
+ writeFileSync(join(tmpRoot, "core", "core.yaml"), `version: 1\nhqVersion: "99.0.0"\n`);
102
+ // No companies/ at this level → not an hq-core root.
103
+ expect(detectHqCoreVersion(tmpRoot)).toBeUndefined();
104
+ });
105
+ it("honors HQ_HOME env override before walking cwd", () => {
106
+ seedHqCore(tmpRoot, "14.3.0");
107
+ process.env.HQ_HOME = tmpRoot;
108
+ // startDir intentionally points elsewhere — env should win.
109
+ const elsewhere = mkdtempSync(join(tmpdir(), "elsewhere-"));
110
+ try {
111
+ expect(detectHqCoreVersion(elsewhere)).toBe("14.3.0");
112
+ }
113
+ finally {
114
+ rmSync(elsewhere, { recursive: true, force: true });
115
+ }
116
+ });
117
+ it("parses unquoted hqVersion values too", () => {
118
+ mkdirSync(join(tmpRoot, "core"), { recursive: true });
119
+ mkdirSync(join(tmpRoot, "companies"), { recursive: true });
120
+ writeFileSync(join(tmpRoot, "core", "core.yaml"), `version: 1\nhqVersion: 14.4.0\n`);
121
+ expect(detectHqCoreVersion(tmpRoot)).toBe("14.4.0");
122
+ });
123
+ });
124
+ describe("VaultClient stamps client headers on outbound requests", () => {
125
+ afterEach(() => {
126
+ vi.restoreAllMocks();
127
+ });
128
+ it("includes x-hq-client-name + x-hq-client-version when clientInfo is set", async () => {
129
+ const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify({ memberships: [] }), {
130
+ status: 200,
131
+ headers: { "Content-Type": "application/json" },
132
+ }));
133
+ const client = new VaultClient({
134
+ apiUrl: "https://vault.test.example.com",
135
+ authToken: "tok",
136
+ clientInfo: {
137
+ name: "@indigoai-us/hq-cli",
138
+ version: "5.15.0",
139
+ hqCoreVersion: "14.1.0",
140
+ },
141
+ });
142
+ await client.listMyMemberships();
143
+ expect(fetchSpy).toHaveBeenCalledOnce();
144
+ const init = fetchSpy.mock.calls[0]?.[1];
145
+ const headers = init.headers;
146
+ expect(headers[HEADER_CLIENT_NAME]).toBe("@indigoai-us/hq-cli");
147
+ expect(headers[HEADER_CLIENT_VERSION]).toBe("5.15.0");
148
+ expect(headers[HEADER_HQ_CORE_VERSION]).toBe("14.1.0");
149
+ expect(headers["User-Agent"]).toBe("@indigoai-us/hq-cli/5.15.0");
150
+ });
151
+ it("omits client headers when clientInfo is not set — back-compat", async () => {
152
+ const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify({ memberships: [] }), {
153
+ status: 200,
154
+ headers: { "Content-Type": "application/json" },
155
+ }));
156
+ const client = new VaultClient({
157
+ apiUrl: "https://vault.test.example.com",
158
+ authToken: "tok",
159
+ });
160
+ await client.listMyMemberships();
161
+ const headers = (fetchSpy.mock.calls[0]?.[1])
162
+ .headers;
163
+ expect(headers[HEADER_CLIENT_NAME]).toBeUndefined();
164
+ expect(headers[HEADER_CLIENT_VERSION]).toBeUndefined();
165
+ expect(headers["User-Agent"]).toBeUndefined();
166
+ });
167
+ });
168
+ //# sourceMappingURL=client-info.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-info.test.js","sourceRoot":"","sources":["../src/client-info.test.ts"],"names":[],"mappings":"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"}
@@ -1 +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;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,2BAA4B,CAAC;AAE5D;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,CAAC,CAoDxB;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"}
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;AA2DpE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,2BAA4B,CAAC;AAE5D;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,CAAC,CAsExB;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,CAcxB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
package/dist/context.js CHANGED
@@ -5,12 +5,59 @@
5
5
  * and returns an EntityContext for S3 operations. Handles auto-refresh when
6
6
  * credentials are within 2 minutes of expiry.
7
7
  */
8
+ import { buildClientHeaders } from "./client-info.js";
8
9
  /** Minimum remaining TTL before auto-refresh triggers (2 minutes). */
9
10
  const REFRESH_THRESHOLD_MS = 2 * 60 * 1000;
10
11
  /** STS session duration requested from vault-service (15 minutes). */
11
12
  const DEFAULT_SESSION_DURATION_SECONDS = 900;
12
- /** Cached contexts keyed by entity UID. */
13
+ /**
14
+ * Cached contexts.
15
+ *
16
+ * Two keying schemes share the map:
17
+ * - UID keys: bare `cmp_xxx` / `prs_xxx`. Globally unique by ULID, so
18
+ * the cached context is safe to return to any caller asking by uid.
19
+ * - Slug keys: `slug:${callerSub}#${slug}`. Under the per-user-namespace
20
+ * model on hq-pro (PR 67, live 2026-05-15), the same slug can
21
+ * legitimately resolve to different company UIDs for different
22
+ * callers. Keying slug entries by `(callerSub, slug)` prevents one
23
+ * caller's resolution from being served to another caller against
24
+ * the wrong tenant. Codex flagged this as a P1 on PR 67's hq-cloud
25
+ * follow-up.
26
+ */
13
27
  const contextCache = new Map();
28
+ /**
29
+ * Extract the Cognito sub claim from a JWT without verifying the
30
+ * signature. We don't need verification here — the token already
31
+ * authorized the upstream API call; we're only using `sub` as a
32
+ * per-caller cache discriminator. If the token is malformed or
33
+ * missing a sub, fall back to a hash of the whole token (slightly
34
+ * larger key space, same correctness — different tokens still
35
+ * get distinct cache entries).
36
+ */
37
+ function callerKeyFromAccessToken(accessToken) {
38
+ const parts = accessToken.split(".");
39
+ if (parts.length === 3) {
40
+ try {
41
+ // base64url decode (Node-compatible, no padding required)
42
+ const padded = parts[1].replace(/-/g, "+").replace(/_/g, "/");
43
+ const payload = Buffer.from(padded, "base64").toString("utf8");
44
+ const claims = JSON.parse(payload);
45
+ if (typeof claims.sub === "string" && claims.sub.length > 0) {
46
+ return claims.sub;
47
+ }
48
+ }
49
+ catch {
50
+ // fall through to token-hash fallback
51
+ }
52
+ }
53
+ // Stable fallback — same token → same key, different tokens → different keys.
54
+ // (Avoids importing crypto for a sha; the raw token suffix is sufficient as
55
+ // a cache discriminator and isn't exposed outside this process.)
56
+ return `tok:${accessToken.slice(-32)}`;
57
+ }
58
+ function slugCacheKey(callerKey, slug) {
59
+ return `slug:${callerKey}#${slug}`;
60
+ }
14
61
  /**
15
62
  * Closed-set of recognised entity-UID prefixes. Adding a new entity type
16
63
  * means appending one entry here AND extending the dispatch in
@@ -25,16 +72,30 @@ export const KNOWN_UID_PREFIXES = ["cmp_", "prs_"];
25
72
  * 2 minutes of expiry.
26
73
  */
27
74
  export async function resolveEntityContext(companyUidOrSlug, config) {
28
- // Check cache return if credentials still fresh
29
- const cached = contextCache.get(companyUidOrSlug);
75
+ // Step 0: Determine whether the input is a UID (globally unique) or a
76
+ // slug (per-caller). The cache key differs accordingly — see the
77
+ // `contextCache` jsdoc.
78
+ const looksLikeUid = KNOWN_UID_PREFIXES.some((p) => companyUidOrSlug.startsWith(p));
79
+ const looksLikePerson = companyUidOrSlug.startsWith("prs_");
80
+ // For slug lookups, scope the cache key by the caller. Resolving the
81
+ // access token here (once) doubles as a way to extract the sub for
82
+ // the key — same hop the downstream fetch makes anyway.
83
+ let cacheKey;
84
+ let callerKey = null;
85
+ if (looksLikeUid) {
86
+ cacheKey = companyUidOrSlug;
87
+ }
88
+ else {
89
+ const token = await resolveAuthToken(config);
90
+ callerKey = callerKeyFromAccessToken(token);
91
+ cacheKey = slugCacheKey(callerKey, companyUidOrSlug);
92
+ }
93
+ // Check cache — return if credentials still fresh.
94
+ const cached = contextCache.get(cacheKey);
30
95
  if (cached && !isExpiringSoon(cached.expiresAt)) {
31
96
  return cached;
32
97
  }
33
- // Step 1: Resolve entity — if it looks like a known UID prefix, fetch directly;
34
- // otherwise look up by slug. Explicit enumeration avoids over-matching slugs
35
- // like foo_bar or team_alpha that happen to look like UIDs.
36
- const looksLikeUid = KNOWN_UID_PREFIXES.some((p) => companyUidOrSlug.startsWith(p));
37
- const looksLikePerson = companyUidOrSlug.startsWith("prs_");
98
+ // Step 1: Resolve entity — direct fetch by UID or namespace lookup by slug.
38
99
  const entity = looksLikeUid
39
100
  ? await fetchEntity(companyUidOrSlug, config)
40
101
  : await fetchEntityBySlug("company", companyUidOrSlug, config);
@@ -61,9 +122,13 @@ export async function resolveEntityContext(companyUidOrSlug, config) {
61
122
  },
62
123
  expiresAt: vendResult.expiresAt,
63
124
  };
64
- // Cache by both UID and slug for fast lookups
125
+ // Cache by UID (globally unique) and if the caller asked by slug —
126
+ // by `(callerSub, slug)` so the same slug can resolve to different
127
+ // entities per caller without cross-caller poisoning.
65
128
  contextCache.set(entity.uid, ctx);
66
- contextCache.set(entity.slug, ctx);
129
+ if (callerKey) {
130
+ contextCache.set(slugCacheKey(callerKey, entity.slug), ctx);
131
+ }
67
132
  return ctx;
68
133
  }
69
134
  /**
@@ -79,8 +144,17 @@ export function isExpiringSoon(expiresAt) {
79
144
  * an expired credentials error.
80
145
  */
81
146
  export async function refreshEntityContext(companyUidOrSlug, config) {
82
- // Evict cache entry to force fresh resolution
83
- contextCache.delete(companyUidOrSlug);
147
+ // Evict the entry under whichever key shape applies. UID inputs key
148
+ // the cache by the bare uid; slug inputs key by `(callerSub, slug)`.
149
+ const looksLikeUid = KNOWN_UID_PREFIXES.some((p) => companyUidOrSlug.startsWith(p));
150
+ if (looksLikeUid) {
151
+ contextCache.delete(companyUidOrSlug);
152
+ }
153
+ else {
154
+ const token = await resolveAuthToken(config);
155
+ const callerKey = callerKeyFromAccessToken(token);
156
+ contextCache.delete(slugCacheKey(callerKey, companyUidOrSlug));
157
+ }
84
158
  return resolveEntityContext(companyUidOrSlug, config);
85
159
  }
86
160
  /**
@@ -103,7 +177,10 @@ async function resolveAuthToken(config) {
103
177
  }
104
178
  async function fetchEntity(uid, config) {
105
179
  const res = await fetch(`${config.apiUrl}/entity/${uid}`, {
106
- headers: { Authorization: `Bearer ${await resolveAuthToken(config)}` },
180
+ headers: {
181
+ Authorization: `Bearer ${await resolveAuthToken(config)}`,
182
+ ...buildClientHeaders(config.clientInfo),
183
+ },
107
184
  });
108
185
  if (!res.ok) {
109
186
  const body = await res.text();
@@ -113,15 +190,34 @@ async function fetchEntity(uid, config) {
113
190
  return data.entity;
114
191
  }
115
192
  async function fetchEntityBySlug(type, slug, config) {
116
- const res = await fetch(`${config.apiUrl}/entity/by-slug/${type}/${slug}`, {
117
- headers: { Authorization: `Bearer ${await resolveAuthToken(config)}` },
193
+ // Resolve the slug inside the CALLER's namespace via the new
194
+ // `/entity/check-slug/me` endpoint added in hq-pro PR 67 (live
195
+ // in prod 2026-05-15). Under the per-user-namespace model the
196
+ // legacy `/entity/by-slug/{type}/{slug}` either over-matches
197
+ // (returns another tenant's entity when more than one user holds
198
+ // the slug) or 409s with SlugNotUniqueError — both wrong for the
199
+ // sync runner's "I want MY company" intent.
200
+ const checkUrl = `${config.apiUrl}/entity/check-slug/me?type=${encodeURIComponent(type)}&slug=${encodeURIComponent(slug)}`;
201
+ const checkRes = await fetch(checkUrl, {
202
+ headers: {
203
+ Authorization: `Bearer ${await resolveAuthToken(config)}`,
204
+ ...buildClientHeaders(config.clientInfo),
205
+ },
118
206
  });
119
- if (!res.ok) {
120
- const body = await res.text();
121
- throw new Error(`Failed to find entity by slug ${type}/${slug}: ${res.status} ${body}`);
207
+ if (!checkRes.ok) {
208
+ const body = await checkRes.text();
209
+ throw new Error(`Failed to check slug ${type}/${slug}: ${checkRes.status} ${body}`);
122
210
  }
123
- const data = (await res.json());
124
- return data.entity;
211
+ const check = (await checkRes.json());
212
+ if (check.available || !check.conflictingCompanyUid) {
213
+ // The slug isn't in the caller's namespace. From the sync
214
+ // runner's perspective this is a genuine "not found" — the
215
+ // caller doesn't have a `${slug}` to sync, even if some other
216
+ // user does.
217
+ throw new Error(`No entity in caller's namespace matches ${type}/${slug}. ` +
218
+ `If you expected to find one, verify you're signed in to the right HQ identity.`);
219
+ }
220
+ return fetchEntity(check.conflictingCompanyUid, config);
125
221
  }
126
222
  async function postVend(route, body, config) {
127
223
  const res = await fetch(`${config.apiUrl}${route}`, {
@@ -129,6 +225,7 @@ async function postVend(route, body, config) {
129
225
  headers: {
130
226
  "Content-Type": "application/json",
131
227
  Authorization: `Bearer ${await resolveAuthToken(config)}`,
228
+ ...buildClientHeaders(config.clientInfo),
132
229
  },
133
230
  body: JSON.stringify({ ...body, durationSeconds: DEFAULT_SESSION_DURATION_SECONDS }),
134
231
  });
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}