@oxy.so/core 1.7.3 → 1.8.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.
@@ -145,9 +145,14 @@ class OxyInferenceClient {
145
145
  * both see the PUBLIC catalogue; only an internal/system application's
146
146
  * service token sees internal-only routes.
147
147
  *
148
- * **`[]` is a normal answer**, and is the only answer today: the catalogue
149
- * is populated by operators, and a route is not publicly exposed until
150
- * somebody has reviewed the right to resell it.
148
+ * **`[]` is a normal answer**: a route is not publicly exposed until
149
+ * somebody has reviewed the right to resell it. An official application's
150
+ * credential also sees the internal catalogue Oxy keeps in step with
151
+ * Kaana automatically.
152
+ *
153
+ * Each entry's `capabilities.reasoningEfforts` lists the efforts a request
154
+ * may name in `reasoning.effort` (empty: none), and `releasedAt`, when
155
+ * present, is the date the upstream provider published the model.
151
156
  */
152
157
  async listModels(options = {}) {
153
158
  const body = await __classPrivateFieldGet(this, _OxyInferenceClient_instances, "m", _OxyInferenceClient_request).call(this, 'GET', '/v1/models', { ...(options.signal === undefined ? {} : { signal: options.signal }) });
@@ -299,10 +299,13 @@ function OxyServicesAuthMixin(Base) {
299
299
  /**
300
300
  * Whether this process can prove what it is (ADR 0026).
301
301
  *
302
- * The internal package subpath selects a client implementation for native
303
- * and browser bundlers, and the signer only for Node hosts. The runtime
304
- * Node guard avoids calling attestation on clients; the module boundary
305
- * keeps node:crypto out of their bundle graphs (a guard alone cannot).
302
+ * `#workload-identity` (package.json `imports`) selects a client
303
+ * implementation for native and browser bundlers, and the signer only for
304
+ * Node hosts. The runtime Node guard avoids calling attestation on clients;
305
+ * the module boundary keeps node:crypto out of their bundle graphs (a guard
306
+ * alone cannot). It is a package import, not a self-reference to an export
307
+ * subpath: a self-reference only resolves where `@oxy.so/core` is reachable
308
+ * through `node_modules`, which bundlers inside this workspace are not.
306
309
  *
307
310
  * @internal
308
311
  */
@@ -310,7 +313,7 @@ function OxyServicesAuthMixin(Base) {
310
313
  if (typeof process === 'undefined' || !process.versions?.node)
311
314
  return false;
312
315
  try {
313
- const { canAttestWorkloadIdentity } = await Promise.resolve().then(() => __importStar(require('@oxy.so/core/internal/workload-identity')));
316
+ const { canAttestWorkloadIdentity } = await Promise.resolve().then(() => __importStar(require('#workload-identity')));
314
317
  return canAttestWorkloadIdentity();
315
318
  }
316
319
  catch {
@@ -338,7 +341,7 @@ function OxyServicesAuthMixin(Base) {
338
341
  return entry.token;
339
342
  if (entry?.pending)
340
343
  return entry.pending;
341
- const { requestWorkloadServiceToken } = await Promise.resolve().then(() => __importStar(require('@oxy.so/core/internal/workload-identity')));
344
+ const { requestWorkloadServiceToken } = await Promise.resolve().then(() => __importStar(require('#workload-identity')));
342
345
  const seeded = entry ?? {
343
346
  token: '',
344
347
  expiresAt: 0,
@@ -496,6 +496,7 @@ function OxyServicesUtilityMixin(Base) {
496
496
  return onError(error);
497
497
  return res.status(401).json(error);
498
498
  }
499
+ const tier = decoded.tier === 'internal' ? 'internal' : 'external';
499
500
  // Read delegated user ID from header
500
501
  const oxyUserIdRaw = req.headers['x-oxy-user-id'];
501
502
  const oxyUserId = isExactNonEmptyServiceClaim(oxyUserIdRaw) ? oxyUserIdRaw : null;
@@ -516,12 +517,21 @@ function OxyServicesUtilityMixin(Base) {
516
517
  return onError(error);
517
518
  return res.status(401).json(error);
518
519
  }
519
- // C3: a service may only act as a user when an explicit
520
- // ServiceActingAs grant exists for that (appId, userId) pair.
521
- // Without the grant we MUST refuse — silently attaching
522
- // `req.userId = oxyUserId` would let any service impersonate
523
- // any user simply by setting the header.
524
- if (oxyUserId) {
520
+ // One of Oxy's own applications acts for a user without a grant:
521
+ // inside the ecosystem that is trust, not consent. It still acts
522
+ // with the USER's authority only — what that user may do is for the
523
+ // receiving service to check.
524
+ if (oxyUserId && tier === 'internal') {
525
+ req.userId = oxyUserId;
526
+ req.user = { id: oxyUserId };
527
+ req.serviceActingAs = { userId: oxyUserId, scopes: [] };
528
+ }
529
+ else if (oxyUserId) {
530
+ // C3: an EXTERNAL service may only act as a user when an explicit
531
+ // ServiceActingAs grant exists for that (appId, userId) pair.
532
+ // Without the grant we MUST refuse — silently attaching
533
+ // `req.userId = oxyUserId` would let any service impersonate
534
+ // any user simply by setting the header.
525
535
  const grant = await oxyInstance.verifyServiceActingAs(appId, oxyUserId);
526
536
  if (!grant || !grant.authorized) {
527
537
  logger_1.logger.warn('[oxy.auth] Service token rejected — no delegation grant', {
@@ -572,6 +582,7 @@ function OxyServicesUtilityMixin(Base) {
572
582
  ownerAccountId,
573
583
  scopes: Array.isArray(decoded.scopes) ? decoded.scopes : [],
574
584
  environment,
585
+ tier,
575
586
  };
576
587
  if (debug) {
577
588
  logger_1.logger.debug(`[oxy.auth] Service token OK app=${decoded.appName} delegateUser=${oxyUserId || '(none)'}`, {
@@ -1004,6 +1015,12 @@ function OxyServicesUtilityMixin(Base) {
1004
1015
  });
1005
1016
  return;
1006
1017
  }
1018
+ // Oxy's own applications are trusted app to app; scopes are the
1019
+ // external lane.
1020
+ if (req.serviceApp.tier === 'internal') {
1021
+ next();
1022
+ return;
1023
+ }
1007
1024
  const appHasScope = appScopes.includes(scope);
1008
1025
  const delegationHasScope = delegatedScopes.includes(scope);
1009
1026
  const hasRequiredScope = req.serviceActingAs
@@ -1,3 +1,11 @@
1
1
  {
2
- "type": "commonjs"
2
+ "type": "commonjs",
3
+ "imports": {
4
+ "#workload-identity": {
5
+ "react-native": "./server/workloadIdentity.client.js",
6
+ "browser": "./server/workloadIdentity.client.js",
7
+ "node": "./server/workloadIdentity.js",
8
+ "default": "./server/workloadIdentity.client.js"
9
+ }
10
+ }
3
11
  }