@uluops/ops-sdk 6.0.0 → 6.3.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 (45) hide show
  1. package/CHANGELOG.md +55 -1
  2. package/README.md +65 -1
  3. package/dist/client.d.ts +69 -78
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +70 -62
  6. package/dist/client.js.map +1 -1
  7. package/dist/config/constants.d.ts +3 -1
  8. package/dist/config/constants.d.ts.map +1 -1
  9. package/dist/config/constants.js +2 -0
  10. package/dist/config/constants.js.map +1 -1
  11. package/dist/config/generated-version.d.ts +1 -1
  12. package/dist/config/generated-version.js +1 -1
  13. package/dist/config/index.d.ts +1 -0
  14. package/dist/config/index.d.ts.map +1 -1
  15. package/dist/config/index.js +1 -0
  16. package/dist/config/index.js.map +1 -1
  17. package/dist/config/workspace-org.d.ts +77 -0
  18. package/dist/config/workspace-org.d.ts.map +1 -0
  19. package/dist/config/workspace-org.js +182 -0
  20. package/dist/config/workspace-org.js.map +1 -0
  21. package/dist/errors/errors.d.ts +47 -0
  22. package/dist/errors/errors.d.ts.map +1 -1
  23. package/dist/errors/errors.js +39 -0
  24. package/dist/errors/errors.js.map +1 -1
  25. package/dist/http/http-client.d.ts +46 -0
  26. package/dist/http/http-client.d.ts.map +1 -1
  27. package/dist/http/http-client.js +88 -5
  28. package/dist/http/http-client.js.map +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +1 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/types/index.d.ts +1 -0
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/index.js +1 -0
  36. package/dist/types/index.js.map +1 -1
  37. package/dist/types/org.d.ts +25 -0
  38. package/dist/types/org.d.ts.map +1 -0
  39. package/dist/types/org.js +2 -0
  40. package/dist/types/org.js.map +1 -0
  41. package/dist/types/response-schemas.d.ts +8 -0
  42. package/dist/types/response-schemas.d.ts.map +1 -1
  43. package/dist/types/response-schemas.js +35 -0
  44. package/dist/types/response-schemas.js.map +1 -1
  45. package/package.json +2 -2
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Workspace org resolution — D13 of the project-org-routing-and-rehome spec.
3
+ *
4
+ * "Where you are" decides which org a tracker call lands in. The unit that
5
+ * distinguishes personal work from work-org work on one machine is the
6
+ * CHECKOUT, not the process or the person: an env var or a profile leaks the
7
+ * last-set org into whatever repo is opened next (spec §2.0 S3/S7). So the
8
+ * default comes from a file in the tree, found by walking upward from `cwd`
9
+ * to the nearest `.uluops.json` — the way `.npmrc` and `.gitignore` are found.
10
+ *
11
+ * Precedence, highest first:
12
+ * 1. `explicit` — a per-call / per-invocation value (`--org`, an MCP tool's
13
+ * `org` argument);
14
+ * 2. the nearest `.uluops.json` above `cwd` whose `org` is set — including
15
+ * the reserved value `"personal"`, which STOPS the walk and
16
+ * resolves to no org (a personal repo cloned under a work
17
+ * tree carries it so the outer file does not win);
18
+ * 3. `ULUOPS_ORG_SLUG` in `env` — kept as the lowest fall-through for
19
+ * headless environments with no checkout;
20
+ * 4. undefined — no header; the API key holder's personal org.
21
+ *
22
+ * The file may carry ONLY `org`. Credentials, base URLs and profiles are
23
+ * refused, not ignored: a `.env` in cwd once retargeted the CLI's base URL,
24
+ * and the one thing that keeps that footgun from transferring is that this
25
+ * file cannot name a target or an identity. An org slug is server-relative —
26
+ * the same file against a different base URL names a different org or none —
27
+ * so callers that print "where it landed" should print the base URL beside
28
+ * the org.
29
+ *
30
+ * Runtime-agnostic: reads are synchronous `fs` calls, no `process.cwd()` is
31
+ * consulted unless the caller passes none, and `env` is injectable so tests
32
+ * never touch the real environment.
33
+ */
34
+ import { existsSync, readFileSync, statSync } from 'fs';
35
+ import { homedir } from 'os';
36
+ import { dirname, join, resolve, relative, isAbsolute } from 'path';
37
+ import { InputValidationError } from './validators.js';
38
+ import { ENV_VARS } from './constants.js';
39
+ /** The workspace file name, found by walking upward from cwd. */
40
+ export const WORKSPACE_ORG_FILE = '.uluops.json';
41
+ /** Reserved `org` value: stop the walk here and use the personal org. */
42
+ export const PERSONAL_ORG_SENTINEL = 'personal';
43
+ /** Same pattern as `OpsHttpClient`'s `orgSlug` — it is a header value. */
44
+ const ORG_SLUG_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,99}$/;
45
+ /**
46
+ * The ONLY keys a workspace file may carry. An allowlist, not a denylist:
47
+ * until 6.3.1 this was a list of ten forbidden names while the error message
48
+ * said "may carry only org" — `baseURL`, `apikey`, `token` and anything
49
+ * unlisted passed (security audit run #187, agentic-security-analyst F10).
50
+ * Inert then (only `org` was ever read), but the stated invariant must be
51
+ * the enforced one. `$schema` is allowed for editor tooling.
52
+ */
53
+ const ALLOWED_KEYS = new Set(['org', '$schema']);
54
+ function assertSlug(value, where) {
55
+ if (typeof value !== 'string' || !ORG_SLUG_PATTERN.test(value)) {
56
+ throw new InputValidationError(`Invalid org in ${where}: must be 1-100 alphanumeric characters, hyphens, or underscores`, [{ code: 'custom', path: ['org'], message: 'must be 1-100 alphanumeric characters, hyphens, or underscores' }]);
57
+ }
58
+ return value;
59
+ }
60
+ /** True when `child` is `parent` or lies beneath it. */
61
+ function isUnder(child, parent) {
62
+ const rel = relative(parent, child);
63
+ return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
64
+ }
65
+ /**
66
+ * Find the nearest `.uluops.json` at or above `cwd`, stopping at `stopAt`.
67
+ * With no `stopAt`, the walk is bounded by `home` (default `os.homedir()`)
68
+ * when `cwd` is under it: security audit run #187 (all four agents) — an
69
+ * unbounded walk makes a file at `/`, `/Users` or a container image root
70
+ * the silent default for every checkout beneath it, which is the
71
+ * single-default leak D13 exists to remove, one layer down. Outside the
72
+ * home directory the walk still reaches the filesystem root; the ownership
73
+ * check in `readWorkspaceOrgFile` is the guard there.
74
+ */
75
+ export function findWorkspaceOrgFile(cwd, stopAt, home = homedir()) {
76
+ let dir = resolve(cwd);
77
+ const homeAbs = resolve(home);
78
+ const stop = stopAt !== undefined ? resolve(stopAt) : isUnder(dir, homeAbs) ? homeAbs : undefined;
79
+ for (;;) {
80
+ const candidate = join(dir, WORKSPACE_ORG_FILE);
81
+ if (existsSync(candidate))
82
+ return candidate;
83
+ if (stop !== undefined && dir === stop)
84
+ return undefined;
85
+ const parent = dirname(dir);
86
+ if (parent === dir)
87
+ return undefined;
88
+ dir = parent;
89
+ }
90
+ }
91
+ /**
92
+ * Read one workspace file. Returns the `org` it declares (`"personal"` is
93
+ * returned verbatim — the caller decides what it means), or `undefined` when
94
+ * the file exists but declares no `org`.
95
+ *
96
+ * A file not owned by `uid` (default: the running user) is REFUSED, not
97
+ * skipped: a shared parent directory another user can write to is the
98
+ * planting vector (run #187, circumvention A2), and a silently skipped file
99
+ * is a silently wrong org. Pass `uid: undefined` explicitly only on
100
+ * platforms with no uids.
101
+ *
102
+ * @throws {InputValidationError} on unreadable JSON, a non-object body, a
103
+ * key other than `org`/`$schema`, a file owned by another user, or an
104
+ * `org` that is not a valid slug (other than the sentinel).
105
+ */
106
+ export function readWorkspaceOrgFile(path, uid = process.getuid?.()) {
107
+ if (uid !== undefined) {
108
+ let ownerUid;
109
+ try {
110
+ ownerUid = statSync(path).uid;
111
+ }
112
+ catch (err) {
113
+ throw new InputValidationError(`Unreadable ${WORKSPACE_ORG_FILE} at ${path}: ${err instanceof Error ? err.message : String(err)}`, [{ code: 'custom', path: [], message: 'cannot stat' }]);
114
+ }
115
+ if (ownerUid !== uid) {
116
+ throw new InputValidationError(`Refusing ${WORKSPACE_ORG_FILE} at ${path}: owned by uid ${String(ownerUid)}, not you (uid ${String(uid)}). ` +
117
+ 'A workspace file decides which org your writes land in; only your own may answer.', [{ code: 'custom', path: [], message: 'not owned by the running user' }]);
118
+ }
119
+ }
120
+ let parsed;
121
+ try {
122
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
123
+ }
124
+ catch (err) {
125
+ throw new InputValidationError(`Unreadable ${WORKSPACE_ORG_FILE} at ${path}: ${err instanceof Error ? err.message : String(err)}`, [{ code: 'custom', path: [], message: 'invalid JSON' }]);
126
+ }
127
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
128
+ throw new InputValidationError(`Invalid ${WORKSPACE_ORG_FILE} at ${path}: expected an object`, [{ code: 'custom', path: [], message: 'expected an object' }]);
129
+ }
130
+ const body = parsed;
131
+ const forbidden = Object.keys(body).filter((k) => !ALLOWED_KEYS.has(k));
132
+ if (forbidden.length > 0) {
133
+ throw new InputValidationError(`Refusing ${WORKSPACE_ORG_FILE} at ${path}: it may carry only "org"; found ${forbidden.map((k) => `"${k}"`).join(', ')}. ` +
134
+ 'Credentials and targets belong in ~/.uluops/credentials.json or the environment, never in the checkout.', forbidden.map((k) => ({ code: 'custom', path: [k], message: 'forbidden in the workspace file' })));
135
+ }
136
+ if (!('org' in body) || body['org'] === undefined || body['org'] === null)
137
+ return undefined;
138
+ if (body['org'] === PERSONAL_ORG_SENTINEL)
139
+ return PERSONAL_ORG_SENTINEL;
140
+ return assertSlug(body['org'], `${WORKSPACE_ORG_FILE} at ${path}`);
141
+ }
142
+ /**
143
+ * Resolve the org for a call: explicit > nearest workspace file > env > personal.
144
+ *
145
+ * Never returns the sentinel — a `"personal"` file yields `{ org: undefined,
146
+ * source: 'personal', path }`. Never consults a cache; the walk is a handful
147
+ * of `existsSync` calls and a resolver that remembered would reintroduce the
148
+ * process-level default the spec rejects.
149
+ *
150
+ * @throws {InputValidationError} for an invalid `explicit` slug, or a
151
+ * malformed / forbidden workspace file (loud on purpose: a silently ignored
152
+ * file is a silently wrong org).
153
+ */
154
+ export function resolveWorkspaceOrg(options = {}) {
155
+ if (options.explicit !== undefined) {
156
+ // The sentinel is honoured on the explicit path too (6.3.1, run #187
157
+ // circumvention A3): a caller told "omit org for your personal org" will
158
+ // sometimes write `org: personal`, and until now that went to the wire
159
+ // as a slug (404, or a real org if anyone registers the name).
160
+ if (options.explicit === PERSONAL_ORG_SENTINEL)
161
+ return { org: undefined, source: 'explicit' };
162
+ return { org: assertSlug(options.explicit, 'explicit org'), source: 'explicit' };
163
+ }
164
+ const cwd = options.cwd ?? process.cwd();
165
+ const path = findWorkspaceOrgFile(cwd, options.stopAt, options.home);
166
+ if (path !== undefined) {
167
+ const declared = readWorkspaceOrgFile(path, 'uid' in options ? options.uid : process.getuid?.());
168
+ if (declared === PERSONAL_ORG_SENTINEL)
169
+ return { org: undefined, source: 'personal', path };
170
+ if (declared !== undefined)
171
+ return { org: declared, source: 'workspace', path };
172
+ // A file with no `org` is not an answer; keep walking would be surprising
173
+ // ("the nearest file wins" is the rule), so fall through to env.
174
+ }
175
+ const env = options.env ?? process.env;
176
+ const fromEnv = env[ENV_VARS.ORG_SLUG];
177
+ if (fromEnv !== undefined && fromEnv !== '') {
178
+ return { org: assertSlug(fromEnv, `env ${ENV_VARS.ORG_SLUG}`), source: 'env' };
179
+ }
180
+ return { org: undefined, source: 'personal' };
181
+ }
182
+ //# sourceMappingURL=workspace-org.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-org.js","sourceRoot":"","sources":["../../src/config/workspace-org.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,iEAAiE;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAEjD,yEAAyE;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAC;AAEhD,0EAA0E;AAC1E,MAAM,gBAAgB,GAAG,kCAAkC,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;AAqCjD,SAAS,UAAU,CAAC,KAAc,EAAE,KAAa;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,oBAAoB,CAC5B,kBAAkB,KAAK,kEAAkE,EACzF,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,gEAAgE,EAAE,CAAC,CAC/G,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wDAAwD;AACxD,SAAS,OAAO,CAAC,KAAa,EAAE,MAAc;IAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpC,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAE,MAAe,EAAE,OAAe,OAAO,EAAE;IACzF,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACvB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAClG,SAAS,CAAC;QACR,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;QAChD,IAAI,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QAC5C,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACzD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,SAAS,CAAC;QACrC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,MAA0B,OAAO,CAAC,MAAM,EAAE,EAAE;IAC7F,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,IAAI,QAAgB,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC;QAChC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,oBAAoB,CAC5B,cAAc,kBAAkB,OAAO,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAClG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CACvD,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;YACrB,MAAM,IAAI,oBAAoB,CAC5B,YAAY,kBAAkB,OAAO,IAAI,kBAAkB,MAAM,CAAC,QAAQ,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,KAAK;gBAC7G,mFAAmF,EACnF,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC,CACzE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAC;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,oBAAoB,CAC5B,cAAc,kBAAkB,OAAO,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAClG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CACxD,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,oBAAoB,CAC5B,WAAW,kBAAkB,OAAO,IAAI,sBAAsB,EAC9D,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAC9D,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,MAAiC,CAAC;IAC/C,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,oBAAoB,CAC5B,YAAY,kBAAkB,OAAO,IAAI,oCAAoC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC1H,yGAAyG,EACzG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,iCAAiC,EAAE,CAAC,CAAC,CAC3G,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5F,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,qBAAqB;QAAE,OAAO,qBAAqB,CAAC;IACxE,OAAO,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,kBAAkB,OAAO,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAsC,EAAE;IAC1E,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACnC,qEAAqE;QACrE,yEAAyE;QACzE,uEAAuE;QACvE,+DAA+D;QAC/D,IAAI,OAAO,CAAC,QAAQ,KAAK,qBAAqB;YAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QAC9F,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACnF,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,EAAE,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACjG,IAAI,QAAQ,KAAK,qBAAqB;YAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QAC5F,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QAChF,0EAA0E;QAC1E,iEAAiE;IACnE,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QAC5C,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,OAAO,EAAE,OAAO,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACjF,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAChD,CAAC"}
@@ -53,4 +53,51 @@ export declare class AnalysisEchoMismatchError extends Error {
53
53
  analysisWrite: unknown;
54
54
  });
