@agentproto/auth 1.1.2 → 1.2.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.
package/README.md CHANGED
@@ -233,6 +233,47 @@ exposure and have `resolveMcpHeaderExposure(exposure, broker)` lay brokered
233
233
  headers onto the transport. `secrets` keeps **zero dependency** on `auth`
234
234
  through that structural seam. See `@agentproto/secrets`' README.
235
235
 
236
+ ## Sub-accounts
237
+
238
+ An *account* (a login or billing identity) can have *sub-accounts* — orgs,
239
+ workspaces, projects — each with its own quota. A profile pins one with the
240
+ optional `subaccount` field, so every pin is a distinct, truthful wallet:
241
+
242
+ ```jsonc
243
+ { "id": "acme-ws01", "endpoint": "<endpoint>", "method": "api-key",
244
+ "source": "<account-source>", "subaccount": { "kind": "org", "id": "org_01ABC", "name": "Ws01" } }
245
+ ```
246
+
247
+ `kind` is `[a-z][a-z0-9-]*`; `id` is opaque; `name` is a display label used in
248
+ usage-limit tags. The field is optional and the on-disk store format is
249
+ unchanged. CLI: `agentproto auth profile create <id> --subaccount <kind>:<id>`
250
+ and `agentproto auth subaccounts list <profile|account> [--create [--prefix]]`.
251
+
252
+ How a pin is *applied* belongs to a provider, never to this package or the
253
+ runtime. A provider implements `SubaccountProvider` and is added with
254
+ `registerSubaccountProvider`:
255
+
256
+ | Member | Purpose |
257
+ | --- | --- |
258
+ | `id`, `source?`, `endpoints?`, `kinds` | identity, and which accounts it owns (by `source` name, else by endpoint) |
259
+ | `list(account)` | discovery: `{ account, subaccounts: [{kind, id, name}] }` |
260
+ | `resolve(profile, { credential? })` | spawn time: `{ credential?, credentialEnvOverride?, env?, isolateDataHome? }` |
261
+ | `migrateLegacySource?(source)` | read-time migration of a pre-pin provider-encoded `source` into the pin |
262
+
263
+ A provider throws `SubaccountError` when a pin cannot be listed or resolved; the
264
+ runtime fails the spawn loudly rather than falling back to the account's default
265
+ scope. Providers ship with their adapter and the host registers them (the CLI
266
+ does so for the built-ins).
267
+
268
+ The registry is process-global and starts EMPTY: a host that uses
269
+ `@agentproto/auth` outside the CLI must call `registerSubaccountProvider` for
270
+ each provider it needs BEFORE creating, reading or spawning pinned profiles.
271
+ Until then, creating a profile with a legacy `opencode-console:<orgId>` source is
272
+ rejected, and a spawn on a pinned profile whose provider is not registered fails
273
+ with `auth_source_unresolved` (it never runs on the account's default scope). A
274
+ pin on a credential-backed profile is accepted at create time even when no
275
+ provider owns its endpoint yet; the clear error comes at spawn time.
276
+
236
277
  ## API surface
237
278
 
238
279
  | Export | Purpose |
@@ -249,6 +290,8 @@ through that structural seam. See `@agentproto/secrets`' README.
249
290
  | `KeychainStore` / `MemoryStore` / `FileStore` / `resolveStoreRef` | built-in backends + ref resolver |
250
291
  | `readKeychainToken` / `writeKeychainToken` / `resolveAccount` | Keychain helpers |
251
292
  | `guildeAuthProvider` / `BUILTIN_AUTH_PROVIDERS` | shipped builtins |
293
+ | `registerSubaccountProvider` / `findSubaccountProvider` / `listSubaccountProviders` | sub-account provider registry |
294
+ | `SubaccountProvider` / `SubaccountPin` / `SubaccountError` / `parseSubaccountPin` | sub-account contract |
252
295
 
253
296
  ## License
254
297
 
package/dist/index.d.ts CHANGED
@@ -609,6 +609,15 @@ interface CostBudget {
609
609
  /** Which spend surface the window is summed over. See {@link CostBudgetScope}. */
610
610
  scope: CostBudgetScope;
611
611
  }
612
+ /** A pinned sub-account of the profile's account: `kind` is free-form (`org`,
613
+ * `workspace`, `project`, …) and only meaningful to the provider that owns the
614
+ * account (see `subaccounts.ts`). */
615
+ interface SubaccountPin {
616
+ kind: string;
617
+ id: string;
618
+ /** Human-readable name captured at discovery time; informational only. */
619
+ name?: string;
620
+ }
612
621
  /** A named, billing-endpoint-scoped credential reference. */
613
622
  interface AuthProfile {
614
623
  /** Stable id, unique across all profiles (the `profileRef` a session
@@ -635,6 +644,13 @@ interface AuthProfile {
635
644
  * fresh at spawn time instead. Mutually exclusive with
636
645
  * {@link credentialRef}. */
637
646
  source?: string;
647
+ /** Pins one SUB-ACCOUNT (org / workspace / project / team) of the account this
648
+ * profile authenticates as — the account being the {@link credentialRef}'d
649
+ * secret or the {@link source}. Each pin is its own wallet; how it is
650
+ * applied at spawn is the registered provider's business (`subaccounts.ts`).
651
+ * ABSENT ⇒ the account's default scope, byte-identical to a profile that
652
+ * predates this field. */
653
+ subaccount?: SubaccountPin;
638
654
  /** Optional human-readable name ("Jeremy Max", "work OpenRouter"). */
639
655
  label?: string;
640
656
  /** Whole-profile enable/disable. ABSENT (or `false`) ⇒ enabled (today's
@@ -665,6 +681,114 @@ interface AuthProfile {
665
681
  origin?: string;
666
682
  }
667
683
 
684
+ /**
685
+ * Sub-accounts: an ACCOUNT (a login / billing identity) can have SUB-ACCOUNTS
686
+ * (org, workspace, project, team) with their own quota and billing. A profile
687
+ * can pin one ({@link AuthProfile.subaccount}); each pin is a distinct,
688
+ * truthful wallet. HOW a pin is applied to a spawn — headers, an inline config
689
+ * block, a scoped token, an isolated data home — is the provider's business.
690
+ *
691
+ * This module is the generic seam: the pin type, the {@link SubaccountProvider}
692
+ * interface, and a process-wide registry. Nothing here (or in any core spawn
693
+ * path) names a vendor; providers live with their adapter and are registered by
694
+ * the host (the CLI registers the built-ins at start-up).
695
+ */
696
+
697
+ /** Conservative charset for a pin's `kind` — it ends up in CLI args and logs. */
698
+ declare const SUBACCOUNT_KIND_RE: RegExp;
699
+ /** `"org:org_01ABC"` → `{ kind: "org", id: "org_01ABC" }`; split on the FIRST
700
+ * colon so an id may itself contain colons. Returns undefined when malformed. */
701
+ declare function parseSubaccountPin(spec: string): SubaccountPin | undefined;
702
+ /** The parent account a provider lists sub-accounts of. */
703
+ interface SubaccountAccountRef {
704
+ /** Billing endpoint the account authenticates against (`opencode-go`, …). */
705
+ endpoint: string;
706
+ /** Source-backed account (the account name a provider owns), no stored secret. */
707
+ source?: string;
708
+ /** Credential-backed account: the resolved secret. Never logged or returned. */
709
+ credential?: string;
710
+ }
711
+ interface DiscoveredSubaccount {
712
+ kind: string;
713
+ id: string;
714
+ name: string;
715
+ }
716
+ interface SubaccountListing {
717
+ account: {
718
+ id: string;
719
+ label?: string;
720
+ };
721
+ subaccounts: DiscoveredSubaccount[];
722
+ }
723
+ /** What a provider hands the spawn path for one pinned profile. */
724
+ interface SubaccountResolution {
725
+ /** Credential to inject, when the provider resolves its own (a source-backed
726
+ * account's scoped token). Omitted ⇒ the profile's stored credential stands. */
727
+ credential?: string;
728
+ /** Env var the credential is set into INSTEAD of the endpoint's conventional
729
+ * key env (the conventional one is then scrubbed). */
730
+ credentialEnvOverride?: string;
731
+ /** Extra env set verbatim beside the credential. A value that is a JSON
732
+ * object is deep-merged over a same-key value the spawn already carries
733
+ * (e.g. a mode's inline config), never clobbering it. */
734
+ env?: Record<string, string>;
735
+ /** `false` opts out of the adapter's isolated, login-less data home for this
736
+ * spawn; omitted ⇒ whatever the adapter declares applies. */
737
+ isolateDataHome?: boolean;
738
+ }
739
+ /** Raised by a provider when a sub-account cannot be listed or resolved. The
740
+ * runtime maps it onto its own spawn-error codes; the message must never carry
741
+ * a secret. */
742
+ declare class SubaccountError extends Error {
743
+ constructor(message: string);
744
+ }
745
+ interface SubaccountProvider {
746
+ /** Registry key, unique per provider. */
747
+ id: string;
748
+ /** The `source` name that identifies this provider's source-backed accounts
749
+ * (`AuthProfile.source`). */
750
+ source?: string;
751
+ /** Endpoints whose credential-backed profiles this provider serves. */
752
+ endpoints?: readonly string[];
753
+ /** Sub-account kinds this provider understands. */
754
+ kinds: readonly string[];
755
+ /** Endpoint a profile created from a discovered sub-account lands on. */
756
+ defaultEndpoint?: string;
757
+ /** Default id prefix for profiles created from a discovery (`<prefix>-<name>`). */
758
+ profilePrefix?: string;
759
+ /** Discovery: the sub-accounts of `account`. */
760
+ list(account: SubaccountAccountRef): Promise<SubaccountListing>;
761
+ /** Spawn-time: how the pinned `profile` is applied. `credential` is the
762
+ * profile's stored credential when it has one. */
763
+ resolve(profile: AuthProfile & {
764
+ subaccount: SubaccountPin;
765
+ }, ctx: {
766
+ credential?: string;
767
+ }): Promise<SubaccountResolution>;
768
+ /** Migrate-on-read hook for a profile shape that predates generic pins: a
769
+ * legacy `source` string → the account `source` + the pin it encoded. */
770
+ migrateLegacySource?(source: string): SubaccountPin | undefined;
771
+ }
772
+ /** Register (or replace, by `id`) a provider. Idempotent. */
773
+ declare function registerSubaccountProvider(provider: SubaccountProvider): void;
774
+ /** Remove a provider; returns whether it was registered. Mainly for tests. */
775
+ declare function unregisterSubaccountProvider(id: string): boolean;
776
+ declare function listSubaccountProviders(): SubaccountProvider[];
777
+ declare function getSubaccountProvider(id: string): SubaccountProvider | undefined;
778
+ /** The provider owning an account: by `source` name first, else by `endpoint`
779
+ * for a credential-backed account. */
780
+ declare function findSubaccountProvider(account: {
781
+ source?: string;
782
+ endpoint?: string;
783
+ }): SubaccountProvider | undefined;
784
+ /**
785
+ * Migrate-on-read: a profile that carries a legacy provider-encoded `source`
786
+ * (and no `subaccount`) is returned with the generic shape — `source` reduced
787
+ * to the account name, the encoded pin lifted into `subaccount`. Any other
788
+ * profile is returned as-is. Pure over the registry; never writes.
789
+ */
790
+ declare function migrateLegacySubaccountProfile(profile: AuthProfile): AuthProfile;
791
+
668
792
  /**
669
793
  * Named auth-profile store — CRUD over `~/.agentproto/auth-profiles.json`
670
794
  * (mode 0600), generalizing `providers-store`'s single-key-per-provider file
@@ -692,6 +816,11 @@ declare const authProfilesFileSchema: z.ZodObject<{
692
816
  }>;
693
817
  credentialRef: z.ZodOptional<z.ZodString>;
694
818
  source: z.ZodOptional<z.ZodString>;
819
+ subaccount: z.ZodOptional<z.ZodObject<{
820
+ kind: z.ZodString;
821
+ id: z.ZodString;
822
+ name: z.ZodOptional<z.ZodString>;
823
+ }, z.core.$strip>>;
695
824
  label: z.ZodOptional<z.ZodString>;
696
825
  disabled: z.ZodOptional<z.ZodBoolean>;
697
826
  models: z.ZodOptional<z.ZodObject<{
@@ -716,6 +845,11 @@ declare const authProfilesFileSchema: z.ZodObject<{
716
845
  method: "oauth-bearer" | "api-key";
717
846
  credentialRef?: string | undefined;
718
847
  source?: string | undefined;
848
+ subaccount?: {
849
+ kind: string;
850
+ id: string;
851
+ name?: string | undefined;
852
+ } | undefined;
719
853
  label?: string | undefined;
720
854
  disabled?: boolean | undefined;
721
855
  models?: {
@@ -766,9 +900,9 @@ declare function removeAuthProfile(id: string): Promise<boolean>;
766
900
 
767
901
  /** Input to {@link createAuthProfile}. `credential` is the raw secret — it is
768
902
  * written to the store and NEVER returned. Exactly one of `credential` /
769
- * `source` must be given for an `oauth-bearer` profile; `api-key` always
770
- * requires `credential` (a source-backed profile only makes sense for a
771
- * self-refreshing subscription bearer). */
903
+ * `source` must be given for an `oauth-bearer` profile; `api-key` requires
904
+ * `credential`, except for a `source` owned by a registered sub-account
905
+ * provider (an account resolved at spawn, pinned to a `subaccount`). */
772
906
  interface CreateAuthProfileInput {
773
907
  /** Stable id, unique across all profiles. */
774
908
  id: string;
@@ -785,6 +919,9 @@ interface CreateAuthProfileInput {
785
919
  * resolved fresh at spawn time instead. Mutually exclusive with
786
920
  * `credential`. */
787
921
  source?: string;
922
+ /** Pin one sub-account (org / workspace / project) of the account — the
923
+ * `credential` or the `source`. See `subaccounts.ts`. */
924
+ subaccount?: SubaccountPin;
788
925
  /** Optional human-readable name. */
789
926
  label?: string;
790
927
  /** Optional explicit credential-store slot. Omitted ⇒ derived from
@@ -806,6 +943,8 @@ interface CreatedAuthProfile {
806
943
  credentialRef?: string;
807
944
  /** Set for a source-backed profile; absent for a credential-backed one. */
808
945
  source?: string;
946
+ /** The pinned sub-account, when the profile has one. */
947
+ subaccount?: SubaccountPin;
809
948
  label?: string;
810
949
  /** Provenance stamped at import time, when given. */
811
950
  origin?: string;
@@ -845,6 +984,7 @@ interface ValidatedCreateInput {
845
984
  method: AuthMethod;
846
985
  credential?: string;
847
986
  source?: string;
987
+ subaccount?: SubaccountPin;
848
988
  label?: string;
849
989
  credentialRef?: string;
850
990
  origin?: string;
@@ -1097,4 +1237,4 @@ declare const BUILTIN_AUTH_PROVIDERS: readonly AuthProviderHandle[];
1097
1237
  declare const SPEC_NAME = "agentauth";
1098
1238
  declare const SPEC_VERSION = "v1";
1099
1239
 
1100
- export { type AdapterAuthManifest, type AuthConfig, type AuthMethod, type AuthProfile, AuthProfileValidationError, type AuthProfilesFile, type AuthProviderDefinition, type AuthProviderFrontmatter, type AuthProviderHandle, type AuthProviderManifest, BUILTIN_AUTH_PROVIDERS, CeremonyRequiredError, type CostBudget, type CostBudgetScope, type CreateAuthProfileInput, type CreatedAuthProfile, CredentialBroker, type CredentialBrokerOptions, type CredentialIdentity, type CredentialStore, type DeletedAuthProfile, type DeviceCodeAuthConfig, type DiscoveredEndpoints, DiscoveryError, FLOW_ENGINES, FileStore, type FlowEngine, type FlowId, type FlowResult, type FlowRunOptions, type InstallConfig, KeychainStore, MemoryStore, type ModelCuration, type PATAuthConfig, type ProfileProvisionDeps, type RefreshAuthProfileModelsResult, type RunFlowOptions, SPEC_NAME, SPEC_VERSION, type ServiceAuthConfig, type StoreRef, type StoredCredential, type TokenStoreSpec, type UpdateAuthProfileInput, type ValidatedCreateInput, addAuthProfile, authConfigSchema, authProfilesPath, authProviderFrontmatterSchema, createAuthProfile, credentialIdentity, defineAuthProvider, deleteAuthProfile, deleteKeychainToken, deriveCredentialRef, discoverEndpoints, eligibleProfiles, fingerprintCredential, getAuthProfile, getAuthProvider, guildeAuthProvider, installConfigSchema, listAuthProfiles, listAuthProviderIds, listAuthProviders, loadAuthProfiles, methodsPresentable, parseAuthProviderManifest, parseAuthProviderManifestRaw, readKeychainToken, refreshAuthProfileModels, registerAuthProvider, removeAuthProfile, resolveAccount, resolveEndpoint, resolveStoreRef, runAuthFlow, setAuthProfileEnabled, setAuthProfileModels, tokenStoreSpecSchema, updateAuthProfile, validateCreateInput, writeKeychainToken };
1240
+ export { type AdapterAuthManifest, type AuthConfig, type AuthMethod, type AuthProfile, AuthProfileValidationError, type AuthProfilesFile, type AuthProviderDefinition, type AuthProviderFrontmatter, type AuthProviderHandle, type AuthProviderManifest, BUILTIN_AUTH_PROVIDERS, CeremonyRequiredError, type CostBudget, type CostBudgetScope, type CreateAuthProfileInput, type CreatedAuthProfile, CredentialBroker, type CredentialBrokerOptions, type CredentialIdentity, type CredentialStore, type DeletedAuthProfile, type DeviceCodeAuthConfig, type DiscoveredEndpoints, type DiscoveredSubaccount, DiscoveryError, FLOW_ENGINES, FileStore, type FlowEngine, type FlowId, type FlowResult, type FlowRunOptions, type InstallConfig, KeychainStore, MemoryStore, type ModelCuration, type PATAuthConfig, type ProfileProvisionDeps, type RefreshAuthProfileModelsResult, type RunFlowOptions, SPEC_NAME, SPEC_VERSION, SUBACCOUNT_KIND_RE, type ServiceAuthConfig, type StoreRef, type StoredCredential, type SubaccountAccountRef, SubaccountError, type SubaccountListing, type SubaccountPin, type SubaccountProvider, type SubaccountResolution, type TokenStoreSpec, type UpdateAuthProfileInput, type ValidatedCreateInput, addAuthProfile, authConfigSchema, authProfilesPath, authProviderFrontmatterSchema, createAuthProfile, credentialIdentity, defineAuthProvider, deleteAuthProfile, deleteKeychainToken, deriveCredentialRef, discoverEndpoints, eligibleProfiles, findSubaccountProvider, fingerprintCredential, getAuthProfile, getAuthProvider, getSubaccountProvider, guildeAuthProvider, installConfigSchema, listAuthProfiles, listAuthProviderIds, listAuthProviders, listSubaccountProviders, loadAuthProfiles, methodsPresentable, migrateLegacySubaccountProfile, parseAuthProviderManifest, parseAuthProviderManifestRaw, parseSubaccountPin, readKeychainToken, refreshAuthProfileModels, registerAuthProvider, registerSubaccountProvider, removeAuthProfile, resolveAccount, resolveEndpoint, resolveStoreRef, runAuthFlow, setAuthProfileEnabled, setAuthProfileModels, tokenStoreSpecSchema, unregisterSubaccountProvider, updateAuthProfile, validateCreateInput, writeKeychainToken };
package/dist/index.mjs CHANGED
@@ -1248,6 +1248,54 @@ var CredentialBroker = class {
1248
1248
  return headers;
1249
1249
  }
1250
1250
  };
1251
+
1252
+ // src/subaccounts.ts
1253
+ var SUBACCOUNT_KIND_RE = /^[a-z][a-z0-9-]*$/;
1254
+ function parseSubaccountPin(spec) {
1255
+ const at = spec.indexOf(":");
1256
+ if (at <= 0) return void 0;
1257
+ const kind = spec.slice(0, at).trim();
1258
+ const id = spec.slice(at + 1).trim();
1259
+ if (!SUBACCOUNT_KIND_RE.test(kind) || id === "") return void 0;
1260
+ return { kind, id };
1261
+ }
1262
+ var SubaccountError = class extends Error {
1263
+ constructor(message) {
1264
+ super(message);
1265
+ this.name = "SubaccountError";
1266
+ }
1267
+ };
1268
+ var providers = /* @__PURE__ */ new Map();
1269
+ function registerSubaccountProvider(provider) {
1270
+ providers.set(provider.id, provider);
1271
+ }
1272
+ function unregisterSubaccountProvider(id) {
1273
+ return providers.delete(id);
1274
+ }
1275
+ function listSubaccountProviders() {
1276
+ return [...providers.values()];
1277
+ }
1278
+ function getSubaccountProvider(id) {
1279
+ return providers.get(id);
1280
+ }
1281
+ function findSubaccountProvider(account) {
1282
+ const all = listSubaccountProviders();
1283
+ if (account.source !== void 0) {
1284
+ return all.find((p) => p.source === account.source) ?? all.find((p) => p.migrateLegacySource?.(account.source) !== void 0);
1285
+ }
1286
+ if (account.endpoint !== void 0) return all.find((p) => p.endpoints?.includes(account.endpoint));
1287
+ return void 0;
1288
+ }
1289
+ function migrateLegacySubaccountProfile(profile) {
1290
+ if (profile.subaccount !== void 0 || profile.source === void 0) return profile;
1291
+ for (const provider of providers.values()) {
1292
+ const pin = provider.migrateLegacySource?.(profile.source);
1293
+ if (pin !== void 0 && provider.source !== void 0) {
1294
+ return { ...profile, source: provider.source, subaccount: pin };
1295
+ }
1296
+ }
1297
+ return profile;
1298
+ }
1251
1299
  var authMethodSchema = z.enum(["oauth-bearer", "api-key"]);
1252
1300
  var modelCurationSchema = z.object({
1253
1301
  mode: z.enum(["all", "allow"]),
@@ -1258,12 +1306,18 @@ var costBudgetSchema = z.object({
1258
1306
  window: z.string(),
1259
1307
  scope: z.enum(["session", "profile"])
1260
1308
  });
1309
+ var subaccountPinSchema = z.object({
1310
+ kind: z.string(),
1311
+ id: z.string(),
1312
+ name: z.string().optional()
1313
+ });
1261
1314
  var authProfileSchema = z.object({
1262
1315
  id: z.string(),
1263
1316
  vendor: z.string(),
1264
1317
  method: authMethodSchema,
1265
1318
  credentialRef: z.string().optional(),
1266
1319
  source: z.string().optional(),
1320
+ subaccount: subaccountPinSchema.optional(),
1267
1321
  label: z.string().optional(),
1268
1322
  // Additive, back-compat fields — an entry that predates them parses
1269
1323
  // unchanged (both optional), and `endpoint` still shadows the on-disk
@@ -1310,12 +1364,13 @@ async function writeAuthProfiles(file) {
1310
1364
  }
1311
1365
  async function listAuthProfiles(endpoint) {
1312
1366
  const file = await loadAuthProfiles();
1313
- const all = Object.values(file.profiles);
1367
+ const all = Object.values(file.profiles).map(migrateLegacySubaccountProfile);
1314
1368
  return endpoint === void 0 ? all : all.filter((p) => p.endpoint === endpoint);
1315
1369
  }
1316
1370
  async function getAuthProfile(id) {
1317
1371
  const file = await loadAuthProfiles();
1318
- return file.profiles[id];
1372
+ const profile = file.profiles[id];
1373
+ return profile ? migrateLegacySubaccountProfile(profile) : void 0;
1319
1374
  }
1320
1375
  async function addAuthProfile(profile) {
1321
1376
  const file = await loadAuthProfiles();
@@ -1360,13 +1415,49 @@ function validateCreateInput(input) {
1360
1415
  }
1361
1416
  const credential = input.credential !== void 0 ? input.credential.trim() : void 0;
1362
1417
  const source = input.source?.trim();
1363
- if (method === "api-key") {
1364
- if (source) {
1418
+ let subaccount;
1419
+ if (input.subaccount !== void 0) {
1420
+ const kind = (input.subaccount.kind ?? "").trim();
1421
+ const subId = (input.subaccount.id ?? "").trim();
1422
+ const subName = input.subaccount.name?.trim();
1423
+ if (!SUBACCOUNT_KIND_RE.test(kind) || !subId) {
1365
1424
  throw new AuthProfileValidationError(
1366
- "source is only supported for oauth-bearer profiles \u2014 api-key profiles require a credential"
1425
+ `subaccount must be "<kind>:<id>" with a lowercase kind (org, workspace, project, \u2026) and a non-empty id`
1367
1426
  );
1368
1427
  }
1369
- if (!credential) throw new AuthProfileValidationError("credential is required");
1428
+ subaccount = { kind, id: subId, ...subName ? { name: subName } : {} };
1429
+ if (!credential && !source) {
1430
+ throw new AuthProfileValidationError(
1431
+ "a subaccount pin needs a parent account \u2014 give a credential or a source"
1432
+ );
1433
+ }
1434
+ }
1435
+ const provider = source ? findSubaccountProvider({ source }) : void 0;
1436
+ if (subaccount && provider && !provider.kinds.includes(subaccount.kind)) {
1437
+ throw new AuthProfileValidationError(
1438
+ `subaccount kind "${subaccount.kind}" is not supported by "${provider.id}" (supported: ${provider.kinds.join(", ")})`
1439
+ );
1440
+ }
1441
+ if (method === "api-key") {
1442
+ if (source) {
1443
+ if (!provider) {
1444
+ throw new AuthProfileValidationError(
1445
+ `source is only supported for oauth-bearer profiles and for accounts owned by a registered sub-account provider \u2014 api-key profiles otherwise require a credential`
1446
+ );
1447
+ }
1448
+ if (!subaccount && provider.migrateLegacySource?.(source) === void 0) {
1449
+ throw new AuthProfileValidationError(
1450
+ `source "${source}" resolves an account with sub-accounts \u2014 pin one with a subaccount ("<kind>:<id>")`
1451
+ );
1452
+ }
1453
+ if (credential) {
1454
+ throw new AuthProfileValidationError(
1455
+ "give either credential or source, not both \u2014 a source-backed profile stores no secret"
1456
+ );
1457
+ }
1458
+ } else if (!credential) {
1459
+ throw new AuthProfileValidationError("credential is required");
1460
+ }
1370
1461
  } else {
1371
1462
  if (credential && source) {
1372
1463
  throw new AuthProfileValidationError(
@@ -1391,6 +1482,7 @@ function validateCreateInput(input) {
1391
1482
  method,
1392
1483
  ...credential ? { credential } : {},
1393
1484
  ...source ? { source } : {},
1485
+ ...subaccount ? { subaccount } : {},
1394
1486
  ...label ? { label } : {},
1395
1487
  ...credentialRef ? { credentialRef } : {},
1396
1488
  ...origin ? { origin } : {}
@@ -1423,20 +1515,22 @@ async function createAuthProfile(input, deps) {
1423
1515
  );
1424
1516
  }
1425
1517
  if (v.source !== void 0) {
1426
- const profile2 = {
1518
+ const profile2 = migrateLegacySubaccountProfile({
1427
1519
  id: v.id,
1428
1520
  endpoint: v.endpoint,
1429
1521
  method: v.method,
1430
1522
  source: v.source,
1523
+ ...v.subaccount ? { subaccount: v.subaccount } : {},
1431
1524
  ...v.label ? { label: v.label } : {},
1432
1525
  ...v.origin ? { origin: v.origin } : {}
1433
- };
1526
+ });
1434
1527
  await deps.addProfile(profile2);
1435
1528
  return {
1436
1529
  id: profile2.id,
1437
1530
  endpoint: profile2.endpoint,
1438
1531
  method: profile2.method,
1439
- source: v.source,
1532
+ source: profile2.source,
1533
+ ...profile2.subaccount ? { subaccount: profile2.subaccount } : {},
1440
1534
  ...profile2.label ? { label: profile2.label } : {},
1441
1535
  ...profile2.origin ? { origin: profile2.origin } : {}
1442
1536
  };
@@ -1457,6 +1551,7 @@ async function createAuthProfile(input, deps) {
1457
1551
  endpoint: v.endpoint,
1458
1552
  method: v.method,
1459
1553
  credentialRef,
1554
+ ...v.subaccount ? { subaccount: v.subaccount } : {},
1460
1555
  ...v.label ? { label: v.label } : {},
1461
1556
  ...v.origin ? { origin: v.origin } : {}
1462
1557
  };
@@ -1466,6 +1561,7 @@ async function createAuthProfile(input, deps) {
1466
1561
  endpoint: profile.endpoint,
1467
1562
  method: profile.method,
1468
1563
  credentialRef,
1564
+ ...v.subaccount ? { subaccount: v.subaccount } : {},
1469
1565
  ...profile.label ? { label: profile.label } : {},
1470
1566
  ...profile.origin ? { origin: profile.origin } : {},
1471
1567
  fingerprint: fingerprintCredential(credential)
@@ -1626,6 +1722,6 @@ function eligibleProfiles(profiles, manifest, route) {
1626
1722
  var SPEC_NAME = "agentauth";
1627
1723
  var SPEC_VERSION = "v1";
1628
1724
 
1629
- export { AuthProfileValidationError, BUILTIN_AUTH_PROVIDERS, CeremonyRequiredError, CredentialBroker, DiscoveryError, FLOW_ENGINES, FileStore, KeychainStore, MemoryStore, SPEC_NAME, SPEC_VERSION, addAuthProfile, authConfigSchema, authProfilesPath, authProviderFrontmatterSchema, createAuthProfile, credentialIdentity, defineAuthProvider, deleteAuthProfile, deleteKeychainToken, deriveCredentialRef, discoverEndpoints, eligibleProfiles, fingerprintCredential, getAuthProfile, getAuthProvider, guildeAuthProvider, installConfigSchema, listAuthProfiles, listAuthProviderIds, listAuthProviders, loadAuthProfiles, methodsPresentable, parseAuthProviderManifest, parseAuthProviderManifestRaw, readKeychainToken, refreshAuthProfileModels, registerAuthProvider, removeAuthProfile, resolveAccount, resolveEndpoint, resolveStoreRef, runAuthFlow, setAuthProfileEnabled, setAuthProfileModels, tokenStoreSpecSchema, updateAuthProfile, validateCreateInput, writeKeychainToken };
1725
+ export { AuthProfileValidationError, BUILTIN_AUTH_PROVIDERS, CeremonyRequiredError, CredentialBroker, DiscoveryError, FLOW_ENGINES, FileStore, KeychainStore, MemoryStore, SPEC_NAME, SPEC_VERSION, SUBACCOUNT_KIND_RE, SubaccountError, addAuthProfile, authConfigSchema, authProfilesPath, authProviderFrontmatterSchema, createAuthProfile, credentialIdentity, defineAuthProvider, deleteAuthProfile, deleteKeychainToken, deriveCredentialRef, discoverEndpoints, eligibleProfiles, findSubaccountProvider, fingerprintCredential, getAuthProfile, getAuthProvider, getSubaccountProvider, guildeAuthProvider, installConfigSchema, listAuthProfiles, listAuthProviderIds, listAuthProviders, listSubaccountProviders, loadAuthProfiles, methodsPresentable, migrateLegacySubaccountProfile, parseAuthProviderManifest, parseAuthProviderManifestRaw, parseSubaccountPin, readKeychainToken, refreshAuthProfileModels, registerAuthProvider, registerSubaccountProvider, removeAuthProfile, resolveAccount, resolveEndpoint, resolveStoreRef, runAuthFlow, setAuthProfileEnabled, setAuthProfileModels, tokenStoreSpecSchema, unregisterSubaccountProvider, updateAuthProfile, validateCreateInput, writeKeychainToken };
1630
1726
  //# sourceMappingURL=index.mjs.map
1631
1727
  //# sourceMappingURL=index.mjs.map