@ax-hub/sdk 0.2.0 → 1.0.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/dist/index.d.cts CHANGED
@@ -124,6 +124,15 @@ declare class StreamConsumedError extends AxHubError {
124
124
  }
125
125
  declare class TenantSlugRequiredError extends AxHubError {
126
126
  }
127
+ /**
128
+ * SDK-self error: tenant-scoped app routes
129
+ * (`POST`/`GET /api/v1/tenants/{tenantID}/apps`) require a tenant **UUID** in
130
+ * the path. Thrown before any request when the SDK holds only a slug (or no
131
+ * tenant context). Mirrors {@link TenantSlugRequiredError}; constructed inline
132
+ * at call sites (crud.ts / scoped.ts), like TenantSlugRequiredError in client.ts.
133
+ */
134
+ declare class TenantIdRequiredError extends AxHubError {
135
+ }
127
136
  declare class ConfigurationError extends AxHubError {
128
137
  }
129
138
  declare class PoolStaleError extends UnauthenticatedError {
@@ -169,15 +178,24 @@ declare class DeviceFlowDeniedError extends AccessDeniedError {
169
178
  }
170
179
  declare class DeviceFlowTimeoutError extends ExpiredTokenError {
171
180
  }
172
-
173
- declare function isOAuthPath(path: string): boolean;
174
- interface DispatchContext {
175
- path: string;
176
- status: number;
177
- body: unknown;
178
- fallbackRequestId: string;
181
+ declare class InvalidTargetError extends OAuthError {
182
+ }
183
+ declare class InvalidClientError extends OAuthError {
184
+ }
185
+ declare class InvalidRequestError extends OAuthError {
186
+ }
187
+ declare class InvalidScopeError extends OAuthError {
188
+ }
189
+ declare class InvalidTokenError extends OAuthError {
190
+ }
191
+ declare class UnauthorizedClientError extends OAuthError {
192
+ }
193
+ declare class UnsupportedGrantTypeError extends OAuthError {
194
+ }
195
+ declare class OAuthServerError extends OAuthError {
196
+ }
197
+ declare class TemporarilyUnavailableError extends OAuthError {
179
198
  }
180
- declare function dispatch(ctx: DispatchContext): AxHubError;
181
199
 
182
200
  declare const brand: unique symbol;
183
201
  type Branded<T, B extends string> = T & {
@@ -305,6 +323,26 @@ declare class HttpClient {
305
323
  private logRequest;
306
324
  }
307
325
 
326
+ interface StaticTokenAuthOptions {
327
+ token: string;
328
+ tokenType: TokenType;
329
+ onRefresh?: () => Promise<string>;
330
+ }
331
+ declare class StaticTokenAuth implements AuthProvider {
332
+ private token;
333
+ readonly tokenType: TokenType;
334
+ private readonly onRefresh?;
335
+ private refreshing;
336
+ constructor(opts: StaticTokenAuthOptions);
337
+ currentToken(): string;
338
+ headersFor(ring: AuthRing): Record<string, string>;
339
+ onUnauthorized(): Promise<boolean>;
340
+ }
341
+ declare class NoAuth implements AuthProvider {
342
+ headersFor(_ring: AuthRing): Record<string, string>;
343
+ onUnauthorized(): Promise<boolean>;
344
+ }
345
+
308
346
  interface PaginatedList<T> {
309
347
  items: T[];
310
348
  nextCursor: string | null;
@@ -378,282 +416,59 @@ declare function decodeCursor(token: string): KeysetCursor;
378
416
  declare function orderByFingerprint(orderBy?: DataOrderBy): string;
379
417
  declare function cursorFromRow(row: Record<string, unknown> | undefined, opts?: CursorBuildOptions): string | null;
380
418
 
381
- interface RequestOptions {
419
+ interface ParsedFrame {
420
+ id?: string;
421
+ event?: string;
422
+ data: string;
423
+ }
424
+ type StreamItem<T> = {
425
+ type: 'item';
426
+ value: T;
427
+ id: string | undefined;
428
+ } | {
429
+ type: 'gap';
430
+ sinceId: string | undefined;
431
+ missingCount?: number;
432
+ } | {
433
+ type: 'decode-skip';
434
+ frame: ParsedFrame;
435
+ error: AxHubError;
436
+ };
437
+ interface SSEStreamOptions<T> {
438
+ url: string;
439
+ http: HttpClient;
440
+ ring: AuthRing;
382
441
  signal?: AbortSignal;
383
- timeoutMs?: number;
384
- idempotencyKey?: string | false;
442
+ maxReconnects?: number;
443
+ parseEvent: (frame: ParsedFrame) => T;
385
444
  }
386
- interface PageRequestOptions extends ListOptions, RequestOptions {
445
+ interface SSEStream<T> extends AsyncIterable<StreamItem<T>> {
446
+ dispose(): void;
387
447
  }
388
448
 
389
- interface AuditEvent {
390
- id: string;
391
- type?: string;
392
- actorId?: string;
393
- hash?: string;
394
- prevHash?: string;
395
- createdAt?: string;
396
- [key: string]: unknown;
397
- }
398
- interface EmitAuditEventInput {
399
- type: string;
400
- actorId?: string;
401
- resource?: string;
402
- payload?: unknown;
449
+ type WebhookVerifyReason = 'signature_mismatch' | 'timestamp_skew' | 'malformed_signature' | 'missing_secret' | 'replay';
450
+ interface VerifyWebhookInput {
451
+ rawBody: Buffer | Uint8Array | string;
452
+ signature: string;
453
+ secret: string;
454
+ tolerance?: number;
455
+ timestamp?: string;
456
+ replayCache?: Set<string>;
457
+ now?: () => number;
403
458
  }
404
- interface IntegrityCheckResult {
459
+ interface VerifyWebhookResult {
405
460
  ok: boolean;
406
- checked: number;
407
- firstBadSeq?: number;
408
- reason?: string;
409
- }
410
- interface AnonymizeInput {
411
- subjectId?: string;
412
- actorId?: string;
413
- anonymizedId?: string;
414
- reason?: string;
415
- }
416
- declare class AuditClient {
417
- private readonly http;
418
- readonly events: AuditEventsClient;
419
- readonly server: AuditServerClient;
420
- constructor(http: HttpClient);
421
- scoped(tenantSlug: string): TenantAuditClient;
422
- integrityCheck(tenantSlug: string, opts?: RequestOptions): Promise<IntegrityCheckResult>;
423
- anonymize(tenantSlug: string, input: AnonymizeInput, opts?: RequestOptions): Promise<AuditEvent | void>;
424
- }
425
- declare class TenantAuditClient {
426
- private readonly http;
427
- private readonly tenantSlug;
428
- readonly events: AuditEventsForTenantClient;
429
- readonly server: AuditServerForTenantClient;
430
- constructor(http: HttpClient, tenantSlug: string);
431
- integrityCheck(opts?: RequestOptions): Promise<IntegrityCheckResult>;
432
- anonymize(input: AnonymizeInput, opts?: RequestOptions): Promise<AuditEvent | void>;
433
- }
434
- declare class AuditEventsClient {
435
- private readonly http;
436
- constructor(http: HttpClient);
437
- forTenant(tenantSlug: string): AuditEventsForTenantClient;
438
- }
439
- declare class AuditEventsForTenantClient {
440
- private readonly http;
441
- private readonly tenantSlug;
442
- constructor(http: HttpClient, tenantSlug: string);
443
- list(opts?: PageRequestOptions & {
444
- type?: string;
445
- }): Promise<PaginatedList<AuditEvent>>;
446
- get(eventId: string, opts?: RequestOptions): Promise<AuditEvent>;
447
- }
448
- declare class AuditServerClient {
449
- private readonly http;
450
- constructor(http: HttpClient);
451
- forTenant(tenantSlug: string): AuditServerForTenantClient;
452
- }
453
- declare class AuditServerForTenantClient {
454
- private readonly http;
455
- private readonly tenantSlug;
456
- constructor(http: HttpClient, tenantSlug: string);
457
- /**
458
- * Emit a server-side audit event.
459
- *
460
- * @remarks The backend route `POST /audit-events/server` is not yet
461
- * implemented (currently 404). Retained ahead of backend support — see
462
- * ADR-0040. Do not rely on this in production until the route ships.
463
- */
464
- emit(input: EmitAuditEventInput, opts?: RequestOptions): Promise<AuditEvent>;
465
- }
466
-
467
- interface AuthzTag {
468
- id: string;
469
- name: string;
470
- parentId?: string;
471
- [key: string]: unknown;
472
- }
473
- interface AuthzSubject {
474
- id: string;
475
- type: string;
476
- externalId?: string;
477
- [key: string]: unknown;
478
- }
479
- interface AuthzGrant {
480
- id: string;
481
- subjectId: string;
482
- resource: string;
483
- action: string;
484
- effect: 'allow' | 'deny';
485
- [key: string]: unknown;
486
- }
487
- interface DecideInput {
488
- subjectId: string;
489
- resource: string;
490
- action: string;
491
- context?: Record<string, unknown>;
492
- }
493
- interface DecideResult {
494
- allowed: boolean;
495
- reason?: string;
496
- matchedGrantId?: string;
497
- }
498
- declare class AuthzClient {
499
- private readonly http;
500
- constructor(http: HttpClient);
501
- scoped(tenantSlug: string): TenantAuthzClient;
502
- }
503
- declare class TenantAuthzClient {
504
- /** @adminOnly Tag governance. `list()` requires tenant_admin (v0.1, SPEC 307) — member callers get ForbiddenError (no auto-retry; permission, not transient). Members browse the data catalog via `gateway.catalog`. */
505
- readonly tags: Crud<AuthzTag>;
506
- /** @adminOnly Subject governance. `list()` requires tenant_admin (v0.1) — member callers get ForbiddenError. */
507
- readonly subjects: Crud<AuthzSubject>;
508
- /** @adminOnly Grant governance. `list()` requires tenant_admin (v0.1) — member callers get ForbiddenError. */
509
- readonly grants: Crud<AuthzGrant>;
510
- readonly evaluator: AuthzEvaluatorClient;
511
- constructor(http: HttpClient, tenantSlug: string);
512
- }
513
- declare class Crud<T extends {
514
- id: string;
515
- }> {
516
- private readonly http;
517
- private readonly base;
518
- constructor(http: HttpClient, base: string);
519
- list(opts?: RequestOptions): Promise<T[]>;
520
- get(id: string, opts?: RequestOptions): Promise<T>;
521
- create(input: Record<string, unknown>, opts?: RequestOptions): Promise<T>;
522
- update(id: string, patch: Record<string, unknown>, opts?: RequestOptions): Promise<T>;
523
- delete(id: string, opts?: RequestOptions): Promise<void>;
524
- }
525
- declare class AuthzEvaluatorClient {
526
- private readonly http;
527
- private readonly base;
528
- private readonly ttlMs;
529
- private readonly cache;
530
- constructor(http: HttpClient, base: string, ttlMs?: number);
531
- decide(input: DecideInput, opts?: RequestOptions): Promise<DecideResult>;
532
- decideMany(inputs: DecideInput[], opts?: RequestOptions): Promise<DecideResult[]>;
461
+ reason?: WebhookVerifyReason;
533
462
  }
463
+ declare function signWebhook(rawBody: Buffer | Uint8Array | string, secret: string, timestamp?: string): string;
464
+ declare function verifyWebhook(input: VerifyWebhookInput): VerifyWebhookResult;
534
465
 
535
- interface Tenant {
536
- id: string;
537
- slug: string;
538
- name: string;
539
- plan?: string;
540
- createdAt?: string;
541
- updatedAt?: string;
542
- [key: string]: unknown;
543
- }
544
- interface CreateTenantInput {
545
- slug: string;
546
- name: string;
547
- plan?: string;
548
- }
549
- interface UpdateTenantInput {
550
- name?: string;
551
- plan?: string;
552
- }
553
- interface TenantMember {
554
- id: string;
555
- userId: string;
556
- role: string;
557
- status?: string;
558
- [key: string]: unknown;
559
- }
560
- interface TenantInvitation {
561
- id: string;
562
- email: string;
563
- role: string;
564
- status: string;
565
- reason?: string;
566
- [key: string]: unknown;
567
- }
568
- interface InviteTenantMemberInput {
569
- email: string;
570
- role: string;
571
- reason?: string;
572
- }
573
- interface BulkInviteResult {
574
- accepted: TenantInvitation[];
575
- rejected: Array<{
576
- email: string;
577
- reason: string;
578
- message?: string;
579
- }>;
580
- }
581
- interface EmailDomain {
582
- domain: string;
583
- verified?: boolean;
584
- [key: string]: unknown;
585
- }
586
- interface TenantIconUploadResult {
587
- uploadUrl: string;
588
- getUrl: string;
589
- expiresAt?: string;
590
- }
591
- declare class TenantsClient {
592
- private readonly http;
593
- constructor(http: HttpClient);
594
- scoped(tenantIdOrSlug: string): TenantScopedTenantsClient;
595
- create(input: CreateTenantInput, opts?: RequestOptions): Promise<Tenant>;
596
- list(opts?: PageRequestOptions): Promise<PaginatedList<Tenant>>;
597
- get(tenantIdOrSlug: string, opts?: RequestOptions): Promise<Tenant>;
598
- update(tenantIdOrSlug: string, patch: UpdateTenantInput, opts?: RequestOptions): Promise<Tenant>;
599
- delete(tenantIdOrSlug: string, opts?: RequestOptions): Promise<void>;
600
- signIconUploadURL(tenantIdOrSlug: string, input: {
601
- contentType: string;
602
- }, opts?: RequestOptions): Promise<TenantIconUploadResult>;
603
- get members(): TenantMembersClient;
604
- get invitations(): TenantInvitationsClient;
605
- get emailDomains(): TenantEmailDomainsClient;
606
- }
607
- declare class TenantScopedTenantsClient {
608
- readonly members: TenantMembersForTenantClient;
609
- readonly invitations: TenantInvitationsForTenantClient;
610
- readonly emailDomains: TenantEmailDomainsForTenantClient;
611
- constructor(http: HttpClient, tenantIdOrSlug: string);
612
- }
613
- declare class TenantMembersClient {
614
- private readonly http;
615
- constructor(http: HttpClient);
616
- forTenant(tenantIdOrSlug: string): TenantMembersForTenantClient;
617
- }
618
- declare class TenantMembersForTenantClient {
619
- private readonly http;
620
- private readonly tenant;
621
- constructor(http: HttpClient, tenant: string);
622
- list(opts?: PageRequestOptions): Promise<PaginatedList<TenantMember>>;
623
- update(membershipId: string, patch: {
624
- role?: string;
625
- }, opts?: RequestOptions): Promise<void>;
626
- deactivate(membershipId: string, opts?: RequestOptions): Promise<void>;
627
- reactivate(membershipId: string, opts?: RequestOptions): Promise<void>;
628
- }
629
- declare class TenantInvitationsClient {
630
- private readonly http;
631
- constructor(http: HttpClient);
632
- forTenant(tenantIdOrSlug: string): TenantInvitationsForTenantClient;
633
- }
634
- declare class TenantInvitationsForTenantClient {
635
- private readonly http;
636
- private readonly tenant;
637
- constructor(http: HttpClient, tenant: string);
638
- list(opts?: PageRequestOptions): Promise<PaginatedList<TenantInvitation>>;
639
- create(input: InviteTenantMemberInput, opts?: RequestOptions): Promise<TenantInvitation>;
640
- bulkCreate(inputs: InviteTenantMemberInput[], opts?: RequestOptions): Promise<BulkInviteResult>;
641
- delete(invitationId: string, opts?: RequestOptions): Promise<void>;
642
- }
643
- declare class TenantEmailDomainsClient {
644
- private readonly http;
645
- constructor(http: HttpClient);
646
- forTenant(tenantIdOrSlug: string): TenantEmailDomainsForTenantClient;
466
+ interface RequestOptions {
467
+ signal?: AbortSignal;
468
+ timeoutMs?: number;
469
+ idempotencyKey?: string | false;
647
470
  }
648
- declare class TenantEmailDomainsForTenantClient {
649
- private readonly http;
650
- private readonly tenant;
651
- constructor(http: HttpClient, tenant: string);
652
- list(opts?: RequestOptions): Promise<EmailDomain[]>;
653
- create(input: {
654
- domain: string;
655
- }, opts?: RequestOptions): Promise<EmailDomain>;
656
- delete(domain: string, opts?: RequestOptions): Promise<void>;
471
+ interface PageRequestOptions extends ListOptions, RequestOptions {
657
472
  }
658
473
 
659
474
  interface EnvVar {
@@ -675,16 +490,18 @@ declare class AppsEnvVarsMixin {
675
490
  listEnvVars(appId: string): Promise<EnvVar[]>;
676
491
  /**
677
492
  * Create or overwrite an env var. Key must match `^[A-Z_][A-Z0-9_]*$` —
678
- * validation happens client-side before any network round-trip.
493
+ * validation happens client-side before any network round-trip. `stage`
494
+ * scopes the value to a deploy stage (wire: `stage`); omit for the default.
679
495
  *
680
496
  * @example happy
681
497
  * await sdk.apps.setEnvVar('app_abc', 'DATABASE_URL', 'postgres://...')
498
+ * await sdk.apps.setEnvVar('app_abc', 'API_KEY', 'k', 'production')
682
499
  *
683
500
  * @example failure
684
501
  * try { await sdk.apps.setEnvVar('app_abc', 'database url', 'x') }
685
502
  * catch (e) { if (e instanceof ValidationError) /* fix key *\/ }
686
503
  */
687
- setEnvVar(appId: string, key: string, value: string): Promise<void>;
504
+ setEnvVar(appId: string, key: string, value: string, stage?: string): Promise<void>;
688
505
  /**
689
506
  * Fetch a single env var by key.
690
507
  *
@@ -707,6 +524,121 @@ declare class AppsEnvVarsMixin {
707
524
  deleteEnvVar(appId: string, key: string): Promise<void>;
708
525
  }
709
526
 
527
+ interface FieldAvailability {
528
+ available: boolean;
529
+ /** Set when `available` is false: `invalid` | `reserved` | `taken`. */
530
+ reason?: string;
531
+ }
532
+ interface AppAvailability {
533
+ slug?: FieldAvailability;
534
+ subdomain?: FieldAvailability;
535
+ }
536
+ interface CheckAvailabilityInput {
537
+ slug?: string;
538
+ subdomain?: string;
539
+ }
540
+ interface IconPreCreateInput {
541
+ /** MIME type of the asset, e.g. `image/png`. */
542
+ contentType: string;
543
+ /** Slug of the app-to-be; becomes the object key suffix. Must match the slug used at create. */
544
+ slug: string;
545
+ /** Icon variant. Defaults to `light`. */
546
+ variant?: 'light' | 'dark';
547
+ }
548
+ interface IconPreCreateResult {
549
+ /** Presigned PUT URL the caller uploads the binary to. Single-use, ~5 min. */
550
+ uploadUrl: string;
551
+ /** Public GET URL to set as `iconUrl`/`iconDarkUrl` on the subsequent create. */
552
+ getUrl: string;
553
+ expiresAt?: string;
554
+ }
555
+ declare class AppsAvailabilityMixin {
556
+ private readonly http;
557
+ private readonly defaultTenantId?;
558
+ constructor(http: HttpClient, defaultTenantId?: string | undefined);
559
+ private requireTenantId;
560
+ /**
561
+ * Check whether an app `slug` and/or `subdomain` is available before
562
+ * creating (member-gated). Tenant-scoped: requires `defaultTenantId`. Each
563
+ * returned field is present only when its input was supplied.
564
+ *
565
+ * @example happy
566
+ * const a = await sdk.apps.checkAvailability({ slug: 'my-app', subdomain: 'my' })
567
+ * if (a.slug?.available && a.subdomain?.available) /* safe to create *\/
568
+ *
569
+ * @example failure
570
+ * try { await sdk.apps.checkAvailability({ slug: 'x' }) }
571
+ * catch (e) { if (e instanceof TenantIdRequiredError) /* set defaultTenantId *\/ }
572
+ */
573
+ checkAvailability(input: CheckAvailabilityInput): Promise<AppAvailability>;
574
+ /**
575
+ * Get a presigned PUT URL for an app icon BEFORE the app is created
576
+ * (member-gated). Tenant-scoped: requires `defaultTenantId`. Use the
577
+ * returned `getUrl` as `iconUrl` (or `iconDarkUrl` for `variant: 'dark'`)
578
+ * when calling `apps.create`. Distinct from `apps.signIconUploadURL`, which
579
+ * targets an existing app.
580
+ *
581
+ * @example happy
582
+ * const { uploadUrl, getUrl } = await sdk.apps.iconPreCreateUrl({ contentType: 'image/png', slug: 'my-app' })
583
+ * await fetch(uploadUrl, { method: 'PUT', body: imageBlob, headers: { 'Content-Type': 'image/png' } })
584
+ * await sdk.apps.create({ slug: 'my-app', name: 'My App', iconUrl: getUrl })
585
+ *
586
+ * @example failure
587
+ * try { await sdk.apps.iconPreCreateUrl({ contentType: 'application/zip', slug: 'my-app' }) }
588
+ * catch (e) { if (e instanceof NotAllowedError) /* unsupported MIME *\/ }
589
+ */
590
+ iconPreCreateUrl(input: IconPreCreateInput): Promise<IconPreCreateResult>;
591
+ }
592
+
593
+ /**
594
+ * Resolves a tenant slug to its UUID for the apps control-ring
595
+ * (`/api/v1/tenants/{UUID}/apps`). A UUID passes through unchanged (no network
596
+ * call); a slug is mapped to its UUID via the caller's own memberships
597
+ * (`GET /api/v1/me` → `tenants[]`).
598
+ *
599
+ * The slug→UUID map is cached per resolver instance. One resolver is owned by
600
+ * the root {@link AxHubClient} and shared with `withTenant`-derived clients via
601
+ * the internal options, so a client tree fires `/me` at most once per distinct
602
+ * slug regardless of how many `sdk.apps` / `sdk.tenant(slug).apps` calls run.
603
+ */
604
+ declare class TenantResolver {
605
+ private readonly me;
606
+ private readonly cache;
607
+ constructor(http: HttpClient);
608
+ /**
609
+ * Returns the tenant UUID for `slugOrUuid`. A UUID is returned as-is (fast
610
+ * path, no `/me`). A slug is looked up in the caller's memberships, caching
611
+ * the result. Throws {@link TenantIdRequiredError} if the slug is not among
612
+ * the caller's tenants (the `/me` call itself succeeded — the slug is simply
613
+ * not a membership — so this is an SDK-self "couldn't resolve a tenant UUID"
614
+ * error, not a backend 404).
615
+ */
616
+ resolveTenantId(slugOrUuid: string, opts?: RequestOptions): Promise<string>;
617
+ }
618
+
619
+ type AppStatus = 'draft' | 'deployed' | 'archived' | 'active' | 'deleted' | 'permanently_deleted' | (string & {});
620
+ type AppVisibility = 'private' | 'tenant' | 'public' | 'invite_only' | (string & {});
621
+ type AppPublicationStatus = 'draft' | 'pending_review' | 'approved' | (string & {});
622
+ type AppReviewStatus = 'not_submitted' | 'pending' | 'approved' | 'rejected' | (string & {});
623
+ interface AppResponseCategory {
624
+ id: string;
625
+ tenantId?: string;
626
+ slug: string;
627
+ name: string;
628
+ description?: string;
629
+ color?: string;
630
+ iconUrl?: string;
631
+ displayOrder?: number;
632
+ appsCount?: number;
633
+ discoverableAppsCount?: number;
634
+ createdAt?: string;
635
+ updatedAt?: string;
636
+ }
637
+ interface AppResponseOwner {
638
+ id: string;
639
+ name?: string;
640
+ avatarUrl?: string;
641
+ }
710
642
  interface AppResponse {
711
643
  id: string;
712
644
  tenantId: string;
@@ -715,35 +647,113 @@ interface AppResponse {
715
647
  name: string;
716
648
  description?: string;
717
649
  iconUrl?: string;
650
+ iconDarkUrl?: string;
718
651
  schemaName: string;
719
- status: 'active' | 'deleted' | 'permanently_deleted';
720
- visibility: 'private' | 'tenant' | 'public';
721
- publicationStatus: 'draft' | 'pending_review' | 'approved';
652
+ status: AppStatus;
653
+ visibility: AppVisibility;
654
+ publicationStatus: AppPublicationStatus;
655
+ reviewStatus?: AppReviewStatus;
656
+ authMode?: string;
657
+ dataScopes?: string[];
658
+ deployMethod?: string;
659
+ resourceTier?: string;
660
+ subdomain?: string;
661
+ accessUrl?: string | null;
662
+ lastDeploymentStatus?: string | null;
663
+ operatingStatus?: string;
664
+ suspendedAt?: string | null;
665
+ resumedAt?: string | null;
666
+ categoryId?: string;
667
+ category?: AppResponseCategory;
668
+ owner?: AppResponseOwner;
722
669
  likeCount: number;
723
670
  subscriberCount: number;
671
+ callsCount?: number;
672
+ sharedTablesCount?: number;
724
673
  createdAt: string;
725
674
  updatedAt: string;
726
675
  deletedAt: string | null;
727
676
  }
677
+
678
+ type AppSort = '-created_at' | '-like_count' | '-subscriber_count' | 'name';
679
+ /**
680
+ * Filter/sort/page params for the tenant-scoped apps list
681
+ * (`GET /api/v1/tenants/{tenantID}/apps`). The `*In` fields accept an array and
682
+ * serialize to the comma-separated string the backend expects. Filtering is
683
+ * only honored on the tenant-scoped path. A tenant UUID (directly configured
684
+ * or resolved from the default tenant slug) is required; callers who want the
685
+ * self/workspace feed should use `sdk.apps.listMine()`.
686
+ */
687
+ interface ListAppsOptions extends ListOptions {
688
+ /** Free-text search. Wire: `q`. */
689
+ q?: string;
690
+ /** Wire: `category_id`. */
691
+ categoryId?: string;
692
+ /** Wire: `status`. */
693
+ status?: string;
694
+ /** CSV on the wire. Wire: `status_in`. */
695
+ statusIn?: string[];
696
+ /** Wire: `review_status`. */
697
+ reviewStatus?: string;
698
+ /** CSV on the wire. Wire: `review_status_in`. */
699
+ reviewStatusIn?: string[];
700
+ /** Wire: `operating_status`. */
701
+ operatingStatus?: string;
702
+ /** CSV on the wire. Wire: `operating_status_in`. */
703
+ operatingStatusIn?: string[];
704
+ /** Wire: `sort`. */
705
+ sort?: AppSort;
706
+ /** 1-based page number. Wire: `page`. */
707
+ page?: number;
708
+ /** Page size for offset pagination. Wire: `per_page`. */
709
+ perPage?: number;
710
+ }
728
711
  interface CreateAppInput {
729
712
  slug: string;
730
713
  name: string;
731
714
  description?: string;
732
715
  iconUrl?: string;
716
+ /** Dark-mode icon. Wire: `icon_dark_url`. */
717
+ iconDarkUrl?: string;
733
718
  visibility?: 'private' | 'tenant' | 'public';
719
+ /** Wire: `auth_mode`. */
720
+ authMode?: string;
721
+ /** Wire: `data_scopes`. */
722
+ dataScopes?: string[];
723
+ /** Wire: `deploy_method`. */
724
+ deployMethod?: string;
725
+ /** Wire: `resource_tier`. */
726
+ resourceTier?: string;
727
+ /** Wire: `subdomain`. */
728
+ subdomain?: string;
734
729
  }
735
730
  interface UpdateAppInput {
736
731
  name?: string;
737
732
  description?: string;
738
733
  iconUrl?: string;
734
+ /** Dark-mode icon. Wire: `icon_dark_url`. */
735
+ iconDarkUrl?: string;
739
736
  visibility?: 'private' | 'tenant' | 'public';
737
+ /** Wire: `auth_mode`. */
738
+ authMode?: string;
739
+ /** Wire: `data_scopes`. */
740
+ dataScopes?: string[];
741
+ /** Wire: `deploy_method`. */
742
+ deployMethod?: string;
743
+ /** Wire: `resource_tier`. */
744
+ resourceTier?: string;
745
+ /** Wire: `subdomain`. */
746
+ subdomain?: string;
747
+ /** Clear the app's subdomain. Wire: `clear_subdomain`. */
748
+ clearSubdomain?: boolean;
740
749
  }
741
750
  declare class AppsCrudMixin {
742
751
  private readonly http;
743
752
  private readonly defaultTenantSlug?;
744
753
  private readonly defaultTenantId?;
745
- constructor(http: HttpClient, defaultTenantSlug?: string | undefined, defaultTenantId?: string | undefined);
746
- private withTenantQuery;
754
+ private readonly tenantResolver?;
755
+ constructor(http: HttpClient, defaultTenantSlug?: string | undefined, defaultTenantId?: string | undefined, tenantResolver?: TenantResolver | undefined);
756
+ private resolveTenantId;
747
757
  /**
748
758
  * Create a new app in the caller's tenant.
749
759
  *
@@ -769,7 +779,7 @@ declare class AppsCrudMixin {
769
779
  * try { await sdk.apps.list() }
770
780
  * catch (e) { if (e instanceof PermissionDeniedError) /* request grant *\/ }
771
781
  */
772
- list(opts?: ListOptions): Promise<PaginatedList<AppResponse>>;
782
+ list(opts?: ListAppsOptions): Promise<PaginatedList<AppResponse>>;
773
783
  /**
774
784
  * Iterate every app across all pages. Yields each item; emits a
775
785
  * `{type:'drift', addedSince:N}` item if backend's total grows mid-scan.
@@ -952,19 +962,11 @@ interface AppCategory {
952
962
  description?: string;
953
963
  [key: string]: unknown;
954
964
  }
955
- interface CreateCategoryInput {
956
- slug: string;
957
- name: string;
958
- description?: string;
959
- }
960
965
  declare class AppsCategoriesMixin {
961
966
  private readonly http;
962
967
  constructor(http: HttpClient);
963
968
  list(tenantIdOrSlug: string, opts?: PageRequestOptions): Promise<PaginatedList<AppCategory>>;
964
- create(tenantIdOrSlug: string, input: CreateCategoryInput, opts?: RequestOptions): Promise<AppCategory>;
965
969
  get(tenantIdOrSlug: string, categoryId: string, opts?: RequestOptions): Promise<AppCategory>;
966
- update(tenantIdOrSlug: string, categoryId: string, patch: Partial<CreateCategoryInput>, opts?: RequestOptions): Promise<AppCategory>;
967
- delete(tenantIdOrSlug: string, categoryId: string, opts?: RequestOptions): Promise<void>;
968
970
  }
969
971
 
970
972
  interface Comment {
@@ -1035,11 +1037,50 @@ interface DiscoverAppsOptions extends PageRequestOptions {
1035
1037
  sort?: 'newest' | 'likes' | 'subscribers' | 'name';
1036
1038
  visibility?: 'private' | 'tenant' | 'public';
1037
1039
  }
1040
+ interface DiscoverFeedOptions {
1041
+ q?: string;
1042
+ category?: string;
1043
+ sort?: string;
1044
+ /** Only apps created within the last N days. */
1045
+ createdWithinDays?: number;
1046
+ /** 1-based page number. */
1047
+ page?: number;
1048
+ /** Apps per page. */
1049
+ perPage?: number;
1050
+ }
1038
1051
  declare class AppsDiscoverMixin {
1039
1052
  private readonly http;
1040
1053
  private readonly defaultTenantSlug?;
1041
- constructor(http: HttpClient, defaultTenantSlug?: string | undefined);
1054
+ private readonly defaultTenantId?;
1055
+ constructor(http: HttpClient, defaultTenantSlug?: string | undefined, defaultTenantId?: string | undefined);
1042
1056
  search(opts?: DiscoverAppsOptions): Promise<PaginatedList<AppResponse>>;
1057
+ private feedQuery;
1058
+ /**
1059
+ * Global curated discovery feed across all tenants the caller can see
1060
+ * (auth-only). Offset-paginated. Distinct from {@link search} (catalog
1061
+ * alias) — this is `GET /api/v1/apps/discover`.
1062
+ *
1063
+ * @example happy
1064
+ * const page = await sdk.apps.discoverGlobal({ sort: 'newest', perPage: 20 })
1065
+ * for (const a of page.items) console.log(a.slug)
1066
+ */
1067
+ discoverGlobal(opts?: DiscoverFeedOptions): Promise<PaginatedList<AppResponse>>;
1068
+ /**
1069
+ * Tenant-scoped discovery feed (member-gated). Requires a tenant UUID — set
1070
+ * `defaultTenantId` on the client. Backend route
1071
+ * `GET /api/v1/tenants/{tenantID}/discover/apps` parses `{tenantID}` as a
1072
+ * UUID; without it this throws `TenantIdRequiredError` before any request.
1073
+ *
1074
+ * @example happy
1075
+ * const page = await sdk.apps.discoverTenant({ sort: 'likes' })
1076
+ * for (const a of page.items) console.log(a.slug)
1077
+ *
1078
+ * @example failure
1079
+ * // No defaultTenantId set:
1080
+ * try { await sdk.apps.discoverTenant() }
1081
+ * catch (e) { if (e instanceof TenantIdRequiredError) /* set defaultTenantId *\/ }
1082
+ */
1083
+ discoverTenant(opts?: DiscoverFeedOptions): Promise<PaginatedList<AppResponse>>;
1043
1084
  }
1044
1085
 
1045
1086
  interface GitConnection {
@@ -1117,6 +1158,85 @@ declare class AppsGitMixin {
1117
1158
  installStart(appId: string, input?: InstallStartInput): Promise<GithubInstallStart>;
1118
1159
  }
1119
1160
 
1161
+ interface AppInvitation {
1162
+ id: string;
1163
+ appId: string;
1164
+ userId: string;
1165
+ allowedScopes: string[];
1166
+ grantedAt: string;
1167
+ createdAt: string;
1168
+ }
1169
+ interface InviteUserInput {
1170
+ /** UUID of the target user. Must be an active member of the same tenant. */
1171
+ userId: string;
1172
+ }
1173
+ declare class AppsInvitationsMixin {
1174
+ private readonly http;
1175
+ constructor(http: HttpClient);
1176
+ /**
1177
+ * Invite a tenant member to an app (owner only). `userId` must be a UUID of
1178
+ * an active member of the same tenant; cross-tenant invites are rejected
1179
+ * (403). Inviting an existing member returns 409.
1180
+ *
1181
+ * @example happy
1182
+ * const inv = await sdk.apps.invitations.create('app_abc', { userId: '66666666-7777-8888-9999-aaaaaaaaaaaa' })
1183
+ * console.log(inv.userId, inv.allowedScopes)
1184
+ *
1185
+ * @example failure
1186
+ * try { await sdk.apps.invitations.create('app_abc', { userId: 'usr_dup' }) }
1187
+ * catch (e) {
1188
+ * if (e instanceof AlreadyMemberError) /* already invited *\/
1189
+ * if (e instanceof PermissionDeniedError) /* cross-tenant / not owner *\/
1190
+ * }
1191
+ */
1192
+ create(appId: string, input: InviteUserInput): Promise<AppInvitation>;
1193
+ /**
1194
+ * Revoke an app invitation by user UUID (owner only). Idempotent at the
1195
+ * transport level — backend returns 204 on success.
1196
+ *
1197
+ * @example happy
1198
+ * await sdk.apps.invitations.delete('app_abc', '66666666-7777-8888-9999-aaaaaaaaaaaa')
1199
+ *
1200
+ * @example failure
1201
+ * try { await sdk.apps.invitations.delete('app_abc', 'usr_missing') }
1202
+ * catch (e) { if (e instanceof NotFoundError) /* no such invitation *\/ }
1203
+ */
1204
+ delete(appId: string, userId: string): Promise<void>;
1205
+ }
1206
+
1207
+ declare class AppsLifecycleMixin {
1208
+ private readonly http;
1209
+ constructor(http: HttpClient);
1210
+ /**
1211
+ * Suspend an app (owner only). The app's dataapi stops serving traffic until
1212
+ * resumed. Suspending an already-suspended app returns 409
1213
+ * (`already_suspended`).
1214
+ *
1215
+ * @example happy
1216
+ * const app = await sdk.apps.suspend('app_abc')
1217
+ * console.log(app.status)
1218
+ *
1219
+ * @example failure
1220
+ * try { await sdk.apps.suspend('app_abc') }
1221
+ * catch (e) { if (e instanceof ConflictError) /* already suspended *\/ }
1222
+ */
1223
+ suspend(appId: string): Promise<AppResponse>;
1224
+ /**
1225
+ * Resume a suspended app (owner only). Resuming an app that is not suspended
1226
+ * returns 409 (`not_suspended`); an app that cannot be reactivated returns
1227
+ * 409 (`cannot_reactivate`).
1228
+ *
1229
+ * @example happy
1230
+ * const app = await sdk.apps.resume('app_abc')
1231
+ * console.log(app.status)
1232
+ *
1233
+ * @example failure
1234
+ * try { await sdk.apps.resume('app_abc') }
1235
+ * catch (e) { if (e instanceof ConflictError) /* not suspended / cannot reactivate *\/ }
1236
+ */
1237
+ resume(appId: string): Promise<AppResponse>;
1238
+ }
1239
+
1120
1240
  interface LikeResult {
1121
1241
  inserted: boolean;
1122
1242
  }
@@ -1126,33 +1246,87 @@ interface UnlikeResult {
1126
1246
  interface LikeStatus {
1127
1247
  liked: boolean;
1128
1248
  }
1129
- declare class AppsLikesMixin {
1249
+ declare class AppsLikesMixin {
1250
+ private readonly http;
1251
+ constructor(http: HttpClient);
1252
+ /**
1253
+ * Like an app. Idempotent — second call returns `{ inserted: false }`. The
1254
+ * app's denormalized `likeCount` is updated server-side only when
1255
+ * `inserted = true`.
1256
+ *
1257
+ * @example happy
1258
+ * const { inserted } = await sdk.apps.likes.like('app_abc')
1259
+ * if (inserted) console.log('first time like')
1260
+ */
1261
+ like(appId: string): Promise<LikeResult>;
1262
+ /**
1263
+ * Unlike an app. Idempotent — second call returns `{ deleted: false }`.
1264
+ *
1265
+ * @example happy
1266
+ * const { deleted } = await sdk.apps.likes.unlike('app_abc')
1267
+ */
1268
+ unlike(appId: string): Promise<UnlikeResult>;
1269
+ /**
1270
+ * Check if caller has liked the app.
1271
+ *
1272
+ * @example happy
1273
+ * const { liked } = await sdk.apps.likes.me('app_abc')
1274
+ */
1275
+ me(appId: string): Promise<LikeStatus>;
1276
+ }
1277
+
1278
+ interface AppMember {
1279
+ id: string;
1280
+ appId: string;
1281
+ userId: string;
1282
+ email?: string;
1283
+ allowedScopes: string[];
1284
+ grantedAt: string;
1285
+ grantedById?: string;
1286
+ }
1287
+ interface ListMembersOptions extends RequestOptions {
1288
+ /** 1-based page number. */
1289
+ page?: number;
1290
+ /** Members per page. */
1291
+ perPage?: number;
1292
+ }
1293
+ declare class AppsMembersMixin {
1130
1294
  private readonly http;
1131
1295
  constructor(http: HttpClient);
1132
1296
  /**
1133
- * Like an app. Idempotent second call returns `{ inserted: false }`. The
1134
- * app's denormalized `likeCount` is updated server-side only when
1135
- * `inserted = true`.
1297
+ * List members of an app (any member of the app can read). Offset-paginated.
1136
1298
  *
1137
1299
  * @example happy
1138
- * const { inserted } = await sdk.apps.likes.like('app_abc')
1139
- * if (inserted) console.log('first time like')
1300
+ * const page = await sdk.apps.members.list('app_abc', { page: 1, perPage: 50 })
1301
+ * for (const m of page.items) console.log(m.userId, m.email, m.allowedScopes)
1302
+ *
1303
+ * @example failure
1304
+ * try { await sdk.apps.members.list('app_abc') }
1305
+ * catch (e) { if (e instanceof PermissionDeniedError) /* not a member *\/ }
1140
1306
  */
1141
- like(appId: string): Promise<LikeResult>;
1307
+ list(appId: string, opts?: ListMembersOptions): Promise<PaginatedList<AppMember>>;
1308
+ }
1309
+
1310
+ declare class AppsMeMixin {
1311
+ private readonly http;
1312
+ constructor(http: HttpClient);
1142
1313
  /**
1143
- * Unlike an app. Idempotent second call returns `{ deleted: false }`.
1314
+ * List apps the caller owns. Self-scoped via the auth principal.
1144
1315
  *
1145
1316
  * @example happy
1146
- * const { deleted } = await sdk.apps.likes.unlike('app_abc')
1317
+ * const page = await sdk.apps.me.owned()
1318
+ * for (const a of page.items) console.log(a.slug)
1147
1319
  */
1148
- unlike(appId: string): Promise<UnlikeResult>;
1320
+ owned(opts?: RequestOptions): Promise<PaginatedList<AppResponse>>;
1149
1321
  /**
1150
- * Check if caller has liked the app.
1322
+ * List apps shared with (received by) the caller. Self-scoped via the auth
1323
+ * principal.
1151
1324
  *
1152
1325
  * @example happy
1153
- * const { liked } = await sdk.apps.likes.me('app_abc')
1326
+ * const page = await sdk.apps.me.received()
1327
+ * for (const a of page.items) console.log(a.slug)
1154
1328
  */
1155
- me(appId: string): Promise<LikeStatus>;
1329
+ received(opts?: RequestOptions): Promise<PaginatedList<AppResponse>>;
1156
1330
  }
1157
1331
 
1158
1332
  interface OAuthClient {
@@ -1181,6 +1355,11 @@ interface CreateOAuthClientInput {
1181
1355
  type?: 'public' | 'confidential';
1182
1356
  tokenEndpointAuthMethod?: 'client_secret_basic' | 'client_secret_post' | 'none';
1183
1357
  allowedGrantTypes?: string[];
1358
+ /**
1359
+ * RFC 8707 audience whitelist. Tokens issued to this client may only target
1360
+ * `resource` values in this list. Maps to wire `allowed_resources`.
1361
+ */
1362
+ allowedResources?: string[];
1184
1363
  }
1185
1364
  declare class AppsOAuthClientsMixin {
1186
1365
  private readonly http;
@@ -1321,6 +1500,8 @@ interface CreateTableInput {
1321
1500
  name: string;
1322
1501
  ownerColumn: string;
1323
1502
  columns: TableColumn[];
1503
+ /** Optional human-readable table description. Wire: `description`. */
1504
+ description?: string;
1324
1505
  }
1325
1506
  interface AddColumnInput {
1326
1507
  name: string;
@@ -1333,6 +1514,39 @@ interface AddGrantInput {
1333
1514
  principalId: string;
1334
1515
  scopes: GrantScope[];
1335
1516
  }
1517
+ interface TableAvailability {
1518
+ available: boolean;
1519
+ /** `ok` | `invalid_format` | `taken`. */
1520
+ reason: string;
1521
+ }
1522
+ interface ColumnTypeOption {
1523
+ /** API value to send (e.g. `text`). */
1524
+ value: string;
1525
+ /** Display label. */
1526
+ label: string;
1527
+ }
1528
+ interface TableRowColumn {
1529
+ name: string;
1530
+ type: string;
1531
+ nullable: boolean;
1532
+ }
1533
+ interface TableRowsMeta {
1534
+ page: number;
1535
+ perPage: number;
1536
+ total: number;
1537
+ totalPages: number;
1538
+ }
1539
+ interface TableRowsPage {
1540
+ columns: TableRowColumn[];
1541
+ rows: Array<Record<string, unknown>>;
1542
+ meta: TableRowsMeta;
1543
+ }
1544
+ interface BrowseRowsOptions extends RequestOptions {
1545
+ /** 1-based page number. */
1546
+ page?: number;
1547
+ /** Rows per page. */
1548
+ perPage?: number;
1549
+ }
1336
1550
  declare class AppsTablesMixin {
1337
1551
  private readonly http;
1338
1552
  constructor(http: HttpClient);
@@ -1432,6 +1646,38 @@ declare class AppsTablesMixin {
1432
1646
  * console.log(schema.indexes.map((i) => i.name))
1433
1647
  */
1434
1648
  inspect(appId: string, tableName: string): Promise<TableSchema>;
1649
+ /**
1650
+ * Check whether a table name is available before creating it (member-gated).
1651
+ * Validates name format client-side first.
1652
+ *
1653
+ * @example happy
1654
+ * const a = await sdk.apps.tables.checkAvailability('app_abc', 'orders')
1655
+ * if (a.available) /* safe to create *\/ else console.log(a.reason)
1656
+ */
1657
+ checkAvailability(appId: string, tableName: string): Promise<TableAvailability>;
1658
+ /**
1659
+ * List the supported column types for table-builder UIs (member-gated).
1660
+ *
1661
+ * @example happy
1662
+ * const types = await sdk.apps.tables.columnTypes('app_abc')
1663
+ * for (const t of types) console.log(t.value, t.label)
1664
+ */
1665
+ columnTypes(appId: string): Promise<ColumnTypeOption[]>;
1666
+ /**
1667
+ * Browse rows of a dynamic table (owner-gated admin view — owner_column is
1668
+ * ignored, all rows returned). Offset-paginated. For app-data access use the
1669
+ * data ring (`sdk.data.table(...)`) instead.
1670
+ *
1671
+ * @example happy
1672
+ * const page = await sdk.apps.tables.browseRows('app_abc', 'orders', { page: 1, perPage: 50 })
1673
+ * console.log(page.meta.totalPages, page.columns.map((c) => c.name))
1674
+ * for (const row of page.rows) console.log(row)
1675
+ *
1676
+ * @example failure
1677
+ * try { await sdk.apps.tables.browseRows('app_abc', 'missing') }
1678
+ * catch (e) { if (e instanceof NotFoundError) /* no such table *\/ }
1679
+ */
1680
+ browseRows(appId: string, tableName: string, opts?: BrowseRowsOptions): Promise<TableRowsPage>;
1435
1681
  }
1436
1682
 
1437
1683
  interface AppTemplate {
@@ -1450,9 +1696,14 @@ declare class AppsTemplatesMixin {
1450
1696
 
1451
1697
  declare class AppsClient {
1452
1698
  private readonly crud;
1699
+ private readonly lifecycle;
1700
+ private readonly availability;
1453
1701
  private readonly envVars;
1454
1702
  readonly publication: AppsPublicationMixin;
1455
1703
  readonly access: AppsAccessMixin;
1704
+ readonly invitations: AppsInvitationsMixin;
1705
+ readonly members: AppsMembersMixin;
1706
+ readonly me: AppsMeMixin;
1456
1707
  readonly categories: AppsCategoriesMixin;
1457
1708
  readonly discover: AppsDiscoverMixin;
1458
1709
  readonly likes: AppsLikesMixin;
@@ -1461,7 +1712,7 @@ declare class AppsClient {
1461
1712
  readonly git: AppsGitMixin;
1462
1713
  readonly tables: AppsTablesMixin;
1463
1714
  readonly templates: AppsTemplatesMixin;
1464
- constructor(http: HttpClient, defaultTenantSlug?: string, defaultTenantId?: string);
1715
+ constructor(http: HttpClient, defaultTenantSlug?: string, defaultTenantId?: string, tenantResolver?: TenantResolver);
1465
1716
  create: (...args: Parameters<AppsCrudMixin["create"]>) => Promise<AppResponse>;
1466
1717
  list: (...args: Parameters<AppsCrudMixin["list"]>) => Promise<PaginatedList<AppResponse>>;
1467
1718
  listAll: (...args: Parameters<AppsCrudMixin["listAll"]>) => AsyncGenerator<ListAllItem<AppResponse>, any, any>;
@@ -1473,15 +1724,19 @@ declare class AppsClient {
1473
1724
  signIconDarkUploadURL: (...args: Parameters<AppsCrudMixin["signIconDarkUploadURL"]>) => Promise<SignIconUploadResult>;
1474
1725
  /**
1475
1726
  * List apps the caller has been granted access to (not necessarily owned).
1476
- * Wraps `GET /users/me/apps`. Distinct from {@link list} which returns all
1477
- * apps in the resolved tenant; `listMine` is per-user scoped via the auth
1478
- * principal in the token.
1727
+ * Wraps `GET /api/v1/me/apps/workspace`. Distinct from {@link list} which
1728
+ * returns all apps in the resolved tenant; `listMine` is per-user scoped via
1729
+ * the auth principal in the token.
1479
1730
  *
1480
1731
  * @example happy
1481
1732
  * const accessible = await sdk.apps.listMine()
1482
1733
  * for (const a of accessible) console.log(a.slug)
1483
1734
  */
1484
1735
  listMine: (...args: Parameters<AppsCrudMixin["listMine"]>) => Promise<AppResponse[]>;
1736
+ suspend: (...args: Parameters<AppsLifecycleMixin["suspend"]>) => Promise<AppResponse>;
1737
+ resume: (...args: Parameters<AppsLifecycleMixin["resume"]>) => Promise<AppResponse>;
1738
+ checkAvailability: (...args: Parameters<AppsAvailabilityMixin["checkAvailability"]>) => Promise<AppAvailability>;
1739
+ iconPreCreateUrl: (...args: Parameters<AppsAvailabilityMixin["iconPreCreateUrl"]>) => Promise<IconPreCreateResult>;
1485
1740
  listEnvVars: (...args: Parameters<AppsEnvVarsMixin["listEnvVars"]>) => Promise<EnvVar[]>;
1486
1741
  setEnvVar: (...args: Parameters<AppsEnvVarsMixin["setEnvVar"]>) => Promise<void>;
1487
1742
  getEnvVar: (...args: Parameters<AppsEnvVarsMixin["getEnvVar"]>) => Promise<EnvVar>;
@@ -1734,17 +1989,15 @@ declare class TenantScopedClient {
1734
1989
  readonly slug: string;
1735
1990
  private readonly root;
1736
1991
  readonly apps: TenantScopedAppsClient;
1737
- constructor(slug: string, scopedRoot: AxHubClient, root: AxHubClient);
1738
- get tenants(): TenantScopedTenantsClient;
1739
- get authz(): TenantAuthzClient;
1740
- get audit(): TenantAuditClient;
1992
+ constructor(slug: string, scopedRoot: AxHubClient, root: AxHubClient, tenantResolver: TenantResolver);
1741
1993
  get gateway(): TenantGatewayClient;
1742
1994
  app(appSlug: string): AppScopedClient;
1743
1995
  }
1744
1996
  declare class TenantScopedAppsClient {
1745
1997
  private readonly tenantSlug;
1746
1998
  private readonly http;
1747
- constructor(tenantSlug: string, http: HttpClient);
1999
+ private readonly tenantResolver;
2000
+ constructor(tenantSlug: string, http: HttpClient, tenantResolver: TenantResolver);
1748
2001
  create(input: CreateAppInput, opts?: RequestOptions): Promise<AppResponse>;
1749
2002
  list(opts?: PageRequestOptions): Promise<PaginatedList<AppResponse>>;
1750
2003
  listAll(opts?: PageRequestOptions): AsyncGenerator<ListAllItem<AppResponse>>;
@@ -1806,9 +2059,92 @@ interface CreateDeploymentInput {
1806
2059
  /** Force a rebuild even if this commit already has a built image (dependency / base-image refresh). */
1807
2060
  forceRebuild?: boolean;
1808
2061
  }
2062
+ interface LogLine {
2063
+ /** RFC3339 timestamp the line was emitted. */
2064
+ ts: string;
2065
+ /** Container that produced the line (compose multi-service). */
2066
+ container: string;
2067
+ /** One stdout/stderr line (JSON string if structured). */
2068
+ message: string;
2069
+ /** Unique key for de-duplication. */
2070
+ insertId: string;
2071
+ }
2072
+ interface LogsPage {
2073
+ lines: LogLine[];
2074
+ /** Pass back as `cursor` to continue; empty string means end. */
2075
+ nextCursor: string;
2076
+ hasMore: boolean;
2077
+ }
2078
+ interface FetchLogsOptions {
2079
+ /** `forward` (newer) or `backward` (older). */
2080
+ direction?: 'forward' | 'backward';
2081
+ /** Opaque cursor from a prior `nextCursor`. */
2082
+ cursor?: string;
2083
+ /** RFC3339 lower bound. */
2084
+ since?: string;
2085
+ /** RFC3339 upper bound. */
2086
+ until?: string;
2087
+ /** Max lines per page. */
2088
+ limit?: number;
2089
+ }
2090
+ interface StartBootstrapInput {
2091
+ name: string;
2092
+ slug: string;
2093
+ subdomain: string;
2094
+ templateId: string;
2095
+ githubInstallationId: number;
2096
+ githubOwner: string;
2097
+ repoName: string;
2098
+ repoPrivate?: boolean;
2099
+ }
2100
+ interface BootstrapAccepted {
2101
+ /** Saga id to poll with `bootstrapStatus`. */
2102
+ bootstrapId: string;
2103
+ /** Polling endpoint path. */
2104
+ statusUrl: string;
2105
+ }
2106
+ type BootstrapStage = 'app' | 'repo' | 'push' | 'connect' | 'deploy' | 'done';
2107
+ type BootstrapStatusValue = 'running' | 'done' | 'failed';
2108
+ interface BootstrapStatus {
2109
+ id: string;
2110
+ stage: string;
2111
+ status: string;
2112
+ appId?: string;
2113
+ deploymentId?: string;
2114
+ repoFullName?: string;
2115
+ errorCode?: string;
2116
+ errorMessage?: string;
2117
+ }
2118
+ interface GithubAccount {
2119
+ login: string;
2120
+ type: string;
2121
+ installed: boolean;
2122
+ installationId?: number;
2123
+ avatarUrl?: string;
2124
+ installUrl?: string;
2125
+ }
2126
+ interface GithubRepo {
2127
+ id: number;
2128
+ name: string;
2129
+ fullName: string;
2130
+ private: boolean;
2131
+ defaultBranch: string;
2132
+ }
2133
+ interface GithubReposPage {
2134
+ repositories: GithubRepo[];
2135
+ /** Total repos before pagination. */
2136
+ totalCount: number;
2137
+ }
2138
+ interface ListReposOptions {
2139
+ /** 1-based page number. */
2140
+ page?: number;
2141
+ /** Repos per page. */
2142
+ perPage?: number;
2143
+ }
1809
2144
  declare class DeploymentsClient {
1810
2145
  private readonly http;
1811
- constructor(http: HttpClient);
2146
+ private readonly defaultTenantId?;
2147
+ constructor(http: HttpClient, defaultTenantId?: string | undefined);
1812
2148
  /**
1813
2149
  * Trigger a new deployment. Returns immediately with the deployment row
1814
2150
  * in `pending` or `building` state; poll `get()` to observe progression.
@@ -1866,24 +2202,77 @@ declare class DeploymentsClient {
1866
2202
  * }
1867
2203
  */
1868
2204
  rollback(appId: string, did: string): Promise<void>;
2205
+ /**
2206
+ * Fetch runtime logs for an app's running containers (owner-gated). Cursor
2207
+ * paginated: pass the returned `nextCursor` back to continue. `forward`
2208
+ * fetches newer lines (poll loop); `backward` fetches older history.
2209
+ *
2210
+ * @example happy
2211
+ * let page = await sdk.deployments.logs('app_abc', { direction: 'backward', limit: 100 })
2212
+ * for (const l of page.lines) console.log(l.ts, l.message)
2213
+ * if (page.hasMore) page = await sdk.deployments.logs('app_abc', { cursor: page.nextCursor })
2214
+ *
2215
+ * @example failure
2216
+ * try { await sdk.deployments.logs('app_abc') }
2217
+ * catch (e) { if (e instanceof UnavailableError) /* log backend down (503) *\/ }
2218
+ */
2219
+ logs(appId: string, opts?: FetchLogsOptions): Promise<LogsPage>;
2220
+ private requireTenantId;
2221
+ /**
2222
+ * Start an app-bootstrap saga (member-gated): create a GitHub repo from a
2223
+ * template, connect it, and deploy in one async flow. Returns immediately
2224
+ * (202) with a `bootstrapId` — poll `bootstrapStatus` to observe progress.
2225
+ * Tenant-scoped: requires `defaultTenantId`.
2226
+ *
2227
+ * @example happy
2228
+ * const { bootstrapId } = await sdk.deployments.startBootstrap({
2229
+ * name: 'My Todo', slug: 'my-todo', subdomain: 'todo',
2230
+ * templateId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
2231
+ * githubInstallationId: 12345678, githubOwner: 'ksro0128', repoName: 'my-todo-frontend',
2232
+ * })
2233
+ *
2234
+ * @example failure
2235
+ * try { await sdk.deployments.startBootstrap({ ...input }) }
2236
+ * catch (e) { if (e instanceof TenantIdRequiredError) /* set defaultTenantId *\/ }
2237
+ */
2238
+ startBootstrap(input: StartBootstrapInput): Promise<BootstrapAccepted>;
2239
+ /**
2240
+ * Poll the status of an app-bootstrap saga (member-gated). Tenant-scoped:
2241
+ * requires `defaultTenantId`. `status` is `running` | `done` | `failed`;
2242
+ * `stage` walks `app → repo → push → connect → deploy → done`.
2243
+ *
2244
+ * @example happy
2245
+ * const st = await sdk.deployments.bootstrapStatus('bs_a1b2c3d4')
2246
+ * if (st.status === 'done') console.log(st.appId, st.deploymentId)
2247
+ * else if (st.status === 'failed') console.error(st.errorCode, st.errorMessage)
2248
+ */
2249
+ bootstrapStatus(bootstrapId: string): Promise<BootstrapStatus>;
2250
+ /**
2251
+ * List GitHub accounts (user + orgs) visible to the caller and whether the
2252
+ * axhub GitHub App is installed on each (auth-only). Use the
2253
+ * `installationId` of an installed account to list its repos.
2254
+ *
2255
+ * @example happy
2256
+ * const accounts = await sdk.deployments.githubAccounts()
2257
+ * for (const a of accounts) console.log(a.login, a.installed, a.installationId)
2258
+ *
2259
+ * @example failure
2260
+ * try { await sdk.deployments.githubAccounts() }
2261
+ * catch (e) { if (e instanceof PreconditionFailedError) /* GitHub link required (428) *\/ }
2262
+ */
2263
+ githubAccounts(): Promise<GithubAccount[]>;
2264
+ /**
2265
+ * List repositories under a GitHub installation (auth-only). Offset
2266
+ * paginated. Get the `installationId` from `githubAccounts`.
2267
+ *
2268
+ * @example happy
2269
+ * const page = await sdk.deployments.githubRepos(12345678, { page: 1, perPage: 30 })
2270
+ * console.log(page.totalCount)
2271
+ * for (const r of page.repositories) console.log(r.fullName, r.defaultBranch)
2272
+ */
2273
+ githubRepos(installationId: number, opts?: ListReposOptions): Promise<GithubReposPage>;
1869
2274
  }
1870
2275
 
1871
- interface GatewayEngine {
1872
- name: 'postgres' | 'mysql' | string;
1873
- capabilities: string[];
1874
- }
1875
- interface GatewayConnector {
1876
- id: string;
1877
- engine: string;
1878
- name: string;
1879
- [key: string]: unknown;
1880
- }
1881
- interface GatewayResource {
1882
- id: string;
1883
- connectorId: string;
1884
- name: string;
1885
- [key: string]: unknown;
1886
- }
1887
2276
  interface GatewayQueryInput {
1888
2277
  resourceId?: string;
1889
2278
  connectorId?: string;
@@ -2003,46 +2392,36 @@ interface InvokeResult<Row = Record<string, unknown>> {
2003
2392
  rowCount: number;
2004
2393
  matchedPolicies?: string[];
2005
2394
  }
2395
+ interface EngineCapability {
2396
+ engine: string;
2397
+ kind: string;
2398
+ displayName: string;
2399
+ supportedActions: string[];
2400
+ /** Per-action allowed effects (action → effect[]). */
2401
+ allowedEffects: Record<string, string[]>;
2402
+ }
2006
2403
  declare class GatewayClient {
2007
2404
  private readonly http;
2008
2405
  constructor(http: HttpClient);
2009
2406
  scoped(tenantSlug: string): TenantGatewayClient;
2407
+ /**
2408
+ * List the gateway's engine capabilities (auth-only; no tenant membership
2409
+ * required). Each entry describes an engine + kind, its invokable actions,
2410
+ * and the effects each action may produce. Tenant-independent.
2411
+ *
2412
+ * @example happy
2413
+ * const engines = await sdk.gateway.engines()
2414
+ * for (const e of engines) console.log(e.engine, e.kind, e.supportedActions)
2415
+ */
2416
+ engines(opts?: RequestOptions): Promise<EngineCapability[]>;
2010
2417
  }
2011
2418
  declare class TenantGatewayClient {
2012
- /** @adminOnly Global engine catalog. Governance read — requires tenant_admin (v0.1, SPEC 307). */
2013
- readonly engines: GatewayEnginesClient;
2014
- /** @adminOnly Connector governance (list/create/update/...). Member callers get ForbiddenError (v0.1). Members use `catalog.listConnectors()`. */
2015
- readonly connectors: GatewayConnectorsClient;
2016
- /** @adminOnly Raw resource governance. Member callers get ForbiddenError (v0.1). Members use `catalog.listResources()`. */
2017
- readonly resources: GatewayResourcesClient;
2018
2419
  /** Run a parameterized read query. Member OK. See also `catalog.invoke()`. */
2019
2420
  readonly query: GatewayQueryClient;
2020
2421
  /** Member-facing catalog: discover connectors/resources you can read + invoke. */
2021
2422
  readonly catalog: GatewayCatalogClient;
2022
2423
  constructor(http: HttpClient, tenantSlug: string);
2023
2424
  }
2024
- declare class GatewayEnginesClient {
2025
- private readonly http;
2026
- private readonly base;
2027
- constructor(http: HttpClient, base: string);
2028
- list(opts?: RequestOptions): Promise<GatewayEngine[]>;
2029
- }
2030
- declare class GatewayCrud<T extends {
2031
- id: string;
2032
- }> {
2033
- protected readonly http: HttpClient;
2034
- protected readonly base: string;
2035
- constructor(http: HttpClient, base: string);
2036
- list(opts?: RequestOptions): Promise<T[]>;
2037
- get(id: string, opts?: RequestOptions): Promise<T>;
2038
- create(input: Record<string, unknown>, opts?: RequestOptions): Promise<T>;
2039
- update(id: string, patch: Record<string, unknown>, opts?: RequestOptions): Promise<T>;
2040
- delete(id: string, opts?: RequestOptions): Promise<void>;
2041
- }
2042
- declare class GatewayConnectorsClient extends GatewayCrud<GatewayConnector> {
2043
- }
2044
- declare class GatewayResourcesClient extends GatewayCrud<GatewayResource> {
2045
- }
2046
2425
  declare class GatewayQueryClient {
2047
2426
  private readonly http;
2048
2427
  private readonly base;
@@ -2065,7 +2444,6 @@ declare class GatewayQueryClient {
2065
2444
  }
2066
2445
  /**
2067
2446
  * Member-facing gateway catalog: connectors/resources the caller can read, plus `invoke`.
2068
- * Distinct from the `@adminOnly` governance clients (`connectors`/`resources`/`engines`).
2069
2447
  */
2070
2448
  declare class GatewayCatalogClient {
2071
2449
  private readonly http;
@@ -2169,6 +2547,36 @@ interface DeviceAuthorizationResponse {
2169
2547
  interval: number;
2170
2548
  raw: Record<string, unknown>;
2171
2549
  }
2550
+ /**
2551
+ * RFC 7591 Dynamic Client Registration request for an MCP client. All fields
2552
+ * optional per the spec; the backend fills defaults. `resource`/`resources`
2553
+ * are the RFC 8707 audiences the client may request.
2554
+ */
2555
+ interface RegisterMcpClientInput {
2556
+ clientName?: string;
2557
+ clientUri?: string;
2558
+ redirectUris?: string[];
2559
+ grantTypes?: string[];
2560
+ responseTypes?: string[];
2561
+ scope?: string;
2562
+ tokenEndpointAuthMethod?: string;
2563
+ resource?: string;
2564
+ resources?: string[];
2565
+ softwareId?: string;
2566
+ softwareVersion?: string;
2567
+ }
2568
+ /** RFC 7591 registration response. Mirrors backend `dynamicRegistrationResponse`. */
2569
+ interface RegisterMcpClientResult {
2570
+ clientId: string;
2571
+ clientIdIssuedAt?: number;
2572
+ clientName?: string;
2573
+ redirectUris?: string[];
2574
+ grantTypes?: string[];
2575
+ responseTypes?: string[];
2576
+ scope?: string;
2577
+ tokenEndpointAuthMethod?: string;
2578
+ raw: Record<string, unknown>;
2579
+ }
2172
2580
  interface IdentityProvider {
2173
2581
  id: string;
2174
2582
  tenantId?: string;
@@ -2193,7 +2601,6 @@ declare class IdentityClient {
2193
2601
  readonly oauth: IdentityOAuthClient;
2194
2602
  readonly oidc: IdentityOIDCClient;
2195
2603
  readonly deviceCode: IdentityDeviceCodeClient;
2196
- readonly idp: IdentityProviderClient;
2197
2604
  readonly systemOAuthClients: IdentitySystemOAuthClientsClient;
2198
2605
  constructor(http: HttpClient, defaultTenantSlug?: string);
2199
2606
  /** @deprecated Use sdk.identity.pat.issue(). Kept until 1.0 RC migration. */
@@ -2223,6 +2630,10 @@ declare class IdentityOAuthClient {
2223
2630
  private readonly defaultTenantSlug?;
2224
2631
  constructor(http: HttpClient, defaultTenantSlug?: string | undefined);
2225
2632
  private tenantQuery;
2633
+ /**
2634
+ * Build the `/oauth/authorize` URL. `resource` is the RFC 8707 audience —
2635
+ * pass the target API's identifier to bind the issued token to it.
2636
+ */
2226
2637
  authorizeUrl(input: {
2227
2638
  clientId: string;
2228
2639
  redirectUri: string;
@@ -2230,6 +2641,7 @@ declare class IdentityOAuthClient {
2230
2641
  state?: string;
2231
2642
  codeChallenge?: string;
2232
2643
  codeChallengeMethod?: string;
2644
+ resource?: string;
2233
2645
  }): string;
2234
2646
  exchangeCode(input: {
2235
2647
  code: string;
@@ -2237,11 +2649,13 @@ declare class IdentityOAuthClient {
2237
2649
  redirectUri: string;
2238
2650
  codeVerifier: string;
2239
2651
  clientSecret?: string;
2652
+ resource?: string;
2240
2653
  }, opts?: RequestOptions): Promise<OAuthTokenResponse>;
2241
2654
  refreshTokens(input: {
2242
2655
  refreshToken: string;
2243
2656
  clientId?: string;
2244
2657
  clientSecret?: string;
2658
+ resource?: string;
2245
2659
  }, opts?: RequestOptions): Promise<OAuthTokenResponse>;
2246
2660
  revoke(input: {
2247
2661
  token: string;
@@ -2249,6 +2663,25 @@ declare class IdentityOAuthClient {
2249
2663
  clientId?: string;
2250
2664
  }, opts?: RequestOptions): Promise<void>;
2251
2665
  userinfo(opts?: RequestOptions): Promise<Record<string, unknown>>;
2666
+ /**
2667
+ * RFC 7591 Dynamic Client Registration for an MCP client. This route is
2668
+ * **unauthenticated** (`POST /oauth/register`, public ring) — no token is
2669
+ * sent. The returned `clientId` (and any backend-issued secret in `raw`) is
2670
+ * the registration result; persist it on the agent side.
2671
+ *
2672
+ * @example happy
2673
+ * const reg = await sdk.identity.oauth.registerMcpClient({
2674
+ * clientName: 'my-mcp-agent',
2675
+ * redirectUris: ['https://agent.example/cb'],
2676
+ * resource: 'https://api.example/mcp',
2677
+ * })
2678
+ * storeClientId(reg.clientId)
2679
+ *
2680
+ * @example failure
2681
+ * try { await sdk.identity.oauth.registerMcpClient({ redirectUris: ['not-a-url'] }) }
2682
+ * catch (e) { if (e instanceof OAuthError) /* invalid_redirect_uri / invalid_client_metadata *\/ }
2683
+ */
2684
+ registerMcpClient(input: RegisterMcpClientInput, opts?: RequestOptions): Promise<RegisterMcpClientResult>;
2252
2685
  getClient(clientId: string, opts?: RequestOptions): Promise<SystemOAuthClient>;
2253
2686
  revokeOwnGrant(clientId: string, opts?: RequestOptions): Promise<void>;
2254
2687
  }
@@ -2260,15 +2693,63 @@ declare class IdentityOIDCClient {
2260
2693
  discovery(opts?: RequestOptions): Promise<Record<string, unknown>>;
2261
2694
  jwks(opts?: RequestOptions): Promise<Record<string, unknown>>;
2262
2695
  providers(opts?: RequestOptions): Promise<IdentityProvider[]>;
2696
+ /**
2697
+ * Resolve the interactive IdP login start URL. `returnOrigin` (RFC: the SPA
2698
+ * origin the backend bounces the browser back to after the IdP round-trip)
2699
+ * maps to the `return_origin` query param on `GET /auth/{providerID}/start`.
2700
+ */
2263
2701
  startURL(providerId: string, input?: {
2264
2702
  redirectTo?: string;
2265
2703
  state?: string;
2704
+ returnOrigin?: string;
2266
2705
  }, opts?: RequestOptions): Promise<string>;
2706
+ /**
2707
+ * Build the Google OAuth login start URL (`GET /auth/google_oauth2/start`,
2708
+ * legacy interactive route). Issues a 302 to Google, so this is a URL the
2709
+ * caller navigates the browser to. `returnTo` / `returnOrigin` map to the
2710
+ * `return_to` / `return_origin` query params the backend reads to bounce the
2711
+ * browser back after the Google round-trip.
2712
+ *
2713
+ * @example happy
2714
+ * const url = sdk.identity.oidc.googleStartUrl({ returnTo: 'https://app.example/done' })
2715
+ * window.location.href = url
2716
+ */
2717
+ googleStartUrl(input?: {
2718
+ returnTo?: string;
2719
+ returnOrigin?: string;
2720
+ }): string;
2267
2721
  exchangeCallback(input: {
2268
2722
  code: string;
2269
2723
  state?: string;
2270
2724
  provider?: string;
2271
2725
  }, opts?: RequestOptions): Promise<OAuthTokenResponse>;
2726
+ /**
2727
+ * Build the GitHub OAuth start URL. `GET /auth/github` issues a 302 redirect
2728
+ * to GitHub, so this is a URL the caller navigates the browser to (it does
2729
+ * not return JSON). `returnTo` is where the backend sends the browser back
2730
+ * after the GitHub round-trip completes.
2731
+ *
2732
+ * @example happy
2733
+ * const url = sdk.identity.oidc.githubStartUrl({ returnTo: 'https://app.example/done' })
2734
+ * window.location.href = url
2735
+ */
2736
+ githubStartUrl(input?: {
2737
+ returnTo?: string;
2738
+ }): string;
2739
+ /**
2740
+ * Build the GitHub OAuth callback URL. `GET /auth/github/callback` also
2741
+ * 302-redirects (the backend sets the session and bounces the browser to
2742
+ * `return_to`), so this is a URL helper, not a JSON-returning request — the
2743
+ * browser hits it directly with GitHub's `code`/`state` query params.
2744
+ *
2745
+ * @example happy
2746
+ * const url = sdk.identity.oidc.githubCallbackUrl({ code: 'gh_code', state: 'xyz' })
2747
+ */
2748
+ githubCallbackUrl(input: {
2749
+ code?: string;
2750
+ state?: string;
2751
+ error?: string;
2752
+ }): string;
2272
2753
  }
2273
2754
  declare class IdentityDeviceCodeClient {
2274
2755
  private readonly http;
@@ -2286,14 +2767,6 @@ declare class IdentityDeviceCodeClient {
2286
2767
  signal?: AbortSignal;
2287
2768
  }, opts?: RequestOptions): Promise<OAuthTokenResponse>;
2288
2769
  }
2289
- declare class IdentityProviderClient {
2290
- private readonly http;
2291
- constructor(http: HttpClient);
2292
- list(tenantId: string, opts?: RequestOptions): Promise<IdentityProvider[]>;
2293
- create(tenantId: string, input: Record<string, unknown>, opts?: RequestOptions): Promise<IdentityProvider>;
2294
- enable(tenantId: string, providerId: string, opts?: RequestOptions): Promise<IdentityProvider>;
2295
- disable(tenantId: string, providerId: string, opts?: RequestOptions): Promise<IdentityProvider>;
2296
- }
2297
2770
  declare class IdentitySystemOAuthClientsClient {
2298
2771
  private readonly http;
2299
2772
  constructor(http: HttpClient);
@@ -2368,6 +2841,59 @@ declare class PublicationRequestsClient {
2368
2841
  * catch (e) { if (e instanceof NotAdminError) /* not platform admin *\/ }
2369
2842
  */
2370
2843
  listPending(opts?: ListOptions): Promise<PaginatedList<PublicationRequest>>;
2844
+ /**
2845
+ * List settled (approved/rejected) publication requests for a tenant
2846
+ * (reviewer-gated). `tenantId` is required by the backend route — passing it
2847
+ * empty throws `RequiredError` before any request.
2848
+ *
2849
+ * @example happy
2850
+ * const page = await sdk.publicationRequests.history('tnt_1', { pageSize: 20 })
2851
+ * for (const pr of page.items) console.log(pr.appId, pr.status)
2852
+ *
2853
+ * @example failure
2854
+ * try { await sdk.publicationRequests.history('') }
2855
+ * catch (e) { if (e instanceof RequiredError) /* tenantId required *\/ }
2856
+ */
2857
+ history(tenantId: string, opts?: ListOptions): Promise<PaginatedList<PublicationRequest>>;
2858
+ }
2859
+
2860
+ interface Tenant {
2861
+ id: string;
2862
+ slug: string;
2863
+ name: string;
2864
+ plan?: string;
2865
+ createdAt?: string;
2866
+ updatedAt?: string;
2867
+ [key: string]: unknown;
2868
+ }
2869
+ declare class TenantsClient {
2870
+ private readonly http;
2871
+ constructor(http: HttpClient);
2872
+ get(tenantIdOrSlug: string, opts?: RequestOptions): Promise<Tenant>;
2873
+ }
2874
+
2875
+ interface PublicConfig {
2876
+ /** Base domain apps are served under, e.g. `axhub.jocodingax.ai`. */
2877
+ baseDomain: string;
2878
+ /** Any additional fields the backend adds are passed through. */
2879
+ [key: string]: unknown;
2880
+ }
2881
+ declare class ConfigClient {
2882
+ private readonly http;
2883
+ constructor(http: HttpClient);
2884
+ /**
2885
+ * Fetch public runtime config (tokenless). No Authorization header is sent;
2886
+ * usable before login. Rate-limited (429) if hammered.
2887
+ *
2888
+ * @example happy
2889
+ * const cfg = await sdk.config.public()
2890
+ * console.log(cfg.baseDomain)
2891
+ *
2892
+ * @example failure
2893
+ * try { await sdk.config.public() }
2894
+ * catch (e) { if (e instanceof RateLimitedError) /* back off (429) *\/ }
2895
+ */
2896
+ public(opts?: RequestOptions): Promise<PublicConfig>;
2371
2897
  }
2372
2898
 
2373
2899
  interface MockClientOptions {
@@ -2418,6 +2944,7 @@ interface AxHubInternalOptions {
2418
2944
  logger: Logger;
2419
2945
  schemaCacheOptions?: SchemaCacheOptions;
2420
2946
  mockStore?: MockStore;
2947
+ tenantResolver?: TenantResolver;
2421
2948
  }
2422
2949
  declare class AxHubClient {
2423
2950
  readonly http: HttpClient;
@@ -2426,22 +2953,25 @@ declare class AxHubClient {
2426
2953
  readonly logger: Logger;
2427
2954
  private readonly schemaCacheOptions?;
2428
2955
  readonly mock?: MockStore;
2956
+ private readonly tenantResolver;
2429
2957
  private _apps?;
2430
- private _audit?;
2431
- private _authz?;
2432
2958
  private _data?;
2433
2959
  private _deployments?;
2434
2960
  private _gateway?;
2435
2961
  private _identity?;
2436
2962
  private _publicationRequests?;
2437
2963
  private _tenants?;
2964
+ private _config?;
2438
2965
  constructor(opts: AxHubClientOptions | AxHubInternalOptions);
2439
2966
  resolveTenantSlug(perCall?: string): string;
2440
2967
  get apps(): AppsClient;
2441
- get audit(): AuditClient;
2442
- get authz(): AuthzClient;
2443
2968
  get data(): DataClient;
2444
2969
  get deployments(): DeploymentsClient;
2970
+ /**
2971
+ * Public, tokenless configuration (`sdk.config.public()`). No Authorization
2972
+ * header is sent — usable before authentication.
2973
+ */
2974
+ get config(): ConfigClient;
2445
2975
  get gateway(): GatewayClient;
2446
2976
  get identity(): IdentityClient;
2447
2977
  get tenants(): TenantsClient;
@@ -2487,63 +3017,4 @@ declare class AxHubClient {
2487
3017
  tenant(tenantSlug: string): TenantScopedClient;
2488
3018
  }
2489
3019
 
2490
- interface StaticTokenAuthOptions {
2491
- token: string;
2492
- tokenType: TokenType;
2493
- onRefresh?: () => Promise<string>;
2494
- }
2495
- declare class StaticTokenAuth implements AuthProvider {
2496
- private token;
2497
- readonly tokenType: TokenType;
2498
- private readonly onRefresh?;
2499
- private refreshing;
2500
- constructor(opts: StaticTokenAuthOptions);
2501
- currentToken(): string;
2502
- headersFor(ring: AuthRing): Record<string, string>;
2503
- onUnauthorized(): Promise<boolean>;
2504
- }
2505
- declare class NoAuth implements AuthProvider {
2506
- headersFor(_ring: AuthRing): Record<string, string>;
2507
- onUnauthorized(): Promise<boolean>;
2508
- }
2509
-
2510
- interface ParsedFrame {
2511
- id?: string;
2512
- event?: string;
2513
- data: string;
2514
- }
2515
- type StreamItem<T> = {
2516
- type: 'item';
2517
- value: T;
2518
- id: string | undefined;
2519
- } | {
2520
- type: 'gap';
2521
- sinceId: string | undefined;
2522
- missingCount?: number;
2523
- } | {
2524
- type: 'decode-skip';
2525
- frame: ParsedFrame;
2526
- error: AxHubError;
2527
- };
2528
- interface SSEStream<T> extends AsyncIterable<StreamItem<T>> {
2529
- dispose(): void;
2530
- }
2531
-
2532
- type WebhookVerifyReason = 'signature_mismatch' | 'timestamp_skew' | 'malformed_signature' | 'missing_secret' | 'replay';
2533
- interface VerifyWebhookInput {
2534
- rawBody: Buffer | Uint8Array | string;
2535
- signature: string;
2536
- secret: string;
2537
- tolerance?: number;
2538
- timestamp?: string;
2539
- replayCache?: Set<string>;
2540
- now?: () => number;
2541
- }
2542
- interface VerifyWebhookResult {
2543
- ok: boolean;
2544
- reason?: WebhookVerifyReason;
2545
- }
2546
- declare function signWebhook(rawBody: Buffer | Uint8Array | string, secret: string, timestamp?: string): string;
2547
- declare function verifyWebhook(input: VerifyWebhookInput): VerifyWebhookResult;
2548
-
2549
- export { AbortError, AccessDeniedError, type AddColumnInput, type AddCommentInput, type AddGrantInput, AlreadyAccessedError, AlreadyActiveError, AlreadyDeletedError, AlreadyInactiveError, AlreadyMemberError, AlreadyRevokedError, AlreadySettledError, type AnonymizeInput, type AppAccess, type AppCategory, type AppID, type AppId, type AppResponse, AppScopedClient, AppScopedDataClient, type AppSlug, type AppTable, type AppTemplate, AppUnavailableError, AppsClient, AuditClient, type AuditEvent, type AuditEventID, type AuthProvider, type AuthRing, AuthorizationPendingError, AuthzClient, type AuthzGrant, type AuthzSubject, type AuthzTag, AxHubClient, type AxHubClientOptions, AxHubError, type AxHubErrorInit, BadRequestError, type Branded, type BulkInviteResult, type CatalogAncestor, type CatalogConnector, type CatalogKind, type CatalogKindAction, type CatalogPermissionsReadDetail, type CatalogPermissionsReadList, type CatalogResourceDetail, type CatalogResourceFilter, type CatalogResourceView, type CatalogTag, type ColumnType, type Comment, ConfigurationError, ConflictError, type ConnectGitInput, type ConnectorID, type CreateAppInput, type CreateCategoryInput, type CreateDeploymentInput, type CreateOAuthClientInput, type CreateTableInput, type CreateTenantInput, type CursorDirection, DEFAULT_BASE_URL, type DataBulkResult, DataClient, type DataCountOptions, type DataGetOptions, type DataListOptions, type DataOrderBy, DataTableClient, type DataTableSchema, type DecideInput, type DecideResult, DecodeError, type DeploymentFailureReason, type DeploymentID, type DeploymentId, type DeploymentResponse, type DeploymentStatus, DeploymentsClient, type DeviceAuthorizationResponse, DeviceFlowDeniedError, DeviceFlowTimeoutError, type DiscoverAppsOptions, type DiscoverOptions, type DispatchContext, DomainTakenError, DuplicateError, type EmailDomain, type EmitAuditEventInput, EmptyError, type EnvVar, ExpiredTokenError, type FetchLike, type FieldError, ForbiddenError, GatewayCatalogClient, GatewayClient, type GatewayConnector, type GatewayEngine, type GatewayQueryColumn, type GatewayQueryInput, type GatewayQueryResult, type GatewayResource, type GitConnection, type GitConnectionSetup, type GitConnectionStatus, type GithubInstallStart, type GrantID, type GrantPrincipalType, type GrantScope, IdentityClient, IdentityDeviceCodeClient, IdentityMeClient, IdentityOAuthClient, IdentityOIDCClient, IdentityPATClient, type IdentityProvider, IdentityProviderClient, IdentitySystemOAuthClientsClient, type InferRow, type InstallStartInput, type IntegrityCheckResult, InternalServerError, IntrospectFailedError, InvalidCursorError, InvalidGrantError, InvalidPathError, InvalidStateTransitionError, InvalidValueError, InvitationExpiredError, type InviteTenantMemberInput, type InvokeInput, type InvokeResult, type IssuePersonalAccessTokenInput, type IssuePersonalAccessTokenResult, type KeysetCursor, LastAdminError, LegacyCursorError, type LikeResult, type LikeStatus, type ListAllItem, type ListAllOptions, type ListOptions, type Logger, type MeResponse, type MockClientOptions, type MockFixtures, MockInProductionError, type MockRow, type MockSchemas, MockStore, NetworkError, NoAuth, NotAdminError, NotAllowedError, NotDeletedError, NotFoundError, NotMemberError, type OAuthClient, type OAuthClientWithSecret, OAuthError, type OAuthErrorInit, type OAuthTokenResponse, type OrderByField, type PATID, type PATSummary, type PaginatedList, type ParsedFrame, type PatId, PendingExistsError, PermanentlyDeletedError, PermissionDeniedError, PoolStaleError, PreconditionFailedError, type PublicationRequest, type PublicationRequestStatus, PublicationRequestsClient, type QueryExpr, type RateLimitStrategy, RateLimitedError, type RequestId, RequiredError, type ResourceID, type RetryInfo, type SSEStream, ScanLimitExceededError, SchemaCache, type SchemaCacheOptions, SchemaNameTakenError, type SchemaShapeFromRow, type SelectColumns, type SettlePublicationInput, type SignIconUploadInput, type SignIconUploadResult, SlowDownError, SlugTakenError, StaticTokenAuth, StreamConsumedError, type StreamItem, type SubjectID, type SubmitPublicationInput, type SystemOAuthClient, type TableColumn, type TableConstraint, type TableGrant, type TableID, type TableIndex, TableNotFoundError, type TableSchema, type TagID, type Tenant, TenantGatewayClient, type TenantID, type TenantId, type TenantInvitation, type TenantMember, TenantScopedAppsClient, TenantScopedClient, type TenantSlug, TenantSlugRequiredError, TenantsClient, TimeoutError, TokenExpiredError, TokenInvalidError, TokenMissingError, type TokenType, UnauthenticatedError, UnavailableError, type UnlikeResult, type UnpublishInput, type UpdateAppInput, type UpdateGitConnectionInput, type UpdateTenantInput, type UserID, type UserId, ValidationError, type VerifyWebhookInput, type VerifyWebhookResult, WebhookVerificationError, type WebhookVerifyReason, and, asAppId, asAppSlug, asDeploymentId, asPatId, asRequestId, asTenantId, asTenantSlug, asUserId, assertMockModeAllowed, createMockStore, cursorFromRow, decodeCursor, defineSchema, dispatch, encodeCursor, escapeLike, formatErrorMessage, getAccessibleColumns, getMaskHint, id, isAllowed, isOAuthPath, isPolicyDeny, isSqlFormatError, not, or, orderByFingerprint, parseRetryAfter, raw, schemaCacheKey, signWebhook, tableFromPath, verifyWebhook, where };
3020
+ export { AbortError, AccessDeniedError, type AddColumnInput, type AddCommentInput, type AddGrantInput, AlreadyAccessedError, AlreadyActiveError, AlreadyDeletedError, AlreadyInactiveError, AlreadyMemberError, AlreadyRevokedError, AlreadySettledError, type AppAccess, type AppAvailability, type AppCategory, type AppID, type AppId, type AppInvitation, type AppMember, type AppPublicationStatus, type AppResponse, type AppResponseCategory, type AppResponseOwner, type AppReviewStatus, AppScopedClient, AppScopedDataClient, type AppSlug, type AppSort, type AppStatus, type AppTable, type AppTemplate, AppUnavailableError, type AppVisibility, AppsClient, type AuditEventID, type AuthProvider, type AuthRing, AuthorizationPendingError, AxHubClient, type AxHubClientOptions, AxHubError, type AxHubErrorInit, BadRequestError, type BootstrapAccepted, type BootstrapStage, type BootstrapStatus, type BootstrapStatusValue, type Branded, type BrowseRowsOptions, type CatalogAncestor, type CatalogConnector, type CatalogKind, type CatalogKindAction, type CatalogPermissionsReadDetail, type CatalogPermissionsReadList, type CatalogResourceDetail, type CatalogResourceFilter, type CatalogResourceView, type CatalogTag, type CheckAvailabilityInput, type ColumnType, type ColumnTypeOption, type Comment, ConfigClient, ConfigurationError, ConflictError, type ConnectGitInput, type ConnectorID, type CreateAppInput, type CreateDeploymentInput, type CreateOAuthClientInput, type CreateTableInput, type CursorBuildOptions, type CursorDirection, DEFAULT_BASE_URL, type DataBulkResult, DataClient, type DataCountOptions, type DataGetOptions, type DataListOptions, type DataOrderBy, DataTableClient, type DataTableSchema, DecodeError, type DeploymentFailureReason, type DeploymentID, type DeploymentId, type DeploymentResponse, type DeploymentStatus, DeploymentsClient, type DeviceAuthorizationResponse, DeviceFlowDeniedError, DeviceFlowTimeoutError, type DiscoverAppsOptions, type DiscoverFeedOptions, type DiscoverOptions, DomainTakenError, DuplicateError, EmptyError, type EngineCapability, type EnvVar, ExpiredTokenError, type FetchLike, type FetchLogsOptions, type FieldAvailability, type FieldError, ForbiddenError, GatewayCatalogClient, GatewayClient, type GatewayQueryColumn, type GatewayQueryInput, type GatewayQueryResult, type GitConnection, type GitConnectionSetup, type GitConnectionStatus, type GithubAccount, type GithubInstallStart, type GithubRepo, type GithubReposPage, type GrantID, type GrantPrincipalType, type GrantScope, type IconPreCreateInput, type IconPreCreateResult, IdentityClient, IdentityDeviceCodeClient, IdentityMeClient, IdentityOAuthClient, IdentityOIDCClient, IdentityPATClient, type IdentityProvider, IdentitySystemOAuthClientsClient, type InferRow, type InstallStartInput, InternalServerError, IntrospectFailedError, InvalidClientError, InvalidCursorError, InvalidGrantError, InvalidPathError, InvalidRequestError, InvalidScopeError, InvalidStateTransitionError, InvalidTargetError, InvalidTokenError, InvalidValueError, InvitationExpiredError, type InviteUserInput, type InvokeInput, type InvokeResult, type IssuePersonalAccessTokenInput, type IssuePersonalAccessTokenResult, type KeysetCursor, LastAdminError, LegacyCursorError, type LikeResult, type LikeStatus, type ListAllItem, type ListAllOptions, type ListAppsOptions, type ListMembersOptions, type ListOptions, type ListReposOptions, type LogLine, type Logger, type LogsPage, type MeResponse, type MockClientOptions, type MockFixtures, MockInProductionError, type MockRow, type MockSchemas, MockStore, NetworkError, NoAuth, NotAdminError, NotAllowedError, NotDeletedError, NotFoundError, NotMemberError, type OAuthClient, type OAuthClientWithSecret, OAuthError, type OAuthErrorInit, OAuthServerError, type OAuthTokenResponse, type OrderByField, type OrderDirection, type PATID, type PATSummary, type PageRequestOptions, type PaginatedList, type ParsedFrame, type PatId, PendingExistsError, PermanentlyDeletedError, PermissionDeniedError, PoolStaleError, PreconditionFailedError, type PublicConfig, type PublicationRequest, type PublicationRequestStatus, PublicationRequestsClient, type QueryExpr, type RateLimitStrategy, RateLimitedError, type RegisterMcpClientInput, type RegisterMcpClientResult, type RequestId, type RequestOptions, RequiredError, type ResourceID, type RetryInfo, type SSEStream, type SSEStreamOptions, ScanLimitExceededError, SchemaCache, type SchemaCacheOptions, SchemaNameTakenError, type SchemaShapeFromRow, type SelectColumns, type SettlePublicationInput, type SignIconUploadInput, type SignIconUploadResult, SlowDownError, SlugTakenError, type StartBootstrapInput, StaticTokenAuth, type StaticTokenAuthOptions, StreamConsumedError, type StreamItem, type SubjectID, type SubmitPublicationInput, type SystemOAuthClient, type TableAvailability, type TableColumn, type TableConstraint, type TableGrant, type TableID, type TableIndex, TableNotFoundError, type TableRowColumn, type TableRowsMeta, type TableRowsPage, type TableSchema, type TagID, TemporarilyUnavailableError, type Tenant, TenantGatewayClient, type TenantID, type TenantId, TenantIdRequiredError, TenantScopedAppsClient, TenantScopedClient, type TenantSlug, TenantSlugRequiredError, TenantsClient, TimeoutError, TokenExpiredError, TokenInvalidError, TokenMissingError, type TokenType, UnauthenticatedError, UnauthorizedClientError, UnavailableError, type UnlikeResult, type UnpublishInput, UnsupportedGrantTypeError, type UpdateAppInput, type UpdateGitConnectionInput, type UserID, type UserId, ValidationError, type VerifyWebhookInput, type VerifyWebhookResult, WebhookVerificationError, type WebhookVerifyReason, and, asAppId, asAppSlug, asDeploymentId, asPatId, asRequestId, asTenantId, asTenantSlug, asUserId, assertMockModeAllowed, createMockStore, cursorFromRow, decodeCursor, defineSchema, encodeCursor, escapeLike, formatErrorMessage, getAccessibleColumns, getMaskHint, id, isAllowed, isPolicyDeny, isSqlFormatError, not, or, orderByFingerprint, parseRetryAfter, raw, schemaCacheKey, signWebhook, tableFromPath, verifyWebhook, where };