55
55
  }
56
+ import { SdkApiError as _SdkApiError } from '@uluops/sdk-core/errors';
57
+ /**
58
+ * 403 — the caller is a member of the org but below the write floor
59
+ * (`publisher`). On tracker writes the API rewrites the body: `details.applied`
60
+ * is `false` and the message forbids retrying without `org`. DO NOT retry the
61
+ * same call without an org: an org-less retry does not "fall back", it files
62
+ * the work in the caller's PERSONAL org (spec §3.1, S6).
63
+ */
64
+ export declare const INSUFFICIENT_ORG_ROLE: "INSUFFICIENT_ORG_ROLE";
65
+ /** 403 — the caller is not a member of the org named by `org` / `orgSlug`, or the key is bound to a different org. */
66
+ export declare const ORG_ACCESS_DENIED: "ORG_ACCESS_DENIED";
67
+ /**
68
+ * 410 — the project this name once denoted in this org has been RE-HOMED to
69
+ * another org (spec D14). `details.target_org.slug` is where it lives now:
70
+ * pass it as `org` and the same call succeeds. Do not create a new project
71
+ * at the old address — the tombstone exists precisely to refuse that fork.
72
+ */
73
+ export declare const PROJECT_REHOMED: "PROJECT_REHOMED";
74
+ /** `details` on an `INSUFFICIENT_ORG_ROLE` rejection from a tracker write. */
75
+ export interface InsufficientOrgRoleDetails {
76
+ currentRole?: string;
77
+ requiredRole?: string;
78
+ /** Present on tracker writes: the org the write was refused in. */
79
+ orgSlug?: string | null;
80
+ /** Present on tracker writes: always `false` — nothing was applied. */
81
+ applied?: boolean;
82
+ }
83
+ /** `details` on a `PROJECT_REHOMED` rejection (spec §4.4a). */
84
+ export interface ProjectRehomedDetails {
85
+ project_id: string;
86
+ target_org: {
87
+ id: string;
88
+ slug: string;
89
+ };
90
+ rehomed_at?: string;
91
+ reason?: string | null;
92
+ }
93
+ /** Type guard: the org write floor refused this call (403 `INSUFFICIENT_ORG_ROLE`). Terminal — do not retry without `org`. */
94
+ export declare function isInsufficientOrgRoleError(err: unknown): err is _SdkApiError & {
95
+ details?: InsufficientOrgRoleDetails;
96
+ };
97
+ /** Type guard: not a member of the named org, or the key is bound elsewhere (403 `ORG_ACCESS_DENIED`). Terminal. */
98
+ export declare function isOrgAccessDeniedError(err: unknown): err is _SdkApiError;
99
+ /** Type guard: the project was re-homed; `err.details.target_org.slug` is the org to pass (410 `PROJECT_REHOMED`). */
100
+ export declare function isProjectRehomedError(err: unknown): err is _SdkApiError & {
101
+ details: ProjectRehomedDetails;
102
+ };
56
103
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,WAAW,IAAI,WAAW,EAC1B,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,uBAAuB,EACvB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,IAAI,aAAa,EAC9B,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,GAAG,uBAAuB,CAAC;IAC5E,kFAAkF;IAClF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,yEAAyE;IACzE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;gBAEpB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QACpC,MAAM,EAAE,cAAc,GAAG,eAAe,GAAG,uBAAuB,CAAC;QACnE,kBAAkB,EAAE,MAAM,CAAC;QAC3B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,GAAG,EAAE,OAAO,CAAC;QACb,aAAa,EAAE,OAAO,CAAC;KACxB;CASF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,WAAW,IAAI,WAAW,EAC1B,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,uBAAuB,EACvB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,IAAI,aAAa,EAC9B,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,GAAG,uBAAuB,CAAC;IAC5E,kFAAkF;IAClF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,0EAA0E;IAC1E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,yEAAyE;IACzE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;gBAEpB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QACpC,MAAM,EAAE,cAAc,GAAG,eAAe,GAAG,uBAAuB,CAAC;QACnE,kBAAkB,EAAE,MAAM,CAAC;QAC3B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,GAAG,EAAE,OAAO,CAAC;QACb,aAAa,EAAE,OAAO,CAAC;KACxB;CASF;AAMD,OAAO,EAAE,WAAW,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEtE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,EAAG,uBAAgC,CAAC;AACtE,sHAAsH;AACtH,eAAO,MAAM,iBAAiB,EAAG,mBAA4B,CAAC;AAC9D;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAG,iBAA0B,CAAC;AAE1D,8EAA8E;AAC9E,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,+DAA+D;AAC/D,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAMD,8HAA8H;AAC9H,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,YAAY,GAAG;IAAE,OAAO,CAAC,EAAE,0BAA0B,CAAA;CAAE,CAEvH;AAED,oHAAoH;AACpH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,YAAY,CAExE;AAED,sHAAsH;AACtH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,YAAY,GAAG;IAAE,OAAO,EAAE,qBAAqB,CAAA;CAAE,CAI5G"}
@@ -56,4 +56,43 @@ export class AnalysisEchoMismatchError extends Error {
56
56
  this.analysisWrite = details.analysisWrite;
57
57
  }
