@oxyhq/core 20.1.0 → 21.0.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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/HttpService.js +47 -8
- package/dist/cjs/boot/sessionColdBoot.js +107 -8
- package/dist/cjs/i18n/locales/en-US.json +26 -4
- package/dist/cjs/i18n/locales/es-ES.json +26 -4
- package/dist/cjs/i18n/locales/locales/en-US.json +26 -4
- package/dist/cjs/i18n/locales/locales/es-ES.json +26 -4
- package/dist/cjs/index.js +57 -16
- package/dist/cjs/inference/OxyInferenceClient.js +330 -0
- package/dist/cjs/mixins/OxyServices.accounts.js +5 -72
- package/dist/cjs/mixins/OxyServices.auth.js +27 -3
- package/dist/cjs/mixins/OxyServices.inference.js +59 -0
- package/dist/cjs/mixins/OxyServices.utility.js +18 -6
- package/dist/cjs/mixins/index.js +6 -0
- package/dist/cjs/server/auth.js +76 -0
- package/dist/cjs/server/index.js +5 -1
- package/dist/cjs/session/SessionClient.js +361 -1
- package/dist/cjs/session/accountDialogController.js +121 -147
- package/dist/cjs/session/accountSwitchTargets.js +75 -0
- package/dist/cjs/session/deviceDirectory.js +143 -0
- package/dist/cjs/session/deviceSwitcherRows.js +76 -0
- package/dist/cjs/session/projectSessionState.js +8 -1
- package/dist/cjs/session/sharedDeviceCredential.js +247 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/HttpService.js +47 -8
- package/dist/esm/boot/sessionColdBoot.js +107 -8
- package/dist/esm/i18n/locales/en-US.json +26 -4
- package/dist/esm/i18n/locales/es-ES.json +26 -4
- package/dist/esm/i18n/locales/locales/en-US.json +26 -4
- package/dist/esm/i18n/locales/locales/es-ES.json +26 -4
- package/dist/esm/index.js +36 -10
- package/dist/esm/inference/OxyInferenceClient.js +325 -0
- package/dist/esm/mixins/OxyServices.accounts.js +5 -72
- package/dist/esm/mixins/OxyServices.auth.js +27 -3
- package/dist/esm/mixins/OxyServices.inference.js +56 -0
- package/dist/esm/mixins/OxyServices.utility.js +18 -6
- package/dist/esm/mixins/index.js +6 -0
- package/dist/esm/server/auth.js +72 -0
- package/dist/esm/server/index.js +1 -1
- package/dist/esm/session/SessionClient.js +362 -2
- package/dist/esm/session/accountDialogController.js +121 -147
- package/dist/esm/session/accountSwitchTargets.js +71 -0
- package/dist/esm/session/deviceDirectory.js +135 -0
- package/dist/esm/session/deviceSwitcherRows.js +72 -0
- package/dist/esm/session/projectSessionState.js +8 -2
- package/dist/esm/session/sharedDeviceCredential.js +239 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/HttpService.d.ts +39 -1
- package/dist/types/boot/sessionColdBoot.d.ts +24 -4
- package/dist/types/index.d.ts +11 -4
- package/dist/types/inference/OxyInferenceClient.d.ts +324 -0
- package/dist/types/mixins/OxyServices.accounts.d.ts +73 -95
- package/dist/types/mixins/OxyServices.auth.d.ts +75 -3
- package/dist/types/mixins/OxyServices.inference.d.ts +95 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +44 -13
- package/dist/types/mixins/index.d.ts +2 -1
- package/dist/types/models/session.d.ts +11 -0
- package/dist/types/server/auth.d.ts +80 -0
- package/dist/types/server/index.d.ts +2 -2
- package/dist/types/session/SessionClient.d.ts +202 -1
- package/dist/types/session/accountDialogController.d.ts +76 -64
- package/dist/types/session/accountSwitchTargets.d.ts +64 -0
- package/dist/types/session/deviceDirectory.d.ts +182 -0
- package/dist/types/session/deviceSwitcherRows.d.ts +92 -0
- package/dist/types/session/projectSessionState.d.ts +29 -0
- package/dist/types/session/sharedDeviceCredential.d.ts +202 -0
- package/package.json +3 -3
- package/src/HttpService.ts +50 -10
- package/src/__tests__/httpServiceUnwrapEnvelope.test.ts +115 -0
- package/src/boot/__tests__/sessionColdBoot.sharedDevice.test.ts +325 -0
- package/src/boot/sessionColdBoot.ts +133 -9
- package/src/i18n/locales/en-US.json +26 -4
- package/src/i18n/locales/es-ES.json +26 -4
- package/src/index.ts +94 -25
- package/src/inference/OxyInferenceClient.ts +590 -0
- package/src/inference/__tests__/OxyInferenceClient.test.ts +383 -0
- package/src/mixins/OxyServices.accounts.ts +75 -176
- package/src/mixins/OxyServices.auth.ts +67 -5
- package/src/mixins/OxyServices.inference.ts +57 -0
- package/src/mixins/OxyServices.utility.ts +58 -14
- package/src/mixins/__tests__/accounts.test.ts +57 -102
- package/src/mixins/__tests__/inferenceFactory.test.ts +58 -0
- package/src/mixins/__tests__/preSessionSkipAuth.test.ts +54 -1
- package/src/mixins/__tests__/serviceAuth.test.ts +2 -0
- package/src/mixins/index.ts +8 -0
- package/src/models/session.ts +11 -0
- package/src/server/__tests__/serviceTokenAttribution.test.ts +396 -0
- package/src/server/auth.ts +118 -0
- package/src/server/index.ts +6 -0
- package/src/session/SessionClient.ts +386 -1
- package/src/session/__tests__/SessionClient.directory.test.ts +688 -0
- package/src/session/__tests__/accountDialogController.test.ts +411 -278
- package/src/session/__tests__/accountDialogShape.test.ts +118 -0
- package/src/session/__tests__/accountSwitchTargets.test.ts +132 -0
- package/src/session/__tests__/deviceDirectory.test.ts +422 -0
- package/src/session/__tests__/deviceSwitcherRows.test.ts +223 -0
- package/src/session/__tests__/projectSessionState.test.ts +17 -0
- package/src/session/__tests__/sharedDeviceCredential.test.ts +300 -0
- package/src/session/accountDialogController.ts +141 -179
- package/src/session/accountSwitchTargets.ts +87 -0
- package/src/session/deviceDirectory.ts +269 -0
- package/src/session/deviceSwitcherRows.ts +145 -0
- package/src/session/projectSessionState.ts +9 -3
- package/src/session/sharedDeviceCredential.ts +349 -0
- package/dist/cjs/session/accountProjection.js +0 -213
- package/dist/esm/session/accountProjection.js +0 -207
- package/dist/types/session/accountProjection.d.ts +0 -198
- package/src/session/__tests__/accountProjection.test.ts +0 -447
- package/src/session/accountProjection.ts +0 -354
package/dist/esm/HttpService.js
CHANGED
|
@@ -962,19 +962,46 @@ export class HttpService {
|
|
|
962
962
|
return this.deviceSecretMintInFlight;
|
|
963
963
|
}
|
|
964
964
|
/**
|
|
965
|
-
* Unwrap standardized API response
|
|
965
|
+
* Unwrap the standardized API response envelope — EXCEPT when the envelope is
|
|
966
|
+
* a page, in which case it travels whole.
|
|
967
|
+
*
|
|
968
|
+
* `{ data: <payload> }` is the house success envelope (`sendSuccess`), and
|
|
969
|
+
* reducing it to `<payload>` is what every call site in the SDK expects. But
|
|
970
|
+
* the reduction DISCARDS every sibling key, silently, and a page's siblings
|
|
971
|
+
* are the only thing that says where the next page starts. That is how
|
|
972
|
+
* `GET /accounts/:id/audit` lost its `nextCursor`: the caller received a bare
|
|
973
|
+
* array, `getNextPageParam` read `undefined`, and pagination was dead past the
|
|
974
|
+
* first page with nothing to show that it was.
|
|
975
|
+
*
|
|
976
|
+
* ## Why the rule is narrow, and not "any sibling key survives"
|
|
977
|
+
*
|
|
978
|
+
* "An object carrying `data` plus anything else is not an envelope" is the
|
|
979
|
+
* tempting general rule, and it is wrong here: this API already answers
|
|
980
|
+
* `{ data, count }` on ~15 routes, plus `{ data, source }`, `{ data, reason }`
|
|
981
|
+
* and `{ data, secretDestroyed }`, and a dozen measured Console call sites
|
|
982
|
+
* type those as the bare payload (`Array<ProviderConnection>`,
|
|
983
|
+
* `AccountBillingState | null`, …). Preserving those envelopes would hand every
|
|
984
|
+
* one of them an object where it expects its payload — at runtime only, since
|
|
985
|
+
* the response type is a call-site assertion. So the rule names PAGINATION
|
|
986
|
+
* specifically: `data` beside {@link PAGE_ENVELOPE_KEYS} is a page.
|
|
987
|
+
*
|
|
988
|
+
* A route whose sibling key genuinely matters to its caller belongs in that
|
|
989
|
+
* list, or should not be a sibling of `data` at all — the cursor-paginated
|
|
990
|
+
* surfaces already in the SDK (`{ follows, nextCursor }`,
|
|
991
|
+
* `{ records, nextCursor }`) sidestep this by never using `data`.
|
|
966
992
|
*/
|
|
967
993
|
unwrapResponse(responseData) {
|
|
968
|
-
|
|
969
|
-
|
|
994
|
+
if (!responseData || typeof responseData !== 'object' || !('data' in responseData)) {
|
|
995
|
+
// Not the success envelope (or not an object at all) — as-is.
|
|
970
996
|
return responseData;
|
|
971
997
|
}
|
|
972
|
-
//
|
|
973
|
-
|
|
974
|
-
|
|
998
|
+
// A page travels whole: its cursor/pagination sibling is unrecoverable
|
|
999
|
+
// information, not decoration.
|
|
1000
|
+
if (HttpService.PAGE_ENVELOPE_KEYS.some((key) => key in responseData)) {
|
|
1001
|
+
return responseData;
|
|
975
1002
|
}
|
|
976
|
-
//
|
|
977
|
-
return responseData;
|
|
1003
|
+
// Regular success envelope: `{ data: ... }` -> the payload.
|
|
1004
|
+
return Array.isArray(responseData) ? responseData : responseData.data;
|
|
978
1005
|
}
|
|
979
1006
|
/**
|
|
980
1007
|
* Update request metrics
|
|
@@ -1151,3 +1178,15 @@ export class HttpService {
|
|
|
1151
1178
|
* ambiguous with a serialized request body.
|
|
1152
1179
|
*/
|
|
1153
1180
|
HttpService.CACHE_IDENTITY_DELIM = ' id=';
|
|
1181
|
+
/**
|
|
1182
|
+
* The keys whose presence beside `data` makes a body a PAGE rather than a
|
|
1183
|
+
* payload — see {@link unwrapResponse} for why this list is narrow.
|
|
1184
|
+
*
|
|
1185
|
+
* - `pagination` — the offset-paginated house envelope (`sendPaginated`).
|
|
1186
|
+
* - `nextCursor` — the keyset-paginated one (the account audit trails).
|
|
1187
|
+
*
|
|
1188
|
+
* Membership is decided by key PRESENCE, never by value: the last page sends
|
|
1189
|
+
* `nextCursor: null`, and an envelope that collapsed into a bare payload
|
|
1190
|
+
* exactly when the stream ended would be a worse bug than the one this fixes.
|
|
1191
|
+
*/
|
|
1192
|
+
HttpService.PAGE_ENVELOPE_KEYS = ['pagination', 'nextCursor'];
|
|
@@ -15,10 +15,17 @@
|
|
|
15
15
|
* origin persisted a `deviceId` + `deviceSecret`, mint a short access token
|
|
16
16
|
* with a single bearer-less POST to `/session/device/token` (no cookie, no
|
|
17
17
|
* navigation) and rotate the secret in-use.
|
|
18
|
-
* 3. `shared-
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* 3. `shared-device-adopt` (native, ACCOUNT mode) — this app has no credential
|
|
19
|
+
* of its own but a sibling official app already put one in the shared native
|
|
20
|
+
* slot: adopt it and mint. This is how a newly installed official app joins
|
|
21
|
+
* the device's existing session WITHOUT another QR and without ever touching
|
|
22
|
+
* the Commons private key.
|
|
23
|
+
* 4. `shared-key-signin` (native, ACCOUNT mode) — the legacy lane: re-mint by
|
|
24
|
+
* signing with the shared-keychain IDENTITY key. Retained as a recovery /
|
|
25
|
+
* compatibility path for devices whose apps have not yet published a shared
|
|
26
|
+
* device credential — OR `identity-key-signin` (IDENTITY mode) — re-mint
|
|
27
|
+
* from THIS device's primary identity key.
|
|
28
|
+
* 5. Signed out.
|
|
22
29
|
*
|
|
23
30
|
* Two session modes (see {@link RunSessionColdBootOptions.sessionMode}):
|
|
24
31
|
* - `account` (default) — the device's ACTIVE account owns the session. Every
|
|
@@ -37,6 +44,7 @@ import { logger } from '../logger/index.js';
|
|
|
37
44
|
import { computeIdentityTag } from '../utils/cacheKey.js';
|
|
38
45
|
import { TOKEN_REFRESH_LEAD_MS, refreshDeviceSecretArm } from '../session/refresh.js';
|
|
39
46
|
import { establishIdentitySession, resolveIdentityPin, } from '../session/identitySession.js';
|
|
47
|
+
import { decideSharedDeviceJoin, } from '../session/sharedDeviceCredential.js';
|
|
40
48
|
/**
|
|
41
49
|
* Run the device-first cold boot. Resolves to the `runColdBoot` outcome and, as
|
|
42
50
|
* a side effect, invokes `onSession` (winning session, token already planted) or
|
|
@@ -236,10 +244,101 @@ export async function runSessionColdBoot(opts) {
|
|
|
236
244
|
},
|
|
237
245
|
});
|
|
238
246
|
}
|
|
239
|
-
else {
|
|
240
|
-
// 3. shared-
|
|
241
|
-
//
|
|
242
|
-
//
|
|
247
|
+
else if (opts.sharedDeviceCredential) {
|
|
248
|
+
// 3. shared-device-adopt (native, ACCOUNT mode) — join the device's existing
|
|
249
|
+
// session through the shared native credential slot.
|
|
250
|
+
//
|
|
251
|
+
// This is the lane that separates identity from session transport. What it
|
|
252
|
+
// reads is an ordinary, individually revocable `deviceId` + `deviceSecret`
|
|
253
|
+
// put there by a sibling official app — never the Commons private key. It
|
|
254
|
+
// is what lets a freshly installed official app land signed in with no QR,
|
|
255
|
+
// and it is why an ordinary app never needs identity-key access at all.
|
|
256
|
+
//
|
|
257
|
+
// `decideSharedDeviceJoin` gates it: an app that already holds its own
|
|
258
|
+
// credential is never moved, and an UNREADABLE slot is never mistaken for
|
|
259
|
+
// an empty one. The lane therefore cannot sign anyone out, in either
|
|
260
|
+
// upgrade order.
|
|
261
|
+
const sharedSlot = opts.sharedDeviceCredential;
|
|
262
|
+
steps.push({
|
|
263
|
+
id: 'shared-device-adopt',
|
|
264
|
+
// The adoption itself is local, but it is only worth committing alongside
|
|
265
|
+
// a mint that proves the credential — so the whole lane is online-gated
|
|
266
|
+
// like every other network step.
|
|
267
|
+
enabled: () => isNative && !isOffline(),
|
|
268
|
+
run: async () => {
|
|
269
|
+
const before = await store.load();
|
|
270
|
+
const decision = decideSharedDeviceJoin(before, await sharedSlot.read());
|
|
271
|
+
if (decision.action === 'skip') {
|
|
272
|
+
logger.debug(`shared device credential not adopted (${decision.reason})`, { component: 'sessionColdBoot', method: 'shared-device-adopt' });
|
|
273
|
+
return { kind: 'skip' };
|
|
274
|
+
}
|
|
275
|
+
// Restore the store to exactly what it held before this lane touched it.
|
|
276
|
+
// A credential we adopted and could not prove must not be left behind for
|
|
277
|
+
// the next boot's mint lane to keep retrying.
|
|
278
|
+
const revert = async () => {
|
|
279
|
+
if (before) {
|
|
280
|
+
await store.save(before);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
await store.clear();
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
const adopted = {
|
|
287
|
+
// The mint fills both in from the device's live state; carrying the
|
|
288
|
+
// previous session's ids into a different device session would be a
|
|
289
|
+
// lie for however long the mint takes.
|
|
290
|
+
sessionId: '',
|
|
291
|
+
userId: '',
|
|
292
|
+
deviceId: decision.credential.deviceId,
|
|
293
|
+
deviceSecret: decision.credential.deviceSecret,
|
|
294
|
+
};
|
|
295
|
+
if (!(await store.save(adopted))) {
|
|
296
|
+
logger.error('adopted the shared device credential but it could not be durably persisted — reverting', undefined, { component: 'sessionColdBoot', method: 'shared-device-adopt' });
|
|
297
|
+
await revert();
|
|
298
|
+
return { kind: 'skip' };
|
|
299
|
+
}
|
|
300
|
+
const result = await refreshDeviceSecretArm({ oxy, store, pin: null });
|
|
301
|
+
if (result.status === 'ok') {
|
|
302
|
+
return {
|
|
303
|
+
kind: 'session',
|
|
304
|
+
session: {
|
|
305
|
+
sessionId: result.sessionId,
|
|
306
|
+
userId: result.userId,
|
|
307
|
+
accessToken: result.token,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (result.status === 'invalid-secret') {
|
|
312
|
+
// The one place we hold POSITIVE proof that the exact bytes in the
|
|
313
|
+
// shared slot are dead — the server rejected them by name. Clearing it
|
|
314
|
+
// signs nobody out (a credential the server does not recognise cannot
|
|
315
|
+
// be minting for anyone) and it is what stops a dead credential from
|
|
316
|
+
// blocking every future install: a stale slot owned by a different
|
|
317
|
+
// `deviceId` is otherwise never overwritten, by design.
|
|
318
|
+
await sharedSlot.clear();
|
|
319
|
+
}
|
|
320
|
+
else if (result.status === 'no-session') {
|
|
321
|
+
signedOutReason = 'no_session';
|
|
322
|
+
}
|
|
323
|
+
await revert();
|
|
324
|
+
return { kind: 'skip' };
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (identityBinding === null) {
|
|
329
|
+
// 4. shared-key-signin (native) — the RECOVERY / COMPATIBILITY lane: sign a
|
|
330
|
+
// challenge with the shared-keychain IDENTITY key to re-mint a session.
|
|
331
|
+
//
|
|
332
|
+
// It runs LAST on purpose. Using the self-custody key to obtain an ordinary
|
|
333
|
+
// session is the over-sharing #937 sets out to end, so it is now reachable
|
|
334
|
+
// only on a device where no sibling app has published a shared device
|
|
335
|
+
// credential yet — an install that predates this lane, or one where the
|
|
336
|
+
// shared slot is unreadable. Its own `store.save` below feeds the shared
|
|
337
|
+
// slot through the mirroring store, so the FIRST boot that takes this lane
|
|
338
|
+
// is also the last one that needs to: every later app joins by credential.
|
|
339
|
+
//
|
|
340
|
+
// Native AND online: it is a network step (challenge + verify round-trips),
|
|
341
|
+
// so it is gated by the same offline hint as the mint lane. `{ retry: false }`
|
|
243
342
|
// keeps the two round-trips as single attempts — the refresh scheduler /
|
|
244
343
|
// 401 lane own later retries — so this step cannot multiply boot latency.
|
|
245
344
|
steps.push({
|
|
@@ -396,13 +396,22 @@
|
|
|
396
396
|
"remoteSignOutFailed": "There was a problem signing out from the device. Please try again.",
|
|
397
397
|
"noOtherDeviceSessions": "No other device sessions found to sign out from.",
|
|
398
398
|
"signOutOthersSuccess": "Signed out from all other devices successfully!",
|
|
399
|
-
"signOutOthersFailed": "There was a problem signing out from other devices. Please try again."
|
|
399
|
+
"signOutOthersFailed": "There was a problem signing out from other devices. Please try again.",
|
|
400
|
+
"contextRemoved": "Removed {{account}}",
|
|
401
|
+
"contextRemoveFailed": "There was a problem removing that account. Please try again.",
|
|
402
|
+
"principalRemoved": "Signed {{name}} out of this device",
|
|
403
|
+
"principalRemoveFailed": "There was a problem signing that person out. Please try again.",
|
|
404
|
+
"activateFailed": "There was a problem switching accounts. Please try again."
|
|
400
405
|
},
|
|
401
406
|
"confirms": {
|
|
402
407
|
"remove": "Are you sure you want to remove {{displayName}} from this device? You'll need to sign in again to access this account.",
|
|
403
408
|
"logoutAll": "Are you sure you want to sign out of all accounts? This will remove all saved accounts from this device.",
|
|
404
409
|
"remoteLogout": "Are you sure you want to sign out from \"{{deviceName}}\"? This will end the session on that device.",
|
|
405
|
-
"logoutOthers": "Are you sure you want to sign out from all {{count}} other device(s)? This will end sessions on all other devices except this one."
|
|
410
|
+
"logoutOthers": "Are you sure you want to sign out from all {{count}} other device(s)? This will end sessions on all other devices except this one.",
|
|
411
|
+
"removeContextTitle": "Remove this account?",
|
|
412
|
+
"removeContext": "Stop {{person}} acting as {{account}} on this device? Anyone else who reaches {{account}} here keeps their access.",
|
|
413
|
+
"removePrincipalTitle": "Sign out of this device?",
|
|
414
|
+
"removePrincipal": "Sign {{name}} out of this device? Every account they reach here is removed. Nobody else is affected."
|
|
406
415
|
},
|
|
407
416
|
"device": {
|
|
408
417
|
"loadingTitle": "Loading device sessions...",
|
|
@@ -439,6 +448,14 @@
|
|
|
439
448
|
"openedInCommons": "Opened in Commons",
|
|
440
449
|
"confirming": "Confirming identity",
|
|
441
450
|
"confirmed": "Identity confirmed"
|
|
451
|
+
},
|
|
452
|
+
"context": {
|
|
453
|
+
"unavailable": "Unavailable right now",
|
|
454
|
+
"operatedBy": "Operated by {{name}}",
|
|
455
|
+
"remove": "Remove {{account}} from {{person}}"
|
|
456
|
+
},
|
|
457
|
+
"principal": {
|
|
458
|
+
"signOut": "Sign {{name}} out of this device"
|
|
442
459
|
}
|
|
443
460
|
},
|
|
444
461
|
"reputation": {
|
|
@@ -2119,8 +2136,13 @@
|
|
|
2119
2136
|
"filesWrite": "Upload and modify your files",
|
|
2120
2137
|
"filesDelete": "Delete your files",
|
|
2121
2138
|
"webhooksReceive": "Receive webhooks",
|
|
2122
|
-
"
|
|
2123
|
-
"
|
|
2139
|
+
"inferenceInvoke": "Run AI requests on your behalf",
|
|
2140
|
+
"inferenceModelsRead": "List available AI models",
|
|
2141
|
+
"inferenceUsageRead": "Read its AI usage and costs",
|
|
2142
|
+
"inferenceRoutingRead": "Read how AI requests are routed",
|
|
2143
|
+
"inferenceRoutingWrite": "Change how AI requests are routed",
|
|
2144
|
+
"inferenceProvidersRead": "Read its connected AI providers",
|
|
2145
|
+
"inferenceProvidersWrite": "Manage its connected AI providers",
|
|
2124
2146
|
"federationWrite": "Act across federated services"
|
|
2125
2147
|
},
|
|
2126
2148
|
"account": {
|
|
@@ -636,13 +636,22 @@
|
|
|
636
636
|
"remoteSignOutFailed": "Hubo un problema al cerrar sesión en el dispositivo. Inténtalo de nuevo.",
|
|
637
637
|
"noOtherDeviceSessions": "No se encontraron otras sesiones de dispositivo para cerrar.",
|
|
638
638
|
"signOutOthersSuccess": "¡Sesiones cerradas correctamente en los demás dispositivos!",
|
|
639
|
-
"signOutOthersFailed": "Hubo un problema al cerrar sesión en otros dispositivos. Inténtalo de nuevo."
|
|
639
|
+
"signOutOthersFailed": "Hubo un problema al cerrar sesión en otros dispositivos. Inténtalo de nuevo.",
|
|
640
|
+
"contextRemoved": "Se quitó {{account}}",
|
|
641
|
+
"contextRemoveFailed": "Hubo un problema al quitar esa cuenta. Inténtalo de nuevo.",
|
|
642
|
+
"principalRemoved": "Se cerró la sesión de {{name}} en este dispositivo",
|
|
643
|
+
"principalRemoveFailed": "Hubo un problema al cerrar esa sesión. Inténtalo de nuevo.",
|
|
644
|
+
"activateFailed": "Hubo un problema al cambiar de cuenta. Inténtalo de nuevo."
|
|
640
645
|
},
|
|
641
646
|
"confirms": {
|
|
642
647
|
"remove": "¿Seguro que quieres eliminar a {{displayName}} de este dispositivo? Tendrás que iniciar sesión de nuevo para acceder a esta cuenta.",
|
|
643
648
|
"logoutAll": "¿Seguro que quieres cerrar sesión en todas las cuentas? Esto eliminará todas las cuentas guardadas de este dispositivo.",
|
|
644
649
|
"remoteLogout": "¿Seguro que quieres cerrar sesión en \"{{deviceName}}\"? Esto finalizará la sesión en ese dispositivo.",
|
|
645
|
-
"logoutOthers": "¿Seguro que quieres cerrar sesión en los otros {{count}} dispositivo(s)? Esto finalizará las sesiones en todos los demás dispositivos excepto en este."
|
|
650
|
+
"logoutOthers": "¿Seguro que quieres cerrar sesión en los otros {{count}} dispositivo(s)? Esto finalizará las sesiones en todos los demás dispositivos excepto en este.",
|
|
651
|
+
"removeContextTitle": "¿Quitar esta cuenta?",
|
|
652
|
+
"removeContext": "¿Dejar de usar {{account}} como {{person}} en este dispositivo? Quien más acceda a {{account}} aquí lo conserva.",
|
|
653
|
+
"removePrincipalTitle": "¿Cerrar sesión en este dispositivo?",
|
|
654
|
+
"removePrincipal": "¿Cerrar la sesión de {{name}} en este dispositivo? Se quitan todas las cuentas a las que llega desde aquí. No afecta a nadie más."
|
|
646
655
|
},
|
|
647
656
|
"device": {
|
|
648
657
|
"loadingTitle": "Cargando sesiones de dispositivo...",
|
|
@@ -679,6 +688,14 @@
|
|
|
679
688
|
"openedInCommons": "Abierto en Commons",
|
|
680
689
|
"confirming": "Confirmando identidad",
|
|
681
690
|
"confirmed": "Identidad confirmada"
|
|
691
|
+
},
|
|
692
|
+
"context": {
|
|
693
|
+
"unavailable": "No disponible ahora mismo",
|
|
694
|
+
"operatedBy": "Gestionada por {{name}}",
|
|
695
|
+
"remove": "Quitar {{account}} de {{person}}"
|
|
696
|
+
},
|
|
697
|
+
"principal": {
|
|
698
|
+
"signOut": "Cerrar la sesión de {{name}} en este dispositivo"
|
|
682
699
|
}
|
|
683
700
|
},
|
|
684
701
|
"feedback": {
|
|
@@ -2119,8 +2136,13 @@
|
|
|
2119
2136
|
"filesWrite": "Subir y modificar tus archivos",
|
|
2120
2137
|
"filesDelete": "Eliminar tus archivos",
|
|
2121
2138
|
"webhooksReceive": "Recibir webhooks",
|
|
2122
|
-
"
|
|
2123
|
-
"
|
|
2139
|
+
"inferenceInvoke": "Ejecutar peticiones de IA en tu nombre",
|
|
2140
|
+
"inferenceModelsRead": "Ver los modelos de IA disponibles",
|
|
2141
|
+
"inferenceUsageRead": "Ver su consumo y costes de IA",
|
|
2142
|
+
"inferenceRoutingRead": "Ver cómo se enrutan las peticiones de IA",
|
|
2143
|
+
"inferenceRoutingWrite": "Cambiar cómo se enrutan las peticiones de IA",
|
|
2144
|
+
"inferenceProvidersRead": "Ver sus proveedores de IA conectados",
|
|
2145
|
+
"inferenceProvidersWrite": "Gestionar sus proveedores de IA conectados",
|
|
2124
2146
|
"federationWrite": "Actuar en servicios federados"
|
|
2125
2147
|
},
|
|
2126
2148
|
"account": {
|
|
@@ -396,13 +396,22 @@
|
|
|
396
396
|
"remoteSignOutFailed": "There was a problem signing out from the device. Please try again.",
|
|
397
397
|
"noOtherDeviceSessions": "No other device sessions found to sign out from.",
|
|
398
398
|
"signOutOthersSuccess": "Signed out from all other devices successfully!",
|
|
399
|
-
"signOutOthersFailed": "There was a problem signing out from other devices. Please try again."
|
|
399
|
+
"signOutOthersFailed": "There was a problem signing out from other devices. Please try again.",
|
|
400
|
+
"contextRemoved": "Removed {{account}}",
|
|
401
|
+
"contextRemoveFailed": "There was a problem removing that account. Please try again.",
|
|
402
|
+
"principalRemoved": "Signed {{name}} out of this device",
|
|
403
|
+
"principalRemoveFailed": "There was a problem signing that person out. Please try again.",
|
|
404
|
+
"activateFailed": "There was a problem switching accounts. Please try again."
|
|
400
405
|
},
|
|
401
406
|
"confirms": {
|
|
402
407
|
"remove": "Are you sure you want to remove {{displayName}} from this device? You'll need to sign in again to access this account.",
|
|
403
408
|
"logoutAll": "Are you sure you want to sign out of all accounts? This will remove all saved accounts from this device.",
|
|
404
409
|
"remoteLogout": "Are you sure you want to sign out from \"{{deviceName}}\"? This will end the session on that device.",
|
|
405
|
-
"logoutOthers": "Are you sure you want to sign out from all {{count}} other device(s)? This will end sessions on all other devices except this one."
|
|
410
|
+
"logoutOthers": "Are you sure you want to sign out from all {{count}} other device(s)? This will end sessions on all other devices except this one.",
|
|
411
|
+
"removeContextTitle": "Remove this account?",
|
|
412
|
+
"removeContext": "Stop {{person}} acting as {{account}} on this device? Anyone else who reaches {{account}} here keeps their access.",
|
|
413
|
+
"removePrincipalTitle": "Sign out of this device?",
|
|
414
|
+
"removePrincipal": "Sign {{name}} out of this device? Every account they reach here is removed. Nobody else is affected."
|
|
406
415
|
},
|
|
407
416
|
"device": {
|
|
408
417
|
"loadingTitle": "Loading device sessions...",
|
|
@@ -439,6 +448,14 @@
|
|
|
439
448
|
"openedInCommons": "Opened in Commons",
|
|
440
449
|
"confirming": "Confirming identity",
|
|
441
450
|
"confirmed": "Identity confirmed"
|
|
451
|
+
},
|
|
452
|
+
"context": {
|
|
453
|
+
"unavailable": "Unavailable right now",
|
|
454
|
+
"operatedBy": "Operated by {{name}}",
|
|
455
|
+
"remove": "Remove {{account}} from {{person}}"
|
|
456
|
+
},
|
|
457
|
+
"principal": {
|
|
458
|
+
"signOut": "Sign {{name}} out of this device"
|
|
442
459
|
}
|
|
443
460
|
},
|
|
444
461
|
"reputation": {
|
|
@@ -2119,8 +2136,13 @@
|
|
|
2119
2136
|
"filesWrite": "Upload and modify your files",
|
|
2120
2137
|
"filesDelete": "Delete your files",
|
|
2121
2138
|
"webhooksReceive": "Receive webhooks",
|
|
2122
|
-
"
|
|
2123
|
-
"
|
|
2139
|
+
"inferenceInvoke": "Run AI requests on your behalf",
|
|
2140
|
+
"inferenceModelsRead": "List available AI models",
|
|
2141
|
+
"inferenceUsageRead": "Read its AI usage and costs",
|
|
2142
|
+
"inferenceRoutingRead": "Read how AI requests are routed",
|
|
2143
|
+
"inferenceRoutingWrite": "Change how AI requests are routed",
|
|
2144
|
+
"inferenceProvidersRead": "Read its connected AI providers",
|
|
2145
|
+
"inferenceProvidersWrite": "Manage its connected AI providers",
|
|
2124
2146
|
"federationWrite": "Act across federated services"
|
|
2125
2147
|
},
|
|
2126
2148
|
"account": {
|
|
@@ -636,13 +636,22 @@
|
|
|
636
636
|
"remoteSignOutFailed": "Hubo un problema al cerrar sesión en el dispositivo. Inténtalo de nuevo.",
|
|
637
637
|
"noOtherDeviceSessions": "No se encontraron otras sesiones de dispositivo para cerrar.",
|
|
638
638
|
"signOutOthersSuccess": "¡Sesiones cerradas correctamente en los demás dispositivos!",
|
|
639
|
-
"signOutOthersFailed": "Hubo un problema al cerrar sesión en otros dispositivos. Inténtalo de nuevo."
|
|
639
|
+
"signOutOthersFailed": "Hubo un problema al cerrar sesión en otros dispositivos. Inténtalo de nuevo.",
|
|
640
|
+
"contextRemoved": "Se quitó {{account}}",
|
|
641
|
+
"contextRemoveFailed": "Hubo un problema al quitar esa cuenta. Inténtalo de nuevo.",
|
|
642
|
+
"principalRemoved": "Se cerró la sesión de {{name}} en este dispositivo",
|
|
643
|
+
"principalRemoveFailed": "Hubo un problema al cerrar esa sesión. Inténtalo de nuevo.",
|
|
644
|
+
"activateFailed": "Hubo un problema al cambiar de cuenta. Inténtalo de nuevo."
|
|
640
645
|
},
|
|
641
646
|
"confirms": {
|
|
642
647
|
"remove": "¿Seguro que quieres eliminar a {{displayName}} de este dispositivo? Tendrás que iniciar sesión de nuevo para acceder a esta cuenta.",
|
|
643
648
|
"logoutAll": "¿Seguro que quieres cerrar sesión en todas las cuentas? Esto eliminará todas las cuentas guardadas de este dispositivo.",
|
|
644
649
|
"remoteLogout": "¿Seguro que quieres cerrar sesión en \"{{deviceName}}\"? Esto finalizará la sesión en ese dispositivo.",
|
|
645
|
-
"logoutOthers": "¿Seguro que quieres cerrar sesión en los otros {{count}} dispositivo(s)? Esto finalizará las sesiones en todos los demás dispositivos excepto en este."
|
|
650
|
+
"logoutOthers": "¿Seguro que quieres cerrar sesión en los otros {{count}} dispositivo(s)? Esto finalizará las sesiones en todos los demás dispositivos excepto en este.",
|
|
651
|
+
"removeContextTitle": "¿Quitar esta cuenta?",
|
|
652
|
+
"removeContext": "¿Dejar de usar {{account}} como {{person}} en este dispositivo? Quien más acceda a {{account}} aquí lo conserva.",
|
|
653
|
+
"removePrincipalTitle": "¿Cerrar sesión en este dispositivo?",
|
|
654
|
+
"removePrincipal": "¿Cerrar la sesión de {{name}} en este dispositivo? Se quitan todas las cuentas a las que llega desde aquí. No afecta a nadie más."
|
|
646
655
|
},
|
|
647
656
|
"device": {
|
|
648
657
|
"loadingTitle": "Cargando sesiones de dispositivo...",
|
|
@@ -679,6 +688,14 @@
|
|
|
679
688
|
"openedInCommons": "Abierto en Commons",
|
|
680
689
|
"confirming": "Confirmando identidad",
|
|
681
690
|
"confirmed": "Identidad confirmada"
|
|
691
|
+
},
|
|
692
|
+
"context": {
|
|
693
|
+
"unavailable": "No disponible ahora mismo",
|
|
694
|
+
"operatedBy": "Gestionada por {{name}}",
|
|
695
|
+
"remove": "Quitar {{account}} de {{person}}"
|
|
696
|
+
},
|
|
697
|
+
"principal": {
|
|
698
|
+
"signOut": "Cerrar la sesión de {{name}} en este dispositivo"
|
|
682
699
|
}
|
|
683
700
|
},
|
|
684
701
|
"feedback": {
|
|
@@ -2119,8 +2136,13 @@
|
|
|
2119
2136
|
"filesWrite": "Subir y modificar tus archivos",
|
|
2120
2137
|
"filesDelete": "Eliminar tus archivos",
|
|
2121
2138
|
"webhooksReceive": "Recibir webhooks",
|
|
2122
|
-
"
|
|
2123
|
-
"
|
|
2139
|
+
"inferenceInvoke": "Ejecutar peticiones de IA en tu nombre",
|
|
2140
|
+
"inferenceModelsRead": "Ver los modelos de IA disponibles",
|
|
2141
|
+
"inferenceUsageRead": "Ver su consumo y costes de IA",
|
|
2142
|
+
"inferenceRoutingRead": "Ver cómo se enrutan las peticiones de IA",
|
|
2143
|
+
"inferenceRoutingWrite": "Cambiar cómo se enrutan las peticiones de IA",
|
|
2144
|
+
"inferenceProvidersRead": "Ver sus proveedores de IA conectados",
|
|
2145
|
+
"inferenceProvidersWrite": "Gestionar sus proveedores de IA conectados",
|
|
2124
2146
|
"federationWrite": "Actuar en servicios federados"
|
|
2125
2147
|
},
|
|
2126
2148
|
"account": {
|
package/dist/esm/index.js
CHANGED
|
@@ -173,16 +173,32 @@ export { SessionClient } from './session/SessionClient.js';
|
|
|
173
173
|
export { createSessionClientHost } from './session/sessionClientHost.js';
|
|
174
174
|
export { createSessionClient } from './session/createSessionClient.js';
|
|
175
175
|
export { deviceStateToClientSessions, activeSessionIdOf, activeUserOf, accountIdsOf, } from './session/projectSessionState.js';
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
|
|
176
|
+
// Pure projections over the device DIRECTORY (`GET /session/device/directory`,
|
|
177
|
+
// ADR 0002) — the read model that keeps the actor (the human who authenticated)
|
|
178
|
+
// and the subject (the account being acted as) apart. The flat
|
|
179
|
+
// `DeviceSessionState` collapses them into one row, so it can neither tell
|
|
180
|
+
// "signed in as an org" from "a person operating that org" nor hold two people
|
|
181
|
+
// reaching the same org on one device.
|
|
182
|
+
// `canActivateContext` is the switchability question — `available` alone, never
|
|
183
|
+
// composed with `onDevice`, which is a different fact in both directions.
|
|
184
|
+
// `projectDevicePrincipals` is the switcher's shape: people, each with what
|
|
185
|
+
// they may become. Grouped rather than flat because the same organization
|
|
186
|
+
// reached through two people is TWO rows under two humans, which a list keyed
|
|
187
|
+
// by account cannot say.
|
|
188
|
+
export { canActivateContext, directoryDisplayName, directoryHandle, projectDevicePrincipals, resolveActiveContext, resolveDeviceContext, } from './session/deviceDirectory.js';
|
|
189
|
+
// The switcher's RENDER model over that projection — names, handles and avatar
|
|
190
|
+
// URLs resolved once. Shared by `@oxyhq/services`' account dialog and the
|
|
191
|
+
// auth.oxy.so chooser so the two cannot drift, the same reason the flat
|
|
192
|
+
// projection lived here before it.
|
|
193
|
+
export { buildSwitcherRows, showsPrincipalHeaders } from './session/deviceSwitcherRows.js';
|
|
194
|
+
// The switch-target predicates over the account GRAPH — a list of accounts to
|
|
195
|
+
// manage, not the device's list of identities to become (that is the directory
|
|
196
|
+
// above). `isSwitchTargetAccount` is the structural half ("is this kind
|
|
197
|
+
// switchable at all?"); `canSwitchIntoAccount` adds the caller's
|
|
198
|
+
// `account:act_as` permission. Exported so the surfaces that render
|
|
199
|
+
// `AccountNode`s — the Console workspace switcher, managed-accounts rows — ask
|
|
200
|
+
// the SAME questions instead of testing a kind literal.
|
|
201
|
+
export { isSwitchTargetAccount, canSwitchIntoAccount, } from './session/accountSwitchTargets.js';
|
|
186
202
|
// Headless controller for the unified account dialog. Framework-agnostic
|
|
187
203
|
// state machine + subscribe/getSnapshot store (bind via `useSyncExternalStore`)
|
|
188
204
|
// — sign-in is passkey (WebAuthn) or the Commons QR / shared-keychain handoff;
|
|
@@ -198,6 +214,12 @@ export { AccountDialogController, createAccountDialogController, } from './sessi
|
|
|
198
214
|
// via `POST /session/device/token`.
|
|
199
215
|
// ---------------------------------------------------------------------------
|
|
200
216
|
export { createWebAuthStateStore, createNativeAuthStateStore, createMemoryAuthStateStore, AUTH_STATE_STORAGE_KEY, } from './session/authStateStore.js';
|
|
217
|
+
// The shared NATIVE DeviceSession credential — how several official apps on one
|
|
218
|
+
// device end up on ONE `DeviceSession` and therefore one active context. It is an
|
|
219
|
+
// ordinary rotatable/revocable `deviceId` + `deviceSecret`, deliberately NOT the
|
|
220
|
+
// Commons private identity key: an app that only needs a session must never be
|
|
221
|
+
// handed the key that signs identity approvals.
|
|
222
|
+
export { createSharedMirroringAuthStateStore, decideSharedDeviceJoin, decideSharedDevicePublish, normalizeSharedDeviceSessionRead, publishProvenDeviceCredential, readLocalDeviceCredential, } from './session/sharedDeviceCredential.js';
|
|
201
223
|
// Identity-bound sessions (the identity vault). The pin is the durable
|
|
202
224
|
// `{publicKey, accountId}` binding between this device's PRIMARY identity key
|
|
203
225
|
// and the account it authenticates as; it is what keeps such a client from
|
|
@@ -209,6 +231,10 @@ export { resolveIdentityPin, establishIdentitySession, } from './session/identit
|
|
|
209
231
|
// the identity session instead of dropping a healthy device credential.
|
|
210
232
|
export { AccountNotOnDeviceError } from './mixins/OxyServices.deviceBoot.js';
|
|
211
233
|
export { refreshPersistedSession, refreshDeviceSecretArm, createAuthRefreshHandler, installAuthRefreshHandler, startTokenRefreshScheduler, TOKEN_REFRESH_LEAD_MS, } from './session/refresh.js';
|
|
234
|
+
// The inference API. `oxyServices.inference()` binds the session bearer into
|
|
235
|
+
// the same client an external developer constructs with an `oxy_sk_…` machine
|
|
236
|
+
// key — one surface, two credential lanes. See `docs/inference/sdk.md`.
|
|
237
|
+
export { OxyInferenceClient, OxyInferenceError, OXY_INFERENCE_BASE_URL, } from './inference/OxyInferenceClient.js';
|
|
212
238
|
export { runSessionColdBoot } from './boot/sessionColdBoot.js';
|
|
213
239
|
// API response contracts (request/response Zod schemas + inferred types) live in
|
|
214
240
|
// `@oxyhq/contracts` — the single source of truth shared by the backend and every
|