58
58
  }
59
+ // ---------------------------------------------------------------------------
60
+ // Org routing (project-org-routing-and-rehome spec, 6.2.0)
61
+ // ---------------------------------------------------------------------------
62
+ import { SdkApiError as _SdkApiError } from '@uluops/sdk-core/errors';
63
+ /**
64
+ * 403 — the caller is a member of the org but below the write floor
65
+ * (`publisher`). On tracker writes the API rewrites the body: `details.applied`
66
+ * is `false` and the message forbids retrying without `org`. DO NOT retry the
67
+ * same call without an org: an org-less retry does not "fall back", it files
68
+ * the work in the caller's PERSONAL org (spec §3.1, S6).
69
+ */
70
+ export const INSUFFICIENT_ORG_ROLE = 'INSUFFICIENT_ORG_ROLE';
71
+ /** 403 — the caller is not a member of the org named by `org` / `orgSlug`, or the key is bound to a different org. */
72
+ export const ORG_ACCESS_DENIED = 'ORG_ACCESS_DENIED';
73
+ /**
74
+ * 410 — the project this name once denoted in this org has been RE-HOMED to
75
+ * another org (spec D14). `details.target_org.slug` is where it lives now:
76
+ * pass it as `org` and the same call succeeds. Do not create a new project
77
+ * at the old address — the tombstone exists precisely to refuse that fork.
78
+ */
79
+ export const PROJECT_REHOMED = 'PROJECT_REHOMED';
80
+ function hasCode(err, code) {
81
+ return err instanceof _SdkApiError && err.code === code;
82
+ }
83
+ /** Type guard: the org write floor refused this call (403 `INSUFFICIENT_ORG_ROLE`). Terminal — do not retry without `org`. */
84
+ export function isInsufficientOrgRoleError(err) {
85
+ return hasCode(err, INSUFFICIENT_ORG_ROLE);
86
+ }
87
+ /** Type guard: not a member of the named org, or the key is bound elsewhere (403 `ORG_ACCESS_DENIED`). Terminal. */
88
+ export function isOrgAccessDeniedError(err) {
89
+ return hasCode(err, ORG_ACCESS_DENIED);
90
+ }
91
+ /** Type guard: the project was re-homed; `err.details.target_org.slug` is the org to pass (410 `PROJECT_REHOMED`). */
92
+ export function isProjectRehomedError(err) {
93
+ if (!hasCode(err, PROJECT_REHOMED))
94
+ return false;
95
+ const d = err.details;
96
+ return typeof d?.target_org?.slug === 'string' && typeof d.project_id === 'string';
97
+ }
59
98
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,WAAW,IAAI,WAAW,EAC1B,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,uBAAuB,EACvB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,IAAI,aAAa,EAC9B,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,sBAAsB;AACtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD,mFAAmF;IAC1E,MAAM,CAA6D;IAC5E,kFAAkF;IACzE,kBAAkB,CAAS;IACpC,0EAA0E;IACjE,gBAAgB,CAAgB;IACzC,yEAAyE;IAChE,GAAG,CAAU;IACtB,qEAAqE;IAC5D,aAAa,CAAU;IAEhC,YAAY,OAAe,EAAE,OAM5B;QACC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;QACrD,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;QACjD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC7C,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,WAAW,IAAI,WAAW,EAC1B,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,uBAAuB,EACvB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,IAAI,aAAa,EAC9B,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,sBAAsB;AACtB,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAClD,mFAAmF;IAC1E,MAAM,CAA6D;IAC5E,kFAAkF;IACzE,kBAAkB,CAAS;IACpC,0EAA0E;IACjE,gBAAgB,CAAgB;IACzC,yEAAyE;IAChE,GAAG,CAAU;IACtB,qEAAqE;IAC5D,aAAa,CAAU;IAEhC,YAAY,OAAe,EAAE,OAM5B;QACC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;QACrD,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;QACjD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC7C,CAAC;CACF;AAED,8EAA8E;AAC9E,2DAA2D;AAC3D,8EAA8E;AAE9E,OAAO,EAAE,WAAW,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAgC,CAAC;AACtE,sHAAsH;AACtH,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAA4B,CAAC;AAC9D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,iBAA0B,CAAC;AAoB1D,SAAS,OAAO,CAAC,GAAY,EAAE,IAAY;IACzC,OAAO,GAAG,YAAY,YAAY,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;AAC1D,CAAC;AAED,8HAA8H;AAC9H,MAAM,UAAU,0BAA0B,CAAC,GAAY;IACrD,OAAO,OAAO,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;AAC7C,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,OAAO,OAAO,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;AACzC,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC;QAAE,OAAO,KAAK,CAAC;IACjD,MAAM,CAAC,GAAG,GAAG,CAAC,OAAqD,CAAC;IACpE,OAAO,OAAO,CAAC,EAAE,UAAU,EAAE,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC;AACrF,CAAC"}
@@ -68,6 +68,10 @@ export interface HttpClientConfig {
68
68
  /** Org slug for multi-tenancy — sets X-Org-Slug header on all requests */
69
69
  orgSlug?: string;
70
70
  }
71
+ /** Alphanumeric + hyphens/underscores, 1–100 chars */
72
+ export declare const ORG_SLUG_PATTERN: RegExp;
73
+ /** The header the tracker's org-context middleware reads (after `:slug` and `X-Org-Id`). */
74
+ export declare const ORG_SLUG_HEADER = "X-Org-Slug";
71
75
  /**
72
76
  * HTTP client for ops-uluops-api using native fetch
73
77
  */
@@ -82,6 +86,48 @@ export declare class OpsHttpClient extends HttpClient {
82
86
  * @throws {InputValidationError} If `orgSlug` is present but not 1–100
83
87
  * alphanumeric characters, hyphens, or underscores.
84
88
  */
89
+ /**
90
+ * Per-call org override (project-org-routing-and-rehome spec §3.2, 6.2.0).
91
+ * Set only on views minted by {@link withOrg}; the root client never has one.
92
+ */
93
+ private orgOverride?;
94
+ /**
95
+ * Set instead of throwing when `withOrg` is handed an invalid slug: every
96
+ * method on OpsClient returns a Promise, so the caller's `.catch`/`await`
97
+ * must see the error — a synchronous throw from inside a non-async arrow
98
+ * would escape both. Surfaced as a rejection of the view's first request.
99
+ */
100
+ private orgInvalid?;
85
101
  constructor(config?: HttpClientConfig);
102
+ /**
103
+ * A view of this client whose every request carries `X-Org-Slug: <org>`,
104
+ * overriding the constructor-level `orgSlug` (per-request headers win over
105
+ * `defaultHeaders` in sdk-core). The view shares this client's auth
106
+ * strategy, fetch adapter and configuration — it is the same client, seen
107
+ * through one org — so a session installed on the root is honoured here.
108
+ *
109
+ * Precedence on the wire, lowest to highest: personal org (no header) <
110
+ * constructor `orgSlug` < this per-call `org`. An API key BOUND to an org
111
+ * ignores both headers and 403s (`ORG_ACCESS_DENIED`) if they name a
112
+ * different org — that is the platform's rule, surfaced verbatim.
113
+ *
114
+ * An invalid `org` (not 1–100 alphanumeric characters, hyphens, or
115
+ * underscores — the same pattern as `orgSlug`; it is a header value, so
116
+ * CRLF/whitespace can never reach the wire) does not throw here: the view's
117
+ * requests REJECT with `InputValidationError` before anything is sent, so
118
+ * `await client.projects.list({ org })` fails the way every other input
119
+ * error does.
120
+ */
121
+ withOrg(org: string): OpsHttpClient;
122
+ /** The org this view is scoped to, or `undefined` on the root client. */
123
+ get scopedOrg(): string | undefined;
124
+ request<T>(method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE', endpoint: string, data?: object, options?: {
125
+ params?: object;
126
+ retries?: number;
127
+ retryMutations?: boolean;
128
+ headers?: Record<string, string>;
129
+ skipAuth?: boolean;
130
+ rawEnvelope?: boolean;
131
+ }): Promise<T>;
86
132
  }
87
133
  //# sourceMappingURL=http-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/http/http-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAKlE,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAKzF,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,QAAQ,EACR,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,KAAK,WAAW,IAAI,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAGjE;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,GAAG,SAAS,CAiBrE;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,4EAA4E;IAC5E,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,kBAAkB,EAAE,aAAa,KAAK,IAAI,CAAC;IAClF,oFAAoF;IACpF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClG;;;;OAIG;IACH,eAAe,CAAC,EAAE,oBAAoB,CAAC;IACvC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAKD;;GAEG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C;;;;;;;;;OASG;gBACS,MAAM,GAAE,gBAAqB;CAkB1C"}
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/http/http-client.ts"],"names":[],"mappings":"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"}
@@ -47,7 +47,14 @@ export function toApiQuery(query) {
47
47
  return Object.keys(params).length > 0 ? params : undefined;
48
48
  }
49
49
  /** Alphanumeric + hyphens/underscores, 1–100 chars */
50
- const ORG_SLUG_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,99}$/;
50
+ export const ORG_SLUG_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,99}$/;
51
+ /** The header the tracker's org-context middleware reads (after `:slug` and `X-Org-Id`). */
52
+ export const ORG_SLUG_HEADER = 'X-Org-Slug';
53
+ function assertOrgSlug(value, path) {
54
+ if (!ORG_SLUG_PATTERN.test(value)) {
55
+ throw new InputValidationError(`Invalid ${path}: must be 1-100 alphanumeric characters, hyphens, or underscores`, [{ code: 'custom', path: [path], message: 'must be 1-100 alphanumeric characters, hyphens, or underscores' }]);
56
+ }
57
+ }
51
58
  /**
52
59
  * HTTP client for ops-uluops-api using native fetch
53
60
  */
@@ -62,10 +69,21 @@ export class OpsHttpClient extends HttpClient {
62
69
  * @throws {InputValidationError} If `orgSlug` is present but not 1–100
63
70
  * alphanumeric characters, hyphens, or underscores.
64
71
  */
72
+ /**
73
+ * Per-call org override (project-org-routing-and-rehome spec §3.2, 6.2.0).
74
+ * Set only on views minted by {@link withOrg}; the root client never has one.
75
+ */
76
+ orgOverride;
77
+ /**
78
+ * Set instead of throwing when `withOrg` is handed an invalid slug: every
79
+ * method on OpsClient returns a Promise, so the caller's `.catch`/`await`
80
+ * must see the error — a synchronous throw from inside a non-async arrow
81
+ * would escape both. Surfaced as a rejection of the view's first request.
82
+ */
83
+ orgInvalid;
65
84
  constructor(config = {}) {
66
- if (config.orgSlug && !ORG_SLUG_PATTERN.test(config.orgSlug)) {
67
- throw new InputValidationError('Invalid orgSlug: must be 1-100 alphanumeric characters, hyphens, or underscores', [{ code: 'custom', path: ['orgSlug'], message: 'must be 1-100 alphanumeric characters, hyphens, or underscores' }]);
68
- }
85
+ if (config.orgSlug)
86
+ assertOrgSlug(config.orgSlug, 'orgSlug');
69
87
  super({
70
88
  ...config,
71
89
  baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
@@ -73,9 +91,74 @@ export class OpsHttpClient extends HttpClient {
73
91
  sdkVersion: SDK_VERSION,
74
92
  loggerPrefix: '[ops-sdk:http]',
75
93
  defaultHeaders: {
76
- ...(config.orgSlug ? { 'X-Org-Slug': config.orgSlug } : {}),
94
+ ...(config.orgSlug ? { [ORG_SLUG_HEADER]: config.orgSlug } : {}),
77
95
  },
78
96
  });
79
97
  }
98
+ /**
99
+ * A view of this client whose every request carries `X-Org-Slug: <org>`,
100
+ * overriding the constructor-level `orgSlug` (per-request headers win over
101
+ * `defaultHeaders` in sdk-core). The view shares this client's auth
102
+ * strategy, fetch adapter and configuration — it is the same client, seen
103
+ * through one org — so a session installed on the root is honoured here.
104
+ *
105
+ * Precedence on the wire, lowest to highest: personal org (no header) <
106
+ * constructor `orgSlug` < this per-call `org`. An API key BOUND to an org
107
+ * ignores both headers and 403s (`ORG_ACCESS_DENIED`) if they name a
108
+ * different org — that is the platform's rule, surfaced verbatim.
109
+ *
110
+ * An invalid `org` (not 1–100 alphanumeric characters, hyphens, or
111
+ * underscores — the same pattern as `orgSlug`; it is a header value, so
112
+ * CRLF/whitespace can never reach the wire) does not throw here: the view's
113
+ * requests REJECT with `InputValidationError` before anything is sent, so
114
+ * `await client.projects.list({ org })` fails the way every other input
115
+ * error does.
116
+ */
117
+ withOrg(org) {
118
+ // `"personal"` is the reserved no-org value (the same sentinel the
119
+ // workspace file uses): the root client, no per-call header. It does not
120
+ // cancel a constructor-level `orgSlug` — that is a defaultHeader sdk-core
121
+ // always sends; callers that need "personal despite a constructor org"
122
+ // construct without one (the tracker MCP has none since 2.1.0).
123
+ if (org === 'personal')
124
+ return this;
125
+ // Prototype-chained view: reads (auth strategy, adapter, config) fall
126
+ // through to this instance; only the override fields are set on the view.
127
+ // sdk-core's verbs (get/post/patch/put/delete) all delegate to `request`,
128
+ // so the override below covers every operation without touching them.
129
+ const view = Object.create(this);
130
+ try {
131
+ assertOrgSlug(org, 'org');
132
+ view.orgOverride = org;
133
+ }
134
+ catch (err) {
135
+ view.orgInvalid = err;
136
+ }
137
+ return view;
138
+ }
139
+ /** The org this view is scoped to, or `undefined` on the root client. */
140
+ get scopedOrg() {
141
+ return this.orgOverride;
142
+ }
143
+ request(method, endpoint, data, options) {
144
+ if (this.orgInvalid)
145
+ return Promise.reject(this.orgInvalid);
146
+ if (this.orgOverride === undefined)
147
+ return super.request(method, endpoint, data, options);
148
+ // The per-call org is set LAST so a caller-supplied header cannot outrank
149
+ // it, and an org header in `options.headers` is refused outright: until
150
+ // 6.3.1 the spread order let `options.headers` win, and `X-Org-Id`
151
+ // outranks `X-Org-Slug` on the server, so a smuggled header would have
152
+ // silently redirected a scoped call (run #187, circumvention A7 /
153
+ // trust-boundary F7 — no live caller did this; the invariant held by luck).
154
+ const smuggled = Object.keys(options?.headers ?? {}).find((h) => /^x-org-(slug|id)$/i.test(h));
155
+ if (smuggled !== undefined) {
156
+ return Promise.reject(new InputValidationError(`Refusing request header ${smuggled} on an org-scoped call: the per-call org (${this.orgOverride}) is the only org channel`, [{ code: 'custom', path: ['headers', smuggled], message: 'org headers may not be set per request on a scoped view' }]));
157
+ }
158
+ return super.request(method, endpoint, data, {
159
+ ...options,
160
+ headers: { ...(options?.headers ?? {}), [ORG_SLUG_HEADER]: this.orgOverride },
161
+ });
162
+ }
80
163
  }
81
164
  //# sourceMappingURL=http-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/http/http-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,2EAA2E;AAC3E,OAAO,EAAE,OAAO,EAA0C,MAAM,wBAAwB,CAAC;AAiBzF,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,KAAyB;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;YAC3G,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChC,qEAAqE;YACrE,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;YACrF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;QACD,mEAAmE;IACrE,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC;AAyCD,sDAAsD;AACtD,MAAM,gBAAgB,GAAG,kCAAkC,CAAC;AAE5D;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IAC3C;;;;;;;;;OASG;IACH,YAAY,SAA2B,EAAE;QACvC,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7D,MAAM,IAAI,oBAAoB,CAC5B,iFAAiF,EACjF,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,gEAAgE,EAAE,CAAC,CACnH,CAAC;QACJ,CAAC;QACD,KAAK,CAAC;YACJ,GAAG,MAAM;YACT,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,gBAAgB;YAC3C,OAAO,EAAE,iBAAiB;YAC1B,UAAU,EAAE,WAAW;YACvB,YAAY,EAAE,gBAAgB;YAC9B,cAAc,EAAE;gBACd,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC5D;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/http/http-client.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -16,4 +16,5 @@ export * from './types/index.js';
16
16
  export { isValidMetric, ANALYTICS_METRICS, type AnalyticsMetric } from './operations/analytics.js';
17
17
  export { loadCredentials, loadConfig, loadEnvFiles, type Credentials, type SdkConfig } from './config/loaders.js';
18
18
  export { DEFAULT_BASE_URL, API_KEY_PREFIX, ENV_VARS } from './config/constants.js';
19
+ export { resolveWorkspaceOrg, findWorkspaceOrgFile, readWorkspaceOrgFile, WORKSPACE_ORG_FILE, PERSONAL_ORG_SENTINEL, type WorkspaceOrgResolution, type WorkspaceOrgSource, type ResolveWorkspaceOrgOptions, } from './config/workspace-org.js';
19
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACrH,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,QAAQ,EACR,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG7H,cAAc,oBAAoB,CAAC;AAGnC,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGnG,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAClH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,KAAK,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACrH,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,QAAQ,EACR,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG7H,cAAc,oBAAoB,CAAC;AAGnC,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAGnG,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAClH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,GAChC,MAAM,2BAA2B,CAAC"}
package/dist/index.js CHANGED
@@ -21,4 +21,5 @@ export { isValidMetric, ANALYTICS_METRICS } from './operations/analytics.js';
21
21
  // Config — credential loading from env vars, .env files, and ~/.uluops/credentials.json
22
22
  export { loadCredentials, loadConfig, loadEnvFiles } from './config/loaders.js';
23
23
  export { DEFAULT_BASE_URL, API_KEY_PREFIX, ENV_VARS } from './config/constants.js';
24
+ export { resolveWorkspaceOrg, findWorkspaceOrgFile, readWorkspaceOrgFile, WORKSPACE_ORG_FILE, PERSONAL_ORG_SENTINEL, } from './config/workspace-org.js';
24
25
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,2DAA2D;AAC3D,OAAO,EAAE,SAAS,EAAwB,MAAM,aAAa,CAAC;AAE9D,oFAAoF;AACpF,OAAO,EAAE,aAAa,EAAiE,MAAM,uBAAuB,CAAC;AAWrH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAsC,MAAM,yBAAyB,CAAC;AAE7H,0EAA0E;AAC1E,cAAc,oBAAoB,CAAC;AAEnC,gEAAgE;AAChE,cAAc,kBAAkB,CAAC;AAEjC,mEAAmE;AACnE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAwB,MAAM,2BAA2B,CAAC;AAEnG,wFAAwF;AACxF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAoC,MAAM,qBAAqB,CAAC;AAClH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,2DAA2D;AAC3D,OAAO,EAAE,SAAS,EAAwB,MAAM,aAAa,CAAC;AAE9D,oFAAoF;AACpF,OAAO,EAAE,aAAa,EAAiE,MAAM,uBAAuB,CAAC;AAWrH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAsC,MAAM,yBAAyB,CAAC;AAE7H,0EAA0E;AAC1E,cAAc,oBAAoB,CAAC;AAEnC,gEAAgE;AAChE,cAAc,kBAAkB,CAAC;AAEjC,mEAAmE;AACnE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAwB,MAAM,2BAA2B,CAAC;AAEnG,wFAAwF;AACxF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAoC,MAAM,qBAAqB,CAAC;AAClH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,GAItB,MAAM,2BAA2B,CAAC"}
@@ -1,5 +1,6 @@
1
1
  export * from './enums.js';
2
2
  export * from './auth.js';
3
+ export * from './org.js';
3
4
  export * from './projects.js';
4
5
  export * from './runs.js';
5
6
  export * from './issues.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAG3B,cAAc,WAAW,CAAC;AAG1B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAG3B,cAAc,WAAW,CAAC;AAG1B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,cAAc,CAAC"}
@@ -3,6 +3,7 @@ export * from './enums.js';
3
3
  // Auth types
4
4
  export * from './auth.js';
5
5
  // Domain types
6
+ export * from './org.js';
6
7
  export * from './projects.js';
7
8
  export * from './runs.js';
8
9
  export * from './issues.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,aAAa;AACb,cAAc,WAAW,CAAC;AAE1B,eAAe;AACf,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAE/B,iBAAiB;AACjB,cAAc,gBAAgB,CAAC;AAE/B,iCAAiC;AACjC,cAAc,cAAc,CAAC;AAE7B,qGAAqG"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,aAAa;AACb,cAAc,WAAW,CAAC;AAE1B,eAAe;AACf,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAE/B,iBAAiB;AACjB,cAAc,gBAAgB,CAAC;AAE/B,iCAAiC;AACjC,cAAc,cAAc,CAAC;AAE7B,qGAAqG"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Org scoping for a single call (project-org-routing-and-rehome spec §3.2, D12).
3
+ *
4
+ * Every project, run, issue and analytics operation accepts this as its last
5
+ * argument. `org` is the target org's slug and becomes the `X-Org-Slug`
6
+ * header on that one request, overriding the client-level `orgSlug`.
7
+ *
8
+ * Omit it to use the client's `orgSlug`, or — with neither — the key-holder's
9
+ * personal org. The API never infers an org from a project name (spec D2):
10
+ * a request that names no org creates or targets the PERSONAL project of that
11
+ * name, even when a work org has a project by the same name. Name the org.
12
+ */
13
+ export interface OrgScopedOptions {
14
+ /** Org slug (1–100 chars: alphanumeric, hyphen, underscore). */
15
+ org?: string;
16
+ }
17
+ /** Run write/preview options: client-side validation escape hatch + org scope. */
18
+ export interface RunCallOptions extends OrgScopedOptions {
19
+ /**
20
+ * Skip the SDK's input validation. A convenience escape for pre-validated
21
+ * callers (MCP, autosave hooks); the server validates regardless.
22
+ */
23
+ _skipClientValidation?: boolean;
24
+ }
25
+ //# sourceMappingURL=org.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org.d.ts","sourceRoot":"","sources":["../../src/types/org.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,kFAAkF;AAClF,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=org.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org.js","sourceRoot":"","sources":["../../src/types/org.ts"],"names":[],"mappings":""}